diff -u --recursive --new-file v2.0.30/linux/CREDITS linux/CREDITS --- v2.0.30/linux/CREDITS Tue Apr 8 09:04:28 1997 +++ linux/CREDITS Wed Aug 13 11:26:10 1997 @@ -240,7 +240,7 @@ N: Juan Jose Ciarlante E: jjciarla@raiz.uncu.edu.ar -E: juanjo@irriga.uncu.edu.ar +E: irriga@impsat1.com.ar D: Network driver alias support D: IP masq hashing and app modules S: Las Cuevas 2385 - Bo Guemes @@ -413,7 +413,7 @@ S: Australia N: Ralf Flaxa -E: rfflaxa@immd4.informatik.uni-erlangen.de +E: rf@lst.de D: The Linux Support Team Erlangen D: Creator of LST distribution D: Author of installation tool LISA @@ -466,7 +466,7 @@ S: USA N: Philip Gladstone -E: philipg@onsett.com +E: philip@raptor.com D: Kernel / timekeeping stuff N: Michael A. Griffith @@ -862,8 +862,9 @@ N: Martin Mares E: mj@k332.feld.cvut.cz +W: http://atrey.karlin.mff.cuni.cz/~mj/ D: BIOS video mode handling code -D: Miscellaneous kernel fixes +D: Miscellaneous kernel fixes and hacks D: MOXA C-218 serial board driver D: BOOTP support S: Kankovskeho 1241 @@ -1124,10 +1125,16 @@ S: 79539 Loerrach S: Germany +N: Joerg Reuter +E: jreuter@poboxes.com +E: dl1bke@db0pra.ampr.org (amateur radio) +W: http://www.rat.de/jr +D: Z8530 SCC driver and DAMA Slave for AX.25 + N: William E. Roadcap -E: roadcapw@cfw.com +E: roadcapw@titus.org W: http://www.cfw.com/~roadcapw -D: Author of menu based configuration tool, Menuconfig. +D: Author of ncurses based configuration tool, Menuconfig. S: 1407 Broad Street S: Waynesboro, Virginia 22980 S: USA @@ -1178,14 +1185,14 @@ S: Finland N: Eric Schenk -E: schenk@cs.toronto.edu +E: Eric.Schenk@dna.lth.se D: Random kernel debugging. D: SYSV Semaphore code rewrite. D: Network layer debugging. D: Dial on demand facility (diald). -S: 7 Borden Street -S: Toronto, Ontario -S: Canada M5S 2M8 +S: Dag Hammerskjolds v. 3E +S: S-226 64 LUND +S: Sweden N: Peter De Schrijver E: stud11@cc4.kuleuven.ac.be @@ -1524,8 +1531,9 @@ S: Finland N: Roger E. Wolff -E: wolff@dutecai.et.tudelft.nl +E: R.E.Wolff@BitWizard.nl D: Written kmalloc/kfree +D: Written Specialix IO8+ driver S: Oosterstraat 23 S: 2611 TT Delft S: The Netherlands diff -u --recursive --new-file v2.0.30/linux/Documentation/Changes linux/Documentation/Changes --- v2.0.30/linux/Documentation/Changes Tue Nov 12 22:23:13 1996 +++ linux/Documentation/Changes Tue Aug 12 11:17:41 1997 @@ -295,7 +295,7 @@ currently at release 2.5. Some may find, especially when using the loop or xiafs file system, NFS, or automounting, that they need to upgrade to the latest release of mount, available from -ftp://ftp.win.tue.nl/pub/linux/util/mount-2.5p.tar.gz. +ftp://ftp.win.tue.nl/pub/linux/util/mount-2.6g.tar.gz. Console ======= @@ -679,12 +679,14 @@ SysVinit utilities ================== -ftp://sunsite.unc.edu/pub/Linux/system/Daemons/init/sysvinit-2.64.tar.gz +ftp://sunsite.unc.edu/pub/Linux/system/daemons/init/sysvinit-2.64.tar.gz +or for the very latest: +ftp://ftp.debian.org/debian/unstable/source/base/sysvinit_2.71-1.tar.gz Util-linux ========== -ftp://sunsite.unc.edu/pub/Linux/system/Misc/util-linux-2.5.tar.gz +ftp://sunsite.unc.edu/pub/Linux/system/misc/util-linux-2.6.tar.gz Mtools ====== diff -u --recursive --new-file v2.0.30/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.0.30/linux/Documentation/Configure.help Tue Apr 8 08:47:45 1997 +++ linux/Documentation/Configure.help Thu Aug 14 10:31:20 1997 @@ -124,18 +124,18 @@ nothing to do with the loopback device used for network connections from the machine to itself. Most users will answer N here. -Enhanced IDE/MFM/RLL disk/cdrom/tape support +Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support CONFIG_BLK_DEV_IDE - This will use the full-featured IDE driver to control up to four IDE - interfaces, for a combination of up to eight IDE disk/cdrom/tape - drives. Useful information about large (>540MB) IDE disks, - soundcard 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 memory in the kernel. To - fine-tune IDE drive/interface parameters for improved performance, - look for the hdparm package at + 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 + contained in Documentation/ide.txt. If you have one or more IDE + drives, say Y here. If your system has no IDE drives, or if memory + requirements are really tight, you could say N here, and select + the Old harddisk driver instead to save about 13kB of memory in + the 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 @@ -190,9 +190,32 @@ ATAPI is a new protocol used by IDE TAPE and ATAPI drives, similar to the SCSI protocol. At boot time, the TAPE drive will be identified along with other IDE devices, as "hdb" or "hdc", - or something similar. Be sure to consult the drivers/block/ide-tape.c + or something similar, and will be mapped to a character device + such as "ht0". Be sure to consult the drivers/block/ide-tape.c and Documentation/ide.txt files for usage information. +Include IDE/ATAPI FLOPPY support (new) +CONFIG_BLK_DEV_IDEFLOPPY + If you have an IDE floppy which uses the ATAPI protocol, say Y. + ATAPI is a new protocol used by IDE cdrom/tape/floppy drives, + similar to the SCSI protocol. IDE floppy drives include the + LS-120 and the ATAPI ZIP (ATAPI PD-CD drives are not supported + by this driver; support for PD-CD drives is available through + the SCSI emulation). At boot time, the FLOPPY drive will be + identified along with other IDE devices, as "hdb" or "hdc", or + something similar. + +SCSI emulation support +CONFIG_BLK_DEV_IDESCSI + This will provide SCSI host adapter emulation for IDE ATAPI devices, + and will allow you to use a SCSI device driver instead of a native + ATAPI driver. This is useful if you have an ATAPI device for which + no native driver has been written (for example, an ATAPI PD-CD + drive); you can then use this emulation together with an appropriate + SCSI device driver. If both this SCSI emulation and native ATAPI + support are compiled into the kernel, the native support will be + used. Normally, say N. + Support removable IDE interfaces (PCMCIA) CONFIG_BLK_DEV_IDE_PCMCIA This option adds code to the IDE driver to handle hot insertion @@ -449,7 +472,10 @@ the same end. SYN cookies use less space than RST cookies, but have a small probability of introducing an non timed-out failure to connect in the remote TCP. You can use both options - simultatenously. + simultatenously. 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. SYN flood protection CONFIG_RST_COOKIES @@ -463,7 +489,10 @@ The SYN_COOKIES option provides an alternative method to accomplish the same end. RST cookies use more space than SYN cookies on your machine, but never increase the probability of a frozen connection - in a remote TCP. You can use both options simultatenously. + in a remote TCP. You can use both options simultatenously. 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 @@ -589,10 +618,11 @@ 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) - and includes the Intel 430FX PCI Triton chipset, you will want to - enable this option to allow use of bus-mastering DMA data transfers. - Read the comments at the beginning of drivers/block/triton.c. Check - the file Documentation/Changes for location and latest version of + 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 + beginning of drivers/block/triton.c and Documentation/ide.txt. + Check the file Documentation/Changes for location and latest version of the hdparm utility. It is safe to say Y to this question. System V IPC @@ -947,12 +977,14 @@ If you want this, say Y. IP: ipautofw masquerade support -CONFIG_IP_MASQUERADE_IPAUTOFW +CONFIG_IP_MASQUERADE_IPAUTOFW (Experimental) ipautofw is a program by Richard Lynch allowing additional support for masquerading protocols which do not (as yet) have additional protocol helpers. Information and source for ipautofw is available from ftp://ftp.netis.com/pub/members/rlynch/ + The ipautofw code is still under development and so is currently + marked EXPERIMENTAL. If you want this, say Y. IP: ICMP masquerading @@ -1528,24 +1560,41 @@ 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. - This driver has been tested OK with linux/i386 and is currently - untested under linux/Alpha. If you intend to use this driver under - linux/Alpha, just try it first with read-only or mounted read-only - devices. Memory mapped io is currently not supported under - linux/Alpha. Please read drivers/scsi/README.ncr53c8xx for more - information. + 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. + Linux/i386 and Linux/Alpha are supported by this driver. + +synchronous data transfers frequency +CONFIG_SCSI_NCR53C8XX_SYNC + 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 + with wide16 devices. This frequency can be used safely with + differential devices but may cause problems with singled-ended + devices. + Specify 0 if you want to only use asynchronous data transfers. + 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 + 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: + echo "setsync #target period" >/proc/scsi/ncr53c8xx/0 + Use a 25 ns period for 10 Mhz synchronous data transfers. + If you don't know what to do now, go with the default. -force normal IO +use normal IO CONFIG_SCSI_NCR53C8XX_IOMAPPED - Under linux/Alpha only normal io is currently supported. - Under linux/i386, this option allows you to force the driver to use - normal IO. Memory mapped IO has less latency than normal IO. - During the initialization phase, the driver first tries to use - memory mapped io. If nothing seems wrong, it will use memory mapped - io. If a flaw is detected, it will use normal io. However, it's - possible that memory mapped does not work properly for you and the - driver has not detected the problem; then you would want to say Y - here. The normal answer therefore is N. + This option allows you to force the driver to use normal IO. + Memory mapped IO has less latency than normal IO and works for most + Intel-based hardware. + Under Linux/Alpha only normal IO is currently supported by the driver + and so, this option has no effect. + The normal answer therefore is N. not allow targets to disconnect CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT @@ -1570,36 +1619,51 @@ The safe answer therefore is N. The normal answer therefore is Y. -force asynchronous transfer mode -CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS - This option allows you to force asynchronous transfer mode for all - devices at linux startup. You can enable synchronous negotiation - with the "setsync" control command after boot-up, for example: - echo "setsync 2 25" >/proc/scsi/ncr53c8xx/0 - asks the driver to set the period to 25 ns (10MB/sec) for target 2 - of controller 0 (please read drivers/scsi/README.ncr53c8xx for more - information). The safe answer therefore is Y. The normal answer - therefore is N. - -force synchronous negotiation -CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO - Some scsi-2 devices support synchronous negotiations but do not - report this feature in byte 7 of inquiry data. - Answer Y only if you suspect some device to be so humble. - The normal answer therefore is N. - -disable master parity checking -CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK - Some hardware may have problems with parity during master cycles on - PCI bus. Only seen once. Answer Y if you suspect such problem. The - normal answer therefore is N. - -disable scsi parity checking -CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK - Parity on scsi bus is a system option. If one device checks parity, - then all devices on the scsi bus must generate parity. However, the - parity can be ignored by the scsi devices. Answer Y only if you - know what you are doing. The normal answer therefore is N. +maximum number of queued commands +CONFIG_SCSI_NCR53C8XX_MAX_TAGS + This option allows you to specify the maximum number of commands + that can be queued to a device, when tagged command queuing is + possible. The default value is 4. Minimum is 2, maximum is 12. The + normal answer therefore is the default one. + +detect and read serial NVRAM +CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT + Enable support for reading the serial NVRAM data on Symbios and + some Symbios compatible cards, and Tekram DC390W/U/F cards. Useful for + systems with more than one Symbios compatible controller where at least + one has a serial NVRAM, or for a system with a mixture of Symbios and + Tekram cards. Enables setting the boot order of host adaptors + to something other than the default order or "reverse probe" order. + Also enables Symbios and Tekram cards to be distinguished so + CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT may be set in a system with a + mixture of Symbios and Tekram cards so the Symbios cards can make use of + the full range of Symbios features, differential, led pin, without + causing problems for the Tekram card(s). + (added by Richard Waltham: dormouse@farsrobt.demon.co.uk) + Also enables setting host and targets SCSI features as defined in the + user setup for each host using a serial NVRAM (added by the maintainer). + The default answer is N, the normal answer should be Y. + Read drivers/scsi/README.ncr53c8xx for more information. + +assume boards are SYMBIOS compatible +CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT + This option allows you to enable some features depending on GPIO + wiring. These General Purpose Input/Output pins can be used for + vendor specific features or implementation of the standard SYMBIOS + features. Genuine SYMBIOS boards use GPIO0 in output for controller + LED and GPIO3 bit as a flag indicating singled-ended/differential + interface. + If all the boards of your system are genuine SYMBIOS boards or use + BIOS and drivers from SYMBIOS, you would want to enable this option. + 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 + 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 to + distinguish Symbios compatible boards from other ones. + So, you can answer Y if all non Symbios compatible boards have NVRAM. Always IN2000 SCSI support CONFIG_SCSI_IN2000 @@ -1716,6 +1780,13 @@ and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. +Tekram DC390(T) (AMD PCscsi) SCSI support +CONFIG_SCSI_DC390T + This driver supports the Tekram DC390(T) PCI SCSI Hostadapter with + the Am53C974A chip, and perhaps other cards using the same chip. + This driver does _not_ support the DC390W/U/F adaptor with the + NCR/Symbios chips. + AM53/79C974 PCI SCSI support CONFIG_SCSI_AM53C974 This is support for the AM53/79C974 SCSI host adapters. Please read @@ -1723,6 +1794,15 @@ available via ftp (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO, is for you. +GDT SCSI Disk Array Controller support +CONFIG_SCSI_GDTH + This is a driver for all SCSI Disk Array Controllers (EISA/ISA/PCI) + manufactured by ICP vortex. It is documented in the kernel source in + 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. + IOMEGA Parallel Port ZIP drive SCSI support CONFIG_SCSI_PPA This driver supports the parallel port version of IOMEGA's ZIP drive @@ -2096,6 +2176,20 @@ mixing an SMC Ultra and an Adaptec AHA1542 SCSI card causes corruption problems with many operating systems. +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 + 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 + sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. + SMC 9194 Support CONFIG_SMC9194 This is support for the SMC9xxx based Ethernet cards. Choose this @@ -2348,6 +2442,12 @@ FMV-184 and it is not working, you may need to disable Plug & Play mode of the card. +Intel EtherExpress/Pro 100B support' +CONFIG_EEXPRESS_PRO100B + If you have an Intel EtherExpress Pro 100 10/100Mbps PCI Ethernet + card, answer yes. As of kernel release 2.0.31 this driver was + still experimental. + EtherExpressPro support CONFIG_EEXPRESS_PRO If you have a network (ethernet) card of this type, say Y and read @@ -2417,19 +2517,18 @@ AT&T WaveLAN & DEC RoamAbout DS support CONFIG_WAVELAN - These are cards for wireless ethernet-like networking. Supported are - AT&T GIS and NCR WaveLAN cards. If you want to use a card of this - type under Linux, say Y and read the Ethernet-HOWTO, available via - ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Some - more specific information is contained in - drivers/net/README.wavelan. 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 - sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. + 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. + 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 + (wavelan.o). It implements many nice feature and the Wireless + Extensions (you must get the Wireless tools from the net). + For documentation, refer to : + o the wavelan man page, wireless tools man pages + o wavelan.p.h and the source code + o Ethernet-HOWTO, Multiple-Ethernet-mini-HOWTO, Module-HOWTO + o More documentation to come when I will have the time :-) HP PCLAN+ (27247B and 27252A) support CONFIG_HPLAN_PLUS @@ -3805,8 +3904,8 @@ (mgetty+sendfax by gert@greenie.muc.de with an extension, available with the ISDN utility package for example), you will be able to use your Linux box as an ISDN-answering machine. Of course, this - must be supported by the lowlevel driver also. Currently, the Teles - driver is the only voice-supporting one. See + must be supported by the lowlevel driver also. Currently HiSax + driver is the only voice-supporting drivers. See Documentation/isdn/README.audio for more information. ICN 2B and 4B support @@ -3819,14 +3918,89 @@ separately. See Documentation/isdn/README and README.icn for more information. -Teles, NICCY1016PC, Creatix support -CONFIG_ISDN_DRV_TELES - This enables support for the Teles ISDN-cards S0-16.0, S0-16.3, S0-8 - and many compatibles. By default, the driver is configured to - support a 16.0-type using EDSS1-protocol. See - Documentation/isdn/README on how to configure it using 16.3, a - different D-channel protocol, or non-standard irq/port/shmem - settings. +HiSax SiemensChipSet driver support +CONFIG_ISDN_DRV_HISAX + This driver replaces the old Teles driver. It supports the Siemens + 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. + See Documentation/isdn/README.HiSax for further informations on + using this driver. + +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. + +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. + +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. + +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. + +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. + +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. PCBIT-D support CONFIG_ISDN_DRV_PCBIT @@ -3836,6 +4010,21 @@ 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 + This enables support for the Spellcaster BRI boards. This driver + currently builds in a modularized version only. + See Documentation/isdn/README.sc and http://www.spellcast.com + for more information. + +AVM-B1 with CAPI2.0 support +CONFIG_ISDN_DRV_AVMB1 + This enables support for the AVM B1 card and also adds a CAPI2.0 + interface for this card. 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.avmb1 for more information. Support for AP1000 multicomputer CONFIG_AP1000 diff -u --recursive --new-file v2.0.30/linux/Documentation/ide.txt linux/Documentation/ide.txt --- v2.0.30/linux/Documentation/ide.txt Wed Sep 25 01:12:11 1996 +++ linux/Documentation/ide.txt Mon Aug 4 11:45:55 1997 @@ -14,6 +14,7 @@ Major features of ide.c & ide-cd.c ("NEW!" marks changes since 1.2.13): +NEW! - support for IDE ATAPI *floppy* drives NEW! - support for IDE ATAPI *tape* drives, courtesy of Gadi Oxman (re-run MAKEDEV.ide to create the tape device entries in /dev/) NEW! - support for up to *four* IDE interfaces on one or more IRQs @@ -111,12 +112,12 @@ Apparently many releases of Slackware 2.2/2.3 have incorrect entries in /dev for hdc* and hdd* -- this can also be corrected by running MAKEDEV.ide -ide.c automatically probes for the primary and secondary interfaces, +ide.c automatically probes for the standard four IDE interfaces, for the drives/geometries attached to those interfaces, and for the -IRQ numbers being used by the interfaces (normally IRQ14 & IRQ15). +IRQ numbers being used by the interfaces (normally 14, 15, 11 and 10). -Interfaces beyond the first two are not normally probed for, but may be -specified using kernel "command line" options. For example, +For special cases, interfaces may be specified using kernel "command line" +options. For example, ide3=0x168,0x36e,10 /* ioports 0x168-0x16f,0x36e, irq 10 */ diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/00-INDEX linux/Documentation/isdn/00-INDEX --- v2.0.30/linux/Documentation/isdn/00-INDEX Thu Jun 6 04:57:43 1996 +++ linux/Documentation/isdn/00-INDEX Mon Aug 4 17:33:58 1997 @@ -10,12 +10,13 @@ - info for running audio over ISDN. README.icn - info on the ICN-ISDN-card and its driver. +README.HiSax + - info on the HiSax driver which replaces the old teles. README.pcbit - info on the PCBIT-D ISDN adapter and driver. README.syncppp - info on running Sync PPP over ISDN. -README.teles - - info on driver for Teles compatible ISDN cards. syncPPP.FAQ - frequently asked questions about running PPP over ISDN. - +README.avmb1 + - info on driver for AVM-B1 ISDN card diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/CREDITS linux/Documentation/isdn/CREDITS --- v2.0.30/linux/Documentation/isdn/CREDITS Sun Apr 21 01:56:13 1996 +++ linux/Documentation/isdn/CREDITS Mon Aug 4 17:33:58 1997 @@ -12,11 +12,18 @@ For contribution of man-pages, the imontty-tool and a perfect maintaining of the mailing-list at hub-wue. +Bernhard Hailer (Bernhard.Hailer@lrz.uni-muenchen.de) + For maintaining the FAQ. + +Michael 'Ghandi' Herold (michael@abadonna.franken.de) + For contribution of the vbox answering machine. + Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) For his Sync-PPP-code. Karsten Keil (isdn4@temic-ech.spacenet.de) For adding 1TR6-support to the Teles-driver. + For the HiSax-driver. Michael Knigge (knick@cove.han.de) For contributing the imon-tool @@ -39,7 +46,9 @@ Gerhard 'Fido' Schneider (fido@wuff.franken.de) For heavy-duty-beta-testing with his BBS ;) -Thomas Uhl (uhl@hn-net.de) +Thomas Uhl (uhl@think.de) For distributing the cards. For pushing me to work ;-) +Carsten Paeth (calle@calle.in-berlin.de) + For the AVM-B1-CAPI2.0 driver diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/INTERFACE linux/Documentation/isdn/INTERFACE --- v2.0.30/linux/Documentation/isdn/INTERFACE Tue Nov 12 22:36:19 1996 +++ linux/Documentation/isdn/INTERFACE Mon Aug 4 17:33:58 1997 @@ -1,4 +1,4 @@ -$Id: INTERFACE,v 1.5 1996/11/06 17:40:47 keil Exp $ +$Id: INTERFACE,v 1.6 1997/02/10 22:40:57 fritz Exp $ Description of the Interface between Linklevel and Hardwarelevel of isdn4linux: @@ -140,7 +140,7 @@ int (*writebuf)(int, int, u_char*, int, int); - ***CHANGEc1.14: Declared obsolete. Do NOT use this field/function + ***CHANGED1.14: Declared obsolete. Do NOT use this field/function anymore, since it will be removed when all current LL drivers have been changed accordingly. Set this field to NULL and use writebuf_skb instead. @@ -199,7 +199,7 @@ int driver-Id. int channel-number locally to the HL-driver. (starts with 0) -***CHANGEc1.14: The driver-Id and channel-number are new since this revision. +***CHANGED1.14: The driver-Id and channel-number are new since this revision. Returnvalue: Length of data accepted on success, else error-code (-EINVAL etc.) @@ -223,7 +223,7 @@ int driver-Id. int channel-number locally to the HL-driver. (starts with 0) -***CHANGEc1.14: The driver-Id and channel-number are new since this revision. +***CHANGED1.14: The driver-Id and channel-number are new since this revision. Returnvalue: Length of data on success, else error-code (-EINVAL etc.) @@ -249,6 +249,9 @@ Until now, the following commands are defined: +***CHANGED1.34: The parameter "num" has been replaced by a union "para" containing + the old "num" and a new setup_type struct used for ISDN_CMD_DIAL + and ISDN_STAT_ICALL callback. ISDN_CMD_IOCTL: @@ -262,10 +265,10 @@ called with the field command set to 1. Parameter: - driver = driver-Id. - command = ISDN_CMD_IOCTL - arg = Original ioctl-cmd - IIOCDRVCTL - num = first bytes filled with (unsigned long)arg + driver = driver-Id. + command = ISDN_CMD_IOCTL + arg = Original ioctl-cmd - IIOCDRVCTL + para.num = first bytes filled with (unsigned long)arg Returnvalue: Depending on driver. @@ -280,12 +283,14 @@ driver = driver-Id. command = ISDN_CMD_DIAL arg = channel-number locally to the driver. (starting with 0) - num = An ASCII-String containing the number to dial, the own - EAZ or MSN, the Service-Indicator and the Additional - Info. Format: - "%s,%s,%d,%d" RemotePhoneNumber,EazOrMsn,SI,AI + + para.setup.phone = An ASCII-String containing the number to dial. + para.setup.eazmsn = An ASCII-Sting containing the own EAZ or MSN. + para.setup.si1 = The Service-Indicator. + para.setup.si2 = Additional Service-Indicator. + If the Line has been designed as SPV (a special german - feature, meaning semi-leased-line) the number has to + feature, meaning semi-leased-line) the phone has to start with an "S". ***CHANGE0.6: In previous versions the EAZ has been given in the highbyte of arg. @@ -300,7 +305,7 @@ driver = driver-Id. command = ISDN_CMD_ACCEPTD arg = channel-number locally to the driver. (starting with 0) - num = unused. + para = unused. ISDN_CMD_ACCEPTB: @@ -311,7 +316,7 @@ driver = driver-Id. command = ISDN_CMD_ACCEPTB arg = channel-number locally to the driver. (starting with 0) - num = unused. + para = unused. ISDN_CMD_HANGUP: @@ -323,7 +328,7 @@ driver = driver-Id. command = ISDN_CMD_HANGUP arg = channel-number locally to the driver. (starting with 0) - num = unused. + para = unused. ISDN_CMD_CLREAZ: @@ -334,7 +339,7 @@ driver = driver-Id. command = ISDN_CMD_CLREAZ arg = channel-number locally to the driver. (starting with 0) - num = unused. + para = unused. ISDN_CMD_SETEAZ: @@ -345,7 +350,7 @@ driver = driver-Id. command = ISDN_CMD_SETEAZ arg = channel-number locally to the driver. (starting with 0) - num = ASCII-String, containing the desired EAZ's/MSN's + para.num = ASCII-String, containing the desired EAZ's/MSN's (comma-separated). If an empty String is given, the HL-driver should respond to ALL incoming calls, regardless of the destination-address. @@ -360,7 +365,7 @@ driver = driver-Id. command = ISDN_CMD_GETEAZ arg = channel-number locally to the driver. (starting with 0) - num = ASCII-String, containing the current EAZ's/MSN's + para.num = ASCII-String, containing the current EAZ's/MSN's ISDN_CMD_SETSIL: (currently unused) @@ -371,7 +376,7 @@ driver = driver-Id. command = ISDN_CMD_SETSIL arg = channel-number locally to the driver. (starting with 0) - num = ASCII-String, containing the desired Service-Indicators. + para.num = ASCII-String, containing the desired Service-Indicators. ISDN_CMD_GETSIL: (currently unused) @@ -382,7 +387,7 @@ driver = driver-Id. command = ISDN_CMD_SETSIL arg = channel-number locally to the driver. (starting with 0) - num = ASCII-String, containing the current Service-Indicators. + para.num = ASCII-String, containing the current Service-Indicators. ISDN_CMD_SETL2: @@ -397,7 +402,7 @@ arg = channel-number locally to the driver. (starting with 0) logical or'ed with (protocol-Id << 8) protocol-Id is one of the constants ISDN_PROTO_L2... - num = unused. + para = unused. ISDN_CMD_GETL2: (currently unused) @@ -408,7 +413,7 @@ driver = driver-Id. command = ISDN_CMD_GETL2 arg = channel-number locally to the driver. (starting with 0) - num = unused. + para = unused. Returnvalue: current protocol-Id (one of the constants ISDN_L2_PROTO) @@ -425,7 +430,7 @@ arg = channel-number locally to the driver. (starting with 0) logical or'ed with (protocol-Id << 8) protocol-Id is one of the constants ISDN_PROTO_L3... - num = unused. + para = unused. ISDN_CMD_GETL2: (currently unused) @@ -436,7 +441,7 @@ driver = driver-Id. command = ISDN_CMD_GETL3 arg = channel-number locally to the driver. (starting with 0) - num = unused. + para = unused. Returnvalue: current protocol-Id (one of the constants ISDN_L3_PROTO) @@ -450,7 +455,7 @@ driver = driver-Id. command = ISDN_CMD_LOCK arg = unused. - num = unused. + para = unused. ISDN_CMD_UNLOCK: @@ -462,7 +467,7 @@ driver = driver-Id. command = ISDN_CMD_UNLOCK arg = unused. - num = unused. + para = unused. 3. Description of the events to be signaled by the HL-driver to th LL. @@ -484,19 +489,23 @@ driver = driver-Id command = ISDN_STAT_STAVAIL arg = length of available data. - num = unused. + para = unused. ISDN_STAT_ICALL: With this call, the HL-driver signals an incoming call to the LL. Parameter: - driver = driver-Id - command = ISDN_STAT_ICALL - arg = channel-number, locally to the driver. (starting with 0) - num = ASCII-String in the following format: - "%s,%d,%d,%s",CallerNumber,ServiceIndicator,AddInfo, - CalledNumber. + driver = driver-Id + command = ISDN_STAT_ICALL + arg = channel-number, locally to the driver. (starting with 0) + para.setup.phone = Callernumber. + para.setup.eazmsn = CalledNumber. + para.setup.si1 = Service Indicator. + para.setup.si2 = Additional Service Indicator. + para.setup.plan = octet 3 from Calling party number Information Element. + para.setup.screen = octet 3a from Calling party number Information Element. + Return: 0 = No device matching this call. 1 = At least one device matching this call (RING on ttyI). @@ -513,7 +522,7 @@ driver = driver-Id command = ISDN_STAT_RUN arg = unused. - num = unused. + para = unused. ISDN_STAT_STOP: @@ -524,7 +533,7 @@ driver = driver-Id command = ISDN_STAT_STOP arg = unused. - num = unused. + para = unused. ISDN_STAT_DCONN: @@ -535,7 +544,7 @@ driver = driver-Id command = ISDN_STAT_DCONN arg = channel-number, locally to the driver. (starting with 0) - num = unused. + para = unused. ISDN_STAT_BCONN: @@ -547,7 +556,7 @@ driver = driver-Id command = ISDN_STAT_BCONN arg = channel-number, locally to the driver. (starting with 0) - num = unused. + para = unused. ISDN_STAT_DHUP: @@ -560,7 +569,7 @@ driver = driver-Id command = ISDN_STAT_DHUP arg = channel-number, locally to the driver. (starting with 0) - num = unused. + para = unused. ISDN_STAT_BHUP: @@ -572,7 +581,7 @@ driver = driver-Id command = ISDN_STAT_BHUP arg = channel-number, locally to the driver. (starting with 0) - num = unused. + para = unused. ISDN_STAT_CINF: @@ -583,7 +592,7 @@ driver = driver-Id command = ISDN_STAT_CINF arg = channel-number, locally to the driver. (starting with 0) - num = ASCII string containing charge-units (digits only). + para.num = ASCII string containing charge-units (digits only). ISDN_STAT_LOAD: (currently unused) @@ -596,7 +605,7 @@ driver = driver-Id command = ISDN_STAT_UNLOAD arg = unused. - num = unused. + para = unused. ISDN_STAT_BSENT: @@ -608,7 +617,7 @@ driver = driver-Id command = ISDN_STAT_BSENT arg = channel-number, locally to the driver. (starting with 0) - num = unused. + para = unused. ISDN_STAT_NODCH: @@ -619,7 +628,7 @@ driver = driver-Id command = ISDN_STAT_NODCH arg = channel-number, locally to the driver. (starting with 0) - num = unused. + para = unused. ISDN_STAT_ADDCH: (currently unused) @@ -633,7 +642,7 @@ driver = driver-Id command = ISDN_STAT_ADDCH arg = to be defined. - num = to be defined. + para = to be defined. ISDN_STAT_CAUSE: @@ -646,5 +655,5 @@ driver = driver-Id command = ISDN_STAT_NODCH arg = channel-number, locally to the driver. (starting with 0) - num = ASCII string containing CAUSE-message. + para.num = ASCII string containing CAUSE-message. diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/README linux/Documentation/isdn/README --- v2.0.30/linux/Documentation/isdn/README Fri Jun 7 06:02:39 1996 +++ linux/Documentation/isdn/README Mon Aug 4 17:33:59 1997 @@ -27,7 +27,14 @@ subscribe isdn4linux To write to the mailing-list, write to isdn4linux@hub-wue.franken.de + + This mailinglist is bidirectionally gated to the newsgroup + + de.alt.comm.isdn4linux + There is also a well maintained FAQ (both english and german) available + at ftp.franken.de in /pub/isdn4linux/FAQ/ + This FAQ is also available at http://www.lrz-muenchen.de/~ui161ab/www/isdn/ 1.1 Technical details @@ -53,7 +60,7 @@ A raw-control-device with the following functions: write: raw D-channel-messages (format: depends on driver). read: raw D-channel-messages (format: depends on driver). - ioctl: depends on driver, for the ICN-driver, the base-address of + 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 the card. @@ -89,6 +96,7 @@ ATI Return "ISDN for Linux...". ATI0 " ATI1 " + ATI2 Report of last connection. ATO On line (data mode). ATQ0 Enable result codes (default). ATQ1 Disable result codes (default). @@ -99,7 +107,7 @@ 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 Teles-Module- + low-level-driver used. i.e.: the HiSax-Module- limit is 2000. You will get NO Error-Message, if you set it to higher Values, because at the time of giving this command the corresponding @@ -112,8 +120,7 @@ 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 for Teles-driver (x = 1..8) (not yet - implemented) + 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). @@ -173,6 +180,10 @@ 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 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 @@ -182,7 +193,7 @@ 15 0 Layer-3 protocol: (at the moment always 0) 0 = transparent 16 250 Send-Packet-size/16 - 17 8 Window-size for Teles-driver (not yet implemented) + 17 8 Window-size (not yet implemented) 18 4 Bit coded register, Service-Octet-1 to accept, or to be used on dialout: Bit 0: Service 1 (audio) when set. @@ -201,6 +212,14 @@ 20 0 Bit coded register (readonly) Service-Octet-1 of last call. Bit mapping is the same like register 18 + 21 0 Bit coded register (readonly) + Set on incoming call (during RING) to + octet 3 of calling party number IE (Numbering plan) + See section 4.5.10 of ITU Q.931 + 22 0 Bit coded register (readonly) + Set on incoming call (during RING) to + octet 3a of calling party number IE (Screening info) + See section 4.5.10 of ITU Q.931 Last but not least a (at the moment fairly primitive) device to request the line-status (/dev/isdninfo) is made available. @@ -243,124 +262,16 @@ 2 System prerequisites: - ATTENTION! The program "insmod" from the Package "modules-1.2.8" (It's - on nearly all newer distributions) has a bug, which makes - it impossible to set both driver-Id's when loading the - icn-module for the Double-ICN-Card. A patch is supplied - in the utility-package called "insmod-1.2.8.patch". Change into - the source-directory of insmod, and type - "patch < insmod-1.2.8.patch". Then recompile it. This will fix - the bug. - This bug does NOT occur when using insmod with the Teles-driver - or a single ICN-card. + ATTENTION! + + Always use the latest module utilities. The current version is + named in Documentation/Changes. Some old versions of insmod + are not capable of setting the driver-Ids correctly. 3. Lowlevel-driver configuration. - Configuration depends on how the drivers are built. - - 3.1 Drivers built into the kernel. - - 3.1.1 Teles driver. - - The Teles driver can be configured using the commandline-feature - while loading the kernel with LILO or LOADLIN. It accepts the - following syntax: - - teles=p0,i0,m0,d0[,p1,i1,m1,d1 ... ,pn,in,mn,dn][,idstring] - - where - - p0 = portbase of 1st card. (default: 0xd80) - i0 = irq of 1st card. (default: 15) - m0 = shared memory of 1st card. (default: 0xd0000) - d0 = D-channel protocol of 1st card. 1=1TR6, 2=EDSS1 (default: 2) - - p1,i1,m1,d1 = Parameters of second card (defaults: none) - pn,in,mn,d1 = Parameters of n'th card (up to 16 cards are supported) - - idstring = Driver-Id for accessing with utilities and identification - when using a Line-monitor. (default: none) - idstring must start with a character! - - The type of the card is determined by the port, irq and shared memory: - - port == 0, shared memory != 0 -> Teles S0-8 - port != 0, shared memory != 0 -> Teles S0-16.0 - port != 0, shared memory == 0 -> Teles S0-16.3 - - ATTENTION: - - Due to limited hardware-capabilities, there is no way to check the - existence of a card. Therefore you need to be sure your card's setup - is correct. Also there are bugs in the printed manual of some newer - 16.3 cards. Have a look to the kernel-syslog. With most of the cards, - you should see a line "HSCX version A:5 B:5" there. - - 3.1.2 ICN driver. - - The ICN driver can be configured using the commandline-feature while - loading the kernel with LILO or LOADLIN. It accepts the following - syntax - - icn=p,m[,idstring1[,idstring2]] - - where - - p = portbase (default: 0x320) - m = shared memory (default: 0xd0000) - - When using the ICN double card, you MUST define TWO idstrings. - idstring must start with a character! - - If you like to use more than one card, you can use the program - "icnctrl" from the utility-package to configure additional cards. - You need to configure shared memory only once, since the icn-driver - maps all cards into the same address-space. - - Using the "icnctrl"-utility, portbase and shared memory can also be - changed during runtime. - - The D-channel protocol is configured by loading different firmware - into the card's memory using the "icnctrl"-utility. - - - 3.2 Drivers built as modules. - - 3.2.1 Teles driver. - - The module teles.o can be configured during "insmod'ing" it by - appending its parameters to the insmod-commandline. The following - syntax is accepted: - - io=m0,i0,p0,d0[,m1,i1,p1,d1 ... ,mn,in,pn,dn] teles_id=idstring - - where - - m0,i0,p0,d0 ... mn,in,pn,dn have the same meanings like the - parameters described for the kernel- - version above. Watch out: different - sequence! - - 3.2.2 ICN driver. - - The module icn.o can be configured during "insmod'ing" it by - appending its parameters to the insmod-commandline. The following - syntax is accepted: - - portbase=p membase=m icn_id=idstring icn_id2=idstring2 - - where p, m, idstring1 and idstring2 have the same meanings like - parameters described for the kernel- - version above. - - When using the ICN double card, you MUST define TWO idstrings. - idstring must start with a character! - - Using the "icnctrl"-utility, the same features apply to the modularized - version like to the kernel-builtin one. - - The D-channel protocol is configured by loading different firmware - into the card's memory using the "icnctrl"-utility. + Configuration depends on how the drivers are built. See the + README. for information on driver-specific setup. 4. Device-inodes @@ -371,26 +282,11 @@ 44 for the ISDN-callout-tty's. 45 for control/info/debug devices. - 5. Application - a) (Only for ICN-cards) Load the firmware into the card: - - cd icn - For 1TR6: - icnctrl [-d IDstring] load download/loadpg.bin download/pc_1t_ca.bin - For Euro-ISDN: - icnctrl [-d IDstring] load download/loadpg.bin download/pc_eu_ca.bin - - When using the ICN-4B, the protocol-software for the second half of - the card must be appended to the command line. - - -> The two LEDs at the back cover of the card (ICN-4B: 4 LEDs) must be - blinking intermittently now. If a connection is up, the corresponding - led is lit continuously. - - For loading pcbit-firmware, refer to Documentation/isdn/README.pcbit - and the pcbit manpage, included in the utility-package. + a) For some card-types, firmware has to be loaded into the cards, before + proceeding with device-independant setup. See README. + for how to do that. b) If you only intend to use ttys, you are nearly ready now. @@ -422,8 +318,7 @@ h) additionally you may activate charge-hang-up (= Hang up before next charge-info, this only works, if your isdn-provider transmits - the charge-info during and after the connection, it does NOT work - with the Teles on an EDSS1-Line.): + 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. @@ -527,7 +422,7 @@ "isdnctrl l2_prot " Selects a layer-2-protocol. - (With the ICN-driver and the Teles-driver, "x75i" and "hdlc" is available. + (With the ICN-driver and the HiSax-driver, "x75i" and "hdlc" is available. With other drivers, "x75ui", "x75bui" may be possible too.) isdnctrl l3_prot diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/README.HiSax linux/Documentation/isdn/README.HiSax --- v2.0.30/linux/Documentation/isdn/README.HiSax Wed Dec 31 16:00:00 1969 +++ linux/Documentation/isdn/README.HiSax Mon Aug 4 17:33:59 1997 @@ -0,0 +1,323 @@ +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 +written by Fritz Elfert. + + 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. + + +Supported cards +--------------- + +Teles 8.0/16.0/16.3 and compatible ones +Teles S0/PCMCIA +Creatix PnP S0 +AVM A1 (Fritz) +ELSA Microlink PCC-16, PCF, PCF-Pro, PCC-8 +ELSA Quickstep 1000 +ELSA PCMCIA +ITK ix1-micro Rev.2 + +Note: PCF, PCF-Pro: up to now, only the ISDN part is supported + PCC-8: not tested yet + Teles PCMCIA is EXPERIMENTAL + +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. + + +Configuring the driver +---------------------- + +The HiSax driver can either be built directly into the kernel or as a module. +It can be configured using the command line feature while loading the kernel +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 +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 +only with the HiSax driver used as a module. + +a) when built as a module +------------------------- + +insmod/modprobe hisax.o \ + io=iobase irq=IRQ mem=membase type=card_type \ + protocol=D_channel_protocol id=idstring + +or, if several cards are installed: + +insmod/modprobe hisax.o \ + io=iobase1,iobase2,... irq=IRQ1,IRQ2,... mem=membase1,membase2,... \ + type=card_type1,card_type2,... \ + protocol=D_channel_protocol1,D_channel_protocol2,... \ + id=idstring1%idstring2 ... + +where "iobaseN" represents the I/O base address of the Nth card, "membaseN" +the memory base address of the Nth card, etc. + +The reason for the delimiter "%" being used in the idstrings is that "," +won't work with the current modules package. + +The parameters may be specified in any order. For example, the "io" +parameter may precede the "irq" parameter, or vice versa. If several +cards are installed, the ordering within the comma separated parameter +lists must of course be consistent. + +Only parameters applicable to the card type need to be specified. For +example, the Teles 16.3 card is not memory-mapped, so the "mem" +parameter may be omitted for this card. Sometimes it may be necessary +to specify a dummy parameter, however. This is the case when there is +a card of a different type later in the list that needs a parameter +which the preceding card does not. For instance, if a Teles 16.0 card +is listed after a Teles 16.3 card, a dummy memory base parameter of 0 +must be specified for the 16.3. Instead of a dummy value, the parameter +can also be skipped by simply omitting the value. For example: +mem=,0xd0000. See example 6 below. + +The parameter for the D-Channel protocol may be omitted if you selected the +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 I/O addresses of the ISAC and HSCX chips, respectively. + +Card types: + + Type Required parameters (in addition to type and protocol) + + 1 Teles 16.0 irq, mem, io + 2 Teles 8.0 irq, mem + 3 Teles 16.3 (non PnP) irq, io + 4 Creatix/Teles PnP irq, io0 (ISAC), io1 (HSCX) + 5 AVM A1 (Fritz) irq, io + 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) + 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. + + +Examples for module loading + +1. Teles 16.3, Euro ISDN, I/O base 280 hex, IRQ 10 + modprobe hisax type=3 protocol=2 io=0x280 irq=10 + +2. Teles 16.0, 1TR6 ISDN, I/O base d80 hex, IRQ 5, Memory d0000 hex + modprobe hisax protocol=1 type=1 io=0xd80 mem=0xd0000 irq=5 + +3. Fritzcard, Euro ISDN, I/O base 340 hex, IRQ 10 and ELSA PCF, Euro ISDN + modprobe hisax type=5,6 protocol=2,2 io=0x340 irq=10 id=Fritz%Elsa + +4. Any ELSA PCC/PCF card, Euro ISDN + modprobe hisax type=6 protocol=2 + +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)) + modprobe hisax type=4 protocol=2 irq=10 io0=0x580 io1=0x180 + +6. Teles 16.3, Euro ISDN, I/O base 280 hex, IRQ 12 and + Teles 16.0, 1TR6, IRQ 5, Memory d0000 hex + modprobe hisax type=3,1 protocol=2,1 io=0x280 mem=0,0xd0000 + + Please note the dummy 0 memory address for the Teles 16.3, used as a + placeholder as described above, in the last example. + +7. Teles PCMCIA, Euro ISDN, I/O base 180 hex, IRQ 15 (default values) + modprobe hisax type=8 protocol=2 io=0x180 irq=15 + + +b) using LILO/LOADLIN, with the driver compiled directly into the kernel +------------------------------------------------------------------------ + +hisax=typ1,dp1,pa_1,pb_1,pc_1[,typ2,dp2,pa_2 ... \ + typn,dpn,pa_n,pb_n,pc_n][,idstring1[,idstring2,...,idstringn]] + +where + typ1 = type of 1st card (default depends on kernel settings) + dp1 = D-Channel protocol of 1st card. 1=1TR6, 2=EDSS1, 3=leased + pa_1 = 1st parameter (depending on the type of the card) + pb_1 = 2nd parameter ( " " " " " " " ) + pc_1 = 3rd parameter ( " " " " " " " ) + + typ2,dp2,pa_2,pb_2,pc_2 = Parameters of the second card (defaults: none) + typn,dpn,pa_n,pb_n,pc_n = Parameters of the n'th card (up to 16 cards are + supported) + + idstring = Driver ID for accessing the particular card with utility + programs and for identification when using a line monitor + (default: "HiSax") + + 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 + 4 Creatix/Teles PNP ONLY WORKS AS A MODULE ! + 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 + 9 ITK ix1-micro Rev.2 pa=irq pb=iobase + + +Running the driver +------------------ + +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: 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) +Apr 13 21:01:59 kke01 kernel: HiSax: Elsa driver Rev. 1.13 +... +Apr 13 21:01:59 kke01 kernel: Elsa: PCF-Pro found at 0x360 Rev.:C IRQ 10 +Apr 13 21:01:59 kke01 kernel: Elsa: timer OK; resetting card +Apr 13 21:01:59 kke01 kernel: Elsa: HSCX version A: V2.1 B: V2.1 +Apr 13 21:01:59 kke01 kernel: Elsa: ISAC 2086/2186 V1.1 +... +Apr 13 21:01:59 kke01 kernel: HiSax: DSS1 Rev. 1.14 +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. + +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 the moment, debugging messages are enabled with the telesctrl tool: + + telesctrl DebugCmd + + default is HiSax, if you didn't specified one. + +DebugCmd is 1 for generic debugging + 11 for layer 1 development debugging + 13 for layer 3 development debugging + +where is the integer sum of the following debugging +options you wish enabled: + +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) + +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 + +With DebugCmd set to 13: + + 1 Warnings (default: on) + 2 l3 protocol discriptor errors + 4 l3 state machine + 8 charge info debugging (1TR6) + +For example, 'telesctrl HiSax 1 0x3ff' enables full generic debugging. + + +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. + + +Limitations +----------- +At this time, HiSax only works on Euro ISDN lines and German 1TR6 lines. + + +Bugs +---- +If you find any, please let me know. + + +Thanks +------ +Special thanks to: + + Emil Stephan for the name HiSax which is a mix of HSCX and ISAC. + + Fritz Elfert, Jan den Ouden, Michael Hipp, Michael Wein, + Andreas Kool, Pekka Sarnila, Sim Yskes, Johan Myrre'en, + Klaus-Peter Nischke (ITK AG), Christof Petig, Werner Fehn (ELSA GmbH), + Volker Schmidt + and more people who are hunting bugs. (If I forgot somebody, please + send me a mail). + + Firma ELSA GmbH + + My girl friend and partner in life Ute for her patience with me. + + +Enjoy, + +Karsten Keil +keil@temic-ech.spacenet.de + + +Appendix: Teles PCMCIA driver +----------------------------- + +See + http://www.stud.uni-wuppertal.de/~ea0141/pcmcia.html +for instructions. diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/README.audio linux/Documentation/isdn/README.audio --- v2.0.30/linux/Documentation/isdn/README.audio Fri Jun 7 06:02:39 1996 +++ linux/Documentation/isdn/README.audio Mon Aug 4 17:33:59 1997 @@ -1,4 +1,4 @@ -$Id: README.audio,v 1.3 1996/06/05 02:19:36 fritz Exp $ +$Id: README.audio,v 1.5 1997/02/23 23:53:46 fritz Exp $ ISDN subsystem for Linux. Description of audio mode. @@ -12,7 +12,7 @@ AT+FCLASS=8 Enable audio mode. This affects the following registers: - S18: Bits 0 and 3 are set. + S18: Bits 0 and 2 are set. S16: Set to 48 and any further change to larger values is blocked. AT+FCLASS=0 Disable audio mode. @@ -86,8 +86,11 @@ starts sending audio data to the application. There are several escape sequences defined, all using DLE (0x10) as Escape char: - End of audio data. Emulator stops + 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. Escape sequence for DLE in data stream. 0 Touchtone "0" received. ... @@ -107,13 +110,16 @@ s silence. Silence detected from the start of recording. - Any character sent by the application, except XON (0x11) or XOFF (0x13) - immediately stops recording. - Audio playback. When sending audio data, upon AT+VTX command, emulator responds with CONNECT, and starts transferring data from application to the phone line. The same DLE sequences apply to this mode. + Full-Duplex-Audio: + + When _both_ commands for recording and playback are given in _one_ + AT-command-line (i.e.: "AT+VTX+VRX"), full-duplex-mode is selected. + In this mode, the only way to stop recording is sending + and the only way to stop playback is to send . diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/README.avmb1 linux/Documentation/isdn/README.avmb1 --- v2.0.30/linux/Documentation/isdn/README.avmb1 Wed Dec 31 16:00:00 1969 +++ linux/Documentation/isdn/README.avmb1 Mon Aug 4 17:33:59 1997 @@ -0,0 +1,62 @@ +The driver provides a kernel capi2.0 Interface (kernelcapi) and +on top of this a User-Level-CAPI2.0-interface (capi) +and a driver to connect isdn4linux with CAPI2.0 (capidrv). + +The Author can be reached at calle@calle.in-berlin.de +The command avmcapictrl is part of the isdn4linux-utils. +t4-files can be found at ftp.avm.de. + +Installing +---------- + +You need at least /dev/capi20 to load the firmware. + +mknod /dev/capi20 c 68 0 +mknod /dev/capi20.00 c 68 1 +mknod /dev/capi20.01 c 68 2 +. +. +. +mknod /dev/capi20.19 c 68 20 + +Running +------- + +To use the card you need the t4-files to download the firmware. +AVM GmbH provides several t4-files for the different D-channel +protocols (b1.t4 for Euro-ISDN). Install these file in /lib/isdn. + +If you not compile the driver as modules, you have to add the +card(s) and load them after booting: + +avmcapictrl add 0x150 15 +avmcapictrl load /lib/isdn/b1.t4 1 + +if you configure as modules you have two possibilities: + +insmod /lib/modules/current/misc/capiutil.o +insmod /lib/modules/current/misc/kernelcapi.o portbase=0x150 irq=15 +insmod /lib/modules/current/misc/capidrv.o +insmod /lib/modules/current/misc/capi.o +avmcapictrl load /lib/isdn/b1.t4 + +or + +insmod /lib/modules/current/misc/capiutil.o +insmod /lib/modules/current/misc/kernelcapi.o +insmod /lib/modules/current/misc/capidrv.o +insmod /lib/modules/current/misc/capi.o +avmcapictrl add 0x150 15 +avmcapictrl load /lib/isdn/b1.t4 + +Questions +--------- +Check out the FAQ (ftp.franken.de). + +Bugs +---- +If you find any please let me know. + +Enjoy, + +Carsten Paeth (calle@calle.in-berlin.de) diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/README.icn linux/Documentation/isdn/README.icn --- v2.0.30/linux/Documentation/isdn/README.icn Fri Jun 7 06:02:39 1996 +++ linux/Documentation/isdn/README.icn Mon Aug 4 17:33:59 1997 @@ -1,9 +1,9 @@ -$Id: README.icn,v 1.4 1996/06/03 19:57:07 fritz Exp $ +$Id: README.icn,v 1.5 1997/04/23 18:55:55 fritz Exp $ You can get the ICN-ISDN-card from: Thinking Objects Software GmbH -Obere Heerbergstr. 17 +Versbacher Röthe 159 97078 Würzburg Tel: +49 931 2877950 Fax: +49 931 2877951 @@ -61,4 +61,88 @@ 1 1 0 1 0x358 1 1 1 0 0x368 1 1 1 1 NOT ALLOWED! + +The ICN driver either may be build into kernel or as a module. Initialization +depends on how the drive is built: + +Driver built into the kernel: + + The ICN driver can be configured using the commandline-feature while + loading the kernel with LILO or LOADLIN. It accepts the following syntax: + + icn=p,m[,idstring1[,idstring2]] + + where + + p = portbase (default: 0x320) + m = shared memory (default: 0xd0000) + + When using the ICN double card (4B), you MUST define TWO idstrings. + idstring must start with a character! There is no way for the driver + to distinguish between a 2B and 4B type card. Therefore, by supplying + TWO idstrings, you tell the driver that you have a 4B installed. + + If you like to use more than one card, you can use the program + "icnctrl" from the utility-package to configure additional cards. + You need to configure shared memory only once, since the icn-driver + maps all cards into the same address-space. + + Using the "icnctrl"-utility, portbase and shared memory can also be + changed during runtime. + + The D-channel protocol is configured by loading different firmware + into the card's memory using the "icnctrl"-utility. + + +Driver built as module: + + The module icn.o can be configured during "insmod'ing" it by + appending its parameters to the insmod-commandline. The following + syntax is accepted: + + portbase=p membase=m icn_id=idstring [icn_id2=idstring2] + + where p, m, idstring1 and idstring2 have the same meanings like + parameters described for the kernel-version above. + + When using the ICN double card (4B), you MUST define TWO idstrings. + idstring must start with a character! There is no way for the driver + to distinguish between a 2B and 4B type card. Therefore, by supplying + TWO idstrings, you tell the driver that you have a 4B installed. + + Using the "icnctrl"-utility, the same features apply to the modularized + version like to the kernel-builtin one. + + The D-channel protocol is configured by loading different firmware + into the card's memory using the "icnctrl"-utility. + +Loading the firmware into the card: + + The firmware is supplied together with the isdn4k-utils package. It + can be found in the subdirectory icnctrl/firmware/ + + There are 3 files: + + loadpg.bin - Image of the bootstrap loader. + pc_1t_ca.bin - Image of firmware for german 1TR6 protocol. + pc_eu_ca.bin - Image if firmware for EDSS1 (Euro-ISDN) protocol. + + Assumed you have installed the utility-package correctly, the firmware + will be downloaded into the 2B-card using the following command: + + icnctrl -d Idstring load /etc/isdn/loadpg.bin /etc/isdn/pc_XX_ca.bin + + where XX is either "1t" or "eu", depending of the D-Channel protocol + used on your S0-bus and Idstring is the Name of the card, given during + insmod-time or (for kernel-builtin driver) on the kernel commandline. + + To load a 4B-card, the same command is used, except a second firmware + file is appended to the commandline of icnctrl. + + -> After dowloading firmware, the two LEDs at the back cover of the card + (ICN-4B: 4 LEDs) must be blinking intermittently now. If a connection + is up, the corresponding led is lit continuously. + + For further documentation (adding more ICN-cards), refer to the manpage + icnctrl.8 which is included in the isdn4k-utils package. diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/README.sc linux/Documentation/isdn/README.sc --- v2.0.30/linux/Documentation/isdn/README.sc Wed Dec 31 16:00:00 1969 +++ linux/Documentation/isdn/README.sc Mon Aug 4 17:33:59 1997 @@ -0,0 +1,274 @@ +Welcome to Beta Release 2 of the combination ISDN driver for SpellCaster's +ISA ISDN adapters. Please note this release 2 includes support for the +DataCommute/BRI and TeleCommute/BRI adapters only and any other use is +guaranteed to fail. If you have a DataCommute/PRI installed in the test +computer, we recommend removing it as it will be detected but will not +be usable. To see what we have done to Beta Release 2, see section 3. + +Speaking of guarantees, THIS IS BETA SOFTWARE and as such contains +bugs and defects either known or unknown. Use this software at your own +risk. There is NO SUPPORT for this software. Some help may be available +through the web site or the mailing list but such support is totally at +our own option and without warrantee. If you choose to assume all and +total risk by using this driver, we encourage you to join the beta +mailing list. + +To join the Linux beta mailing list, send a message to: +majordomo@spellcast.com with the words "subscribe linux-beta" as the only +contents of the message. Do not include a signature. If you choose to +remove yourself from this list at a later date, send another message to +the same address with the words "unsubscribe linux-beta" as it's only +contents. + +TABLE OF CONTENTS +----------------- + 1. Introduction + 1.1 What is ISDN4Linux? + 1.2 What is different between this driver and previous drivers? + 1.3 How do I setup my system with the correct software to use + this driver release? + + 2. Basic Operations + 2.1 Unpacking and installing the driver + 2.2 Read the man pages!!! + 2.3 Installing the driver + 2.4 Removing the driver + 2.5 What to do if it doesn't load + 2.6 How to setup ISDN4Linux with the driver + + 3. Beta Change Summaries and Miscellaneous Notes + +1. Introduction +--------------- + +The revision 2 Linux driver for SpellCaster ISA ISDN adapters is built +upon ISDN4Linux available seperately or as included in Linux 2.0 and later. +The driver will support a maximum of 4 adapters in any one system of any +type including DataCommute/BRI, DataCommute/PRI and TeleCommute/BRI for a +maximum of 92 channels for host. The driver is supplied as a module in +source form and needs to be complied before it can be used. It has been +tested on Linux 2.0.20. + +1.1 What Is ISDN4Linux + +ISDN4Linux is a driver and set of tools used to access and use ISDN devices +on a Linux platform in a common and standard way. It supports HDLC and PPP +protocols and offers channel bundling and MLPPP support. To use ISDN4Linux +you need to configure your kernel for ISDN support and get the ISDN4Linux +tool kit from our web site. + +ISDN4Linux creates a channel pool from all of the available ISDN channels +and therefore can function across adapters. When an ISDN4Linux compliant +driver (such as ours) is loaded, all of the channels go into a pool and +are used on a first-come first-served basis. In addition, individual +channels can be specifically bound to particular interfaces. + +1.2 What is different between this driver and previous drivers? + +The revision 2 driver besides adopting the ISDN4Linux architecture has many +subtle and not so subtle functional differences from previous releases. These +include: + - More efficient shared memory management combined with a simpler + configuration. All adapters now use only 16Kbytes of shared RAM + versus between 16K and 64K. New methods for using the shared RAM + allow us to utilize all of the available RAM on the adapter through + only one 16K page. + - Better detection of available upper memory. The probing routines + have been improved to better detect avaialble shared RAM pages and + used pages are now locked. + - Decreased loading time and a wider range of I/O ports probed. + We have significantly reduced the amount of time it takes to load + the driver and at the same time doubled the number of I/O ports + probed increasing the likelyhood of finding an adapter. + - We now support all ISA adapter models with a single driver instead + of seperate drivers for each model. The revision 2 driver supports + the DataCommute/BRI, DataCommute/PRI and TeleCommute/BRI in any + combination up to a maximum of four adapters per system. + - On board PPP protocol support has been removed in favour of the + sync-PPP support used in ISDN4Linux. This means more control of + the protocol parameters, faster negotiation time and a more + familiar interface. + +1.3 How do I setup my system with the correct software to use + this driver release? + +Before you can compile, install and use the SpellCaster ISA ISDN driver, you +must ensure that the following software is installed, configuraed and running: + + - Linux kernel 2.0.20 or later with the required init and ps + versions. Please see your distribution vendor for the correct + utility packages. The latest kernel is available from + ftp://sunsite.unc.edu/pub/Linux/kernel/v2.0/ + + - The latest modules package (modules-2.0.0.tar.gz) from + ftp://sunsite.unc.edu/pub/Linux/kernel/modules-2.0.0.tar.gz + + - The ISDN4Linux tools available from + ftp://ftp.franken.de/pub/isdn4linux/v2.0/isdn4k-utils-2.0.tar.gz + This package may fail to compile for you so you can alternatively + get a pre-compiled version from + ftp://ftp.spellcast.com/pub/drivers/isdn4linux/isdn4k-bin-2.0.tar.gz + + +2. Basic Operations +------------------- + +2.1 Unpacking and installing the driver + + 1. As root, create a directory in a convienient place. We suggest + /usr/src/spellcaster. + + 2. Unpack the archive with : + tar xzf sc-n.nn.tar.gz -C /usr/src/spellcaster + + 3. Change directory to /usr/src/spellcaster + + 4. Read the README and RELNOTES files. + + 5. Run 'make' and if all goes well, run 'make install'. + +2.2 Read the man pages!!! + +Make sure you read the scctrl(8) and sc(4) manual pages before continuing +any further. Type 'man 8 scctrl' and 'man 4 sc'. + +2.3 Installing the driver + +To install the driver, type '/sbin/insmod sc' as root. sc(4) details options +you can specify but you shouldn't need to use any unless this doesn't work. + +Make sure the driver loaded and detected all of the adapters by typing +'dmesg'. + +The driver can be configured so that it is loaded upon startup. To do this, +edit the file "/etc/modules/'uname -f'/'uname -v'" and insert the driver name +"sc" into this file. + +2.4 Removing the driver + +To remove the driver, delete any interfaces that may exist (see isdnctrl(8) +for more on this) and then type '/sbin/rmmod sc'. + +2.5 What to do if it doesn't load + +If, when you try to install the driver, you get a message mentioning +'register_isdn' then you do not have the ISDN4Linux system installed. Please +make sure that ISDN support is configured in the kernel. + +If you get a message that says 'initialization of sc failed', then the +driver failed to detect an adapter or failed to find resources needed such +as a free IRQ line or shared memory segment. If you are sure there are free +resources available, use the insmod options detailed in sc(4) to override +the probing function. + +Upon testing, the following problem was noted, the driver would load without +problems, but the board would not respond beyond that point. When a check was +done with 'cat /proc/interrupts' the interrupt count for sc was 0. In the event +of this problem, change the BIOS settings so that the interrupts in question are +reserved for ISA use only. + + +2.6 How to setup ISDN4Linux with the driver + +There are two main configurations which you can use with the driver: + +A) Basic HDLC connection +B) PPP connection +C) MLPPP connection + +It should be mentioned here that you may also use a tty connection if you desire. +The Documentation directory of the isdn4linux subsystem offers a good documentation +on this feature. + +A) 10 steps to the establishment of a basic HDLC connection +----------------------------------------------------------- + +- please open the isdn-hdlc file in the examples directory and follow along... + + This file is a script used to configure a BRI ISDN TA to establish a basic HDLC + connection between its two channels. There two network interfaces which are + created and two routes added between the channels. + + i) using the isdnctrl utitity, add an interface with "addif" and name it "isdn0" + ii) add the outgoing and inbound telephone numbers + iii) set the Layer 2 protocol to hdlc + iv) set the eaz of the interface to be the phone number of that specific channel + v) to turn the callback features off, set the callback to "off" and + the callback delay (cbdelay) to 0. + vi) the hangup timeout can be set to a specified number of seconds + vii) the hangup upon incomming call can be set on or off + viii) use the ifconfig command to bring-up the network interface with a specific + IP address and point to point address + viv) add a route to the IP address through the isdn0 interface + x) a ping should result in the establishment of the connection + + +B) Establishment of a PPP connection +------------------------------------ + +- please open the isdn-ppp file in the examples directory and follow along... + + This file is a script used to configure a BRI ISDN TA to establish a PPP connection + between the two channels. The file is almost identical to the HDLC connection + example except that the packet ecapsulation type has to be set. + + use the same procedure as in the HDLC connection from steps i) to iii) then, + after the Layer 2 protocol is set, set the encapsulation "encap" to syncppp. + With this done, the rest of the steps, iv) to x) can be followed from above. + + Then, the ipppd (ippp daemon) must be setup: + + xi) use the ipppd function found in /sbin/ipppd to set the following: + xii) take out (minus) VJ compression and bsd compression + xiii) set the mru size to 2000 + xiv) link the two /dev interfaces to the daemon + +NOTE: A "*" in the inbound telephone number specifies that a call can be accepted + on any number. + +C) Establishment of a MLPPP connection +-------------------------------------- + +- please open the isdn-mppp file in the examples directory and follow along... + + This file is a script used to configure a BRI ISDN TA to accept a Multi Link PPP + connection. + + i) using the isdnctrl utitity, add an interface with "addif" and name it "ippp0" + ii) add the inbound telephone number + iii) set the Layer 2 protocol to hdlc and the Layer 3 protocol to trans (transparent) + iv) set the packet encapsulation to syncppp + v) set the eaz of the interface to be the phone number of that specific channel + vi) to turn the callback features off, set the callback to "off" and + the callback delay (cbdelay) to 0. + vi) the hangup timeout can be set to a specified number of seconds + vii) the hangup upon incomming call can be set on or off + viii) add a slave interface and name it "ippp32" for example + viv) set the similar parameters for the ippp32 interface + x) use the ifconfig command to bring-up the ippp0 interface with a specific + IP address and point to point address + xi) add a route to the IP address through the ippp0 interface + xii) use the ipppd function found in /sbin/ipppd to set the following: + xiii) take out (minus) bsd compression + xiv) set the mru size to 2000 + xv) add (+) the multi-link function "+mp" + xv) link the two /dev interfaces to the daemon + +NOTE: To use the MLPPP connection to dial OUT to a MLPPP connection, change the + inbound telephone numbers to the outgoing telephone numbers of the MLPPP + host. + + +3. Beta Change Summaries and Miscellaneous Notes +------------------------------------------------ +When using the "scctrl" utility to upload firmware revisions on the board, please +note that the byte count displayed at the end of the operation may be different +than the total number of bytes in the "dcbfwn.nn.sr" file. Please disregard the +displayed byte count. + +It was noted that in Beta Release 1, the module would fail to load and result in a +segmentation fault when insmod"ed". This problem was created when one of the +isdn4linux parameters, (isdn_ctrl, data field) was filled in. In some cases, this +data field was NULL, and was left unchecked, so when it was referenced.. segv. +The bug has been fixed around line 63-68 of event.c. + diff -u --recursive --new-file v2.0.30/linux/Documentation/isdn/README.teles linux/Documentation/isdn/README.teles --- v2.0.30/linux/Documentation/isdn/README.teles Sat Jun 29 10:36:22 1996 +++ linux/Documentation/isdn/README.teles Wed Dec 31 16:00:00 1969 @@ -1,73 +0,0 @@ -This is my Linux hardware level driver for Teles compatible ISDN cards. It is -meant to be used with isdn4isdn4linux, an ISDN Link-level module for Linux written -by Fritz Elfert. - -Isdn4linux can be obtained from ftp.franken.de:/pub/isdn4linux. The most recent -Teles driver can be found on my homepage, http://www.xs4all.nl:/~jdo. - -Warning -------- -Teles4isdn4linux 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. - -Limitations ------------ -Teles4isdn4linux only works on Euro ISDN lines and german 1TR6-lines. - -For the B channel transparent (HDLC) protocol and X.75 have been implemented. - -Running -------- -When you insmod isdn.o and teles.o (or with the kernel-version, during boottime) -a few lines should appear in your syslog. Look for something like: - -Oct 11 16:53:30 jedi kernel: channels 2 -Oct 11 16:53:31 jedi kernel: Teles module installed - -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 teles_id= to the insmod-commandline. 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. Debugging messages are enabled with the telesctrl tool: - - teles/telesctrl 1 - -where is the integer sum of the following debugging -options you wish enabled: - - 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 Lowlevel (irq and Layer1 stuff) - -For example 'teles/telesctrl MyTeles 1 63' enables full -debugging. - -Questions ---------- -Check out the FAQ (ftp.franken.de). - -Bugs ----- -If you find any please let me know. - -Thanks ------- -Special thanks to: - - Erik Bos,Beat Doebeli,Fritz Elfert, - Pauline Middelink,Paula de Nie, - Bernd Oerding,Stephan Seidl,Matthias Urlichs, - Rogier Wolff - - - -Enjoy, - -Jan den Ouden denouden@groovin.xs4all.nl - diff -u --recursive --new-file v2.0.30/linux/Documentation/locks.txt linux/Documentation/locks.txt --- v2.0.30/linux/Documentation/locks.txt Wed May 15 01:22:04 1996 +++ linux/Documentation/locks.txt Sun Aug 3 13:59:07 1997 @@ -2,37 +2,30 @@ Andy Walker - 15 May 1996 + 12 May 1997 -What's New? ------------ +1. What's New? +-------------- -Flock Emulation Warnings ------------------------- -Many people will have noticed the ugly messages that the file locking -code started generating with the release of kernel version 1.3.95. The -messages look something like this: +1.1 Broken Flock Emulation +-------------------------- - fcntl_setlk() called by process XX with broken flock() emulation +The old flock(2) emulation in the kernel was swapped for proper BSD +compatible flock(2) support in the 1.3.x series of kernels. With the +release of the 2.1.x kernel series, support for the old emulation has +been totally removed, so that we don't need to carry this baggage +forever. -This is a warning for people using older C libraries that those libraries -are still calling the pre 1.3.x flock() emulation routines, instead of -the real flock() system call. The old routines are quite badly broken, -especially with respect to parent-child lock sharing, and can give bad -results if, for example, sendmail attempts to use them. +This should not cause problems for anybody, since everybody using a +2.1.x kernel should have updated their C library to a suitable version +anyway (see the file "linux/Documentation/Changes".) -Fixed versions of the C libraries have been on public release for many -months. The latest versions are 5.2.18 or 5.3.12 for ELF, and I believe -somebody made a 4.7.6 release for people using a.out systems. +1.2 Allow Mixed Locks Again +--------------------------- -In 1.3.96 Linus decided to be lenient on the stragglers and changed the -warning message so that the kernel will only complain five times and -then shut up. That should make life more bearable even for people who, -for some reason, don't want to upgrade. - -Sendmail Problems ------------------ +1.2.1 Typical Problems - Sendmail +--------------------------------- Because sendmail was unable to use the old flock() emulation, many sendmail installations use fcntl() instead of flock(). This is true of Slackware 3.0 for example. This gave rise to some other subtle problems if sendmail was @@ -42,23 +35,50 @@ over time, or under a very heavy mail load, would eventually cause the kernel to lock solid with deadlocked processes. -Disallow Mixed Locks --------------------- -I have chosen the rather cruel solution of disallowing mixed locking styles -on a given file at a given time. Attempts to lock a file with flock() when -fcntl() locks exist, or vice versa, return with an error status of EBUSY. -This seemed to be the only way to avoid all possible deadlock conditions, -as flock() locks do not strictly have one owner process and so can't be -checked for deadlocking in the usual manner. - -The process that created a lock with flock() might have forked multiple -children and exited. Previously the parent process would have been marked -as the owner of the lock, but deadlocks could just have easily occurred in -one or more of the children, which we would not have been able to identify -and avoid. - -Some programs may break (again, groan). In particular the aforementioned -sendmail may have problems running in 'newaliases' mode. It will no longer -deadlock though. Recompile sendmail to use flock() and your troubles will -be over. +1.2.2 The Solution +------------------ +The solution I have chosen, after much experimentation and discussion, +is to make flock() and fcntl() locks oblivious to each other. Both can +exists, and neither will have any effect on the other. + +I wanted the two lock styles to be cooperative, but there were so many +race and deadlock conditions that the current solution was the only +practical one. It puts us in the same position as, for example, SunOS +4.1.x and serveral other commercial Unices. The only OS's that support +cooperative flock()/fcntl() are those that emulate flock() using +fcntl(), with all the problems that implies. + + +1.3 Mandatory Locking As A Mount Option +--------------------------------------- + +Mandatory locking, as described in 'Documentation/mandatory.txt' was prior +to this release a general configuration option that was valid for all +mounted filesystems. This had a number of inherent dangers, not the least +of which was the ability to freeze an NFS server by asking it to read a +file for which a mandatory lock existed. + +From this release of the kernel, mandatory locking can be turned on and off +on a per-filesystem basis, using the mount options 'mand' and 'nomand'. +The default is to disallow mandatory locking. The intention is that +mandatory locking only be enabled on a local filesystem as the specific need +arises. + +Until an updated version of mount(8) becomes available you may have to apply +this patch to the mount sources (based on the version distributed with Rick +Faiths util-linux-2.5 package): + +*** mount.c.orig Sat Jun 8 09:14:31 1996 +--- mount.c Sat Jun 8 09:13:02 1996 +*************** +*** 100,105 **** +--- 100,107 ---- + { "noauto", 0, MS_NOAUTO }, /* Can only be mounted explicitly */ + { "user", 0, MS_USER }, /* Allow ordinary user to mount */ + { "nouser", 1, MS_USER }, /* Forbid ordinary user to mount */ ++ { "mand", 0, MS_MANDLOCK }, /* Allow mandatory locks on this FS */ ++ { "nomand", 1, MS_MANDLOCK }, /* Forbid mandatory locks on this FS */ + /* add new options here */ + #ifdef MS_NOSUB + { "sub", 1, MS_NOSUB }, /* allow submounts */ diff -u --recursive --new-file v2.0.30/linux/Documentation/mandatory.txt linux/Documentation/mandatory.txt --- v2.0.30/linux/Documentation/mandatory.txt Tue Apr 16 00:27:10 1996 +++ linux/Documentation/mandatory.txt Sun Aug 3 13:59:07 1997 @@ -5,8 +5,8 @@ 15 April 1996 -What is mandatory locking? ---------------------------- +1. What is mandatory locking? +------------------------------ Mandatory locking is kernel enforced file locking, as opposed to the more usual cooperative file locking used to guarantee sequential access to files among @@ -44,8 +44,8 @@ borrowing the fcntl() locking scheme from System V. The mandatory locking scheme is defined by the System V Interface Definition (SVID) Version 3. -Marking a file for mandatory locking ------------------------------------- +2. Marking a file for mandatory locking +--------------------------------------- A file is marked as a candidate for mandatory by setting the group-id bit in its file mode but removing the group-execute bit. This is an otherwise @@ -58,8 +58,8 @@ refrain from clearing this bit. Similarly the kernel has been modified not to run mandatory lock candidates with setgid privileges. -Available implementations -------------------------- +3. Available implementations +---------------------------- I have considered the implementations of mandatory locking available with SunOS 4.1.x, Solaris 2.x and HP-UX 9.x. @@ -93,8 +93,8 @@ below are just as valid as any others, so long as the main points seem to agree. -Semantics ---------- +4. Semantics +------------ 1. Mandatory locks can only be applied via the fcntl()/lockf() locking interface - in other words the System V/POSIX interface. BSD style @@ -124,8 +124,8 @@ that has any mandatory locks in effect will be rejected with the error status EAGAIN. -Which system calls are affected? --------------------------------- +5. Which system calls are affected? +----------------------------------- Those which modify a file's contents, not just the inode. That gives read(), write(), readv(), writev(), open(), creat(), mmap(), truncate() and @@ -142,8 +142,8 @@ checking in my eagerness to get this code out the door. Please let me know, or better still fix the system calls yourself and submit a patch to me or Linus. -Warning! --------- +6. Warning! +----------- Not even root can override a mandatory lock, so runaway process can wreak havoc if they lock crucial files. The way around it is to change the file diff -u --recursive --new-file v2.0.30/linux/Documentation/memory-tuning.txt linux/Documentation/memory-tuning.txt --- v2.0.30/linux/Documentation/memory-tuning.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/memory-tuning.txt Tue Aug 12 11:17:41 1997 @@ -0,0 +1,48 @@ +There are several files in /proc/sys/vm you can use to tune the +memory system with. + +You inspect them with 'cat', and set them with 'echo'. For example, +/proc/sys/vm/freepages: + +'# cat /proc/sys/vm/freepages' may yield: +64 96 128 + +These three numbers are: min_free_pages, free_pages_low and +free_pages_high. + +You can adjust these with a command such as: + +# echo 128 256 512 > /proc/sys/vm/freepages + +Free memory never goes down below min_free_pages except for atomic +allocation. Background swapping is started if the number of free +pages falls below free_pages_high, and intensive swapping is started +below free_pages_low. A "page" is 4 kB. + +The values selected as boot defaults are the following: For a +machine with n>=8 Megabytes of memory, set min_free_pages = n*2, +free_pages_low = n*3 and free_pages_high = n*4. Machines with less +than 8 Megabytes or less as if they had 8 Megabytes. + +If "out of memory" errors sometimes occur, or if your machine does lots +of networking, increasing min_free_pages to 64 or more may be a good +idea. + +free_pages_low should probably be about double of min_free_pages. + +After a period of inactivity, the difference between free_pages_high and +free_pages low is immediately available for any program you want to +start up, without any need to swap out anything else. If your memory +is large enough (e.g. > 16 Meg), keeping 2 or 3 megabytes of memory +ready for this purpose is probably a good idea. + +I've found that + +# echo 128 256 1024 > /proc/sys/vm/freepages + +gives good performance for a 32 Meg system used as a small server and +personal workstation. + +The other three files in /proc/sys/vm are undocumented, as yet. + +Thomas Koenig, ig25@rz.uni-karlsruhe.de diff -u --recursive --new-file v2.0.30/linux/Documentation/networking/alias.txt linux/Documentation/networking/alias.txt --- v2.0.30/linux/Documentation/networking/alias.txt Wed Jan 3 10:36:23 1996 +++ linux/Documentation/networking/alias.txt Tue Aug 12 11:17:41 1997 @@ -1,9 +1,11 @@ -NET_ALIAS device aliasing v0.4x +NET_ALIAS device aliasing v0.5x =============================== The main step taken in versions 0.40+ is the implementation of a device aliasing mechanism that creates *actual* devices. This development includes NET_ALIAS (generic aliasing) plus IP_ALIAS (specific IP) support. + From version 0.50, dynamic configuration of max alias per device and + tx/rx stats for aliases added. Features -------- @@ -13,6 +15,10 @@ o hashed alias address lookup o net_alias_type objs registration/unreg., module-ables. o /proc/net/aliases & /proc/net/alias_types entries +o /proc/sys/net/core/net_alias_max entry (affects hash table size + also) +o tx/rx stats + o IP alias implementation: static or runtime module. @@ -23,10 +29,14 @@ # cat /proc/net/alias* For IP aliasing you must have IP_ALIAS support included by - static linking ('y' to 2nd question above), or runtime module - insertion ('m' to 2nd q. above): - # insmod /usr/src/linux/modules/ip_alias.o (1.3.xx) - # insmod /usr/src/ip_alias/ip_alias.o (1.2.xx) see above. + static linking ('y' to CONFIG_IP_ALIAS? question), or runtime module + insertion ('m'): + # insmod /usr/src/linux/modules/ip_alias.o (2.0.xx) or + # modprobe ip_alias.o + + Also, dynamic loading is supported (kerneld). + You should have the following line in /etc/conf.modules: + alias net_alias-2 ip_alias o Alias creation. Alias creation is done by 'magic' iface naming: eg. to create a @@ -49,11 +59,11 @@ alias device is closed before deletion, so all network stuff that points to it (routes, arp entries, ...) will be released. -Alias (re-)configuring +o Alias (re-)configuring Aliases *are* devices, so you configure and refer to them as usual (ifconfig, route, etc). -o Procfs entries +o PROCfs entries 2 entries are added to help fetching alias runtime configuration: a) /proc/net/alias_types Will show you alias_types registered (ie. address families that @@ -70,7 +80,50 @@ # cat /proc/net/aliases device family address eth0:0 2 200.1.1.1 - + +o PROCfs dynamic configuration + You can now change the max aliases per device limit via + /proc/sys/net/core/net_alias_max entry + # cat /proc/sys/net/core/net_alias_max + 256 + # echo 1000 > /proc/sys/net/core/net_alias_max + # cat /proc/sys/net/core/net_alias_max + 1000 + # _ + + With this funcionality you can disable net_alias creation from now on + # echo 0 > /proc/sys/net/core/net_alias_max + + The new aliasing limit is considered (grabbed) when creating the + FIRST alias for the main device. + Eg: + # echo 10 > /proc/sys/net/core/net_alias_max + # ifconfig eth0:0 xx.xx.xx.xx (first alias creation for eth0, + eth0 will 'remember' max==10) + # echo 1000 > /proc/sys/net/core/net_alias_max + # ifconfig eth0:999 xx.xx.xx.xx + SIOCIFSADDR: No such device + Of course these semantics can be changed, please let me know. + + Configuration changes get logged as usual (klogd -> /var/log/messages) + +o Alias devices rx/tx stats + Fake rx/tx stats are accounted: + - TX + When the packet is ``switched'' from logical alias device to + physical device, tx counter gets incr. + - RX + When an incoming packet's address equals alias device's addr it + gets ``switched'' from physical to logical device, rx counter gets + incr. + + Please NOTE that for ``same'' network alias devices you usually have + one net-route through physical device (eg. eth0), so output pkts + will NOT pass down via alias device (so, no tx++ will occur). + + Also NOTE that currently ifconfig does not handle the ``:'' of alias devices + names, a little patch (attached) solves the problem. + Relationship with main device ----------------------------- - On main device closing, all aliases will be closed and freed. @@ -78,12 +131,12 @@ main device (aliases get 'stacked' after main_dev), eg: lo->eth0->eth0:0->eth0:2->eth1->0 If eth0 is unregistered, all it aliases will also be: - lo->eth1->0 + lo->eth1->0 Contact ------- -Please finger or e-mail me: - Juan Jose Ciarlante +Please e-mail me: + Juan Jose Ciarlante or ; local variables: diff -u --recursive --new-file v2.0.30/linux/Documentation/networking/ipx.txt linux/Documentation/networking/ipx.txt --- v2.0.30/linux/Documentation/networking/ipx.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/networking/ipx.txt Tue Aug 12 11:17:41 1997 @@ -0,0 +1,19 @@ +The IPX support in the Linux kernel has two modes of operation: +With and without the full internal IPX network. For all normal +operations, you do not need the full internal IPX network. + +The full internal IPX network enables you to allocate sockets on +different virtual nodes of the internal network. This is done by +evaluating the field sipx_node of the socket address given to the bind +call. So applications should always initialize the node field to 0 +when binding a socket on the primary network. In this case the socket +is assigned the default node that has been given to the kernel when +the internal network was created. +By enabling the full internal IPX network the cross-forwarding of +packets targeted at 'special' sockets to sockets listening on the +primary network is disabled. This might break existing applications, +especially RIP/SAP daemons. A RIP/SAP daemon that works well with +the full internal net can be found on ftp.gwdg.de:/pub/linux/misc/ncpfs. + +If you want the full internal network, please uncomment the correspondig +#define in line 19 of include/net/ipx.h diff -u --recursive --new-file v2.0.30/linux/Documentation/networking/so_bindtodevice.txt linux/Documentation/networking/so_bindtodevice.txt --- v2.0.30/linux/Documentation/networking/so_bindtodevice.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/networking/so_bindtodevice.txt Tue Aug 12 11:17:41 1997 @@ -0,0 +1,69 @@ +SO_BINDTODEVICE socket option for Linux 2.0.30+ +by Elliot Poger (elliot@poger.com) +of Stanford's MosquitoNet project (http://mosquitonet.stanford.edu) + +Using the SO_BINDTODEVICE socket option allows your user-level Berkeley +sockets code to explicitly select which network interface is used for +both input and output on a per-socket basis. I originally wrote it to +allow the Internet Software Consortium DHCP server +(http://www.fugue.com/dhcp/) to run on Linux machines with multiple +interfaces. It has been tested with UDP and TCP sockets. + +Usage is as follows: + + + int skfd; + struct ifreq interface; + + skfd = socket(AF_INET, SOCK_DGRAM, 0); + strncpy(interface.ifr_ifrn.ifrn_name, "eth1", IFNAMSIZ); + if (setsockopt(skfd, SOL_SOCKET, SO_BINDTODEVICE, + (char *)&interface, sizeof(interface)) < 0) { + perror("sendpacket: setting SO_BINDTODEVICE"); + exit(1); + } + + +Once the BINDTODEVICE socket option has been set for a socket, as above, +any data sent over this socket is guaranteed to go out of the "eth1" +interface, and any data received through the socket is guaranteed to +have arrived on eth1. If you want to send and receive over multiple +interfaces, keeping them separate, you can open several sockets and bind +each one to a different interface with SO_BINDTODEVICE. (You _can_ call +BINDTODEVICE more than once for a socket to change the interface it's +bound to, but results may be unpredictable because of caching effects +in the kernel...) + +Note that the routing table is still consulted when packets are transmitted. +Basically, routing proceeds as usual, except that any routes which go +through a network interface other than the one specified in the BINDTODEVICE +call are ignored. If you attempt to send a packet to a certain IP address +through an interface which provides no route to that IP address, you'll get +a "network unreachable" error. Here is an example of a routing table which +will allow you to send packets to any IP address through either eth0 or +eth1: + +Destination Gateway Genmask Flags Metric Ref Use Iface +171.64.69.0 0.0.0.0 255.255.255.192 U 0 0 37 eth0 +171.64.69.192 0.0.0.0 255.255.255.192 U 0 0 677 eth1 +127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 4 lo +0.0.0.0 171.64.69.1 0.0.0.0 UG 0 0 45 eth0 +0.0.0.0 171.64.69.193 0.0.0.0 UG 1 0 5 eth1 + +Note that there are actually TWO default routes. The routing table is +searched from top to bottom, so every time you send out a packet, the first +(uppermost) matching route which the kernel routing function finds which +matches the destination IP address is used. In this case, packets sent to +the IP address 152.2.128.159 will normally be sent through eth0 and gateway +171.64.69.1; if the socket is bound to the eth1 device, the packets will be +sent through eth1 and gateway 171.64.69.193; if the socket is bound to some +other device, you will get a "network unreachable" error. + +By the way, you can add multiple default routes and set the order of +preference as follows: + +route add default gateway 171.64.69.1 +route add default gateway 171.64.69.193 metric 1 + +Routes with a higher "metric" are put lower in the table and thus have a +lower preference. diff -u --recursive --new-file v2.0.30/linux/Documentation/networking/z8530drv.txt linux/Documentation/networking/z8530drv.txt --- v2.0.30/linux/Documentation/networking/z8530drv.txt Thu Apr 11 23:49:29 1996 +++ linux/Documentation/networking/z8530drv.txt Tue Aug 12 14:47:03 1997 @@ -1,18 +1,8 @@ This is a subset of the documentation. To use this driver you MUST have the -full package from: - -Internet: -========= - -ftp.ucsd.edu:/hamradio/packet/tcpip/incoming/z8530drv-2.0.dl1bke.real.tar.gz - -[ - if you can't find it there, try: - .../tcpip/linux/z8530drv-2.0.dl1bke.tar.gz - -] - -and various mirrors (i.e. nic.switch.ch) +full package "z8530drv-2.4c.dl1bke.tar.gz" from either ftp.pspt.fi, +sunsite.unc.edu or db0bm.automation.fh-aachen.de. Do not try to use the +utilities from z8530drv-utils-3.0 as they will not work with the 2.4 series +of the driver! --------------------------------------------------------------------------- @@ -21,7 +11,7 @@ ******************************************************************** - (c) 1993,1995 by Joerg Reuter DL1BKE + (c) 1993,1997 by Joerg Reuter DL1BKE portions (c) 1993 Guido ten Dolle PE1NNZ @@ -787,5 +777,5 @@ in the Linux standard distribution and their support. Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org - AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU - Internet: jreuter@lykos.tng.oche.de + WWW : http://www.rat.de/jr + Internet: jreuter@poboxes.com diff -u --recursive --new-file v2.0.30/linux/Documentation/specialix.txt linux/Documentation/specialix.txt --- v2.0.30/linux/Documentation/specialix.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/specialix.txt Tue Aug 12 11:17:41 1997 @@ -0,0 +1,334 @@ + + specialix.txt -- specialix IO8+ multiport serial driver readme. + + + + Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) + + Specialix pays for the development and support of this driver. + Please DO contact io8-linux@specialix.co.uk if you require + support. + + This driver was developed in the BitWizard linux device + driver service. If you require a linux device driver for your + product, please contact devices@BitWizard.nl for a quote. + + This code is firmly based on the riscom/8 serial driver, + written by Dmitry Gorodchanin. The specialix IO8+ card + programming information was obtained from the CL-CD1865 Data + Book, and Specialix document number 6200059: IO8+ Hardware + Functional Specification. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + USA. + + +Intro +===== + + +This file contains some random information, that I like to have online +instead of in a manual that can get lost. Ever misplace your Linux +kernel sources? And the manual of one of the boards in your computer? + + +Adresses and interrupts +======================= + +Addres dip switch settings: +The dip switch sets bits 2-9 of the IO address. + + 9 8 7 6 5 4 3 2 + +-----------------+ + 0 | X X X X X X X | + | | = IoBase = 0x100 + 1 | X | + +-----------------+ ------ RS232 connectors ----> + + | | | + edge connector + | | | + V V V + +Base address 0x100 caused a conflict in one of my computers once. I +haven't the foggiest why. My Specialix card is now at 0x180. My +other computer runs just fine with the Specialix card at 0x100.... +The card occupies 4 addresses, but actually only two are really used. + +The driver now still autoprobes at 0x100, 0x180, 0x250 and 0x260. If +that causes trouble for you, please report that. I'll remove +autoprobing then. + +The driver will tell the card what IRQ to use, so you don't have to +change any jumpers to change the IRQ. Just use a command line +argument (irq=xx) to the insmod program to set the interrupt. + +If your specialix cards are not at the default locations, you can use +the kernel command line argument "specialix=io0,irq0,io1,irq1...". +Here "io0" is the io address for the first card, and "irq0" is the +irq line that the first card should use. And so on. + +Examples. + +You use the driver as a module and have three cards at 0x100, 0x250 +and 0x180. And some way or another you want them detected in that +order. Moreover irq 12 is taken (e.g. by your PS/2 mouse). + + insmod specialix.o iobase=0x100,0x250,0x180 irq=9,11,15 + +The same three cards, but now in the kernel would require you to +add + + specialix=0x100,9,0x250,11,0x180,15 + +to the command line. This would become + + append="specialix=0x100,9,0x250,11,0x180,15" + +in your /etc/lilo.conf file if you use lilo. + + +Baud rates +========== + +The rev 1.2 and below boards use a CL-CD1864. These chips can only +do 64kbit. The rev 1.3 and newer boards use a CL-CD1865. These chips +are officially capable of 115k2. + +The Specialix card uses a 25MHz crystal (in times two mode, which in +fact is a divided by two mode). This is not enough to reach the rated +115k2 on all ports at the same time. With this clock rate you can only +do 37% of this rate. This means that at 115k2 on all ports you are +going to loose characters (The chip cannot handle that many incoming +bits at this clock rate.) (Yes, you read that correctly: there is a +limit to the number of -=bits=- per second that the chip can handle.) + +If you near the "limit" you will first start to see a graceful +degradation in that the chip cannot keep the transmitter busy at all +times. However with a central clock this slow, you can also get it to +miss incoming characters. + +The specialix card cannot reliably do 115k2. If you use it, you have +to do "extensive testing" (*) to verify if it actually works. + +When "mgetty" communicates with my modem at 115k2 it reports: +got: +++[0d]ATQ0V1H0[0d][0d][8a]O[cb][0d][8a] + ^^^^ ^^^^ ^^^^ + +The three characters that have the "^^^" under them have suffered a +bit error in the highest bit. In conclusion: I've tested it, and found +that it simply DOESN"T work for me. I also suspect that this is also +caused by the baud rate being just a little bit out of tune. + + +(*) Cirrus logic CD1864 databook, page 40. + + +Cables for the Specialix IO8+ +============================= + +The pinout of the connectors on the IO8+ is: + + pin short direction long name + name + Pin 1 DCD input Data Carrier Detect + Pin 2 RXD input Receive + Pin 3 DTR/RTS output Data Terminal Ready/Ready To Send + Pin 4 GND - Ground + Pin 5 TXD output Transmit + Pin 6 CTS input Clear To Send + + + -- 6 5 4 3 2 1 -- + | | + | | + | | + | | + +----- -----+ + |__________| + clip + + Front view of an RJ12 connector. Cable moves "into" the paper. + (the plug is ready to plug into your mouth this way...) + + + NULL cable. I don't know who is going to use these except for + testing purposes, but I tested the cards with this cable. (It + took quite a while to figure out, so I'm not going to delete + it. So there! :-) + + + This end goes This end needs + straight into the some twists in + RJ12 plug. the wiring. + IO8+ RJ12 IO8+ RJ12 + 1 DCD white - + - - 1 DCD + 2 RXD black 5 TXD + 3 DTR/RTS red 6 CTS + 4 GND green 4 GND + 5 TXD yellow 2 RXD + 6 CTS blue 3 DTR/RTS + + + Same NULL cable, but now sorted on the second column. + + 1 DCD white - + - - 1 DCD + 5 TXD yellow 2 RXD + 6 CTS blue 3 DTR/RTS + 4 GND green 4 GND + 2 RXD black 5 TXD + 3 DTR/RTS red 6 CTS + + + + This is a modem cable usable for hardware handshaking: + RJ12 DB25 DB9 + 1 DCD white 8 DCD 1 DCD + 2 RXD black 3 RXD 2 RXD + 3 DTR/RTS red 4 RTS 7 RTS + 4 GND green 7 GND 5 GND + 5 TXD yellow 2 TXD 3 TXD + 6 CTS blue 5 CTS 8 CTS + +---- 6 DSR 6 DSR + +---- 20 DTR 4 DTR + + This is a modem cable usable for software handshaking: + It allows you to reset the modem using the DTR ioctls. + I (REW) have never tested this, "but xxxxxxxxxxxxx + says that it works." If you test this, please + tell me and I'll fill in your name on the xxx's. + + RJ12 DB25 DB9 + 1 DCD white 8 DCD 1 DCD + 2 RXD black 3 RXD 2 RXD + 3 DTR/RTS red 20 DTR 4 DTR + 4 GND green 7 GND 5 GND + 5 TXD yellow 2 TXD 3 TXD + 6 CTS blue 5 CTS 8 CTS + +---- 6 DSR 6 DSR + +---- 4 RTS 7 RTS + + I bought a 6 wire flat cable. It was colored as indicated. + Check that yours is the same before you trust me on this. + + +Hardware handshaking issues. +============================ + +The driver can be compiled in two different ways. The default +("Specialix DTR/RTS pin is RTS" is off) the pin behaves as DTR when +hardware handshaking is off. It behaves as the RTS hardware +handshaking signal when hardware handshaking is selected. + +When you use this, you have to use the appropriate cable. The +cable will either be compatible with hardware handshaking or with +software handshaking. So switching on the fly is not really an +option. + +I actually prefer to use the "Specialix DTR/RTS pin is RTS" option. +This makes the DTR/RTS pin always an RTS pin, and ioctls to +change DTR are always ignored. I have a cable that is configured +for this. + + +Ports and devices +================= + +Port 0 is the one furthest from the ISA connector. + +Devices: + +You should make the devices as follows: + +bash +cd /dev +for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 \ + 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 +do + echo -n "$i " + mknod /dev/ttyW$i c 75 $i + mknod /dev/cuw$i c 76 $i +done +echo "" + + +You cannot have more than 4 boards in one computer. The card only +supports 4 different interrupts. If you really want this, contact me +about this and I'll give you a few tips (requires soldering iron).... + + +------------------------------------------------------------------------ + + + Fixed bugs and restrictions: + - During intialization, interrupts are blindly turned on. + Having a shadow variable would cause an extra memory + access on every IO instruction. + - The interrupt (on the card) should be disabled when we + don't allocate the Linux end of the interrupt. This allows + a different driver/card to use it while all ports are not in + use..... (a la standard serial port) + == An extra _off variant of the sx_in and sx_out macros are + now available. They don't set the interrupt enable bit. + These are used during initialization. Normal operation uses + the old variant which enables the interrupt line. + - RTS/DTR issue needs to be implemented according to + specialix' spec. + I kind of like the "determinism" of the current + implementation. Compile time flag? + == Ok. Compile time flag! Default is how Specialix likes it. + == Now a config time flag! Gets saved in your config file. Neat! + - Can you set the IO address from the lilo command line? + If you need this, bug me about it, I'll make it. + == Hah! No bugging needed. Fixed! :-) + - Cirrus logic hasn't gotten back to me yet why the CD1865 can + and the CD1864 can't do 115k2. I suspect that this is + because the CD1864 is not rated for 33MHz operation. + Therefore the CD1864 versions of the card can't do 115k2 on + all ports just like the CD1865 versions. The driver does + not block 115k2 on CD1864 cards. + == I called the Cirrus Logic representative here in Holland. + The CD1864 databook is identical to the CD1865 databook, + except for an extra warning at the end. Similar Bit errors + have been observed in testing at 115k2 on both an 1865 and + a 1864 chip. I see no reason why I would prohibit 115k2 on + 1864 chips and not do it on 1865 chips. Actually there is + reason to prohibit it on BOTH chips. I print a warning. + If you use 115k2, you're on your own. + - A spiky CD may send spurious HUPs. Also in CLOCAL??? + -- A fix for this turned out to be counter productive. + Different fix? Current behaviour is acceptable? + -- Maybe the current implementation is correct. If anybody + gets bitten by this, please report, and it will get fixed. + + -- Testing revealed that when in CLOCAL, the problem doesn't + occur. As warned for in the CD1865 manual, the chip may + send modem intr's on a spike. We could filter those out, + but that would be a cludge anyway (You'd still risk getting + a spurious HUP when two spikes occur.)..... + + + + Bugs & restrictions: + - This is a difficult card to autoprobe. + You have to WRITE to the address register to even + read-probe a CD186x register. Disable autodetection? + -- Specialix: any suggestions? + - Arbitrary baud rates are not implemented yet. + If you need this, bug me about it. + + diff -u --recursive --new-file v2.0.30/linux/MAINTAINERS linux/MAINTAINERS --- v2.0.30/linux/MAINTAINERS Tue Apr 8 08:47:45 1997 +++ linux/MAINTAINERS Tue Aug 12 14:46:12 1997 @@ -110,6 +110,12 @@ M: jt@hplb.hpl.hp.com S: Maintained +TOKEN-RING NETWORK DRIVER +P: Paul Norton +M: pnorton@cts.com +L: linux-net@vger.rutgers.edu +S: Maintained + APM DRIVER P: Rik Faith & Stephen Rothwell M: faith@cs.unc.edu, Stephen.Rothwell@canb.auug.org.au @@ -128,6 +134,24 @@ L: linux-hams@vger.rutgers.edu S: Maintained +IP MASQUERADING (IPV4) +P: Nigel Metheringham +M: Nigel.Metheringham@ThePLAnet.net +L: masq@indyramp.com +S: Maintained + +AX.25 DAMA SLAVE +P: Joerg Reuter +M: jreuter@poboxes.com +L: linux-hams@vger.rutgers.edu +S: Maintained + +Z8530 SCC DRIVER FOR AX.25 +P: Joerg Reuter +M: jreuter@poboxes.com +L: linux-hams@vger.rutgers.edu +S: Maintained + BUSLOGIC SCSI DRIVER P: Leonard N. Zubkoff M: Leonard N. Zubkoff @@ -164,6 +188,12 @@ L: linux-eata@i-connect.net, linux-scsi@vger.rutgers.edu S: Maintained +FILE LOCKING (flock() and fcntl()/lockf()) +P: Andy Walker +M: andy@lysaker.kvaerner.no +L: linux-kernel@vger.rutgers.edu +S: Maintained + FRAME RELAY DLCI/FRAD (Sangoma drivers too) P: Mike McLagan M: mike.mclagan@linux.org @@ -206,6 +236,12 @@ L: linux-kernel@vger.rutgers.edu S: Maintained +IDE/ATAPI TAPE/FLOPPY DRIVERS +P: Gadi Oxman +M: Gadi Oxman +L: linux-kernel@vger.rutgers.edu +S: Maintained + ISDN SUBSYSTEM P: Fritz Elfert M: fritz@wuemaus.franken.de @@ -288,7 +324,7 @@ SVGA HANDLING: P: Martin Mares M: mj@k332.feld.cvut.cz -L: linux-kernel@vger.rutgers.edu +L: linux-video@atrey.karlin.mff.cuni.cz S: Maintained VFAT FILESYSTEM: @@ -310,6 +346,13 @@ L: linux-kernel@vger.rutgers.edu S: Maintained +SPECIALIX IO8+ MULTIPORT SERIAL CARD DRIVER +P: Roger Wolff +M: R.E.Wolff@BitWizard.nl +M: io8-linux@specialix.co.uk +L: linux-kernel@vger.rutgers.edu ? +S: Supported + MOUSE AND MISC DEVICES [GENERAL] P: Alessandro Rubini M: rubini@ipvvis.unipv.it @@ -318,7 +361,7 @@ MENUCONFIG: P: William Roadcap -M: roadcapw@cfw.com +M: roadcapw@titus.org L: linux-kernel@vger.rutgers.edu S: Maintained diff -u --recursive --new-file v2.0.30/linux/Makefile linux/Makefile --- v2.0.30/linux/Makefile Mon Mar 17 14:58:22 1997 +++ linux/Makefile Mon Aug 4 15:23:02 1997 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 0 -SUBLEVEL = 30 +SUBLEVEL = 31 ARCH = i386 @@ -319,7 +319,6 @@ mrproper: clean rm -f include/linux/autoconf.h include/linux/version.h rm -f drivers/sound/local.h drivers/sound/.defines - rm -f drivers/scsi/aic7xxx_asm drivers/scsi/aic7xxx_seq.h rm -f drivers/char/uni_hash.tbl drivers/char/conmakehash rm -f .version .config* config.in config.old rm -f scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp diff -u --recursive --new-file v2.0.30/linux/arch/alpha/defconfig linux/arch/alpha/defconfig --- v2.0.30/linux/arch/alpha/defconfig Tue Oct 29 17:42:40 1996 +++ linux/arch/alpha/defconfig Mon Aug 4 17:33:59 1997 @@ -169,6 +169,7 @@ # ISDN subsystem # # CONFIG_ISDN is not set +CONFIG_HISAX_EURO=y # # CD-ROM drivers (not for SCSI or IDE/ATAPI drives) @@ -179,7 +180,6 @@ # Filesystems # # CONFIG_QUOTA is not set -# CONFIG_LOCK_MANDATORY is not set # CONFIG_MINIX_FS is not set # CONFIG_EXT_FS is not set CONFIG_EXT2_FS=y diff -u --recursive --new-file v2.0.30/linux/arch/alpha/kernel/osf_sys.c linux/arch/alpha/kernel/osf_sys.c --- v2.0.30/linux/arch/alpha/kernel/osf_sys.c Tue Aug 20 23:18:07 1996 +++ linux/arch/alpha/kernel/osf_sys.c Sun Aug 3 10:58:38 1997 @@ -714,29 +714,25 @@ osf_getsysinfo (unsigned long op, void * buffer, unsigned long nbytes, int * start, void *arg) { - extern unsigned long rdfpcr (void); - unsigned long fpcw; - - switch (op) { - case 45: /* GSI_IEEE_FP_CONTROL */ - /* build and return current fp control word: */ - fpcw = current->tss.flags & IEEE_TRAP_ENABLE_MASK; - fpcw |= ((rdfpcr() >> 52) << 17) & IEEE_STATUS_MASK; - put_user(fpcw, (unsigned long *) buffer); - return 0; - - case 46: /* GSI_IEEE_STATE_AT_SIGNAL */ - /* - * Not sure anybody will ever use this weird stuff. These - * ops can be used (under OSF/1) to set the fpcr that should - * be used when a signal handler starts executing. - */ - break; - - default: - break; - } - return -EOPNOTSUPP; + switch (op) { + case 45: /* GSI_IEEE_FP_CONTROL */ + /* Return current sw control & status bits. */ + put_user(current->tss.flags & IEEE_SW_MASK, + (unsigned long *)buffer); + return 0; + + case 46: /* GSI_IEEE_STATE_AT_SIGNAL */ + /* + * Not sure anybody will ever use this weird stuff. These + * ops can be used (under OSF/1) to set the fpcr that should + * be used when a signal handler starts executing. + */ + break; + + default: + break; + } + return -EOPNOTSUPP; } @@ -744,25 +740,43 @@ osf_setsysinfo (unsigned long op, void * buffer, unsigned long nbytes, int * start, void *arg) { - unsigned long fpcw; - - switch (op) { - case 14: /* SSI_IEEE_FP_CONTROL */ - /* update trap enable bits: */ - fpcw = get_user((unsigned long *) buffer); - current->tss.flags &= ~IEEE_TRAP_ENABLE_MASK; - current->tss.flags |= (fpcw & IEEE_TRAP_ENABLE_MASK); - return 0; - - case 15: /* SSI_IEEE_STATE_AT_SIGNAL */ - case 16: /* SSI_IEEE_IGNORE_STATE_AT_SIGNAL */ - /* - * Not sure anybody will ever use this weird stuff. These - * ops can be used (under OSF/1) to set the fpcr that should - * be used when a signal handler starts executing. - */ - default: - break; - } - return -EOPNOTSUPP; + switch (op) { + case 14: { /* SSI_IEEE_FP_CONTROL */ + unsigned long sw, fpcw; + + /* + * Alpha Architecture Handbook 4.7.7.3: + * To be fully IEEE compiant, we must track the current IEEE + * exception state in software, because spurrious bits can be + * set in the trap shadow of a software-complete insn. + */ + + /* Update software trap enable bits. */ + sw = get_user((unsigned long *) buffer) & IEEE_SW_MASK; + current->tss.flags &= ~IEEE_SW_MASK; + current->tss.flags |= sw & IEEE_SW_MASK; + + /* Update the real fpcr. For exceptions that are disabled, + but that we have seen, turn off exceptions in h/w. + Otherwise leave them enabled so that we can update our + software status mask. */ + fpcw = rdfpcr() & (~FPCR_MASK | FPCR_DYN_MASK); + fpcw |= ieee_sw_to_fpcr(sw | ((~sw & IEEE_STATUS_MASK) >> 16)); + wrfpcr(fpcw); + return 0; + } + + case 15: /* SSI_IEEE_STATE_AT_SIGNAL */ + case 16: /* SSI_IEEE_IGNORE_STATE_AT_SIGNAL */ + /* + * Not sure anybody will ever use this weird stuff. These + * ops can be used (under OSF/1) to set the fpcr that should + * be used when a signal handler starts executing. + */ + break; + + default: + break; + } + return -EOPNOTSUPP; } diff -u --recursive --new-file v2.0.30/linux/arch/alpha/kernel/ptrace.c linux/arch/alpha/kernel/ptrace.c --- v2.0.30/linux/arch/alpha/kernel/ptrace.c Wed Sep 11 07:57:13 1996 +++ linux/arch/alpha/kernel/ptrace.c Mon Aug 4 15:41:59 1997 @@ -641,7 +641,7 @@ return; current->exit_code = SIGTRAP; current->state = TASK_STOPPED; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); /* * this isn't the same as continuing with a signal, but it will do diff -u --recursive --new-file v2.0.30/linux/arch/alpha/kernel/signal.c linux/arch/alpha/kernel/signal.c --- v2.0.30/linux/arch/alpha/kernel/signal.c Tue Aug 20 23:18:07 1996 +++ linux/arch/alpha/kernel/signal.c Mon Aug 4 15:41:48 1997 @@ -291,7 +291,7 @@ if ((current->flags & PF_PTRACED) && signr != SIGKILL) { current->exit_code = signr; current->state = TASK_STOPPED; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); single_stepping |= ptrace_cancel_bpt(current); if (!(signr = current->exit_code)) @@ -330,7 +330,7 @@ current->exit_code = signr; if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & SA_NOCLDSTOP)) - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); single_stepping |= ptrace_cancel_bpt(current); continue; diff -u --recursive --new-file v2.0.30/linux/arch/alpha/kernel/traps.c linux/arch/alpha/kernel/traps.c --- v2.0.30/linux/arch/alpha/kernel/traps.c Sun Feb 2 05:00:20 1997 +++ linux/arch/alpha/kernel/traps.c Wed Aug 13 12:54:02 1997 @@ -420,7 +420,7 @@ unsigned long a3, unsigned long a4, unsigned long a5, struct pt_regs regs) { - if (regs.r0 != 112) + if (regs.r0 != 112 && regs.r0 < 300) printk("", regs.r0, a0, a1, a2); return -1; } diff -u --recursive --new-file v2.0.30/linux/arch/alpha/math-emu/fp-emul.c linux/arch/alpha/math-emu/fp-emul.c --- v2.0.30/linux/arch/alpha/math-emu/fp-emul.c Thu Apr 11 23:49:30 1996 +++ linux/arch/alpha/math-emu/fp-emul.c Sun Aug 3 10:58:38 1997 @@ -298,19 +298,29 @@ * * - Set the appropriate bits in the FPCR * - If the specified exception is enabled in the FPCR, - * return. The caller (mxr_signal_handler) will dispatch + * return. The caller (entArith) will dispatch * the appropriate signal to the translated program. + * + * In addition, properly track the exception state in software + * as described in Alpha Architecture Handbook 4.7.7.3. */ if (res) { - fpcr |= FPCR_SUM | res; - wrfpcr(fpcr); - if (((res & FPCR_INV) && (fpcw & IEEE_TRAP_ENABLE_INV)) || - ((res & FPCR_DZE) && (fpcw & IEEE_TRAP_ENABLE_DZE)) || - ((res & FPCR_OVF) && (fpcw & IEEE_TRAP_ENABLE_OVF)) || - ((res & FPCR_UNF) && (fpcw & IEEE_TRAP_ENABLE_UNF)) || - ((res & FPCR_INE) && (fpcw & IEEE_TRAP_ENABLE_INE))) + /* record exceptions in software control word. */ + fpcw |= res >> 35; + current->tss.flags = fpcw; + + /* update hardware control register, disabling hardware + exceptions for disabled software exceptions for which + we have a status. (no, really.) */ + fpcr &= (~FPCR_MASK | FPCR_DYN_MASK); + fpcr |= ieee_sw_to_fpcr(fpcw | (~fpcw & IEEE_STATUS_MASK)>>16); + wrfpcr(fpcr); + + /* Do we generate a signal? */ + if (res >> 51 & fpcw & IEEE_TRAP_ENABLE_MASK) return 0; } + /* * Whoo-kay... we got this far, and we're not generating a signal * to the translated program. All that remains is to write the diff -u --recursive --new-file v2.0.30/linux/arch/i386/defconfig linux/arch/i386/defconfig --- v2.0.30/linux/arch/i386/defconfig Tue Oct 29 17:42:40 1996 +++ linux/arch/i386/defconfig Tue Aug 12 14:19:24 1997 @@ -42,11 +42,13 @@ # CONFIG_BLK_DEV_HD_IDE is not set CONFIG_BLK_DEV_IDECD=y # CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set # CONFIG_BLK_DEV_IDE_PCMCIA is not set CONFIG_BLK_DEV_CMD640=y # CONFIG_BLK_DEV_CMD640_ENHANCED is not set CONFIG_BLK_DEV_RZ1000=y -# CONFIG_BLK_DEV_TRITON is not set +CONFIG_BLK_DEV_TRITON=y # CONFIG_IDE_CHIPSETS is not set # @@ -66,7 +68,10 @@ CONFIG_INET=y # CONFIG_IP_FORWARD is not set # CONFIG_IP_MULTICAST is not set +# CONFIG_SYN_COOKIES is not set # CONFIG_IP_ACCT is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set # # (it is safe to leave these untouched) @@ -129,7 +134,6 @@ # Filesystems # # CONFIG_QUOTA is not set -# CONFIG_LOCK_MANDATORY is not set CONFIG_MINIX_FS=y # CONFIG_EXT_FS is not set CONFIG_EXT2_FS=y @@ -156,6 +160,7 @@ # CONFIG_STALDRV is not set # CONFIG_RISCOM8 is not set # CONFIG_PRINTER is not set +# CONFIG_SPECIALIX is not set # CONFIG_MOUSE is not set # CONFIG_UMISC is not set # CONFIG_QIC02_TAPE is not set diff -u --recursive --new-file v2.0.30/linux/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c --- v2.0.30/linux/arch/i386/kernel/bios32.c Tue Apr 8 08:47:45 1997 +++ linux/arch/i386/kernel/bios32.c Mon Aug 4 08:38:56 1997 @@ -1,6 +1,8 @@ /* * bios32.c - BIOS32, PCI BIOS functions. * + * $Id: bios32.c,v 1.3.2.4 1997/08/02 22:24:23 mj Exp $ + * * Sponsored by * iX Multiuser Multitasking Magazine * Hannover, Germany @@ -52,6 +54,13 @@ * Feb 3, 1997 : Set internal functions to static, save/restore flags * avoid dead locks reading broken PCI BIOS, werner@suse.de * + * Apr 26, 1997 : Fixed case when there is BIOS32, but not PCI BIOS + * (mj@atrey.karlin.mff.cuni.cz) + * + * May 7, 1997 : Added some missing cli()'s. [mj] + * + * Jun 20, 1997 : Corrected problems in "conf1" type accesses. + * (paubert@iram.es) */ #include @@ -156,7 +165,7 @@ unsigned long entry; /* %edx */ unsigned long flags; - save_flags(flags); + save_flags(flags); cli(); __asm__("lcall (%%edi)" : "=a" (return_code), "=b" (address), @@ -171,10 +180,10 @@ case 0: return address + entry; case 0x80: /* Not present */ - printk("bios32_service(%ld) : not present\n", service); + printk("bios32_service(0x%lx) : not present\n", service); return 0; default: /* Shouldn't happen */ - printk("bios32_service(%ld) : returned 0x%x, mail drew@colorado.edu\n", + printk("bios32_service(0x%lx) : returned 0x%x, mail drew@colorado.edu\n", service, return_code); return 0; } @@ -187,7 +196,7 @@ } pci_indirect = { 0, KERNEL_CS }; -extern unsigned long check_pcibios(unsigned long memory_start, unsigned long memory_end) +static int check_pcibios(void) { unsigned long signature; unsigned char present_status; @@ -199,7 +208,7 @@ if ((pcibios_entry = bios32_service(PCI_SERVICE))) { pci_indirect.address = pcibios_entry; - save_flags(flags); + save_flags(flags); cli(); __asm__("lcall (%%edi)\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" @@ -230,9 +239,10 @@ if (pcibios_entry) { printk ("pcibios_init : PCI BIOS revision %x.%02x entry at 0x%lx\n", major_revision, minor_revision, pcibios_entry); + return 1; } } - return memory_start; + return 0; } @@ -243,7 +253,7 @@ unsigned long ret; unsigned long flags; - save_flags(flags); + save_flags(flags); cli(); __asm__ ("lcall (%%edi)\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" @@ -268,7 +278,7 @@ unsigned short ret; unsigned long flags; - save_flags(flags); + save_flags(flags); cli(); __asm__("lcall (%%edi)\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" @@ -293,7 +303,7 @@ unsigned long bx = (bus << 8) | device_fn; unsigned long flags; - save_flags(flags); + save_flags(flags); cli(); __asm__("lcall (%%esi)\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" @@ -315,7 +325,7 @@ unsigned long bx = (bus << 8) | device_fn; unsigned long flags; - save_flags(flags); + save_flags(flags); cli(); __asm__("lcall (%%esi)\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" @@ -337,7 +347,7 @@ unsigned long bx = (bus << 8) | device_fn; unsigned long flags; - save_flags(flags); + save_flags(flags); cli(); __asm__("lcall (%%esi)\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" @@ -359,7 +369,7 @@ unsigned long bx = (bus << 8) | device_fn; unsigned long flags; - save_flags(flags); + save_flags(flags); cli(); __asm__("lcall (%%esi)\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" @@ -381,7 +391,7 @@ unsigned long bx = (bus << 8) | device_fn; unsigned long flags; - save_flags(flags); + save_flags(flags); cli(); __asm__("lcall (%%esi)\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" @@ -403,7 +413,7 @@ unsigned long bx = (bus << 8) | device_fn; unsigned long flags; - save_flags(flags); + save_flags(flags); cli(); __asm__("lcall (%%esi)\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" @@ -444,21 +454,17 @@ { unsigned int curr = 0; struct pci_dev *dev; - unsigned long flags; - save_flags(flags); for (dev = pci_devices; dev; dev = dev->next) { if (dev->vendor == vendor && dev->device == device_id) { if (curr == index) { *devfn = dev->devfn; *bus = dev->bus->number; - restore_flags(flags); return PCIBIOS_SUCCESSFUL; } ++curr; } } - restore_flags(flags); return PCIBIOS_DEVICE_NOT_FOUND; } @@ -472,21 +478,17 @@ { unsigned int curr = 0; struct pci_dev *dev; - unsigned long flags; - save_flags(flags); for (dev = pci_devices; dev; dev = dev->next) { if (dev->class == class_code) { if (curr == index) { *devfn = dev->devfn; *bus = dev->bus->number; - restore_flags(flags); return PCIBIOS_SUCCESSFUL; } ++curr; } } - restore_flags(flags); return PCIBIOS_DEVICE_NOT_FOUND; } @@ -500,18 +502,9 @@ { unsigned long flags; - save_flags(flags); + save_flags(flags); cli(); outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); - switch (where & 3) { - case 0: *value = inb(0xCFC); - break; - case 1: *value = inb(0xCFD); - break; - case 2: *value = inb(0xCFE); - break; - case 3: *value = inb(0xCFF); - break; - } + *value = inb(0xCFC + (where&3)); restore_flags(flags); return PCIBIOS_SUCCESSFUL; } @@ -521,12 +514,10 @@ { unsigned long flags; - save_flags(flags); + if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER; + save_flags(flags); cli(); outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); - if (where & 2) - *value = inw(0xCFE); - else - *value = inw(0xCFC); + *value = inw(0xCFC + (where&2)); restore_flags(flags); return PCIBIOS_SUCCESSFUL; } @@ -536,7 +527,8 @@ { unsigned long flags; - save_flags(flags); + if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER; + save_flags(flags); cli(); outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); *value = inl(0xCFC); restore_flags(flags); @@ -548,9 +540,9 @@ { unsigned long flags; - save_flags(flags); + save_flags(flags); cli(); outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); - outb(value, 0xCFC); + outb(value, 0xCFC + (where&3)); restore_flags(flags); return PCIBIOS_SUCCESSFUL; } @@ -560,9 +552,10 @@ { unsigned long flags; - save_flags(flags); + if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER; + save_flags(flags); cli(); outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); - outw(value, 0xCFC); + outw(value, 0xCFC + (where&2)); restore_flags(flags); return PCIBIOS_SUCCESSFUL; } @@ -572,7 +565,8 @@ { unsigned long flags; - save_flags(flags); + if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER; + save_flags(flags); cli(); outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); outl(value, 0xCFC); restore_flags(flags); @@ -608,7 +602,7 @@ if (device_fn & 0x80) return PCIBIOS_DEVICE_NOT_FOUND; - save_flags(flags); + save_flags(flags); cli(); outb (FUNC(device_fn), 0xCF8); outb (bus, 0xCFA); *value = inb(IOADDR(device_fn,where)); @@ -624,7 +618,7 @@ if (device_fn & 0x80) return PCIBIOS_DEVICE_NOT_FOUND; - save_flags(flags); + save_flags(flags); cli(); outb (FUNC(device_fn), 0xCF8); outb (bus, 0xCFA); *value = inw(IOADDR(device_fn,where)); @@ -640,7 +634,7 @@ if (device_fn & 0x80) return PCIBIOS_DEVICE_NOT_FOUND; - save_flags(flags); + save_flags(flags); cli(); outb (FUNC(device_fn), 0xCF8); outb (bus, 0xCFA); *value = inl (IOADDR(device_fn,where)); @@ -654,7 +648,7 @@ { unsigned long flags; - save_flags(flags); + save_flags(flags); cli(); outb (FUNC(device_fn), 0xCF8); outb (bus, 0xCFA); outb (value, IOADDR(device_fn,where)); @@ -668,7 +662,7 @@ { unsigned long flags; - save_flags(flags); + save_flags(flags); cli(); outb (FUNC(device_fn), 0xCF8); outb (bus, 0xCFA); outw (value, IOADDR(device_fn,where)); @@ -682,7 +676,7 @@ { unsigned long flags; - save_flags(flags); + save_flags(flags); cli(); outb (FUNC(device_fn), 0xCF8); outb (bus, 0xCFA); outl (value, IOADDR(device_fn,where)); @@ -714,7 +708,7 @@ unsigned int tmp; unsigned long flags; - save_flags(flags); + save_flags(flags); cli(); /* * check if configuration type 1 works @@ -736,7 +730,7 @@ outb (0x00, 0xCFB); outb (0x00, 0xCF8); outb (0x00, 0xCFA); - if (inb (0xCF8) == 0x00 && inb (0xCFC) == 0x00) { + if (inb (0xCF8) == 0x00 && inb (0xCFB) == 0x00) { restore_flags(flags); printk("pcibios_init: Using configuration type 2\n"); return &pci_direct_conf2; @@ -883,7 +877,9 @@ * */ - for (check = (union bios32 *) 0xe0000; check <= (union bios32 *) 0xffff0; ++check) { + for (check = (union bios32 *) 0xe0000; + check <= (union bios32 *) 0xffff0; + ++check) { if (check->fields.signature != BIOS32_SIGNATURE) continue; length = check->fields.length * 16; @@ -908,13 +904,11 @@ bios32_entry = check->fields.entry; printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry); bios32_indirect.address = bios32_entry; - access_pci = &pci_bios_access; } } } - if (bios32_entry) { - memory_start = check_pcibios (memory_start, memory_end); - } + if (bios32_entry && check_pcibios()) + access_pci = &pci_bios_access; #endif return memory_start; } diff -u --recursive --new-file v2.0.30/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S --- v2.0.30/linux/arch/i386/kernel/entry.S Wed Dec 11 06:41:01 1996 +++ linux/arch/i386/kernel/entry.S Mon Aug 11 13:42:11 1997 @@ -132,7 +132,10 @@ decl SYMBOL_NAME(syscall_count); \ decl SYMBOL_NAME(kernel_counter); \ jnz 1f; \ - movb $(NO_PROC_ID), SYMBOL_NAME(active_kernel_processor); \ + movb SYMBOL_NAME(saved_active_kernel_processor), %al; \ + movb %al, SYMBOL_NAME(active_kernel_processor); \ + cmpb $(NO_PROC_ID), %al; \ + jnz 1f; \ lock; \ btrl $0, SYMBOL_NAME(kernel_flag); \ 1: popfl; @@ -163,9 +166,13 @@ #define ENTER_KERNEL \ pushl %eax; \ + pushl %ebx; \ + pushl %ecx; \ pushl %edx; \ pushfl; \ cli; \ + movl $6000, %ebx; \ + movl SYMBOL_NAME(smp_loops_per_tick), %ecx; \ GET_PROCESSOR_ID \ btsl $ SMP_FROM_SYSCALL,SYMBOL_NAME(smp_proc_in_lock)(,%eax,4); \ SMP_PROF_A \ @@ -182,7 +189,18 @@ jnc 5f; \ movl %cr3,%edx; \ movl %edx,%cr3; \ -5: btl $0, SYMBOL_NAME(kernel_flag); \ +5: sti; \ + decl %ecx; \ + cli; \ + jne 7f; \ + decl %ebx; \ + jne 6f; \ + call SYMBOL_NAME(non_irq_deadlock_detected); \ +6: movl SYMBOL_NAME(smp_loops_per_tick), %ecx; \ + cmpb SYMBOL_NAME(boot_cpu_id), %al; \ + jne 7f; \ + incl SYMBOL_NAME(jiffies); \ +7: btl $0, SYMBOL_NAME(kernel_flag); \ jc 2b; \ jmp 1b; \ 3: movb %al, SYMBOL_NAME(active_kernel_processor); \ @@ -190,6 +208,8 @@ incl SYMBOL_NAME(syscall_count); \ popfl; \ popl %edx; \ + popl %ecx; \ + popl %ebx; \ popl %eax; @@ -325,6 +345,10 @@ 9: movl SYMBOL_NAME(bh_mask),%eax andl SYMBOL_NAME(bh_active),%eax jne handle_bottom_half +#ifdef __SMP__ + cmpb $(NO_PROC_ID), SYMBOL_NAME(saved_active_kernel_processor) + jne 2f +#endif movl EFLAGS(%esp),%eax # check VM86 flag: CS/SS are testl $(VM_MASK),%eax # different then jne 1f diff -u --recursive --new-file v2.0.30/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c --- v2.0.30/linux/arch/i386/kernel/ptrace.c Wed Sep 11 07:57:13 1996 +++ linux/arch/i386/kernel/ptrace.c Mon Aug 4 12:12:22 1997 @@ -535,7 +535,7 @@ return; current->exit_code = SIGTRAP; current->state = TASK_STOPPED; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); /* * this isn't the same as continuing with a signal, but it will do diff -u --recursive --new-file v2.0.30/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c --- v2.0.30/linux/arch/i386/kernel/signal.c Wed Dec 11 06:41:01 1996 +++ linux/arch/i386/kernel/signal.c Mon Aug 4 12:12:51 1997 @@ -286,7 +286,7 @@ if ((current->flags & PF_PTRACED) && signr != SIGKILL) { current->exit_code = signr; current->state = TASK_STOPPED; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); if (!(signr = current->exit_code)) continue; @@ -324,7 +324,7 @@ current->exit_code = signr; if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & SA_NOCLDSTOP)) - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); continue; diff -u --recursive --new-file v2.0.30/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c --- v2.0.30/linux/arch/i386/kernel/smp.c Sun May 12 21:17:23 1996 +++ linux/arch/i386/kernel/smp.c Mon Aug 11 13:42:11 1997 @@ -1202,3 +1202,15 @@ apic_read(APIC_SPIV); /* Dummy read */ apic_write(APIC_EOI, 0); /* Docs say use 0 for future compatibility */ } + +void irq_deadlock_detected(void) +{ + printk("IRQ DEADLOCK DETECTED BY CPU %d\n", smp_processor_id()); + __asm__("hlt"); +} + +void non_irq_deadlock_detected(void) +{ + printk("NON-IRQ DEADLOCK DETECTED BY CPU %d\n", smp_processor_id()); + __asm__("hlt"); +} diff -u --recursive --new-file v2.0.30/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c --- v2.0.30/linux/arch/i386/kernel/traps.c Wed Dec 11 06:41:01 1996 +++ linux/arch/i386/kernel/traps.c Mon Aug 11 13:37:24 1997 @@ -276,7 +276,10 @@ asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs, long error_code) { +#if 0 + /* No need to warn about this any longer. */ printk("Ignoring P6 Local APIC Spurious Interrupt Bug...\n"); +#endif } /* diff -u --recursive --new-file v2.0.30/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c --- v2.0.30/linux/arch/i386/mm/init.c Thu Nov 14 05:20:09 1996 +++ linux/arch/i386/mm/init.c Tue Aug 12 13:06:54 1997 @@ -35,6 +35,8 @@ #endif #endif +const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n"; + extern void die_if_kernel(char *,struct pt_regs *,long); extern void show_net_buffers(void); diff -u --recursive --new-file v2.0.30/linux/drivers/block/Config.in linux/drivers/block/Config.in --- v2.0.30/linux/drivers/block/Config.in Mon Aug 5 00:13:50 1996 +++ linux/drivers/block/Config.in Mon Aug 4 11:45:55 1997 @@ -5,7 +5,7 @@ comment 'Floppy, IDE, and other block devices' tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD -bool 'Enhanced IDE/MFM/RLL disk/cdrom/tape support' CONFIG_BLK_DEV_IDE +bool 'Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE comment 'Please see Documentation/ide.txt for help/info on IDE drives' if [ "$CONFIG_BLK_DEV_IDE" = "n" ]; then bool 'Old harddisk (MFM/RLL/IDE) driver' CONFIG_BLK_DEV_HD_ONLY @@ -13,6 +13,8 @@ bool ' Use old disk-only driver on primary interface' CONFIG_BLK_DEV_HD_IDE bool ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD bool ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE + bool ' Include IDE/ATAPI FLOPPY support (new)' CONFIG_BLK_DEV_IDEFLOPPY + bool ' SCSI emulation support' CONFIG_BLK_DEV_IDESCSI bool ' Support removable IDE interfaces (PCMCIA)' CONFIG_BLK_DEV_IDE_PCMCIA bool ' CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640 if [ "$CONFIG_BLK_DEV_CMD640" = "y" ]; then diff -u --recursive --new-file v2.0.30/linux/drivers/block/Makefile linux/drivers/block/Makefile --- v2.0.30/linux/drivers/block/Makefile Sat Aug 10 00:03:14 1996 +++ linux/drivers/block/Makefile Mon Aug 4 11:45:55 1997 @@ -97,6 +97,10 @@ L_OBJS += ide-tape.o endif +ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),y) +L_OBJS += ide-floppy.o +endif + ifeq ($(CONFIG_BLK_DEV_XD),y) L_OBJS += xd.o else diff -u --recursive --new-file v2.0.30/linux/drivers/block/genhd.c linux/drivers/block/genhd.c --- v2.0.30/linux/drivers/block/genhd.c Tue Aug 20 23:18:07 1996 +++ linux/drivers/block/genhd.c Mon Aug 4 11:45:55 1997 @@ -331,7 +331,7 @@ && (q->sector & 63) == 1 && (q->end_sector & 63) == 63) { unsigned int heads = q->end_head + 1; - if (heads == 32 || heads == 64 || heads == 128) { + if (heads == 32 || heads == 64 || heads == 128 || heads == 255) { (void) ide_xlate_1024(dev, heads, " [PTBL]"); break; diff -u --recursive --new-file v2.0.30/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c --- v2.0.30/linux/drivers/block/ide-cd.c Tue Mar 11 13:28:36 1997 +++ linux/drivers/block/ide-cd.c Mon Aug 4 11:45:55 1997 @@ -125,7 +125,8 @@ * you must define IHAVEADOLPHIN) * 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 + * * 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. * @@ -1141,6 +1142,11 @@ /* Number of sectors to transfer. */ nsect = rq->nr_sectors; +#if !STANDARD_ATAPI + if (nsect > drive->cdrom_info.max_sectors) + nsect = drive->cdrom_info.max_sectors; +#endif /* not STANDARD_ATAPI */ + /* Starting sector. */ sector = rq->sector; @@ -2674,6 +2680,8 @@ CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0; #if ! STANDARD_ATAPI + drive->cdrom_info.max_sectors = 252; + CDROM_CONFIG_FLAGS (drive)->old_readcd = 0; CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 0; CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 0; @@ -2698,6 +2706,9 @@ /* Vertos 600 ESD. */ CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 1; } + + else if (strcmp (drive->id->model, "GCD-R580B") == 0) + drive->cdrom_info.max_sectors = 124; else if (strcmp (drive->id->model, "NEC CD-ROM DRIVE:260") == 0 && diff -u --recursive --new-file v2.0.30/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c --- v2.0.30/linux/drivers/block/ide-floppy.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/ide-floppy.c Mon Aug 4 11:45:55 1997 @@ -0,0 +1,1388 @@ +/* + * linux/drivers/block/ide-floppy.c Version 0.7 - ALPHA Aug 4, 1997 + * + * Copyright (C) 1996, 1997 Gadi Oxman + */ + +/* + * IDE ATAPI floppy driver. + * + * The driver currently doesn't have any fancy features, just the bare + * minimum read/write support. + * + * Many thanks to Lode Leroy , who tested so many + * ALPHA patches to this driver on an EASYSTOR LS-120 ATAPI floppy drive. + * + * Ver 0.1 Oct 17 96 Initial test version, mostly based on ide-tape.c. + * Ver 0.2 Oct 31 96 Minor changes. + * Ver 0.3 Dec 2 96 Fixed error recovery bug. + * Ver 0.4 Jan 26 97 Add support for the HDIO_GETGEO ioctl. + * Ver 0.5 Feb 21 97 Add partitions support. + * Use the minimum of the LBA and CHS capacities. + * Avoid hwgroup->rq == NULL on the last irq. + * Fix potential null dereferencing with DEBUG_LOG. + * Ver 0.6 Jul 3 97 Limit max sectors per read/write command to 64. + * Ver 0.7 Aug 4 97 Increase irq timeout from 10 to 100 seconds. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * Main Linux ide driver include file + */ +#include "ide.h" + +/* + * The following are used to debug the driver. + */ +#define IDEFLOPPY_DEBUG_LOG 0 +#define IDEFLOPPY_DEBUG_INFO 0 +#define IDEFLOPPY_DEBUG_BUGS 1 + +/* + * After each failed packet command we issue a request sense command + * and retry the packet command IDEFLOPPY_MAX_PC_RETRIES times. + */ +#define IDEFLOPPY_MAX_PC_RETRIES 3 + +/* + * With each packet command, we allocate a buffer of + * IDEFLOPPY_PC_BUFFER_SIZE bytes. + */ +#define IDEFLOPPY_PC_BUFFER_SIZE 256 + +/* + * In various places in the driver, we need to allocate storage + * for packet commands and requests, which will remain valid while + * we leave the driver to wait for an interrupt or a timeout event. + */ +#define IDEFLOPPY_PC_STACK (10 + IDEFLOPPY_MAX_PC_RETRIES) + +/* + * Some drives fail read/write requests with 64 or more sectors. + */ +#define IDEFLOPPY_MAX_SECTORS 64 + +/* + * Some drives require a longer irq timeout. + */ +#define IDEFLOPPY_WAIT_CMD (10 * WAIT_CMD) + +/* + * Our view of a packet command. + */ +typedef struct idefloppy_packet_command_s { + u8 c[12]; /* Actual packet bytes */ + int retries; /* On each retry, we increment retries */ + int error; /* Error code */ + int request_transfer; /* Bytes to transfer */ + int actually_transferred; /* Bytes actually transferred */ + int buffer_size; /* Size of our data buffer */ + char *b_data; /* Pointer which runs on the buffers */ + int b_count; /* Missing/Available data on the current buffer */ + struct request *rq; /* The corresponding request */ + byte *buffer; /* Data buffer */ + byte *current_position; /* Pointer into the above buffer */ + void (*callback) (ide_drive_t *); /* Called when this packet command is completed */ + byte pc_buffer[IDEFLOPPY_PC_BUFFER_SIZE]; /* Temporary buffer */ + unsigned int flags; /* Status/Action bit flags */ +} idefloppy_pc_t; + +/* + * Packet command flag bits. + */ +#define PC_ABORT 0 /* Set when an error is considered normal - We won't retry */ +#define PC_DMA_RECOMMENDED 2 /* 1 when we prefer to use DMA if possible */ +#define PC_DMA_IN_PROGRESS 3 /* 1 while DMA in progress */ +#define PC_DMA_ERROR 4 /* 1 when encountered problem during DMA */ +#define PC_WRITING 5 /* Data direction */ + +/* + * Removable Block Access Capabilities Page + */ +typedef struct { + unsigned page_code :6; /* Page code - Should be 0x1b */ + unsigned reserved1_6 :1; /* Reserved */ + unsigned ps :1; /* Should be 0 */ + u8 page_length; /* Page Length - Should be 0xa */ + unsigned reserved2 :6; + unsigned srfp :1; /* Supports reporting progress of format */ + unsigned sflp :1; /* System floppy type device */ + unsigned tlun :3; /* Total logical units supported by the device */ + unsigned reserved3 :3; + unsigned sml :1; /* Single / Multiple lun supported */ + unsigned ncd :1; /* Non cd optical device */ + u8 reserved[8]; +} idefloppy_capabilities_page_t; + +/* + * Flexible disk page. + */ +typedef struct { + unsigned page_code :6; /* Page code - Should be 0x5 */ + unsigned reserved1_6 :1; /* Reserved */ + unsigned ps :1; /* The device is capable of saving the page */ + u8 page_length; /* Page Length - Should be 0x1e */ + u16 transfer_rate; /* In kilobits per second */ + u8 heads, sectors; /* Number of heads, Number of sectors per track */ + u16 sector_size; /* Byes per sector */ + u16 cyls; /* Number of cylinders */ + u8 reserved10[10]; + u8 motor_delay; /* Motor off delay */ + u8 reserved21[7]; + u16 rpm; /* Rotations per minute */ + u8 reserved30[2]; +} idefloppy_flexible_disk_page_t; + +/* + * Format capacity + */ +typedef struct { + u8 reserved[3]; + u8 length; /* Length of the following descriptors in bytes */ +} idefloppy_capacity_header_t; + +typedef struct { + u32 blocks; /* Number of blocks */ + unsigned dc :2; /* Descriptor Code */ + unsigned reserved :6; + u8 length_msb; /* Block Length (MSB)*/ + u16 length; /* Block Length */ +} idefloppy_capacity_descriptor_t; + +#define CAPACITY_INVALID 0x00 +#define CAPACITY_UNFORMATTED 0x01 +#define CAPACITY_CURRENT 0x02 +#define CAPACITY_NO_CARTRIDGE 0x03 + +/* + * Most of our global data which we need to save even as we leave the + * driver due to an interrupt or a timer event is stored in a variable + * of type idefloppy_floppy_t, defined below. + */ +typedef struct { + ide_drive_t *drive; + + idefloppy_pc_t *pc; /* Current packet command */ + idefloppy_pc_t *failed_pc; /* Last failed packet command */ + idefloppy_pc_t pc_stack[IDEFLOPPY_PC_STACK];/* Packet command stack */ + int pc_stack_index; /* Next free packet command storage space */ + struct request rq_stack[IDEFLOPPY_PC_STACK]; + int rq_stack_index; /* We implement a circular array */ + + /* + * Last error information + */ + byte sense_key, asc, ascq; + + /* + * Device information + */ + int blocks, block_size, bs_factor; /* Current format */ + idefloppy_capacity_descriptor_t capacity; /* Last format capacity */ + idefloppy_flexible_disk_page_t flexible_disk_page; /* Copy of the flexible disk page */ + + unsigned int flags; /* Status/Action flags */ +} idefloppy_floppy_t; + +/* + * Floppy flag bits values. + */ +#define IDEFLOPPY_DRQ_INTERRUPT 0 /* DRQ interrupt device */ +#define IDEFLOPPY_MEDIA_CHANGED 1 /* Media may have changed */ +#define IDEFLOPPY_USE_READ12 2 /* Use READ12/WRITE12 or READ10/WRITE10 */ + +/* + * ATAPI floppy drive packet commands + */ +#define IDEFLOPPY_FORMAT_UNIT_CMD 0x04 +#define IDEFLOPPY_INQUIRY_CMD 0x12 +#define IDEFLOPPY_MODE_SELECT_CMD 0x55 +#define IDEFLOPPY_MODE_SENSE_CMD 0x5a +#define IDEFLOPPY_READ10_CMD 0x28 +#define IDEFLOPPY_READ12_CMD 0xa8 +#define IDEFLOPPY_READ_CAPACITY_CMD 0x23 +#define IDEFLOPPY_REQUEST_SENSE_CMD 0x03 +#define IDEFLOPPY_PREVENT_REMOVAL_CMD 0x1e +#define IDEFLOPPY_SEEK_CMD 0x2b +#define IDEFLOPPY_START_STOP_CMD 0x1b +#define IDEFLOPPY_TEST_UNIT_READY_CMD 0x00 +#define IDEFLOPPY_VERIFY_CMD 0x2f +#define IDEFLOPPY_WRITE10_CMD 0x2a +#define IDEFLOPPY_WRITE12_CMD 0xaa +#define IDEFLOPPY_WRITE_VERIFY_CMD 0x2e + +/* + * Defines for the mode sense command + */ +#define MODE_SENSE_CURRENT 0x00 +#define MODE_SENSE_CHANGEABLE 0x01 +#define MODE_SENSE_DEFAULT 0x02 +#define MODE_SENSE_SAVED 0x03 + +/* + * Special requests for our block device strategy routine. + */ +#define IDEFLOPPY_FIRST_RQ 90 + +/* + * IDEFLOPPY_PC_RQ is used to queue a packet command in the request queue. + */ +#define IDEFLOPPY_PC_RQ 90 + +#define IDEFLOPPY_LAST_RQ 90 + +/* + * A macro which can be used to check if a given request command + * originated in the driver or in the buffer cache layer. + */ +#define IDEFLOPPY_RQ_CMD(cmd) ((cmd >= IDEFLOPPY_FIRST_RQ) && (cmd <= IDEFLOPPY_LAST_RQ)) + +/* + * Error codes which are returned in rq->errors to the higher part + * of the driver. + */ +#define IDEFLOPPY_ERROR_GENERAL 101 + +/* + * The ATAPI Status Register. + */ +typedef union { + unsigned all :8; + struct { + unsigned check :1; /* Error occurred */ + unsigned idx :1; /* Reserved */ + unsigned corr :1; /* Correctable error occurred */ + unsigned drq :1; /* Data is request by the device */ + unsigned dsc :1; /* Media access command finished */ + unsigned reserved5 :1; /* Reserved */ + unsigned drdy :1; /* Ignored for ATAPI commands (ready to accept ATA command) */ + unsigned bsy :1; /* The device has access to the command block */ + } b; +} idefloppy_status_reg_t; + +/* + * The ATAPI error register. + */ +typedef union { + unsigned all :8; + struct { + unsigned ili :1; /* Illegal Length Indication */ + unsigned eom :1; /* End Of Media Detected */ + unsigned abrt :1; /* Aborted command - As defined by ATA */ + unsigned mcr :1; /* Media Change Requested - As defined by ATA */ + unsigned sense_key :4; /* Sense key of the last failed packet command */ + } b; +} idefloppy_error_reg_t; + +/* + * ATAPI Feature Register + */ +typedef union { + unsigned all :8; + struct { + unsigned dma :1; /* Using DMA or PIO */ + unsigned reserved321 :3; /* Reserved */ + unsigned reserved654 :3; /* Reserved (Tag Type) */ + unsigned reserved7 :1; /* Reserved */ + } b; +} idefloppy_feature_reg_t; + +/* + * ATAPI Byte Count Register. + */ +typedef union { + unsigned all :16; + struct { + unsigned low :8; /* LSB */ + unsigned high :8; /* MSB */ + } b; +} idefloppy_bcount_reg_t; + +/* + * ATAPI Interrupt Reason Register. + */ +typedef union { + unsigned all :8; + struct { + unsigned cod :1; /* Information transferred is command (1) or data (0) */ + unsigned io :1; /* The device requests us to read (1) or write (0) */ + unsigned reserved :6; /* Reserved */ + } b; +} idefloppy_ireason_reg_t; + +/* + * ATAPI floppy Drive Select Register + */ +typedef union { + unsigned all :8; + struct { + unsigned sam_lun :3; /* Logical unit number */ + unsigned reserved3 :1; /* Reserved */ + unsigned drv :1; /* The responding drive will be drive 0 (0) or drive 1 (1) */ + unsigned one5 :1; /* Should be set to 1 */ + unsigned reserved6 :1; /* Reserved */ + unsigned one7 :1; /* Should be set to 1 */ + } b; +} idefloppy_drivesel_reg_t; + +/* + * ATAPI Device Control Register + */ +typedef union { + unsigned all :8; + struct { + unsigned zero0 :1; /* Should be set to zero */ + unsigned nien :1; /* Device interrupt is disabled (1) or enabled (0) */ + unsigned srst :1; /* ATA software reset. ATAPI devices should use the new ATAPI srst. */ + unsigned one3 :1; /* Should be set to 1 */ + unsigned reserved4567 :4; /* Reserved */ + } b; +} idefloppy_control_reg_t; + +/* + * The following is used to format the general configuration word of + * the ATAPI IDENTIFY DEVICE command. + */ +struct idefloppy_id_gcw { + unsigned packet_size :2; /* Packet Size */ + unsigned reserved234 :3; /* Reserved */ + unsigned drq_type :2; /* Command packet DRQ type */ + unsigned removable :1; /* Removable media */ + unsigned device_type :5; /* Device type */ + unsigned reserved13 :1; /* Reserved */ + unsigned protocol :2; /* Protocol type */ +}; + +/* + * INQUIRY packet command - Data Format + */ +typedef struct { + unsigned device_type :5; /* Peripheral Device Type */ + unsigned reserved0_765 :3; /* Peripheral Qualifier - Reserved */ + unsigned reserved1_6t0 :7; /* Reserved */ + unsigned rmb :1; /* Removable Medium Bit */ + unsigned ansi_version :3; /* ANSI Version */ + unsigned ecma_version :3; /* ECMA Version */ + unsigned iso_version :2; /* ISO Version */ + unsigned response_format :4; /* Response Data Format */ + unsigned reserved3_45 :2; /* Reserved */ + unsigned reserved3_6 :1; /* TrmIOP - Reserved */ + unsigned reserved3_7 :1; /* AENC - Reserved */ + u8 additional_length; /* Additional Length (total_length-4) */ + u8 rsv5, rsv6, rsv7; /* Reserved */ + u8 vendor_id[8]; /* Vendor Identification */ + u8 product_id[16]; /* Product Identification */ + u8 revision_level[4]; /* Revision Level */ + u8 vendor_specific[20]; /* Vendor Specific - Optional */ + u8 reserved56t95[40]; /* Reserved - Optional */ + /* Additional information may be returned */ +} idefloppy_inquiry_result_t; + +/* + * REQUEST SENSE packet command result - Data Format. + */ +typedef struct { + unsigned error_code :7; /* Current error (0x70) */ + unsigned valid :1; /* The information field conforms to SFF-8070i */ + u8 reserved1 :8; /* Reserved */ + unsigned sense_key :4; /* Sense Key */ + unsigned reserved2_4 :1; /* Reserved */ + unsigned ili :1; /* Incorrect Length Indicator */ + unsigned reserved2_67 :2; + u32 information __attribute__ ((packed)); + u8 asl; /* Additional sense length (n-7) */ + u32 command_specific; /* Additional command specific information */ + u8 asc; /* Additional Sense Code */ + u8 ascq; /* Additional Sense Code Qualifier */ + u8 replaceable_unit_code; /* Field Replaceable Unit Code */ + u8 reserved[3]; + u8 pad[2]; /* Padding to 20 bytes */ +} idefloppy_request_sense_result_t; + +/* + * Pages of the SELECT SENSE / MODE SENSE packet commands. + */ +#define IDEFLOPPY_CAPABILITIES_PAGE 0x1b +#define IDEFLOPPY_FLEXIBLE_DISK_PAGE 0x05 + +/* + * Mode Parameter Header for the MODE SENSE packet command + */ +typedef struct { + u16 mode_data_length; /* Length of the following data transfer */ + u8 medium_type; /* Medium Type */ + unsigned reserved3 :7; + unsigned wp :1; /* Write protect */ + u8 reserved[4]; +} idefloppy_mode_parameter_header_t; + +#define IDEFLOPPY_MIN(a,b) ((a)<(b) ? (a):(b)) +#define IDEFLOPPY_MAX(a,b) ((a)>(b) ? (a):(b)) + +/* + * Too bad. The drive wants to send us data which we are not ready to accept. + * Just throw it away. + */ +static void idefloppy_discard_data (ide_drive_t *drive, unsigned int bcount) +{ + while (bcount--) + IN_BYTE (IDE_DATA_REG); +} + +#if IDEFLOPPY_DEBUG_BUGS +static void idefloppy_write_zeros (ide_drive_t *drive, unsigned int bcount) +{ + while (bcount--) + OUT_BYTE (0, IDE_DATA_REG); +} +#endif /* IDEFLOPPY_DEBUG_BUGS */ + +/* + * idefloppy_end_request is used to finish servicing a request. + * + * For read/write requests, we will call ide_end_request to pass to the + * next buffer. + */ +void idefloppy_end_request (byte uptodate, ide_hwgroup_t *hwgroup) +{ + ide_drive_t *drive = hwgroup->drive; + idefloppy_floppy_t *floppy = drive->floppy; + struct request *rq = hwgroup->rq; + int error; + +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "Reached idefloppy_end_request\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + switch (uptodate) { + case 0: error = IDEFLOPPY_ERROR_GENERAL; break; + case 1: error = 0; break; + default: error = uptodate; + } + if (error) + floppy->failed_pc = NULL; + if (!IDEFLOPPY_RQ_CMD (rq->cmd)) { + ide_end_request (uptodate, hwgroup); + return; + } + rq->errors = error; + ide_end_drive_cmd (drive, 0, 0); +} + +static void idefloppy_input_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount) +{ + struct request *rq = pc->rq; + struct buffer_head *bh = rq->bh; + int count; + + while (bcount) { + if (pc->b_count == bh->b_size) { + idefloppy_end_request (1, HWGROUP(drive)); + if ((bh = rq->bh) != NULL) + pc->b_count = 0; + } + if (bh == NULL) { + printk (KERN_ERR "%s: bh == NULL in idefloppy_input_buffers, bcount == %d\n", drive->name, bcount); + idefloppy_discard_data (drive, bcount); + return; + } + count = IDEFLOPPY_MIN (bh->b_size - pc->b_count, bcount); + atapi_input_bytes (drive, bh->b_data + pc->b_count, count); + bcount -= count; pc->b_count += count; + if (pc->b_count == bh->b_size) { + rq->sector += rq->current_nr_sectors; + rq->nr_sectors -= rq->current_nr_sectors; + } + } +} + +static void idefloppy_output_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount) +{ + struct request *rq = pc->rq; + struct buffer_head *bh = rq->bh; + int count; + + while (bcount) { + if (!pc->b_count) { + idefloppy_end_request (1, HWGROUP(drive)); + if ((bh = rq->bh) != NULL) { + pc->b_data = bh->b_data; + pc->b_count = bh->b_size; + } + } + if (bh == NULL) { + printk (KERN_ERR "%s: bh == NULL in idefloppy_output_buffers, bcount == %d\n", drive->name, bcount); + idefloppy_write_zeros (drive, bcount); + return; + } + count = IDEFLOPPY_MIN (pc->b_count, bcount); + atapi_output_bytes (drive, pc->b_data, count); + bcount -= count; pc->b_data += count; pc->b_count -= count; + if (!pc->b_count) { + rq->sector += rq->current_nr_sectors; + rq->nr_sectors -= rq->current_nr_sectors; + } + } +} + +#ifdef CONFIG_BLK_DEV_TRITON +static void idefloppy_update_buffers (ide_drive_t *drive, idefloppy_pc_t *pc) +{ + struct request *rq = pc->rq; + struct buffer_head *bh = rq->bh; + + while ((bh = rq->bh) != NULL) + idefloppy_end_request (1, HWGROUP(drive)); +} +#endif /* CONFIG_BLK_DEV_TRITON */ + +/* + * idefloppy_queue_pc_head generates a new packet command request in front + * of the request queue, before the current request, so that it will be + * processed immediately, on the next pass through the driver. + */ +static void idefloppy_queue_pc_head (ide_drive_t *drive,idefloppy_pc_t *pc,struct request *rq) +{ + ide_init_drive_cmd (rq); + rq->buffer = (char *) pc; + rq->cmd = IDEFLOPPY_PC_RQ; + (void) ide_do_drive_cmd (drive, rq, ide_preempt); +} + +static idefloppy_pc_t *idefloppy_next_pc_storage (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->floppy; + + if (floppy->pc_stack_index==IDEFLOPPY_PC_STACK) + floppy->pc_stack_index=0; + return (&floppy->pc_stack[floppy->pc_stack_index++]); +} + +static struct request *idefloppy_next_rq_storage (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->floppy; + + if (floppy->rq_stack_index==IDEFLOPPY_PC_STACK) + floppy->rq_stack_index=0; + return (&floppy->rq_stack[floppy->rq_stack_index++]); +} + +/* + * idefloppy_analyze_error is called on each failed packet command retry + * to analyze the request sense. + */ +static void idefloppy_analyze_error (ide_drive_t *drive,idefloppy_request_sense_result_t *result) +{ + idefloppy_floppy_t *floppy = drive->floppy; + + floppy->sense_key = result->sense_key; floppy->asc = result->asc; floppy->ascq = result->ascq; +#if IDEFLOPPY_DEBUG_LOG + if (floppy->failed_pc) + printk (KERN_INFO "ide-floppy: pc = %x, sense key = %x, asc = %x, ascq = %x\n",floppy->failed_pc->c[0],result->sense_key,result->asc,result->ascq); + else + printk (KERN_INFO "ide-floppy: sense key = %x, asc = %x, ascq = %x\n",result->sense_key,result->asc,result->ascq); +#endif /* IDEFLOPPY_DEBUG_LOG */ +} + +static void idefloppy_request_sense_callback (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->floppy; + +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "ide-floppy: Reached idefloppy_request_sense_callback\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + if (!floppy->pc->error) { + idefloppy_analyze_error (drive,(idefloppy_request_sense_result_t *) floppy->pc->buffer); + idefloppy_end_request (1,HWGROUP (drive)); + } else { + printk (KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n"); + idefloppy_end_request (0,HWGROUP (drive)); + } +} + +/* + * General packet command callback function. + */ +static void idefloppy_pc_callback (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->floppy; + +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_callback\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + idefloppy_end_request (floppy->pc->error ? 0:1, HWGROUP(drive)); +} + +/* + * idefloppy_init_pc initializes a packet command. + */ +static void idefloppy_init_pc (idefloppy_pc_t *pc) +{ + memset (pc->c, 0, 12); + pc->retries = 0; + pc->flags = 0; + pc->request_transfer = 0; + pc->buffer = pc->pc_buffer; + pc->buffer_size = IDEFLOPPY_PC_BUFFER_SIZE; + pc->b_data = NULL; + pc->callback = &idefloppy_pc_callback; +} + +static void idefloppy_create_request_sense_cmd (idefloppy_pc_t *pc) +{ + idefloppy_init_pc (pc); + pc->c[0] = IDEFLOPPY_REQUEST_SENSE_CMD; + pc->c[4] = 255; + pc->request_transfer = 18; + pc->callback = &idefloppy_request_sense_callback; +} + +/* + * idefloppy_retry_pc is called when an error was detected during the + * last packet command. We queue a request sense packet command in + * the head of the request list. + */ +static void idefloppy_retry_pc (ide_drive_t *drive) +{ + idefloppy_pc_t *pc; + struct request *rq; + idefloppy_error_reg_t error; + + error.all = IN_BYTE (IDE_ERROR_REG); + pc = idefloppy_next_pc_storage (drive); + rq = idefloppy_next_rq_storage (drive); + idefloppy_create_request_sense_cmd (pc); + idefloppy_queue_pc_head (drive, pc, rq); +} + +/* + * idefloppy_pc_intr is the usual interrupt handler which will be called + * during a packet command. + */ +static void idefloppy_pc_intr (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->floppy; + idefloppy_status_reg_t status; + idefloppy_bcount_reg_t bcount; + idefloppy_ireason_reg_t ireason; + idefloppy_pc_t *pc=floppy->pc; + struct request *rq = pc->rq; + unsigned int temp; + +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_intr interrupt handler\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + +#ifdef CONFIG_BLK_DEV_TRITON + if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) { + if (HWIF(drive)->dmaproc(ide_dma_status_bad, drive)) { + set_bit (PC_DMA_ERROR, &pc->flags); + } else { + pc->actually_transferred=pc->request_transfer; + idefloppy_update_buffers (drive, pc); + } + (void) (HWIF(drive)->dmaproc(ide_dma_abort, drive)); /* End DMA */ +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "ide-floppy: DMA finished\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + } +#endif /* CONFIG_BLK_DEV_TRITON */ + + status.all = GET_STAT(); /* Clear the interrupt */ + + if (!status.b.drq) { /* No more interrupts */ +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred); +#endif /* IDEFLOPPY_DEBUG_LOG */ + clear_bit (PC_DMA_IN_PROGRESS, &pc->flags); + + sti(); + + if (status.b.check || test_bit (PC_DMA_ERROR, &pc->flags)) { /* Error detected */ +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "ide-floppy: %s: I/O error, ",drive->name); +#endif /* IDEFLOPPY_DEBUG_LOG */ + rq->errors++; + if (pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) { + printk (KERN_ERR "ide-floppy: I/O error in request sense command\n"); + ide_do_reset (drive); + return; + } + idefloppy_retry_pc (drive); /* Retry operation */ + return; + } + pc->error = 0; + if (floppy->failed_pc == pc) + floppy->failed_pc=NULL; + pc->callback(drive); /* Command finished - Call the callback function */ + return; + } +#ifdef CONFIG_BLK_DEV_TRITON + if (clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) { + printk (KERN_ERR "ide-floppy: The floppy wants to issue more interrupts in DMA mode\n"); + printk (KERN_ERR "ide-floppy: DMA disabled, reverting to PIO\n"); + drive->using_dma=0; + ide_do_reset (drive); + return; + } +#endif /* CONFIG_BLK_DEV_TRITON */ + bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG); /* Get the number of bytes to transfer */ + bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG); /* on this interrupt */ + ireason.all=IN_BYTE (IDE_IREASON_REG); + + if (ireason.b.cod) { + printk (KERN_ERR "ide-floppy: CoD != 0 in idefloppy_pc_intr\n"); + ide_do_reset (drive); + return; + } + if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) { /* Hopefully, we will never get here */ + printk (KERN_ERR "ide-floppy: We wanted to %s, ", ireason.b.io ? "Write":"Read"); + printk (KERN_ERR "but the floppy wants us to %s !\n",ireason.b.io ? "Read":"Write"); + ide_do_reset (drive); + return; + } + if (!test_bit (PC_WRITING, &pc->flags)) { /* Reading - Check that we have enough space */ + temp = pc->actually_transferred + bcount.all; + if ( temp > pc->request_transfer) { + if (temp > pc->buffer_size) { + printk (KERN_ERR "ide-floppy: The floppy wants to send us more data than expected - discarding data\n"); + idefloppy_discard_data (drive,bcount.all); + ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD); + return; + } +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_NOTICE "ide-floppy: The floppy wants to send us more data than expected - allowing transfer\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + } + } + if (test_bit (PC_WRITING, &pc->flags)) { + if (pc->buffer != NULL) + atapi_output_bytes (drive,pc->current_position,bcount.all); /* Write the current buffer */ + else + idefloppy_output_buffers (drive, pc, bcount.all); + } else { + if (pc->buffer != NULL) + atapi_input_bytes (drive,pc->current_position,bcount.all); /* Read the current buffer */ + else + idefloppy_input_buffers (drive, pc, bcount.all); + } + pc->actually_transferred+=bcount.all; /* Update the current position */ + pc->current_position+=bcount.all; + + ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD); /* And set the interrupt handler again */ +} + +static void idefloppy_transfer_pc (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->floppy; + idefloppy_ireason_reg_t ireason; + + if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { + printk (KERN_ERR "ide-floppy: Strange, packet command initiated yet DRQ isn't asserted\n"); + return; + } + ireason.all=IN_BYTE (IDE_IREASON_REG); + if (!ireason.b.cod || ireason.b.io) { + printk (KERN_ERR "ide-floppy: (IO,CoD) != (0,1) while issuing a packet command\n"); + ide_do_reset (drive); + return; + } + ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD); /* Set the interrupt routine */ + atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */ +} + +/* + * Issue a packet command + */ +static void idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc) +{ + idefloppy_floppy_t *floppy = drive->floppy; + idefloppy_bcount_reg_t bcount; + int dma_ok = 0; + +#if IDEFLOPPY_DEBUG_BUGS + if (floppy->pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD && pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) { + printk (KERN_ERR "ide-floppy: possible ide-floppy.c bug - Two request sense in serial were issued\n"); + } +#endif /* IDEFLOPPY_DEBUG_BUGS */ + + if (floppy->failed_pc == NULL && pc->c[0] != IDEFLOPPY_REQUEST_SENSE_CMD) + floppy->failed_pc=pc; + floppy->pc=pc; /* Set the current packet command */ + + if (pc->retries > IDEFLOPPY_MAX_PC_RETRIES || test_bit (PC_ABORT, &pc->flags)) { + /* + * We will "abort" retrying a packet command in case + * a legitimate error code was received. + */ + if (!test_bit (PC_ABORT, &pc->flags)) { + printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n", + drive->name, pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq); + pc->error = IDEFLOPPY_ERROR_GENERAL; /* Giving up */ + } + floppy->failed_pc=NULL; + pc->callback(drive); + return; + } +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "Retry number - %d\n",pc->retries); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + pc->retries++; + pc->actually_transferred=0; /* We haven't transferred any data yet */ + pc->current_position=pc->buffer; + bcount.all=pc->request_transfer; /* Request to transfer the entire buffer at once */ + +#ifdef CONFIG_BLK_DEV_TRITON + if (clear_bit (PC_DMA_ERROR, &pc->flags)) { + printk (KERN_WARNING "ide-floppy: DMA disabled, reverting to PIO\n"); + drive->using_dma=0; + } + if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma) + dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive); +#endif /* CONFIG_BLK_DEV_TRITON */ + + OUT_BYTE (drive->ctl,IDE_CONTROL_REG); + OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG); /* Use PIO/DMA */ + OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG); + OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG); + OUT_BYTE (drive->select.all,IDE_SELECT_REG); + +#ifdef CONFIG_BLK_DEV_TRITON + if (dma_ok) { /* Begin DMA, if necessary */ + set_bit (PC_DMA_IN_PROGRESS, &pc->flags); + (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); + } +#endif /* CONFIG_BLK_DEV_TRITON */ + + if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) { + ide_set_handler (drive, &idefloppy_transfer_pc, IDEFLOPPY_WAIT_CMD); + OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* Issue the packet command */ + } else { + OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); + idefloppy_transfer_pc (drive); + } +} + +static void idefloppy_rw_callback (ide_drive_t *drive) +{ +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "ide-floppy: Reached idefloppy_rw_callback\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + idefloppy_end_request(1, HWGROUP(drive)); + return; +} + +static void idefloppy_create_prevent_cmd (idefloppy_pc_t *pc, int prevent) +{ +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "ide-floppy: creating prevent removal command, prevent = %d\n", prevent); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + idefloppy_init_pc (pc); + pc->c[0] = IDEFLOPPY_PREVENT_REMOVAL_CMD; + pc->c[4] = prevent; +} + +static void idefloppy_create_read_capacity_cmd (idefloppy_pc_t *pc) +{ + idefloppy_init_pc (pc); + pc->c[0] = IDEFLOPPY_READ_CAPACITY_CMD; + pc->c[7] = 255; + pc->c[8] = 255; +} + +/* + * A mode sense command is used to "sense" floppy parameters. + */ +static void idefloppy_create_mode_sense_cmd (idefloppy_pc_t *pc, byte page_code, byte type) +{ + unsigned short length = sizeof (idefloppy_mode_parameter_header_t); + + idefloppy_init_pc (pc); + pc->c[0] = IDEFLOPPY_MODE_SENSE_CMD; + pc->c[1] = 0; + pc->c[2] = page_code + (type << 6); + + switch (page_code) { + case IDEFLOPPY_CAPABILITIES_PAGE: + length += 12; + break; + case IDEFLOPPY_FLEXIBLE_DISK_PAGE: + length += 32; + break; + default: + printk (KERN_ERR "ide-floppy: unsupported page code in create_mode_sense_cmd\n"); + } + put_unaligned (htons (length), (unsigned short *) &pc->c[7]); + pc->request_transfer = length; +} + +static void idefloppy_create_start_stop_cmd (idefloppy_pc_t *pc, int start) +{ + idefloppy_init_pc (pc); + pc->c[0] = IDEFLOPPY_START_STOP_CMD; + pc->c[4] = start; +} + +static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq, unsigned long sector) +{ + int block = sector / floppy->bs_factor; + int blocks = IDEFLOPPY_MIN(rq->nr_sectors / floppy->bs_factor, IDEFLOPPY_MAX_SECTORS); + +#if IDEFLOPPY_DEBUG_LOG + printk ("create_rw1%d_cmd: block == %d, blocks == %d\n", + 2 * test_bit (IDEFLOPPY_USE_READ12, &floppy->flags), block, blocks); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + idefloppy_init_pc (pc); + if (test_bit (IDEFLOPPY_USE_READ12, &floppy->flags)) { + pc->c[0] = rq->cmd == READ ? IDEFLOPPY_READ12_CMD : IDEFLOPPY_WRITE12_CMD; + put_unaligned (htonl (blocks), (unsigned int *) &pc->c[6]); + } else { + pc->c[0] = rq->cmd == READ ? IDEFLOPPY_READ10_CMD : IDEFLOPPY_WRITE10_CMD; + put_unaligned (htons (blocks), (unsigned short *) &pc->c[7]); + } + put_unaligned (htonl (block), (unsigned int *) &pc->c[2]); + pc->callback = &idefloppy_rw_callback; + pc->rq = rq; + pc->b_data = rq->buffer; + pc->b_count = rq->cmd == READ ? 0 : rq->bh->b_size; + if (rq->cmd == WRITE) + set_bit (PC_WRITING, &pc->flags); + pc->buffer = NULL; + pc->request_transfer = pc->buffer_size = blocks * floppy->block_size; + set_bit (PC_DMA_RECOMMENDED, &pc->flags); +} + +/* + * idefloppy_do_request is our request handling function. + */ +void idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) +{ + idefloppy_floppy_t *floppy = drive->floppy; + idefloppy_pc_t *pc; + +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors); + printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + if (rq->errors >= ERROR_MAX) { + if (floppy->failed_pc != NULL) + printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n", + drive->name, floppy->failed_pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq); + else + printk (KERN_ERR "ide-floppy: %s: I/O error\n", drive->name); + idefloppy_end_request (0, HWGROUP(drive)); + return; + } + switch (rq->cmd) { + case READ: + case WRITE: + if (rq->sector % floppy->bs_factor || rq->nr_sectors % floppy->bs_factor) { + printk ("%s: unsupported r/w request size\n", drive->name); + idefloppy_end_request (0, HWGROUP(drive)); + return; + } + pc = idefloppy_next_pc_storage (drive); + idefloppy_create_rw_cmd (floppy, pc, rq, block); + break; + case IDEFLOPPY_PC_RQ: + pc = (idefloppy_pc_t *) rq->buffer; + break; + default: + printk (KERN_ERR "ide-floppy: unsupported command %x in request queue\n", rq->cmd); + idefloppy_end_request (0,HWGROUP (drive)); + return; + } + pc->rq = rq; + idefloppy_issue_pc (drive, pc); +} + +/* + * idefloppy_queue_pc_tail adds a special packet command request to the + * tail of the request queue, and waits for it to be serviced. + */ +static int idefloppy_queue_pc_tail (ide_drive_t *drive,idefloppy_pc_t *pc) +{ + struct request rq; + + ide_init_drive_cmd (&rq); + rq.buffer = (char *) pc; + rq.cmd = IDEFLOPPY_PC_RQ; + return ide_do_drive_cmd (drive, &rq, ide_wait); +} + +/* + * Look at the flexible disk page parameters. We will ignore the CHS + * capacity parameters and use the LBA parameters instead. + */ +static int idefloppy_get_flexible_disk_page (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->floppy; + idefloppy_pc_t pc; + idefloppy_mode_parameter_header_t *header; + idefloppy_flexible_disk_page_t *page; + int capacity, lba_capacity; + + idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_FLEXIBLE_DISK_PAGE, MODE_SENSE_CURRENT); + if (idefloppy_queue_pc_tail (drive,&pc)) { + printk (KERN_ERR "ide-floppy: Can't get flexible disk page parameters\n"); + return 1; + } + header = (idefloppy_mode_parameter_header_t *) pc.buffer; + page = (idefloppy_flexible_disk_page_t *) (header + 1); + + page->transfer_rate = ntohs (page->transfer_rate); + page->sector_size = ntohs (page->sector_size); + page->cyls = ntohs (page->cyls); + page->rpm = ntohs (page->rpm); + capacity = page->cyls * page->heads * page->sectors * page->sector_size; + if (memcmp (page, &floppy->flexible_disk_page, sizeof (idefloppy_flexible_disk_page_t))) + printk (KERN_INFO "%s: %dkB, %d/%d/%d CHS, %d kBps, %d sector size, %d rpm\n", + drive->name, capacity / 1024, page->cyls, page->heads, page->sectors, + page->transfer_rate / 8, page->sector_size, page->rpm); + + floppy->flexible_disk_page = *page; + drive->bios_cyl = page->cyls; + drive->bios_head = page->heads; + drive->bios_sect = page->sectors; + lba_capacity = floppy->blocks * floppy->block_size; + if (capacity != lba_capacity) { + printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n", + drive->name, capacity, lba_capacity); + capacity = IDEFLOPPY_MIN(capacity, lba_capacity); + floppy->blocks = floppy->block_size ? capacity / floppy->block_size : 0; + } + return 0; +} + +/* + * Determine if a media is present in the floppy drive, and if so, + * its LBA capacity. + */ +static int idefloppy_get_capacity (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->floppy; + idefloppy_pc_t pc; + idefloppy_capacity_header_t *header; + idefloppy_capacity_descriptor_t *descriptor; + int i, descriptors, rc = 1, blocks, length; + + drive->bios_cyl = 0; + drive->bios_head = drive->bios_sect = 0; + floppy->blocks = floppy->bs_factor = 0; + drive->part[0].nr_sects = 0; + + idefloppy_create_read_capacity_cmd (&pc); + if (idefloppy_queue_pc_tail (drive, &pc)) { + printk (KERN_ERR "ide-floppy: Can't get floppy parameters\n"); + return 1; + } + header = (idefloppy_capacity_header_t *) pc.buffer; + descriptors = header->length / sizeof (idefloppy_capacity_descriptor_t); + descriptor = (idefloppy_capacity_descriptor_t *) (header + 1); + for (i = 0; i < descriptors; i++, descriptor++) { + blocks = descriptor->blocks = ntohl (descriptor->blocks); + length = descriptor->length = ntohs (descriptor->length); + if (!i && descriptor->dc == CAPACITY_CURRENT) { + if (memcmp (descriptor, &floppy->capacity, sizeof (idefloppy_capacity_descriptor_t))) + printk (KERN_INFO "%s: %dkB, %d blocks, %d sector size\n", drive->name, blocks * length / 1024, blocks, length); + floppy->capacity = *descriptor; + if (!length || length % 512) + printk (KERN_ERR "%s: %d bytes block size not supported\n", drive->name, length); + else { + floppy->blocks = blocks; + floppy->block_size = length; + if ((floppy->bs_factor = length / 512) != 1) + printk (KERN_NOTICE "%s: warning: non 512 bytes block size not fully supported\n", drive->name); + rc = 0; + } + } +#if IDEFLOPPY_DEBUG_INFO + if (!i) printk (KERN_INFO "Descriptor 0 Code: %d\n", descriptor->dc); + printk (KERN_INFO "Descriptor %d: %dkB, %d blocks, %d sector size\n", i, blocks * length / 1024, blocks, length); +#endif /* IDEFLOPPY_DEBUG_INFO */ + } + (void) idefloppy_get_flexible_disk_page (drive); + drive->part[0].nr_sects = floppy->blocks * floppy->bs_factor; + return rc; +} + +/* + * Our special ide-floppy ioctl's. + * + * Currently there aren't any ioctl's. + */ +int idefloppy_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return -EIO; +} + +/* + * Our open/release functions + */ +int idefloppy_open (struct inode *inode, struct file *filp, ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->floppy; + idefloppy_pc_t pc; + +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "Reached idefloppy_open\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + MOD_INC_USE_COUNT; + if (drive->usage == 1) { + idefloppy_create_start_stop_cmd (&pc, 1); + (void) idefloppy_queue_pc_tail (drive, &pc); + if (idefloppy_get_capacity (drive)) { + drive->usage--; + MOD_DEC_USE_COUNT; + return -EIO; + } + set_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags); + idefloppy_create_prevent_cmd (&pc, 1); + (void) idefloppy_queue_pc_tail (drive, &pc); + check_disk_change(inode->i_rdev); + } + return 0; +} + +void idefloppy_release (struct inode *inode, struct file *filp, ide_drive_t *drive) +{ + idefloppy_pc_t pc; + +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "Reached idefloppy_release\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + if (!drive->usage) { + invalidate_buffers (inode->i_rdev); + idefloppy_create_prevent_cmd (&pc, 0); + (void) idefloppy_queue_pc_tail (drive, &pc); + } + MOD_DEC_USE_COUNT; +} + +/* + * Check media change. Use a simple algorithm for now. + */ +int idefloppy_media_change (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->floppy; + + return clear_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags); +} + +/* + * Return the current floppy capacity to ide.c. + */ +unsigned long idefloppy_capacity (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->floppy; + unsigned long capacity = floppy->blocks * floppy->bs_factor; + + return capacity; +} + +/* + * idefloppy_identify_device checks if we can support a drive, + * based on the ATAPI IDENTIFY command results. + */ +int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id) +{ + struct idefloppy_id_gcw gcw; + idefloppy_floppy_t *floppy; +#if IDEFLOPPY_DEBUG_INFO + unsigned short mask,i; + char buffer[80]; +#endif /* IDEFLOPPY_DEBUG_INFO */ + + *((unsigned short *) &gcw) = id->config; + +#if IDEFLOPPY_DEBUG_INFO + printk (KERN_INFO "Dumping ATAPI Identify Device floppy parameters\n"); + switch (gcw.protocol) { + case 0: case 1: sprintf (buffer, "ATA");break; + case 2: sprintf (buffer, "ATAPI");break; + case 3: sprintf (buffer, "Reserved (Unknown to ide-floppy)");break; + } + printk (KERN_INFO "Protocol Type: %s\n", buffer); + switch (gcw.device_type) { + case 0: sprintf (buffer, "Direct-access Device");break; + case 1: sprintf (buffer, "Streaming Tape Device");break; + case 2: case 3: case 4: sprintf (buffer, "Reserved");break; + case 5: sprintf (buffer, "CD-ROM Device");break; + case 6: sprintf (buffer, "Reserved"); + case 7: sprintf (buffer, "Optical memory Device");break; + case 0x1f: sprintf (buffer, "Unknown or no Device type");break; + default: sprintf (buffer, "Reserved"); + } + printk (KERN_INFO "Device Type: %x - %s\n", gcw.device_type, buffer); + printk (KERN_INFO "Removable: %s\n",gcw.removable ? "Yes":"No"); + switch (gcw.drq_type) { + case 0: sprintf (buffer, "Microprocessor DRQ");break; + case 1: sprintf (buffer, "Interrupt DRQ");break; + case 2: sprintf (buffer, "Accelerated DRQ");break; + case 3: sprintf (buffer, "Reserved");break; + } + printk (KERN_INFO "Command Packet DRQ Type: %s\n", buffer); + switch (gcw.packet_size) { + case 0: sprintf (buffer, "12 bytes");break; + case 1: sprintf (buffer, "16 bytes");break; + default: sprintf (buffer, "Reserved");break; + } + printk (KERN_INFO "Command Packet Size: %s\n", buffer); + printk (KERN_INFO "Model: %s\n",id->model); + printk (KERN_INFO "Firmware Revision: %s\n",id->fw_rev); + printk (KERN_INFO "Serial Number: %s\n",id->serial_no); + printk (KERN_INFO "Write buffer size(?): %d bytes\n",id->buf_size*512); + printk (KERN_INFO "DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n"); + printk (KERN_INFO "LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n"); + printk (KERN_INFO "IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n"); + printk (KERN_INFO "IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n"); + printk (KERN_INFO "ATAPI overlap supported: %s",id->capability & 0x20 ? "Yes\n":"No\n"); + printk (KERN_INFO "PIO Cycle Timing Category: %d\n",id->tPIO); + printk (KERN_INFO "DMA Cycle Timing Category: %d\n",id->tDMA); + printk (KERN_INFO "Single Word DMA supported modes:\n"); + for (i=0,mask=1;i<8;i++,mask=mask << 1) { + if (id->dma_1word & mask) + printk (KERN_INFO " Mode %d%s\n", i, (id->dma_1word & (mask << 8)) ? " (active)" : ""); + } + printk (KERN_INFO "Multi Word DMA supported modes:\n"); + for (i=0,mask=1;i<8;i++,mask=mask << 1) { + if (id->dma_mword & mask) + printk (KERN_INFO " Mode %d%s\n", i, (id->dma_mword & (mask << 8)) ? " (active)" : ""); + } + if (id->field_valid & 0x0002) { + printk (KERN_INFO "Enhanced PIO Modes: %s\n",id->eide_pio_modes & 1 ? "Mode 3":"None"); + if (id->eide_dma_min == 0) + sprintf (buffer, "Not supported"); + else + sprintf (buffer, "%d ns",id->eide_dma_min); + printk (KERN_INFO "Minimum Multi-word DMA cycle per word: %s\n", buffer); + if (id->eide_dma_time == 0) + sprintf (buffer, "Not supported"); + else + sprintf (buffer, "%d ns",id->eide_dma_time); + printk (KERN_INFO "Manufacturer\'s Recommended Multi-word cycle: %s\n", buffer); + if (id->eide_pio == 0) + sprintf (buffer, "Not supported"); + else + sprintf (buffer, "%d ns",id->eide_pio); + printk (KERN_INFO "Minimum PIO cycle without IORDY: %s\n", buffer); + if (id->eide_pio_iordy == 0) + sprintf (buffer, "Not supported"); + else + sprintf (buffer, "%d ns",id->eide_pio_iordy); + printk (KERN_INFO "Minimum PIO cycle with IORDY: %s\n", buffer); + } else + printk (KERN_INFO "According to the device, fields 64-70 are not valid.\n"); +#endif /* IDEFLOPPY_DEBUG_INFO */ + + if (gcw.protocol != 2) + printk (KERN_ERR "ide-floppy: Protocol is not ATAPI\n"); + else if (gcw.device_type != 0) + printk (KERN_ERR "ide-floppy: Device type is not set to floppy\n"); + else if (!gcw.removable) + printk (KERN_ERR "ide-floppy: The removable flag is not set\n"); + else if (gcw.drq_type == 3) + printk (KERN_ERR "ide-floppy: Sorry, DRQ type %d not supported\n", gcw.drq_type); + else if (gcw.packet_size != 0) + printk (KERN_ERR "ide-floppy: Packet size is not 12 bytes long\n"); + else if ((floppy = (idefloppy_floppy_t *) kmalloc (sizeof (idefloppy_floppy_t), GFP_KERNEL)) == NULL) + printk (KERN_ERR "ide-floppy: %s: Can't allocate a floppy structure\n", drive->name); + else { + drive->floppy = floppy; + return 1; + } + printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name); + return 0; +} + +/* + * idefloppy_get_capabilities asks the floppy about its various + * parameters. + */ +static void idefloppy_get_capabilities (ide_drive_t *drive) +{ + idefloppy_pc_t pc; + idefloppy_mode_parameter_header_t *header; + idefloppy_capabilities_page_t *capabilities; + + idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_CAPABILITIES_PAGE, MODE_SENSE_CURRENT); + if (idefloppy_queue_pc_tail (drive,&pc)) { + printk (KERN_ERR "ide-floppy: Can't get drive capabilities\n"); + return; + } + header = (idefloppy_mode_parameter_header_t *) pc.buffer; + capabilities = (idefloppy_capabilities_page_t *) (header + 1); + + if (!capabilities->sflp) + printk (KERN_INFO "%s: Warning - system floppy device bit is not set\n", drive->name); + +#if IDEFLOPPY_DEBUG_INFO + printk (KERN_INFO "Dumping the results of the MODE SENSE packet command\n"); + printk (KERN_INFO "Mode Parameter Header:\n"); + printk (KERN_INFO "Mode Data Length - %d\n",header->mode_data_length); + printk (KERN_INFO "Medium Type - %d\n",header->medium_type); + printk (KERN_INFO "WP - %d\n",header->wp); + + printk (KERN_INFO "Capabilities Page:\n"); + printk (KERN_INFO "Page code - %d\n",capabilities->page_code); + printk (KERN_INFO "Page length - %d\n",capabilities->page_length); + printk (KERN_INFO "PS - %d\n",capabilities->ps); + printk (KERN_INFO "System Floppy Type device - %s\n",capabilities->sflp ? "Yes":"No"); + printk (KERN_INFO "Supports Reporting progress of Format - %s\n",capabilities->srfp ? "Yes":"No"); + printk (KERN_INFO "Non CD Optical device - %s\n",capabilities->ncd ? "Yes":"No"); + printk (KERN_INFO "Multiple LUN support - %s\n",capabilities->sml ? "Yes":"No"); + printk (KERN_INFO "Total LUN supported - %s\n",capabilities->tlun ? "Yes":"No"); +#endif /* IDEFLOPPY_DEBUG_INFO */ +} + +/* + * Driver initialization. + */ +void idefloppy_setup (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->floppy; + struct idefloppy_id_gcw gcw; + + *((unsigned short *) &gcw) = drive->id->config; + drive->ready_stat = 0; + memset (floppy, 0, sizeof (idefloppy_floppy_t)); + floppy->drive = drive; + floppy->pc = floppy->pc_stack; + if (gcw.drq_type == 1) + set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags); + + idefloppy_get_capabilities (drive); + (void) idefloppy_get_capacity (drive); +} diff -u --recursive --new-file v2.0.30/linux/drivers/block/ide.c linux/drivers/block/ide.c --- v2.0.30/linux/drivers/block/ide.c Tue Nov 19 06:21:06 1996 +++ linux/drivers/block/ide.c Mon Aug 4 11:45:55 1997 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/ide.c Version 5.52 Sep 24, 1996 + * linux/drivers/block/ide.c Version 5.53 Jun 24, 1997 * * Copyright (C) 1994-1996 Linus Torvalds & authors (see below) */ @@ -261,6 +261,17 @@ * change delay_10ms() to delay_50ms() to fix problems * Version 5.52 fix incorrect invalidation of removable devices * add "hdx=slow" command line option + * Version 5.53 add ATAPI floppy drive support + * change default media for type 0 to floppy + * add support for Exabyte Nest + * add missing set_blocksize() in revalidate_disk() + * handle bad status bit sequencing in ide_wait_stat() + * support partition table translations with 255 heads + * probe all interfaces by default + * add probe for the i82371AB chipset + * acknowledge media change on removable drives + * add work-around for BMI drives + * remove "LBA" from boot messages * * Some additional driver compile-time options are in ide.h * @@ -367,7 +378,6 @@ /* fill in any non-zero initial values */ hwif->index = index; - hwif->noprobe = (index > 1); hwif->io_base = default_io_base[index]; hwif->ctl_port = hwif->io_base ? hwif->io_base+0x206 : 0x000; #ifdef CONFIG_BLK_DEV_HD @@ -536,6 +546,29 @@ } /* + * The following routines are mainly used by the ATAPI drivers. + * + * These routines will round up any request for an odd number of bytes, + * so if an odd bytecount is specified, be sure that there's at least one + * extra byte allocated for the buffer. + */ +void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) +{ + ++bytecount; + ide_input_data (drive, buffer, bytecount / 4); + if ((bytecount & 0x03) >= 2) + insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1); +} + +void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) +{ + ++bytecount; + ide_output_data (drive, buffer, bytecount / 4); + if ((bytecount & 0x03) >= 2) + outsw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1); +} + +/* * This should get invoked any time we exit the driver to * wait for an interrupt response from a drive. handler() points * at the appropriate code to handle the next interrupt, and a @@ -593,6 +626,10 @@ if (!drive->present) return 0; +#ifdef CONFIG_BLK_DEV_IDEFLOPPY + if (drive->media == ide_floppy) + return idefloppy_capacity(drive); +#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ if (drive->media != ide_disk) return 0x7fffffff; /* cdrom or tape */ drive->select.b.lba = 0; @@ -625,8 +662,13 @@ if (drive->present && drive->media == ide_tape) idetape_setup(drive); #endif /* CONFIG_BLK_DEV_IDETAPE */ +#ifdef CONFIG_BLK_DEV_IDEFLOPPY + if (drive->present && drive->media == ide_floppy) + idefloppy_setup(drive); +#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ drive->part[0].nr_sects = current_capacity(drive); - if (!drive->present || drive->media != ide_disk) { + if (!drive->present || (drive->media != ide_disk && drive->media != ide_floppy) || + !drive->part[0].nr_sects) { drive->part[0].start_sect = -1; /* skip partition check */ } } @@ -1005,6 +1047,8 @@ rq->errors = ERROR_MAX; else if (err & TRK0_ERR) /* help it find track zero */ rq->errors |= ERROR_RECAL; + else if (err & MC_ERR) + drive->special.b.mc = 1; } if ((stat & DRQ_STAT) && rq->cmd != WRITE) try_to_flush_leftover_data(drive); @@ -1017,9 +1061,20 @@ if (drive->media == ide_tape) { rq->errors = 0; idetape_end_request(0, HWGROUP(drive)); - } - else + } else #endif /* CONFIG_BLK_DEV_IDETAPE */ +#ifdef CONFIG_BLK_DEV_IDEFLOPPY + if (drive->media == ide_floppy) { + rq->errors = 0; + idefloppy_end_request(0, HWGROUP(drive)); + } else +#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ +#ifdef CONFIG_BLK_DEV_IDESCSI + if (drive->media == ide_scsi) { + rq->errors = 0; + idescsi_end_request(0, HWGROUP(drive)); + } else +#endif /* CONFIG_BLK_DEV_IDESCSI */ ide_end_request(0, HWGROUP(drive)); } else { @@ -1230,6 +1285,19 @@ } /* + * mc_intr() is invoked on completion of a WIN_ACKMC cmd. + */ +static void mc_intr (ide_drive_t *drive) +{ + byte stat = GET_STAT(); + + sti(); + if (!OK_STAT(stat,READY_STAT,BAD_STAT)) + ide_error(drive, "mc_intr", stat); + drive->special.b.mc = 0; +} + +/* * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD. */ static void drive_cmd_intr (ide_drive_t *drive) @@ -1265,7 +1333,7 @@ #endif if (s->b.set_geometry) { s->b.set_geometry = 0; - if (drive->media == ide_disk) { + if (drive->media == ide_disk && !drive->no_geom) { OUT_BYTE(drive->sect,IDE_SECTOR_REG); OUT_BYTE(drive->cyl,IDE_LCYL_REG); OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG); @@ -1291,6 +1359,10 @@ ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr); } else drive->mult_req = 0; + } else if (s->b.mc) { + s->b.mc = 0; + if (drive->media == ide_disk && !IS_PROMISE_DRIVE) + ide_cmd(drive, WIN_ACKMC, drive->sect, &mc_intr); } else if (s->all) { int special = s->all; s->all = 0; @@ -1314,27 +1386,24 @@ byte stat; unsigned long flags; -test: - udelay(1); /* spec allows drive 400ns to change "BUSY" */ - if (OK_STAT((stat = GET_STAT()), good, bad)) - return 0; /* fast exit for most frequent case */ - if (!(stat & BUSY_STAT)) { - ide_error(drive, "status error", stat); - return 1; - } - - save_flags(flags); - sti(); - timeout += jiffies; - do { - if (!((stat = GET_STAT()) & BUSY_STAT)) { - restore_flags(flags); - goto test; + udelay(1); /* spec allows drive 400ns to assert "BUSY" */ + if ((stat = GET_STAT()) & BUSY_STAT) { + save_flags(flags); + sti(); + timeout += jiffies; + while ((stat = GET_STAT()) & BUSY_STAT) { + if (jiffies > timeout) { + restore_flags(flags); + ide_error(drive, "status timeout", stat); + return 1; + } } - } while (jiffies <= timeout); - - restore_flags(flags); - ide_error(drive, "status timeout", GET_STAT()); + restore_flags(flags); + } + udelay(1); /* allow status to settle, then read it again */ + if (OK_STAT((stat = GET_STAT()), good, bad)) + return 0; + ide_error(drive, "status error", stat); return 1; } @@ -1532,6 +1601,16 @@ idetape_do_request (drive, rq, block); return; #endif /* CONFIG_BLK_DEV_IDETAPE */ +#ifdef CONFIG_BLK_DEV_IDEFLOPPY + case ide_floppy: + idefloppy_do_request (drive, rq, block); + return; +#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ +#ifdef CONFIG_BLK_DEV_IDESCSI + case ide_scsi: + idescsi_do_request (drive, rq, block); + return; +#endif /* CONFIG_BLK_DEV_IDESCSI */ default: printk("%s: media type %d not supported\n", @@ -1588,6 +1667,7 @@ if (rq != NULL && rq->rq_status != RQ_INACTIVE) goto got_rq; } while ((hwif = hwif->next) != hwgroup->next_hwif); + hwgroup->active = 0; return; /* no work left for this hwgroup */ } got_rq: @@ -1612,6 +1692,7 @@ if (hwgroup->handler == NULL) { ide_hwif_t *hgif = hwgroup->hwif; ide_hwif_t *hwif = hgif; + hwgroup->active = 1; do { disable_irq(hwif->irq); } while ((hwif = hwif->next) != hgif); @@ -1859,13 +1940,8 @@ if (cur_rq == NULL || action == ide_preempt) { rq->next = cur_rq; bdev->current_request = rq; - if (action == ide_preempt) { + if (action == ide_preempt) HWGROUP(drive)->rq = NULL; - } else - if (HWGROUP(drive)->rq == NULL) { /* is this necessary (?) */ - bdev->request_fn(); - cli(); - } } else { if (action == ide_wait || action == ide_end) { while (cur_rq->next != NULL) /* find end of list */ @@ -1874,6 +1950,10 @@ rq->next = cur_rq->next; cur_rq->next = rq; } + if (!HWGROUP(drive)->active) { + do_hwgroup_request(HWGROUP(drive)); + cli(); + } if (action == ide_wait && rq->rq_status != RQ_INACTIVE) down(&sem); /* wait for it to be serviced */ restore_flags(flags); @@ -1901,6 +1981,14 @@ if (drive->media == ide_tape) return idetape_blkdev_open (inode, filp, drive); #endif /* CONFIG_BLK_DEV_IDETAPE */ +#ifdef CONFIG_BLK_DEV_IDEFLOPPY + if (drive->media == ide_floppy) + return idefloppy_open (inode, filp, drive); +#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ +#ifdef CONFIG_BLK_DEV_IDESCSI + if (drive->media == ide_scsi) + return idescsi_open (inode, filp, drive); +#endif /* CONFIG_BLK_DEV_IDESCSI */ if (drive->removable && drive->usage == 1) { byte door_lock[] = {WIN_DOORLOCK,0,0,0}; struct request rq; @@ -1940,6 +2028,18 @@ return; } #endif /* CONFIG_BLK_DEV_IDETAPE */ +#ifdef CONFIG_BLK_DEV_IDEFLOPPY + if (drive->media == ide_floppy) { + idefloppy_release (inode, file, drive); + return; + } +#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ +#ifdef CONFIG_BLK_DEV_IDESCSI + if (drive->media == ide_scsi) { + idescsi_ide_release (inode, file, drive); + return; + } +#endif /* CONFIG_BLK_DEV_IDESCSI */ if (drive->removable && !drive->usage) { byte door_unlock[] = {WIN_DOORUNLOCK,0,0,0}; struct request rq; @@ -1985,13 +2085,14 @@ fsync_dev (devp); invalidate_inodes (devp); invalidate_buffers (devp); + set_blocksize(devp, 1024); } drive->part[p].start_sect = 0; drive->part[p].nr_sects = 0; }; drive->part[0].nr_sects = current_capacity(drive); - if (drive->media != ide_disk) + if ((drive->media != ide_disk && drive->media != ide_floppy) || !drive->part[0].nr_sects) drive->part[0].start_sect = -1; resetup_one_dev(HWIF(drive)->gd, drive->select.b.unit); @@ -2029,7 +2130,7 @@ case HDIO_GETGEO: { struct hd_geometry *loc = (struct hd_geometry *) arg; - if (!loc || drive->media != ide_disk) return -EINVAL; + if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL; err = verify_area(VERIFY_WRITE, loc, sizeof(*loc)); if (err) return err; put_user(drive->bios_head, (byte *) &loc->heads); @@ -2223,6 +2324,14 @@ if (drive->media == ide_tape) return idetape_blkdev_ioctl(drive, inode, file, cmd, arg); #endif /* CONFIG_BLK_DEV_IDETAPE */ +#ifdef CONFIG_BLK_DEV_IDEFLOPPY + if (drive->media == ide_floppy) + return idefloppy_ioctl(drive, inode, file, cmd, arg); +#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ +#ifdef CONFIG_BLK_DEV_IDESCSI + if (drive->media == ide_scsi) + return idescsi_ioctl(drive, inode, file, cmd, arg); +#endif /* CONFIG_BLK_DEV_IDESCSI */ return -EPERM; } } @@ -2237,6 +2346,10 @@ if (drive->media == ide_cdrom) return ide_cdrom_check_media_change (drive); #endif /* CONFIG_BLK_DEV_IDECD */ +#ifdef CONFIG_BLK_DEV_IDEFLOPPY + if (drive->media == ide_floppy) + return idefloppy_media_change (drive); +#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ if (drive->removable) /* for disks */ return 1; /* always assume it was changed */ return 0; @@ -2307,6 +2420,9 @@ ide_fixstring (id->fw_rev, sizeof(id->fw_rev), bswap); ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap); + if (strstr(id->model, "E X A B Y T E N E S T")) + return; + #ifdef CONFIG_BLK_DEV_IDEATAPI /* * Check for an ATAPI device @@ -2322,7 +2438,22 @@ } #endif /* CONFIG_BLK_DEV_PROMISE */ switch (type) { - case 0: /* Early cdrom models used zero */ + case 0: + if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP")) + printk("cdrom or floppy?, assuming "); + if (drive->media != ide_cdrom) { +#ifdef CONFIG_BLK_DEV_IDEFLOPPY + printk("FLOPPY drive\n"); + drive->media = ide_floppy; + if (idefloppy_identify_device(drive, id)) + drive->present = 1; + return; +#else + printk("FLOPPY "); + break; +#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ + } + /* Early cdrom models used zero */ case 5: #ifdef CONFIG_BLK_DEV_IDECD printk ("CDROM drive\n"); @@ -2361,8 +2492,15 @@ printk("Type %d - Unknown device\n", type); return; } +#ifdef CONFIG_BLK_DEV_IDESCSI + printk("drive - enabling SCSI emulation\n"); + drive->media = ide_scsi; + drive->present = 1; + idescsi_setup(drive); +#else drive->present = 0; printk("- not supported by this kernel\n"); +#endif /* CONFIG_BLK_DEV_IDESCSI */ return; } #endif /* CONFIG_BLK_DEV_IDEATAPI */ @@ -2381,7 +2519,7 @@ return; } } - + drive->media = ide_disk; /* Extract geometry if we did not already have one for the drive */ if (!drive->present) { @@ -2431,9 +2569,13 @@ (void) current_capacity (drive); /* initialize LBA selection */ - printk ("%s: %.40s, %ldMB w/%dkB Cache, %sCHS=%d/%d/%d", + if (!strncmp(id->model, "BMI ", 4) && + strstr(id->model, " ENHANCED IDE ") && + drive->select.b.lba) + drive->no_geom = 1; + + printk ("%s: %.40s, %ldMB w/%dkB Cache, CHS=%d/%d/%d", drive->name, id->model, current_capacity(drive)/2048L, id->buf_size/2, - drive->select.b.lba ? "LBA, " : "", drive->bios_cyl, drive->bios_head, drive->bios_sect); drive->mult_count = 0; @@ -2607,6 +2749,34 @@ return rc; } +static void enable_nest (ide_drive_t *drive) +{ + unsigned long timeout; + + printk("%s: enabling %s -- ", HWIF(drive)->name, drive->id->model); + SELECT_DRIVE(HWIF(drive), drive); + delay_50ms(); + OUT_BYTE(EXABYTE_ENABLE_NEST, IDE_COMMAND_REG); + timeout = jiffies + WAIT_WORSTCASE; + do { + if (jiffies > timeout) { + printk("failed (timeout)\n"); + return; + } + delay_50ms(); + } while (GET_STAT() & BUSY_STAT); + delay_50ms(); + if (!OK_STAT(GET_STAT(), 0, BAD_STAT)) + printk("failed (status = 0x%02x)\n", GET_STAT()); + else + printk("success\n"); + if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */ +#ifdef CONFIG_BLK_DEV_IDEATAPI + (void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */ +#endif /* CONFIG_BLK_DEV_IDEATAPI */ + } +} + /* * probe_for_drive() tests for existence of a given drive using do_probe(). * @@ -2622,6 +2792,8 @@ (void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */ #endif /* CONFIG_BLK_DEV_IDEATAPI */ } + if (drive->id && strstr(drive->id->model, "E X A B Y T E N E S T")) + enable_nest(drive); if (!drive->present) return 0; /* drive not found */ if (drive->id == NULL) { /* identification failed? */ @@ -3250,6 +3422,7 @@ else hwgroup->drive = &hwif->drives[1]; hwgroup->poll_timeout = 0; + hwgroup->active = 0; init_timer(&hwgroup->timer); hwgroup->timer.function = &timer_expiry; hwgroup->timer.data = (unsigned long) hwgroup; @@ -3353,6 +3526,7 @@ */ ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1); ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, &ide_init_triton, 0); + ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, &ide_init_triton, 0); #endif /* CONFIG_BLK_DEV_TRITON */ } #endif /* CONFIG_PCI */ diff -u --recursive --new-file v2.0.30/linux/drivers/block/ide.h linux/drivers/block/ide.h --- v2.0.30/linux/drivers/block/ide.h Tue Mar 11 15:39:13 1997 +++ linux/drivers/block/ide.h Tue Aug 12 15:00:22 1997 @@ -51,7 +51,8 @@ #endif #endif /* CONFIG_BLK_DEV_CMD640 */ -#if defined(CONFIG_BLK_DEV_IDECD) || defined(CONFIG_BLK_DEV_IDETAPE) +#if defined(CONFIG_BLK_DEV_IDECD) || defined(CONFIG_BLK_DEV_IDETAPE) || \ + defined(CONFIG_BLK_DEV_IDEFLOPPY) || defined(CONFIG_BLK_DEV_IDESCSI) #define CONFIG_BLK_DEV_IDEATAPI 1 #endif @@ -110,6 +111,9 @@ #define IDE_FEATURE_REG IDE_ERROR_REG #define IDE_COMMAND_REG IDE_STATUS_REG #define IDE_ALTSTATUS_REG IDE_CONTROL_REG +#define IDE_IREASON_REG IDE_NSECTOR_REG +#define IDE_BCOUNTL_REG IDE_LCYL_REG +#define IDE_BCOUNTH_REG IDE_HCYL_REG #ifdef REALLY_FAST_IO #define OUT_BYTE(b,p) outb((b),(p)) @@ -126,7 +130,7 @@ #define BAD_W_STAT (BAD_R_STAT | WRERR_STAT) #define BAD_STAT (BAD_R_STAT | DRQ_STAT) #define DRIVE_READY (READY_STAT | SEEK_STAT) -#define DATA_READY (DRIVE_READY | DRQ_STAT) +#define DATA_READY (DRQ_STAT) /* * Some more useful definitions @@ -285,6 +289,8 @@ /* The result of the last successful request sense command on this device. */ struct atapi_request_sense sense_data; + + int max_sectors; }; #endif /* CONFIG_BLK_DEV_IDECD */ @@ -293,7 +299,7 @@ * Now for the data we need to maintain per-drive: ide_drive_t */ -typedef enum {ide_disk, ide_cdrom, ide_tape} ide_media_t; +typedef enum {ide_disk, ide_cdrom, ide_tape, ide_floppy, ide_scsi} ide_media_t; typedef union { unsigned all : 8; /* all of the bits together */ @@ -302,7 +308,8 @@ unsigned recalibrate : 1; /* seek to cyl 0 */ unsigned set_multmode : 1; /* set multmode count */ unsigned set_tune : 1; /* tune interface for drive */ - unsigned reserved : 4; /* unused */ + unsigned mc : 1; /* acknowledge media change */ + unsigned reserved : 3; /* unused */ } b; } special_t; @@ -335,7 +342,8 @@ #if FAKE_FDISK_FOR_EZDRIVE unsigned remap_0_to_1 : 1; /* flag: partitioned with ezdrive */ #endif /* FAKE_FDISK_FOR_EZDRIVE */ - ide_media_t media; /* disk, cdrom, tape */ + unsigned no_geom : 1; /* flag: do not set geometry */ + ide_media_t media; /* disk, cdrom, tape, floppy */ select_t select; /* basic drive/head select reg value */ byte ctl; /* "normal" value for IDE_CONTROL_REG */ byte ready_stat; /* min status value for drive ready */ @@ -363,6 +371,12 @@ #ifdef CONFIG_BLK_DEV_IDETAPE idetape_tape_t tape; /* for ide-tape.c */ #endif /* CONFIG_BLK_DEV_IDETAPE */ +#ifdef CONFIG_BLK_DEV_IDEFLOPPY + void *floppy; /* for ide-floppy.c */ +#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ +#ifdef CONFIG_BLK_DEV_IDESCSI + void *scsi; /* for ide-scsi.c */ +#endif /* CONFIG_BLK_DEV_IDESCSI */ } ide_drive_t; /* @@ -465,6 +479,7 @@ struct timer_list timer; /* failsafe timer */ struct request wrq; /* local copy of current write rq */ unsigned long poll_timeout; /* timeout value during long polls */ + int active; /* set when servicing requests */ } ide_hwgroup_t; /* @@ -503,6 +518,12 @@ void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount); /* + * This is used for (nearly) all ATAPI data transfers from/to the IDE interface + */ +void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount); +void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount); + +/* * This is used on exit from the driver, to designate the next irq handler * and also to start the safety timer. */ @@ -692,6 +713,28 @@ void idetape_register_chrdev (void); #endif /* CONFIG_BLK_DEV_IDETAPE */ + +#ifdef CONFIG_BLK_DEV_IDEFLOPPY +int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id); +void idefloppy_setup (ide_drive_t *drive); +void idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block); +void idefloppy_end_request (byte uptodate, ide_hwgroup_t *hwgroup); +int idefloppy_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +int idefloppy_open (struct inode *inode, struct file *filp, ide_drive_t *drive); +void idefloppy_release (struct inode *inode, struct file *filp, ide_drive_t *drive); +int idefloppy_media_change (ide_drive_t *drive); +unsigned long idefloppy_capacity (ide_drive_t *drive); +#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ + +#ifdef CONFIG_BLK_DEV_IDESCSI +void idescsi_setup (ide_drive_t *drive); +void idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block); +void idescsi_end_request (byte uptodate, ide_hwgroup_t *hwgroup); +int idescsi_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +int idescsi_open (struct inode *inode, struct file *filp, ide_drive_t *drive); +void idescsi_ide_release (struct inode *inode, struct file *filp, ide_drive_t *drive); +#endif /* CONFIG_BLK_DEV_IDESCSI */ #ifdef CONFIG_BLK_DEV_TRITON void ide_init_triton (byte, byte); diff -u --recursive --new-file v2.0.30/linux/drivers/block/loop.c linux/drivers/block/loop.c --- v2.0.30/linux/drivers/block/loop.c Mon Jul 15 03:47:39 1996 +++ linux/drivers/block/loop.c Tue Aug 12 13:06:54 1997 @@ -12,6 +12,8 @@ * Modularized and updated for 1.1.16 kernel - Mitch Dsouza 28th May 1994 * * Adapted for 1.3.59 kernel - Andries Brouwer, 1 Feb 1996 + * + * Fixed do_loop_request() re-entrancy - Mar 20, 1997 */ #include @@ -181,12 +183,15 @@ char *dest_addr; struct loop_device *lo; struct buffer_head *bh; + struct request *current_request; repeat: INIT_REQUEST; - if (MINOR(CURRENT->rq_dev) >= MAX_LOOP) + current_request=CURRENT; + CURRENT=current_request->next; + if (MINOR(current_request->rq_dev) >= MAX_LOOP) goto error_out; - lo = &loop_dev[MINOR(CURRENT->rq_dev)]; + lo = &loop_dev[MINOR(current_request->rq_dev)]; if (!lo->lo_inode || !lo->transfer) goto error_out; @@ -197,14 +202,14 @@ blksize = BLOCK_SIZE; } - dest_addr = CURRENT->buffer; + dest_addr = current_request->buffer; if (blksize < 512) { - block = CURRENT->sector * (512/blksize); + block = current_request->sector * (512/blksize); offset = 0; } else { - block = CURRENT->sector / (blksize >> 9); - offset = (CURRENT->sector % (blksize >> 9)) << 9; + block = current_request->sector / (blksize >> 9); + offset = (current_request->sector % (blksize >> 9)) << 9; } block += lo->lo_offset / blksize; offset += lo->lo_offset % blksize; @@ -212,13 +217,13 @@ block++; offset -= blksize; } - len = CURRENT->current_nr_sectors << 9; + len = current_request->current_nr_sectors << 9; - if (CURRENT->cmd == WRITE) { + if (current_request->cmd == WRITE) { if (lo->lo_flags & LO_FLAGS_READ_ONLY) goto error_out; - } else if (CURRENT->cmd != READ) { - printk("unknown loop device command (%d)?!?", CURRENT->cmd); + } else if (current_request->cmd != READ) { + printk("unknown loop device command (%d)?!?", current_request->cmd); goto error_out; } while (len > 0) { @@ -237,7 +242,7 @@ block, blksize); goto error_out; } - if (!buffer_uptodate(bh) && ((CURRENT->cmd == READ) || + if (!buffer_uptodate(bh) && ((current_request->cmd == READ) || (offset || (len < blksize)))) { ll_rw_block(READ, 1, &bh); wait_on_buffer(bh); @@ -250,13 +255,13 @@ if (size > len) size = len; - if ((lo->transfer)(lo, CURRENT->cmd, bh->b_data + offset, + if ((lo->transfer)(lo, current_request->cmd, bh->b_data + offset, dest_addr, size)) { printk("loop: transfer error block %d\n", block); brelse(bh); goto error_out; } - if (CURRENT->cmd == WRITE) { + if (current_request->cmd == WRITE) { mark_buffer_uptodate(bh, 1); mark_buffer_dirty(bh, 1); } @@ -266,9 +271,13 @@ offset = 0; block++; } + current_request->next=CURRENT; + CURRENT=current_request; end_request(1); goto repeat; error_out: + current_request->next=CURRENT; + CURRENT=current_request; end_request(0); goto repeat; } diff -u --recursive --new-file v2.0.30/linux/drivers/char/Config.in linux/drivers/char/Config.in --- v2.0.30/linux/drivers/char/Config.in Tue Apr 8 08:47:45 1997 +++ linux/drivers/char/Config.in Tue Aug 12 13:06:54 1997 @@ -14,6 +14,10 @@ fi tristate 'SDL RISCom/8 card support' CONFIG_RISCOM8 tristate 'Parallel printer support' CONFIG_PRINTER +tristate 'Specialix IO8+ card support' CONFIG_SPECIALIX +if [ "$CONFIG_SPECIALIX" = "y" -o "$CONFIG_SPECIALIX" = "m" ]; then + bool 'Specialix DTR/RTS pin is RTS' CONFIG_SPECIALIX_RTSCTS +fi bool 'Mouse Support (not serial mice)' CONFIG_MOUSE diff -u --recursive --new-file v2.0.30/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v2.0.30/linux/drivers/char/Makefile Thu Oct 31 04:31:41 1996 +++ linux/drivers/char/Makefile Tue Aug 12 13:06:54 1997 @@ -78,6 +78,14 @@ endif endif +ifeq ($(CONFIG_SPECIALIX),y) +L_OBJS += specialix.o +else + ifeq ($(CONFIG_SPECIALIX),m) + M_OBJS += specialix.o + endif +endif + ifeq ($(CONFIG_ATIXL_BUSMOUSE),y) M = y L_OBJS += atixlmouse.o diff -u --recursive --new-file v2.0.30/linux/drivers/char/README.scc linux/drivers/char/README.scc --- v2.0.30/linux/drivers/char/README.scc Mon Dec 18 21:09:00 1995 +++ linux/drivers/char/README.scc Tue Aug 12 14:47:16 1997 @@ -1,24 +1,14 @@ - -You will find subset of the documentation in +You will find a subset of the documentation in linux/Documentation/networking/z8530drv.txt +To use this driver you MUST have the full package "z8530drv-2.4c.dl1bke.tar.gz" +from either ftp.pspt.fi, sunsite.unc.edu or db0bm.automation.fh-aachen.de. +The package includes the utilities necessary to initialize and control the driver. -To use this driver you MUST have the full package from - -ftp.ucsd.edu:/hamradio/packet/tcpip/incoming/z8530drv-2.01.dl1bke.real.tar.gz - -[ - if you can't find it there, try: - .../tcpip/linux/z8530drv-2.01.dl1bke.tar.gz - -] - -and various mirrors (i.e. nic.switch.ch) - -The package includes the utilities necessary to initialize and -control the driver. +Do not try to use the utilities from z8530drv-utils-3.0 as they will not work +with the 2.4 series of the driver! Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org - AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU - Internet: jreuter@lykos.tng.oche.de + WWW : http://www.rat.de/jr + Internet: jreuter@poboxes.com diff -u --recursive --new-file v2.0.30/linux/drivers/char/cd1865.h linux/drivers/char/cd1865.h --- v2.0.30/linux/drivers/char/cd1865.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/cd1865.h Tue Aug 12 13:06:54 1997 @@ -0,0 +1,263 @@ +/* + * linux/drivers/char/cd1865.h -- Definitions relating to the CD1865 + * for the Specialix IO8+ multiport serial driver. + * + * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net) + * + * Specialix pays for the development and support of this driver. + * Please DO contact io8-linux@specialix.co.uk if you require + * support. + * + * This driver was developped in the BitWizard linux device + * driver service. If you require a linux device driver for your + * product, please contact devices@BitWizard.nl for a quote. + * + */ + +/* + * Definitions for Driving CD180/CD1864/CD1865 based eightport serial cards. + */ + + +/* Values of choice for Interrupt ACKs */ +/* These values are "obligatory" if you use the register based + * interrupt acknowledgements. See page 99-101 of V2.0 of the CD1865 + * databook */ +#define SX_ACK_MINT 0x75 /* goes to PILR1 */ +#define SX_ACK_TINT 0x76 /* goes to PILR2 */ +#define SX_ACK_RINT 0x77 /* goes to PILR3 */ + +/* Chip ID (is used when chips ar daisy chained.) */ +#define SX_ID 0x10 + +/* Definitions for Cirrus Logic CL-CD186x 8-port async mux chip */ + +#define CD186x_NCH 8 /* Total number of channels */ +#define CD186x_TPC 16 /* Ticks per character */ +#define CD186x_NFIFO 8 /* TX FIFO size */ + + +/* Global registers */ + +#define CD186x_GIVR 0x40 /* Global Interrupt Vector Register */ +#define CD186x_GICR 0x41 /* Global Interrupting Channel Register */ +#define CD186x_PILR1 0x61 /* Priority Interrupt Level Register 1 */ +#define CD186x_PILR2 0x62 /* Priority Interrupt Level Register 2 */ +#define CD186x_PILR3 0x63 /* Priority Interrupt Level Register 3 */ +#define CD186x_CAR 0x64 /* Channel Access Register */ +#define CD186x_SRSR 0x65 /* Channel Access Register */ +#define CD186x_GFRCR 0x6b /* Global Firmware Revision Code Register */ +#define CD186x_PPRH 0x70 /* Prescaler Period Register High */ +#define CD186x_PPRL 0x71 /* Prescaler Period Register Low */ +#define CD186x_RDR 0x78 /* Receiver Data Register */ +#define CD186x_RCSR 0x7a /* Receiver Character Status Register */ +#define CD186x_TDR 0x7b /* Transmit Data Register */ +#define CD186x_EOIR 0x7f /* End of Interrupt Register */ +#define CD186x_MRAR 0x75 /* Modem Request Acknowlege register */ +#define CD186x_TRAR 0x76 /* Transmit Request Acknowlege register */ +#define CD186x_RRAR 0x77 /* Recieve Request Acknowlege register */ +#define CD186x_SRCR 0x66 /* Service Request Configuration register */ + +/* Channel Registers */ + +#define CD186x_CCR 0x01 /* Channel Command Register */ +#define CD186x_IER 0x02 /* Interrupt Enable Register */ +#define CD186x_COR1 0x03 /* Channel Option Register 1 */ +#define CD186x_COR2 0x04 /* Channel Option Register 2 */ +#define CD186x_COR3 0x05 /* Channel Option Register 3 */ +#define CD186x_CCSR 0x06 /* Channel Control Status Register */ +#define CD186x_RDCR 0x07 /* Receive Data Count Register */ +#define CD186x_SCHR1 0x09 /* Special Character Register 1 */ +#define CD186x_SCHR2 0x0a /* Special Character Register 2 */ +#define CD186x_SCHR3 0x0b /* Special Character Register 3 */ +#define CD186x_SCHR4 0x0c /* Special Character Register 4 */ +#define CD186x_MCOR1 0x10 /* Modem Change Option 1 Register */ +#define CD186x_MCOR2 0x11 /* Modem Change Option 2 Register */ +#define CD186x_MCR 0x12 /* Modem Change Register */ +#define CD186x_RTPR 0x18 /* Receive Timeout Period Register */ +#define CD186x_MSVR 0x28 /* Modem Signal Value Register */ +#define CD186x_MSVRTS 0x29 /* Modem Signal Value Register */ +#define CD186x_MSVDTR 0x2a /* Modem Signal Value Register */ +#define CD186x_RBPRH 0x31 /* Receive Baud Rate Period Register High */ +#define CD186x_RBPRL 0x32 /* Receive Baud Rate Period Register Low */ +#define CD186x_TBPRH 0x39 /* Transmit Baud Rate Period Register High */ +#define CD186x_TBPRL 0x3a /* Transmit Baud Rate Period Register Low */ + + +/* Global Interrupt Vector Register (R/W) */ + +#define GIVR_ITMASK 0x07 /* Interrupt type mask */ +#define GIVR_IT_MODEM 0x01 /* Modem Signal Change Interrupt */ +#define GIVR_IT_TX 0x02 /* Transmit Data Interrupt */ +#define GIVR_IT_RCV 0x03 /* Receive Good Data Interrupt */ +#define GIVR_IT_REXC 0x07 /* Receive Exception Interrupt */ + + +/* Global Interrupt Channel Register (R/W) */ + +#define GICR_CHAN 0x1c /* Channel Number Mask */ +#define GICR_CHAN_OFF 2 /* Channel Number shift */ + + +/* Channel Address Register (R/W) */ + +#define CAR_CHAN 0x07 /* Channel Number Mask */ +#define CAR_A7 0x08 /* A7 Address Extension (unused) */ + + +/* Receive Character Status Register (R/O) */ + +#define RCSR_TOUT 0x80 /* Rx Timeout */ +#define RCSR_SCDET 0x70 /* Special Character Detected Mask */ +#define RCSR_NO_SC 0x00 /* No Special Characters Detected */ +#define RCSR_SC_1 0x10 /* Special Char 1 (or 1 & 3) Detected */ +#define RCSR_SC_2 0x20 /* Special Char 2 (or 2 & 4) Detected */ +#define RCSR_SC_3 0x30 /* Special Char 3 Detected */ +#define RCSR_SC_4 0x40 /* Special Char 4 Detected */ +#define RCSR_BREAK 0x08 /* Break has been detected */ +#define RCSR_PE 0x04 /* Parity Error */ +#define RCSR_FE 0x02 /* Frame Error */ +#define RCSR_OE 0x01 /* Overrun Error */ + + +/* Channel Command Register (R/W) (commands in groups can be OR-ed) */ + +#define CCR_HARDRESET 0x81 /* Reset the chip */ + +#define CCR_SOFTRESET 0x80 /* Soft Channel Reset */ + +#define CCR_CORCHG1 0x42 /* Channel Option Register 1 Changed */ +#define CCR_CORCHG2 0x44 /* Channel Option Register 2 Changed */ +#define CCR_CORCHG3 0x48 /* Channel Option Register 3 Changed */ + +#define CCR_SSCH1 0x21 /* Send Special Character 1 */ + +#define CCR_SSCH2 0x22 /* Send Special Character 2 */ + +#define CCR_SSCH3 0x23 /* Send Special Character 3 */ + +#define CCR_SSCH4 0x24 /* Send Special Character 4 */ + +#define CCR_TXEN 0x18 /* Enable Transmitter */ +#define CCR_RXEN 0x12 /* Enable Receiver */ + +#define CCR_TXDIS 0x14 /* Disable Transmitter */ +#define CCR_RXDIS 0x11 /* Disable Receiver */ + + +/* Interrupt Enable Register (R/W) */ + +#define IER_DSR 0x80 /* Enable interrupt on DSR change */ +#define IER_CD 0x40 /* Enable interrupt on CD change */ +#define IER_CTS 0x20 /* Enable interrupt on CTS change */ +#define IER_RXD 0x10 /* Enable interrupt on Receive Data */ +#define IER_RXSC 0x08 /* Enable interrupt on Receive Spec. Char */ +#define IER_TXRDY 0x04 /* Enable interrupt on TX FIFO empty */ +#define IER_TXEMPTY 0x02 /* Enable interrupt on TX completely empty */ +#define IER_RET 0x01 /* Enable interrupt on RX Exc. Timeout */ + + +/* Channel Option Register 1 (R/W) */ + +#define COR1_ODDP 0x80 /* Odd Parity */ +#define COR1_PARMODE 0x60 /* Parity Mode mask */ +#define COR1_NOPAR 0x00 /* No Parity */ +#define COR1_FORCEPAR 0x20 /* Force Parity */ +#define COR1_NORMPAR 0x40 /* Normal Parity */ +#define COR1_IGNORE 0x10 /* Ignore Parity on RX */ +#define COR1_STOPBITS 0x0c /* Number of Stop Bits */ +#define COR1_1SB 0x00 /* 1 Stop Bit */ +#define COR1_15SB 0x04 /* 1.5 Stop Bits */ +#define COR1_2SB 0x08 /* 2 Stop Bits */ +#define COR1_CHARLEN 0x03 /* Character Length */ +#define COR1_5BITS 0x00 /* 5 bits */ +#define COR1_6BITS 0x01 /* 6 bits */ +#define COR1_7BITS 0x02 /* 7 bits */ +#define COR1_8BITS 0x03 /* 8 bits */ + + +/* Channel Option Register 2 (R/W) */ + +#define COR2_IXM 0x80 /* Implied XON mode */ +#define COR2_TXIBE 0x40 /* Enable In-Band (XON/XOFF) Flow Control */ +#define COR2_ETC 0x20 /* Embedded Tx Commands Enable */ +#define COR2_LLM 0x10 /* Local Loopback Mode */ +#define COR2_RLM 0x08 /* Remote Loopback Mode */ +#define COR2_RTSAO 0x04 /* RTS Automatic Output Enable */ +#define COR2_CTSAE 0x02 /* CTS Automatic Enable */ +#define COR2_DSRAE 0x01 /* DSR Automatic Enable */ + + +/* Channel Option Register 3 (R/W) */ + +#define COR3_XONCH 0x80 /* XON is a pair of characters (1 & 3) */ +#define COR3_XOFFCH 0x40 /* XOFF is a pair of characters (2 & 4) */ +#define COR3_FCT 0x20 /* Flow-Control Transparency Mode */ +#define COR3_SCDE 0x10 /* Special Character Detection Enable */ +#define COR3_RXTH 0x0f /* RX FIFO Threshold value (1-8) */ + + +/* Channel Control Status Register (R/O) */ + +#define CCSR_RXEN 0x80 /* Receiver Enabled */ +#define CCSR_RXFLOFF 0x40 /* Receive Flow Off (XOFF was sent) */ +#define CCSR_RXFLON 0x20 /* Receive Flow On (XON was sent) */ +#define CCSR_TXEN 0x08 /* Transmitter Enabled */ +#define CCSR_TXFLOFF 0x04 /* Transmit Flow Off (got XOFF) */ +#define CCSR_TXFLON 0x02 /* Transmit Flow On (got XON) */ + + +/* Modem Change Option Register 1 (R/W) */ + +#define MCOR1_DSRZD 0x80 /* Detect 0->1 transition of DSR */ +#define MCOR1_CDZD 0x40 /* Detect 0->1 transition of CD */ +#define MCOR1_CTSZD 0x20 /* Detect 0->1 transition of CTS */ +#define MCOR1_DTRTH 0x0f /* Auto DTR flow control Threshold (1-8) */ +#define MCOR1_NODTRFC 0x0 /* Automatic DTR flow control disabled */ + + +/* Modem Change Option Register 2 (R/W) */ + +#define MCOR2_DSROD 0x80 /* Detect 1->0 transition of DSR */ +#define MCOR2_CDOD 0x40 /* Detect 1->0 transition of CD */ +#define MCOR2_CTSOD 0x20 /* Detect 1->0 transition of CTS */ + +/* Modem Change Register (R/W) */ + +#define MCR_DSRCHG 0x80 /* DSR Changed */ +#define MCR_CDCHG 0x40 /* CD Changed */ +#define MCR_CTSCHG 0x20 /* CTS Changed */ + + +/* Modem Signal Value Register (R/W) */ + +#define MSVR_DSR 0x80 /* Current state of DSR input */ +#define MSVR_CD 0x40 /* Current state of CD input */ +#define MSVR_CTS 0x20 /* Current state of CTS input */ +#define MSVR_DTR 0x02 /* Current state of DTR output */ +#define MSVR_RTS 0x01 /* Current state of RTS output */ + + +/* Escape characters */ + +#define CD186x_C_ESC 0x00 /* Escape character */ +#define CD186x_C_SBRK 0x81 /* Start sending BREAK */ +#define CD186x_C_DELAY 0x82 /* Delay output */ +#define CD186x_C_EBRK 0x83 /* Stop sending BREAK */ + +#define SRSR_RREQint 0x10 /* This chip wants "rec" serviced */ +#define SRSR_TREQint 0x04 /* This chip wants "transmit" serviced */ +#define SRSR_MREQint 0x01 /* This chip wants "mdm change" serviced */ + + + +#define SRCR_PKGTYPE 0x80 +#define SRCR_REGACKEN 0x40 +#define SRCR_DAISYEN 0x20 +#define SRCR_GLOBPRI 0x10 +#define SRCR_UNFAIR 0x08 +#define SRCR_AUTOPRI 0x02 +#define SRCR_PRISEL 0x01 + + diff -u --recursive --new-file v2.0.30/linux/drivers/char/console.c linux/drivers/char/console.c --- v2.0.30/linux/drivers/char/console.c Thu Nov 7 01:25:21 1996 +++ linux/drivers/char/console.c Tue Aug 12 13:06:54 1997 @@ -2320,3 +2320,22 @@ { return set_get_font (arg,0,video_mode_512ch); } + +/* + * Report the current status of the vc. This is exported to modules (ARub) + */ + +int con_get_info(int *mode, int *shift, int *col, int *row, + struct tty_struct **tty) +{ + extern int shift_state; + extern struct tty_driver console_driver; + struct tty_struct **console_table=console_driver.table; + + if (mode) *mode = vt_cons[fg_console]->vc_mode; + if (shift) *shift = shift_state; + if (col) *col = video_num_columns; + if (row) *row = video_num_lines; + if (tty) *tty = console_table[fg_console]; + return fg_console; +} diff -u --recursive --new-file v2.0.30/linux/drivers/char/keyboard.c linux/drivers/char/keyboard.c --- v2.0.30/linux/drivers/char/keyboard.c Tue Mar 11 15:55:22 1997 +++ linux/drivers/char/keyboard.c Mon Aug 11 13:42:11 1997 @@ -1078,6 +1078,8 @@ reply_expected = 1; outb_p(data, 0x60); for(i=0; i<0x200000; i++) { + extern void allow_interrupts(void); + allow_interrupts(); inb_p(0x64); /* just as a delay */ if (acknowledge) return 1; diff -u --recursive --new-file v2.0.30/linux/drivers/char/misc.c linux/drivers/char/misc.c --- v2.0.30/linux/drivers/char/misc.c Wed Nov 6 04:39:42 1996 +++ linux/drivers/char/misc.c Tue Aug 12 13:06:54 1997 @@ -57,7 +57,7 @@ /* * Assigned numbers, used for dynamic minors */ -#define DYNAMIC_MINORS 64 /* like dynamic majors */ +#define DYNAMIC_MINORS 64 /* like dynamic majors used to do */ static unsigned char misc_minors[DYNAMIC_MINORS / 8]; #ifndef MODULE @@ -70,6 +70,8 @@ extern void wdt_init(void); extern void pcwatchdog_init(void); extern int rtc_init(void); +extern int con_get_info(int *mode, int *shift, int *col, int *row, + struct tty_struct **tty); #ifdef CONFIG_PROC_FS static int proc_misc_read(char *buf, char **start, off_t offset, int len, int unused) @@ -181,6 +183,7 @@ #ifndef MODULE X(set_selection), /* used by the kmouse module, can only */ X(paste_selection), /* be exported if misc.c is in linked in */ + X(con_get_info), #endif #include }; diff -u --recursive --new-file v2.0.30/linux/drivers/char/n_tty.c linux/drivers/char/n_tty.c --- v2.0.30/linux/drivers/char/n_tty.c Mon Sep 2 05:18:26 1996 +++ linux/drivers/char/n_tty.c Thu Aug 7 09:34:37 1997 @@ -89,6 +89,13 @@ */ int n_tty_chars_in_buffer(struct tty_struct *tty) { + if (tty->icanon) { + if (!tty->canon_data) return 0; + + return (tty->canon_head > tty->read_tail) ? + tty->canon_head - tty->read_tail : + tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail); + } return tty->read_cnt; } diff -u --recursive --new-file v2.0.30/linux/drivers/char/pty.c linux/drivers/char/pty.c --- v2.0.30/linux/drivers/char/pty.c Mon Sep 30 06:56:48 1996 +++ linux/drivers/char/pty.c Thu Aug 7 09:34:37 1997 @@ -153,14 +153,32 @@ return to->ldisc.receive_room(to); } +/* + * Modified for asymmetric master/slave behavior + * The chars_in_buffer() value is used by the ldisc select() function + * to hold off writing when chars_in_buffer > WAKEUP_CHARS (== 256). + * To allow typed-ahead commands to accumulate, the master side returns 0 + * until the buffer is half full. The slave side returns the true count. + */ static int pty_chars_in_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; + int count; if (!to || !to->ldisc.chars_in_buffer) return 0; - return to->ldisc.chars_in_buffer(to); + /* The ldisc must report 0 if no characters available to be read */ + count = to->ldisc.chars_in_buffer(to); + + if (tty->driver.subtype == PTY_TYPE_SLAVE) return count; + + /* + * Master side driver ... return 0 if the other side's read buffer + * is less than half full. This allows room for typed-ahead commands + * with a reasonable margin to avoid overflow. + */ + return ((count < N_TTY_BUF_SIZE/2) ? 0 : count); } static void pty_flush_buffer(struct tty_struct *tty) diff -u --recursive --new-file v2.0.30/linux/drivers/char/random.c linux/drivers/char/random.c --- v2.0.30/linux/drivers/char/random.c Tue Apr 8 08:47:45 1997 +++ linux/drivers/char/random.c Thu Aug 14 10:05:47 1997 @@ -1071,10 +1071,8 @@ * If we gave the user some bytes and we have an inode pointer, * update the access time. */ - if (inode && count != 0) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } + if (inode && count != 0) + UPDATE_ATIME(inode); return (count ? count : retval); } @@ -1339,8 +1337,13 @@ do_gettimeofday(&tv); seq = tmp[1] + tv.tv_usec+tv.tv_sec*1000000; #if 0 - printk("init_seq(%lx, %lx, %d, %d) = %d\n", - saddr, daddr, sport, dport, seq); + /* + ugh...we can only use in_ntoa once per printk, splitting + a single line of info into multiple printk's confuses klogd, + and Linus says in_ntoa sucks anyway :) + */ + printk("init_seq(%d.%d.%d.%d:%d, %d.%d.%d.%d:%d) = %d\n", + NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, seq); #endif return (seq); } @@ -1363,7 +1366,7 @@ /* * Pick a random secret the first time we open a TCP - * connection, and expire secretes older than 5 minutes. + * connection, and expire secrets older than 5 minutes. */ if (is_init == 0 || jiffies-secret_timestamp[offset] > 600*HZ) { if (is_init == 0) valid_secret[0] = valid_secret[1] = 0; @@ -1388,14 +1391,14 @@ if (!validate) { if (seq == sseq) seq++; #if 0 - printk("init_seq(%lx, %lx, %d, %d, %d) = %d\n", - saddr, daddr, sport, dport, sseq, seq); + printk("init_seq(%d.%d.%d.%d:%d %d.%d.%d.%d:%d, %d) = %d\n", + NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, sseq, seq); #endif return (seq); } else { if (seq == sseq || (seq+1) == sseq) { - printk("validated probe(%lx, %lx, %d, %d, %d)\n", - saddr, daddr, sport, dport, sseq); + printk("validated probe(%d.%d.%d.%d:%d, %d.%d.%d.%d:%d, %d)\n", + NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, sseq); return 1; } if (jiffies-secret_timestamp[(offset+1)%2] <= 1200*HZ) { @@ -1407,15 +1410,15 @@ seq = tmp[1]; if (seq == sseq || (seq+1) == sseq) { #ifdef 0 - printk("validated probe(%lx, %lx, %d, %d, %d)\n", - saddr, daddr, sport, dport, sseq); + printk("validated probe(%d.%d.%d.%d:%d, %d.%d.%d.%d:%d, %d)\n", + NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, sseq); #endif return 1; } } #ifdef 0 - printk("failed validation on probe(%lx, %lx, %d, %d, %d)\n", - saddr, daddr, sport, dport, sseq); + printk("failed validation on probe(%d.%d.%d.%d:%d, %d.%d.%d.%d:%d, %d)\n", + NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, sseq); #endif return 0; } diff -u --recursive --new-file v2.0.30/linux/drivers/char/scc.c linux/drivers/char/scc.c --- v2.0.30/linux/drivers/char/scc.c Thu Apr 11 23:49:35 1996 +++ linux/drivers/char/scc.c Tue Aug 12 14:43:42 1997 @@ -1,6 +1,6 @@ -#define RCS_ID "$Id: scc.c,v 1.41 1995/12/17 22:36:40 jreuter Exp jreuter $" +#define RCS_ID "$Id: scc.c,v 1.67 1997/04/10 15:24:41 jreuter Exp jreuter $" -#define BANNER "Z8530 SCC driver version 2.01.dl1bke (alpha) by DL1BKE\n" +#define BANNER "Z8530 SCC driver version 2.4c.dl1bke (experimental) by DL1BKE\n" /* @@ -11,7 +11,7 @@ ******************************************************************** - Copyright (c) 1993, 1995 Joerg Reuter DL1BKE + Copyright (c) 1993, 1997 Joerg Reuter DL1BKE portions (c) 1993 Guido ten Dolle PE1NNZ @@ -48,16 +48,6 @@ ******************************************************************** - - ...If you find any portions of the code that are copyrighted to you, - and you don't want to see in here, please send me a private (!) - message to my internet site. I will change it as soon as possible. - Please don't flame to the tcp-group or anywhere else. Thanks! - - Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org - AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU - Internet: jreuter@lykos.tng.oche.de - Incomplete history of z8530drv: ------------------------------- @@ -74,89 +64,105 @@ 950131 - changed copyright notice to GPL without limitations. . - . + . . - 950922 - using kernel timer chain - - 951002 - splitting timer routine, adding support for throttle/ - unthrottle calls, removed typo in kiss decoder, - corrected termios settings. - - 951011 - at last found one (the?) problem which caused the - driver to mess up buffers on heavy load pe1ayx was - complaining about. Quite simple to reproduce: - set a 9k6 port into audio loopback, add a route - to 44.99.99.99 on that route and do a - ping -f 44.99.99.99 + 951114 - rewrote memory management, took first steps to allow + compilation as a module. Well, it looks like a whole + new driver now. + + 960413 - Heiko Eissfeld fixed missing verify_area() calls + + 960507 - the driver may be used as a network driver now. + (for kernel AX.25). + + 960512 - added scc_net_ioctl(), restructured ioctl routines + added /proc filesystem support - 951013 - it still does not survive a flood ping... + 960517 - fixed fullduplex mode 2 operation and waittime bug - 951107 - axattach w/o "-s" option (or setting an invalid - baudrate) does not produce "division by zero" faults - anymore. + 960518 - added fullduplex mode 3 to allow direct hardware + access via protocol layer. Removed kiss.not_slip, + I do not think someone really needed it. - 951114 - rewrote memory management, took first steps to allow - compilation as a module. Well, it looks like a whole - new driver now. BTW: It d o e s survive the flood - ping at last... - - 951116 - scc_config.h is gone -- the driver will be initialized - through ioctl-commands. Use sccinit.c for this purpose. + 960607 - switching off receiver while transmission in halfduplex + mode with external MODEM clock. - 951117 - Well --- what should I say: You can compile it as a - module now. And I solved the problem with slip.c... + 960715 - rewrote interrupt service routine for "polling" mode, + fixed bug in grouping algorithm. - 951120 - most ioctl() routines may be called w/o suser() - permissions, check if you've set the permissions of - /dev/scc* right! NOT 0666, you know it's evil ;-) + 960719 - New transmit timer routines (let's see what it breaks), + some improvements regarding DCD and SYNC interrupts, + clean-up of printk(). - 951217 - found silly bug in init_channel(), some bugfixes - for the "standalone" module + 960725 - Fixed Maxkeyup problems. Will generate HDLC abort and + recover now. + 960808 - Maxkeyup will set dev->tbusy until all remaining frames + are sent. + + 970115 - Fixed return values in scc_net_tx(), added missing + scc_enqueue_buffer() + + 970410 - Fixed problem with new *_timer() code in sched.h for + kernel 2.0.30. Thanks to: ---------- - PE1CHL Rob - for a lot of good ideas from his SCC driver for DOS - PE1NNZ Guido - for his port of the original driver to Linux - KA9Q Phil - from whom we stole the mbuf-structure - PA3AYX Hans - for some useful changes - DL8MBT Flori - for support - DG0FT Rene - for the BayCom USCC support - PA3AOU Harry - for ESCC testing, information supply and support - + PE1CHL Rob - for a lot of good ideas from his SCC driver for DOS + PE1NNZ Guido - for his port of the original driver to Linux + KA9Q Phil - from whom we stole the mbuf-structure + PA3AYX Hans - for some useful changes + DL8MBT Flori - for support + DG0FT Rene - for the BayCom USCC support + PA3AOU Harry - for ESCC testing, information supply and support + Heiko Eissfeld - verify_area() clean-up + DL3KHB Klaus - fullduplex mode 2 bug-hunter + PA3GCU Richard - for support and finding various bugs. + PE1KOX Rob, DG1RTF Thomas, ON5QK Roland, G4XYW Andy, Linus, - EI9GL Paul, - + GW4PTS Alan, EI9GL Paul, G0DZB Peter, G8IMB Martin + and all who sent me bug reports and ideas... NB -- if you find errors, change something, please let me know first before you distribute it... And please don't touch the version number. Just replace my callsign in - "v2.01.dl1bke" with your own. Just to avoid confusion... - + "v2.4c.dl1bke" with your own. Just to avoid confusion... + If you want to add your modification to the linux distribution please (!) contact me first. - - Jörg Reuter DL1BKE - + + New versions of the driver will be announced on the linux-hams + mailing list on vger.rutgers.edu. To subscribe send an e-mail + to majordomo@vger.rutgers.edu with the following line in + the body of the mail: + + subscribe linux-hams + + The content of the "Subject" field will be ignored. + + vy 73, + Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org + AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU + Internet: jreuter@poboxes.com */ /* ----------------------------------------------------------------------- */ -#define DEBUG_BUFFERS /* keep defined unless it is really stable... */ - -#undef SCC_DELAY /* perhaps a 486DX2 is a *bit* too fast */ -#undef SCC_LDELAY 5 /* slow it even a bit more down */ +#undef SCC_DELAY /* perhaps your ISA bus is a *bit* too fast? */ +#undef SCC_LDELAY 1 /* slow it even a bit more down */ #undef DONT_CHECK /* don't look if the SCCs you specified are available */ #define MAXSCC 4 /* number of max. supported chips */ #define RXBUFFERS 8 /* default number of RX buffers */ #define TXBUFFERS 8 /* default number of TX buffers */ #define BUFSIZE 384 /* must not exceed 4096-sizeof(mbuf) */ -#define TPS 25 /* scc_tx_timer(): Ticks Per Second */ +#undef DISABLE_ALL_INTS /* use cli()/sti() in ISR instead of */ + /* enable_irq()/disable_irq() */ +#undef SCC_DEBUG #define DEFAULT_CLOCK 4915200 /* default pclock if nothing is specified */ @@ -177,12 +183,26 @@ #include #include #include +#include #include #include #include #include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_SCC_STANDALONE +#include "scc.h" +#else #include +#endif +#include +#include #include #include #include @@ -193,6 +213,7 @@ #include #include #include +#include #ifdef MODULE int init_module(void); @@ -203,6 +224,15 @@ #define Z8530_MAJOR 34 #endif +#ifndef PROC_NET_Z8530 +#define PROC_NET_Z8530 PROC_NET_LAST+10 /* sorry... */ +#endif + +#if !defined(CONFIG_SCC_DEV) && !defined(CONFIG_SCC_TTY) +#define CONFIG_SCC_DEV +#define CONFIG_SCC_TTY +#endif + int scc_init(void); static struct mbuf * scc_enqueue_buffer(struct mbuf **queue, struct mbuf * buffer); @@ -211,6 +241,7 @@ static void free_buffer_pool(struct scc_channel *scc); static struct mbuf * scc_get_buffer(struct scc_channel *scc, char type); +#ifdef CONFIG_SCC_TTY int scc_open(struct tty_struct *tty, struct file *filp); static void scc_close(struct tty_struct *tty, struct file *filp); int scc_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count); @@ -219,81 +250,112 @@ static int scc_write_room(struct tty_struct *tty); static int scc_chars_in_buffer(struct tty_struct *tty); static void scc_flush_buffer(struct tty_struct *tty); -static int scc_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); +static int scc_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); static void scc_set_termios(struct tty_struct *tty, struct termios *old_termios); static void scc_set_ldisc(struct tty_struct *tty); static void scc_throttle(struct tty_struct *tty); static void scc_unthrottle(struct tty_struct *tty); static void scc_start(struct tty_struct *tty); static void scc_stop(struct tty_struct *tty); +static void kiss_encode(struct scc_channel *scc); +static void scc_rx_timer(unsigned long); +static void scc_change_speed(struct scc_channel *scc); +#endif -static void z8530_init(void); +static void t_dwait(unsigned long); +static void t_txdelay(unsigned long); +static void t_tail(unsigned long); +static void t_busy(unsigned long); +static void t_maxkeyup(unsigned long); +static void t_idle(unsigned long); +static void scc_tx_done(struct scc_channel *); +static void scc_start_tx_timer(struct scc_channel *, void (*)(unsigned long), unsigned long); +static void scc_start_maxkeyup(struct scc_channel *); +static void scc_start_defer(struct scc_channel *); -static void scc_change_speed(struct scc_channel *scc); -static void kiss_encode(struct scc_channel *scc); +static int scc_ioctl(struct scc_channel *scc, unsigned int cmd, void * arg); + +static void z8530_init(void); static void init_channel(struct scc_channel *scc); static void scc_key_trx (struct scc_channel *scc, char tx); -static void scc_txint(register struct scc_channel *scc); -static void scc_exint(register struct scc_channel *scc); -static void scc_rxint(register struct scc_channel *scc); -static void scc_spint(register struct scc_channel *scc); static void scc_isr(int irq, void *dev_id, struct pt_regs *regs); -static void scc_tx_timer(unsigned long); -static void scc_rx_timer(unsigned long); static void scc_init_timer(struct scc_channel *scc); -/* from serial.c */ +#ifdef CONFIG_SCC_DEV +static int scc_net_setup(struct scc_channel *scc, unsigned char *name); +static void scc_net_rx(struct scc_channel *scc, struct mbuf *bp); +#endif + +static unsigned char *SCC_DriverName = "scc"; +#ifdef CONFIG_SCC_TTY static int baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 0 }; +struct semaphore scc_sem = MUTEX; +static struct termios scc_std_termios; +unsigned char scc_wbuf[BUFSIZE]; struct tty_driver scc_driver; -static int scc_refcount; static struct tty_struct *scc_table[2*MAXSCC]; static struct termios scc_termios[2 * MAXSCC]; static struct termios scc_termios_locked[2 * MAXSCC]; +static int scc_refcount; +#endif + -struct irqflags { unsigned char used : 1; } Ivec[16]; +static struct irqflags { unsigned char used : 1; } Ivec[16]; -struct scc_channel SCC_Info[2 * MAXSCC]; /* information per channel */ -io_port SCC_ctrl[2 * MAXSCC]; /* Control ports */ +static struct scc_channel SCC_Info[2 * MAXSCC]; /* information per channel */ -unsigned char Random = 0; /* random number for p-persist */ -unsigned char Driver_Initialized = 0; -int Nchips = 0; -io_port Vector_Latch = 0; +static struct scc_ctrl { + io_port chan_A; + io_port chan_B; + int irq; +} SCC_ctrl[MAXSCC+1]; -struct semaphore scc_sem = MUTEX; -unsigned char scc_wbuf[BUFSIZE]; +static unsigned char Driver_Initialized = 0; +static int Nchips = 0; +static io_port Vector_Latch = 0; -static struct termios scc_std_termios; +#define SCC_DEVNAME (scc->stat.is_netdev? scc->dev->name:kdevname(scc->tty->device)) /* ******************************************************************** */ /* * Port Access Functions * */ /* ******************************************************************** */ -static inline unsigned char -InReg(register io_port port, register unsigned char reg) +/* These provide interrupt save 2-step access to the Z8530 registers */ + +static __inline__ unsigned char +InReg(io_port port, unsigned char reg) { + unsigned long flags; + unsigned char r; + + save_flags(flags); + cli(); #ifdef SCC_LDELAY - register unsigned char r; Outb(port, reg); udelay(SCC_LDELAY); r=Inb(port); udelay(SCC_LDELAY); - return r; #else Outb(port, reg); - return Inb(port); + r=Inb(port); #endif + restore_flags(flags); + return r; } -static inline void -OutReg(register io_port port, register unsigned char reg, register unsigned char val) +static __inline__ void +OutReg(io_port port, unsigned char reg, unsigned char val) { + unsigned long flags; + + save_flags(flags); + cli(); #ifdef SCC_LDELAY Outb(port, reg); udelay(SCC_LDELAY); Outb(port, val); udelay(SCC_LDELAY); @@ -301,26 +363,40 @@ Outb(port, reg); Outb(port, val); #endif + restore_flags(flags); } -static inline void -wr(register struct scc_channel *scc, register unsigned char reg, register unsigned char val) +static __inline__ void +wr(struct scc_channel *scc, unsigned char reg, unsigned char val) { OutReg(scc->ctrl, reg, (scc->wreg[reg] = val)); } -static inline void -or(register struct scc_channel *scc, register unsigned char reg, register unsigned char val) +static __inline__ void +or(struct scc_channel *scc, unsigned char reg, unsigned char val) { OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val)); } -static inline void -cl(register struct scc_channel *scc, register unsigned char reg, register unsigned char val) +static __inline__ void +cl(struct scc_channel *scc, unsigned char reg, unsigned char val) { OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val)); } +#ifdef DISABLE_ALL_INTS +static __inline__ void scc_cli(int irq) +{ cli(); } +static __inline__ void scc_sti(int irq) +{ sti(); } +#else +static __inline__ void scc_cli(int irq) +{ disable_irq(irq); } +static __inline__ void scc_sti(int irq) +{ enable_irq(irq); } +#endif + + /* ******************************************************************** */ /* * Memory Buffer Management */ /* ******************************************************************** */ @@ -331,6 +407,9 @@ * fast. It has the disadvantage to mess things up double if something * is wrong. * + * We could have used the socket buffer routines from skbuff.h instead, + * but our own routines keep the driver portable... + * * Every scc channel has its own buffer pool now with an adjustable * number of buffers for transmit and receive buffers. The buffer * size remains the same for each AX.25 frame, but is (as a semi- @@ -354,23 +433,6 @@ save_flags(flags); cli(); /* do not disturb! */ -#ifdef DEBUG_BUFFERS - if (queue == NULLBUFP) /* called with illegal parameters, notify the user */ - - { - printk("z8530drv: Pointer to queue anchor is NULL pointer [enq]\n"); - restore_flags(flags); - return NULLBUF; - } - - if (buffer == NULLBUF) - { - printk("z8530drv: can't enqueue a NULL pointer\n"); - restore_flags(flags); - return NULLBUF; - } -#endif - anchor = *queue; /* the anchor is the "start" of the chain */ if (anchor == NULLBUF) /* found an empty list */ @@ -380,22 +442,9 @@ } else if (anchor->prev == NULLBUF) /* list has one member only */ { -#ifdef DEBUG_BUFFERS - if (anchor->prev != NULLBUF) /* oops?! */ - printk("weird --- anchor->prev is NULL but not anchor->next [enq]\n"); -#endif - anchor->next = anchor->prev = buffer; - buffer->next = buffer->prev = anchor; - } else -#ifdef DEBUG_BUFFERS - if (anchor->next == NULLBUF) /* this has to be an error. Okay, make the best out of it */ - { - printk("z8530drv: weird --- anchor->next is NULL but not anchor->prev [enq]\n"); anchor->next = anchor->prev = buffer; buffer->next = buffer->prev = anchor; - } else -#endif - { /* every other case */ + } else { /* every other case */ buffer->prev = anchor->prev; /* self explaining, isn't it? */ buffer->next = anchor; anchor->prev->next = buffer; @@ -418,28 +467,12 @@ save_flags(flags); cli(); -#ifdef DEBUG_BUFFERS - if (queue == NULLBUFP) /* called with illegal parameter */ - - { - printk("z8530drv: Pointer to queue anchor is NULL pointer [deq]\n"); - restore_flags(flags); - return NULLBUF; - } -#endif - buffer = *queue; /* head of the chain */ if (buffer != NULLBUF) /* not an empty list? */ { if (buffer->prev != NULLBUF) /* not last buffer? */ { -#ifdef DEBUG_BUFFERS - if (buffer->next == NULLBUF) - { /* what?! */ - printk("z8530drv: weird --- buffer->next is NULL but not buffer->prev [deq]\n"); - } else -#endif if (buffer->prev->next == buffer->prev->prev) { /* only one buffer remaining... */ buffer->next->prev = NULLBUF; @@ -449,10 +482,7 @@ buffer->prev->next = buffer->next; } } -#ifdef DEBUG_BUFFERS - else if (buffer->next != NULLBUF) - printk("z8530drv: weird --- buffer->prev is NULL but not buffer->next [deq]\n"); -#endif + *queue = buffer->next; /* new head of chain */ buffer->next = NULLBUF; /* for security only... */ @@ -476,6 +506,12 @@ struct mbuf * bptr; int buflen; + if (scc->mempool) + { + printk(KERN_DEBUG "z8530drv: alloc_buffer_pool() had buffers already allocated\n"); + return; + } + buflen = sizeof(struct mbuf) + scc->stat.bufsize; if (scc->stat.bufsize < 336) scc->stat.bufsize = 336; @@ -487,7 +523,6 @@ if (scc->stat.rxbuffers < 4) scc->stat.rxbuffers = 4; if (scc->stat.txbuffers < 4) scc->stat.txbuffers = 4; - /* allocate receive buffers for this channel */ @@ -502,7 +537,7 @@ if (bptr == NULLBUF) { - printk("z8530drv: %s: can't allocate memory for rx buffer pool", kdevname(scc->tty->device)); + printk(KERN_WARNING "z8530drv: %s: can't allocate memory for rx buffer pool", SCC_DEVNAME); break; } @@ -527,7 +562,7 @@ if (bptr == NULLBUF) { - printk("z8530drv: %s: can't allocate memory for tx buffer pool", kdevname(scc->tty->device)); + printk(KERN_WARNING "z8530drv: %s: can't allocate memory for tx buffer pool", SCC_DEVNAME); break; } @@ -537,6 +572,8 @@ scc_enqueue_buffer(&scc->tx_buffer_pool, bptr); } + + scc->mempool = 1; } @@ -545,102 +582,89 @@ static void free_buffer_pool(struct scc_channel *scc) { - struct mbuf * bptr; unsigned long flags; - int cnt; + int rx_cnt, tx_cnt; + + if (!scc->mempool) + return; /* this one is a bit tricky and probably dangerous. */ + rx_cnt = tx_cnt = 0; save_flags(flags); cli(); /* esp. to free the buffers currently in use by ISR */ - bptr = scc->rx_bp; - if (bptr != NULLBUF) + if (scc->rx_bp != NULLBUF) { + kfree(scc->rx_bp); scc->rx_bp = NULLBUF; - scc_enqueue_buffer(&scc->rx_buffer_pool, bptr); + rx_cnt++; } - bptr = scc->tx_bp; - if (bptr != NULLBUF) + if (scc->tx_bp != NULLBUF) { + kfree(scc->tx_bp); scc->tx_bp = NULLBUF; - scc_enqueue_buffer(&scc->tx_buffer_pool, bptr); + tx_cnt++; } - bptr = scc->kiss_decode_bp; - if (bptr != NULLBUF) + if (scc->kiss_decode_bp != NULLBUF) { + kfree(scc->kiss_decode_bp); scc->kiss_decode_bp = NULLBUF; - scc_enqueue_buffer(&scc->tx_buffer_pool, bptr); + tx_cnt++; } - - bptr = scc->kiss_encode_bp; - if (bptr != NULLBUF) + +#ifdef CONFIG_SCC_TTY + if (scc->kiss_encode_bp != NULLBUF) { + kfree(scc->kiss_encode_bp); scc->kiss_encode_bp = NULLBUF; - scc_enqueue_buffer(&scc->rx_buffer_pool, bptr); + rx_cnt++; } +#endif restore_flags(flags); while (scc->rx_queue != NULLBUF) { - bptr = scc_dequeue_buffer(&scc->rx_queue); - scc_enqueue_buffer(&scc->rx_buffer_pool, bptr); + kfree(scc_dequeue_buffer(&scc->rx_queue)); + rx_cnt++; } + scc->stat.rx_queued = 0; while (scc->tx_queue != NULLBUF) { - bptr = scc_dequeue_buffer(&scc->tx_queue); - scc_enqueue_buffer(&scc->tx_buffer_pool, bptr); + kfree(scc_dequeue_buffer(&scc->tx_queue)); + tx_cnt++; } - - /* you want to know why we move every buffer back to the - buffer pool? Well, good question... ;-) - */ - - cnt = 0; - - /* Probably because we just want a central position in the - code were we actually call free()? - */ + scc->stat.tx_queued = 0; while (scc->rx_buffer_pool != NULLBUF) { - bptr = scc_dequeue_buffer(&scc->rx_buffer_pool); - - if (bptr != NULLBUF) - { - cnt++; - kfree(bptr); - } + kfree(scc_dequeue_buffer(&scc->rx_buffer_pool)); + rx_cnt++; } - - if (cnt < scc->stat.rxbuffers) /* hmm... hmm... :-( */ - printk("z8530drv: oops, deallocated only %d of %d rx buffers\n", cnt, scc->stat.rxbuffers); - if (cnt > scc->stat.rxbuffers) /* WHAT?!! */ - printk("z8530drv: oops, deallocated %d instead of %d rx buffers. Very strange.\n", cnt, scc->stat.rxbuffers); - - cnt = 0; while (scc->tx_buffer_pool != NULLBUF) { - bptr = scc_dequeue_buffer(&scc->tx_buffer_pool); - - if (bptr != NULLBUF) - { - cnt++; - kfree(bptr); - } + kfree(scc_dequeue_buffer(&scc->tx_buffer_pool)); + tx_cnt++; } - - if (cnt < scc->stat.txbuffers) - printk("z8530drv: oops, deallocated only %d of %d tx buffers\n", cnt, scc->stat.txbuffers); - if (cnt > scc->stat.txbuffers) - printk("z8530drv: oops, deallocated %d instead of %d tx buffers. Very strange.\n", cnt, scc->stat.txbuffers); + + if (rx_cnt < scc->stat.rxbuffers) /* hmm... hmm... :-( */ + printk(KERN_ERR "z8530drv: oops, deallocated only %d of %d rx buffers\n", rx_cnt, scc->stat.rxbuffers); + if (rx_cnt > scc->stat.rxbuffers) /* WHAT?!! */ + printk(KERN_ERR "z8530drv: oops, deallocated %d instead of %d rx buffers. Very strange.\n", rx_cnt, scc->stat.rxbuffers); + + if (tx_cnt < scc->stat.txbuffers) + printk(KERN_ERR "z8530drv: oops, deallocated only %d of %d tx buffers\n", tx_cnt, scc->stat.txbuffers); + if (tx_cnt > scc->stat.txbuffers) + printk(KERN_ERR "z8530drv: oops, deallocated %d instead of %d tx buffers. Very strange.\n", tx_cnt, scc->stat.txbuffers); + + scc->mempool = 0; } @@ -666,28 +690,33 @@ if (bptr == NULLBUF) { - printk("z8530drv: scc_get_buffer(%s): tx buffer pool empty\n", kdevname(scc->tty->device)); - +#if 0 + printk(KERN_DEBUG "z8530drv: tx buffer pool for %s empty\n", SCC_DEVNAME); +#endif /* use the oldest from the queue instead */ bptr = scc_dequeue_buffer(&scc->tx_queue); + scc->stat.tx_queued--; + scc->stat.nospace++; /* this should never, ever happen... */ if (bptr == NULLBUF) - printk("z8530drv: scc_get_buffer(): panic - even no buffer found in tx queue\n"); + printk(KERN_ERR "z8530drv: panic - all tx buffers for %s gone\n", SCC_DEVNAME); } } else { bptr = scc_dequeue_buffer(&scc->rx_buffer_pool); if (bptr == NULLBUF) { - printk("z8530drv: scc_get_buffer(%s): rx buffer pool empty\n", kdevname(scc->tty->device)); + printk(KERN_DEBUG "z8530drv: rx buffer pool for %s empty\n", SCC_DEVNAME); bptr = scc_dequeue_buffer(&scc->rx_queue); + scc->stat.rx_queued--; + scc->stat.nospace++; if (bptr == NULLBUF) - printk("z8530drv: scc_get_buffer(): panic - even no buffer found in rx queue\n"); + printk(KERN_ERR "z8530drv: panic - all rx buffers for %s gone\n", SCC_DEVNAME); } } @@ -705,153 +734,117 @@ /* * Interrupt Service Routines * */ /* ******************************************************************** */ -/* ----> interrupt service routine for the 8530 <---- */ -/* it's recommended to keep this function "inline" ;-) */ - -static inline void -scc_isr_dispatch(register struct scc_channel *scc, register int vector) -{ - switch (vector & VECTOR_MASK) - { - case TXINT: scc_txint(scc); break; - case EXINT: scc_exint(scc); break; - case RXINT: scc_rxint(scc); break; - case SPINT: scc_spint(scc); break; - default : printk("scc_isr(): unknown interrupt status (addr %4.4x, state %2.2x)\n",scc->ctrl,vector); - } -} +/* ----> subroutines for the interrupt handlers <---- */ +#ifdef CONFIG_SCC_TTY +/* kick rx_timer (try to send received frame or a status message ASAP) */ - -/* If the card has a latch for the interrupt vector (like the PA0HZP card) - use it to get the number of the chip that generated the int. - If not: poll all defined chips. +/* of course we could define a "bottom half" routine to do the job, + but since its structures are saved in an array instead of a linked + list we would get in trouble if it clashes with another driver. + IMHO we are fast enough with a timer routine called on the next + timer-INT... Your opinions? */ -static void -scc_isr(int irq, void *dev_id, struct pt_regs *regs) +static __inline__ void +kick_rx_timer(struct scc_channel *scc) { - register unsigned char vector; - register struct scc_channel *scc; - register io_port q; - register io_port *p; - register int k; - + del_timer(&(scc->rx_t)); + scc->rx_t.expires = jiffies + 1; + scc->rx_t.function = scc_rx_timer; + scc->rx_t.data = (unsigned long) scc; + add_timer(&scc->rx_t); +} +#endif - cli(); +static __inline__ void +scc_notify(struct scc_channel *scc, int event) +{ + struct mbuf *bp; - if (Vector_Latch) - { - while(1) /* forever...? */ - { - Outb(Vector_Latch, 0); /* Generate INTACK */ - - /* Read the vector */ - if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; - /* ...not forever! */ - - /* Extract channel number and status from vector. */ - /* Isolate channel number */ - /* Call handler */ - - if (vector & 0x01) break; - - scc=&SCC_Info[(((vector>>1)&0x7c)^0x04) >> 2]; - - if (!scc->tty) break; - - scc_isr_dispatch(scc, vector); - - Outb(scc->ctrl,0x38); /* Reset Highest IUS" opcode to WR0 */ - } - - sti(); + if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA) return; - } - - /* Find the SCC generating the interrupt by polling all attached SCCs - * reading RR3A (the interrupt pending register) - */ + bp = scc_get_buffer(scc, BT_RECEIVE); - k = 0; - p = SCC_ctrl; - - while (k++ < Nchips) - { - if (!(q=*p++)) break; - - Outb(q,3); + if (bp == NULLBUF) + return; - if (Inb(q)) - { - if (!(q=*p++)) break; - - Outb(q,2); - vector=Inb(q); /* Read the vector */ - - /* Extract channel number and status from vector. */ - /* Isolate channel number */ - /* Call handler */ - + bp->data[0] = PARAM_HWEVENT; + bp->data[1] = event; + bp->cnt += 2; - if (vector & 1) break; - - scc = &SCC_Info[(((vector >> 1) & 0x7c) ^ 0x04) >> 2]; - - if (!scc->tty) break; - - /* Isolate status info from vector, call handler */ - - scc_isr_dispatch(scc, vector); - - k = 0; - p = SCC_ctrl; + if (scc->stat.is_netdev) + { +#ifdef CONFIG_SCC_DEV + scc_net_rx(scc, bp); + scc_enqueue_buffer(&scc->rx_buffer_pool, bp); +#endif + } else { +#ifdef CONFIG_SCC_TTY + scc_enqueue_buffer(&scc->rx_queue, bp); + scc->stat.rx_queued++; + kick_rx_timer(scc); +#endif + } +} - } else p++; - } +static __inline__ void +flush_rx_FIFO(struct scc_channel *scc) +{ + int k; - sti(); + for (k=0; k<3; k++) + Inb(scc->data); + + if(scc->rx_bp != NULLBUF) /* did we receive something? */ + { + scc->stat.rxerrs++; /* then count it as an error */ + scc_enqueue_buffer(&scc->rx_buffer_pool, scc->rx_bp); + + scc->rx_bp = NULLBUF; + } } - /* ----> four different interrupt handlers for Tx, Rx, changing of */ /* DCD/CTS and Rx/Tx errors */ - /* Transmitter interrupt handler */ -static void -scc_txint(register struct scc_channel *scc) +static __inline__ void +scc_txint(struct scc_channel *scc) { - register struct mbuf *bp; + struct mbuf *bp; scc->stat.txints++; bp = scc->tx_bp; + /* send first octet */ + if (bp == NULLBUF) { - do + do { if (bp != NULLBUF) + { scc_enqueue_buffer(&scc->tx_buffer_pool, bp); + scc->stat.tx_queued--; + } bp = scc_dequeue_buffer(&scc->tx_queue); if (bp == NULLBUF) { - scc->stat.tx_state = TXS_BUSY; - scc->t_tail = scc->kiss.tailtime; - + scc_tx_done(scc); Outb(scc->ctrl, RES_Tx_P); /* clear int */ return; } - if ( scc->kiss.not_slip && (bp->cnt > 0) ) + if (bp->cnt > 0) { bp->rw_ptr++; bp->cnt--; @@ -859,57 +852,47 @@ } while (bp->cnt < 1); - - Outb(scc->ctrl, RES_Tx_CRC); /* reset CRC generator */ + scc->tx_bp = bp; + scc->stat.tx_state = TXS_ACTIVE; + + OutReg(scc->ctrl, R0, RES_Tx_CRC); + /* reset CRC generator */ or(scc,R10,ABUNDER); /* re-install underrun protection */ - Outb(scc->data,*bp->rw_ptr); /* send byte */ + Outb(scc->data,*bp->rw_ptr++); /* send byte */ + if (!scc->enhanced) /* reset EOM latch */ - Outb(scc->ctrl, RES_EOM_L); - - scc->tx_bp = bp; - scc->stat.tx_state = TXS_ACTIVE; /* next byte... */ - } else + Outb(scc->ctrl,RES_EOM_L); + + bp->cnt--; + return; + } + + /* End Of Frame... */ + if (bp->cnt <= 0) { - if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0; - Outb(scc->ctrl, RES_Tx_P); /* reset pending int */ cl(scc, R10, ABUNDER); /* send CRC */ scc_enqueue_buffer(&scc->tx_buffer_pool, bp); + scc->stat.tx_queued--; scc->tx_bp = NULLBUF; scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */ return; - } else { - Outb(scc->data,*bp->rw_ptr); - } + } + /* send octet */ + + Outb(scc->data,*bp->rw_ptr); bp->rw_ptr++; /* increment pointer */ bp->cnt--; /* decrease byte count */ } -static inline void -flush_FIFO(register struct scc_channel *scc) + +/* External/Status interrupt handler */ +static __inline__ void +scc_exint(struct scc_channel *scc) { - register int k; - - for (k=0; k<3; k++) - Inb(scc->data); - - if(scc->rx_bp != NULLBUF) /* did we receive something? */ - { - scc->stat.rxerrs++; /* then count it as an error */ - scc_enqueue_buffer(&scc->rx_buffer_pool, scc->rx_bp); - - scc->rx_bp = NULLBUF; - } -} - - -/* External/Status interrupt handler */ -static void -scc_exint(register struct scc_channel *scc) -{ - register unsigned char status,changes,chg_and_stat; + unsigned char status,changes,chg_and_stat; scc->stat.exints++; @@ -920,9 +903,9 @@ /* ABORT: generated whenever DCD drops while receiving */ if (chg_and_stat & BRK_ABRT) /* Received an ABORT */ - flush_FIFO(scc); - - + flush_rx_FIFO(scc); + + /* DCD: on = start to receive packet, off = ABORT condition */ /* (a successfully received packet generates a special condition int) */ @@ -936,69 +919,67 @@ or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */ } else { /* DCD is now OFF */ cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */ - flush_FIFO(scc); + flush_rx_FIFO(scc); } + + if (!scc->kiss.softdcd) + scc_notify(scc, (status & DCD)? HWEV_DCD_ON:HWEV_DCD_OFF); + } + + /* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */ + + if (changes & SYNC_HUNT) + { + if (scc->kiss.softdcd) + scc_notify(scc, (status & SYNC_HUNT)? HWEV_DCD_OFF:HWEV_DCD_ON); + else + cl(scc,R15,SYNCIE); /* oops, we were too lazy to disable this? */ } #ifdef notdef - /* CTS: use external TxDelay (what's that good for?!) */ - + /* CTS: use external TxDelay (what's that good for?!) + * Anyway: If we _could_ use it (BayCom USCC uses CTS for + * own purposes) we _should_ use the "autoenable" feature + * of the Z8530 and not this interrupt... + */ + if (chg_and_stat & CTS) /* CTS is now ON */ - { - if (!Running(t_txdel) && scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */ - scc->t_txdel = 0; /* kick it! */ - + { + if (scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */ + scc_start_tx_timer(scc, t_txdelay, 0); } #endif - if ( (status & TxEOM) && (scc->stat.tx_state == TXS_ACTIVE) ) + if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM)) { scc->stat.tx_under++; /* oops, an underrun! count 'em */ - Outb(scc->ctrl, RES_Tx_P); Outb(scc->ctrl, RES_EXT_INT); /* reset ext/status interrupts */ - scc->t_maxk = 1; - + if (scc->tx_bp != NULLBUF) { scc_enqueue_buffer(&scc->tx_buffer_pool, scc->tx_bp); + scc->stat.tx_queued--; scc->tx_bp = NULLBUF; } - if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0; or(scc,R10,ABUNDER); + scc_start_tx_timer(scc, t_txdelay, 1); /* restart transmission */ } - if (status & ZCOUNT) /* Oops? */ - { - scc->stat.tx_under = 9999; /* errr... yes. */ - Outb(scc->ctrl, RES_Tx_P); /* just to be sure */ - scc->t_maxk = 1; - - if (scc->tx_bp != NULLBUF) - { - scc_enqueue_buffer(&scc->tx_buffer_pool, scc->tx_bp); - scc->tx_bp = NULLBUF; - } - - if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0; - scc->kiss.tx_inhibit = 1; /* don't try it again! */ - } - - scc->status = status; Outb(scc->ctrl,RES_EXT_INT); } /* Receiver interrupt handler */ -static void -scc_rxint(register struct scc_channel *scc) +static __inline__ void +scc_rxint(struct scc_channel *scc) { - register struct mbuf *bp; + struct mbuf *bp; scc->stat.rxints++; - if( Running(t_maxk) && !(scc->kiss.fulldup)) + if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF) { Inb(scc->data); /* discard char */ or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ @@ -1012,20 +993,22 @@ bp = scc_get_buffer(scc, BT_RECEIVE); if (bp == NULLBUF) { - printk("scc_rxint(): panic --- cannot get a buffer\n"); Inb(scc->data); or(scc, R3, ENT_HM); - scc->stat.nospace++; return; } scc->rx_bp = bp; + + *bp->rw_ptr=0; /* KISS data */ + bp->rw_ptr++; + bp->cnt++; } if (bp->cnt > scc->stat.bufsize) { #ifdef notdef - printk("scc_rxint(): oops, received huge frame...\n"); + printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n"); #endif scc_enqueue_buffer(&scc->rx_buffer_pool, bp); scc->rx_bp = NULLBUF; @@ -1040,33 +1023,13 @@ bp->cnt++; } -/* kick rx_timer (try to send received frame or part of it ASAP) */ - -/* of course we could define a "bottom half" routine to do the job, - but since its structures are saved in an array instead of a linked - list we would get in trouble if it clashes with another driver or - when we try to modularize the driver. IMHO we are fast enough - with a timer routine called on the next timer-INT... Your opinions? - */ - -static inline void -kick_rx_timer(register struct scc_channel *scc) -{ - if (scc->rx_t.next) - del_timer(&(scc->rx_t)); - - scc->rx_t.expires = jiffies + 1; - scc->rx_t.function = scc_rx_timer; - scc->rx_t.data = (unsigned long) scc; - add_timer(&scc->rx_t); -} /* Receive Special Condition interrupt handler */ -static void -scc_spint(register struct scc_channel *scc) +static __inline__ void +scc_spint(struct scc_channel *scc) { - register unsigned char status; - register struct mbuf *bp; + unsigned char status; + struct mbuf *bp; scc->stat.spints++; @@ -1083,7 +1046,7 @@ if (bp) scc_enqueue_buffer(&scc->rx_buffer_pool, bp); scc->rx_bp = NULLBUF; } - + if(status & END_FR && bp != NULLBUF) /* end of frame */ { /* CRC okay, frame ends on 8 bit boundary and received something ? */ @@ -1092,23 +1055,141 @@ { /* ignore last received byte (first of the CRC bytes) */ bp->cnt--; - - scc_enqueue_buffer(&scc->rx_queue, bp); + + if (scc->stat.is_netdev) + { +#ifdef CONFIG_SCC_DEV + scc_net_rx(scc, bp); + scc_enqueue_buffer(&scc->rx_buffer_pool, bp); +#endif + } else { +#ifdef CONFIG_SCC_TTY + scc_enqueue_buffer(&scc->rx_queue, bp); + scc->stat.rx_queued++; + kick_rx_timer(scc); +#endif + } + scc->rx_bp = NULLBUF; scc->stat.rxframes++; - scc->stat.rx_queued++; - kick_rx_timer(scc); } else { /* a bad frame */ scc_enqueue_buffer(&scc->rx_buffer_pool, bp); scc->rx_bp = NULLBUF; scc->stat.rxerrs++; } + } + + Outb(scc->ctrl,ERR_RES); +} + + +/* ----> interrupt service routine for the Z8530 <---- */ + +static void +scc_isr_dispatch(struct scc_channel *scc, int vector) +{ + switch (vector & VECTOR_MASK) + { + case TXINT: scc_txint(scc); break; + case EXINT: scc_exint(scc); break; + case RXINT: scc_rxint(scc); break; + case SPINT: scc_spint(scc); break; } +} + +/* If the card has a latch for the interrupt vector (like the PA0HZP card) + use it to get the number of the chip that generated the int. + If not: poll all defined chips. + */ + +#define SCC_IRQTIMEOUT 30000 + +static void +scc_isr(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned char vector; + struct scc_channel *scc; + struct scc_ctrl *ctrl; + int k; - Outb(scc->ctrl,ERR_RES); + scc_cli(irq); + + if (Vector_Latch) + { + for(k=0; k < SCC_IRQTIMEOUT; k++) + { + Outb(Vector_Latch, 0); /* Generate INTACK */ + + /* Read the vector */ + if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; + if (vector & 0x01) break; + + scc=&SCC_Info[vector >> 3 ^ 0x01]; + if (!scc->tty && !scc->dev) break; + + scc_isr_dispatch(scc, vector); + + OutReg(scc->ctrl,R0,RES_H_IUS); /* Reset Highest IUS */ + } + scc_sti(irq); + + if (k == SCC_IRQTIMEOUT) + printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n"); + + return; + } + + /* Find the SCC generating the interrupt by polling all attached SCCs + * reading RR3A (the interrupt pending register) + */ + + ctrl = SCC_ctrl; + while (ctrl->chan_A) + { + if (ctrl->irq != irq) + { + ctrl++; + continue; + } + + scc = NULL; + for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++) + { + vector=InReg(ctrl->chan_B,R2); /* Read the vector */ + if (vector & 0x01) break; + + scc = &SCC_Info[vector >> 3 ^ 0x01]; + if (!scc->tty && !scc->dev) break; + + scc_isr_dispatch(scc, vector); + } + + if (k == SCC_IRQTIMEOUT) + { + printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n"); + break; + } + + /* This looks wierd and it is. At least the BayCom USCC doesn't + * use the Interrupt Daisy Chain, thus we'll have to start + * all over again to be sure not to miss an interrupt from + * (any of) the other chip(s)... + * Honestly, the situation *is* braindamaged... + */ + + if (scc != NULL) + { + OutReg(scc->ctrl,R0,RES_H_IUS); + ctrl = SCC_ctrl; + } else + ctrl++; + } + + scc_sti(irq); } + /* ******************************************************************** */ /* * Init Channel */ /* ******************************************************************** */ @@ -1116,30 +1197,31 @@ /* ----> set SCC channel speed <---- */ -static inline void set_brg(register struct scc_channel *scc, unsigned int tc) +static __inline__ void +set_brg(struct scc_channel *scc, unsigned int tc) { - unsigned long flags; - - save_flags(flags); cli(); /* 2-step register accesses... */ - cl(scc,R14,BRENABL); /* disable baudrate generator */ wr(scc,R12,tc & 255); /* brg rate LOW */ wr(scc,R13,tc >> 8); /* brg rate HIGH */ or(scc,R14,BRENABL); /* enable baudrate generator */ - - restore_flags(flags); } -static inline void set_speed(register struct scc_channel *scc) +static __inline__ void +set_speed(struct scc_channel *scc) { + disable_irq(scc->irq); + if (scc->modem.speed > 0) /* paranoia... */ set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2); + + enable_irq(scc->irq); } /* ----> initialize a SCC channel <---- */ -static inline void init_brg(register struct scc_channel *scc) +static __inline__ void +init_brg(struct scc_channel *scc) { wr(scc, R14, BRSRC); /* BRG source = PCLK */ OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */ @@ -1192,14 +1274,17 @@ */ static void -init_channel(register struct scc_channel *scc) +init_channel(struct scc_channel *scc) { - unsigned long flags; + disable_irq(scc->irq); - if (scc->rx_t.next) del_timer(&(scc->rx_t)); - if (scc->tx_t.next) del_timer(&(scc->tx_t)); +#ifdef CONFIG_SCC_TTY + del_timer(&scc->rx_t); +#endif + del_timer(&scc->tx_t); + del_timer(&scc->tx_wdog); + - save_flags(flags); cli(); wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */ wr(scc,R1,0); /* no W/REQ operation */ @@ -1273,9 +1358,12 @@ or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */ } - /* enable CTS (not for Baycom), ABORT & DCD interrupts */ - wr(scc,R15,((scc->brand & BAYCOM) ? 0 : CTSIE)|BRKIE|DCDIE|TxUIE); - + /* enable ABORT, DCD & SYNC/HUNT interrupts */ + + wr(scc,R15, BRKIE|TxUIE|DCDIE); + if (scc->kiss.softdcd) + or(scc,R15, SYNCIE); + Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */ @@ -1287,7 +1375,7 @@ scc_init_timer(scc); - restore_flags(flags); + enable_irq(scc->irq); } @@ -1305,21 +1393,29 @@ scc_key_trx(struct scc_channel *scc, char tx) { unsigned int time_const; - - if (scc->modem.speed < baud_table[1]) - scc->modem.speed = 1200; if (scc->brand & PRIMUS) Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0)); - + + if (scc->modem.speed < 300) + scc->modem.speed = 1200; + time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2; - + + disable_irq(scc->irq); + + if (tx) + { + or(scc, R1, TxINT_ENAB); /* t_maxkeyup may have reset these */ + or(scc, R15, TxUIE); + } + if (scc->modem.clocksrc == CLK_DPLL) - { /* simplex operation */ + { /* force simplex operation */ if (tx) { - cl(scc,R3,RxENABLE|ENT_HM); /* then switch off receiver */ - + cl(scc, R3, RxENABLE|ENT_HM); /* switch off receiver */ + cl(scc, R15, DCDIE); /* No DCD changes, please */ set_brg(scc, time_const); /* reprogram baudrate generator */ /* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */ @@ -1335,21 +1431,140 @@ wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); or(scc,R3,RxENABLE|ENT_HM); + or(scc,R15, DCDIE); } } else { if (tx) + { + if (scc->kiss.fulldup == KISS_DUPLEX_HALF) + { + cl(scc, R3, RxENABLE); + cl(scc, R15, DCDIE); + } + + or(scc,R5,RTS|TxENAB); /* enable tx */ - else + } else { cl(scc,R5,RTS|TxENAB); /* disable tx */ + + if (scc->kiss.fulldup == KISS_DUPLEX_HALF) + { + or(scc, R3, RxENABLE|ENT_HM); + or(scc, R15, DCDIE); + } + } + } + + enable_irq(scc->irq); +} + + +/* ----> SCC timer interrupt handler and friends. <---- */ + +static void +scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + del_timer(&scc->tx_t); + + if (when == 0) + { + handler((unsigned long) scc); + } else + if (when != TIMER_OFF) + { + scc->tx_t.data = (unsigned long) scc; + scc->tx_t.function = handler; + scc->tx_t.expires = jiffies + (when*HZ)/100; + add_timer(&scc->tx_t); + } + + restore_flags(flags); +} + +static void +scc_start_defer(struct scc_channel *scc) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + del_timer(&scc->tx_wdog); + + if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF) + { + scc->tx_wdog.data = (unsigned long) scc; + scc->tx_wdog.function = t_busy; + scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer; + add_timer(&scc->tx_wdog); + } + restore_flags(flags); +} + +static void +scc_start_maxkeyup(struct scc_channel *scc) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + del_timer(&scc->tx_wdog); + + if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF) + { + scc->tx_wdog.data = (unsigned long) scc; + scc->tx_wdog.function = t_maxkeyup; + scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup; + add_timer(&scc->tx_wdog); } + + restore_flags(flags); } +/* + * This is called from scc_txint() when there are no more frames to send. + * Not exactly a timer function, but it is a close friend of the family... + */ + +static void +scc_tx_done(struct scc_channel *scc) +{ + /* + * trx remains keyed in fulldup mode 2 until t_idle expires. + */ + + switch (scc->kiss.fulldup) + { + case KISS_DUPLEX_LINK: + scc->stat.tx_state = TXS_IDLE2; + if (scc->kiss.idletime != TIMER_OFF) + scc_start_tx_timer(scc, t_idle, scc->kiss.idletime*100); + break; + case KISS_DUPLEX_OPTIMA: + scc_notify(scc, HWEV_ALL_SENT); + break; + default: + scc->stat.tx_state = TXS_BUSY; + scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); + } + +#ifdef CONFIG_SCC_DEV + if (scc->stat.is_netdev) + scc->dev->tbusy = 0; +#endif +} -/* ----> SCC timer interrupt handler and friends. Will be called every 1/TPS s <---- */ static unsigned char Rand = 17; -static inline int is_grouped(register struct scc_channel *scc) +static __inline__ int +is_grouped(struct scc_channel *scc) { int k; struct scc_channel *scc2; @@ -1362,8 +1577,8 @@ scc2 = &SCC_Info[k]; grp2 = scc2->kiss.group; - if (scc2 == scc || !(scc2->tty && grp2)) - return 0; + if (scc2 == scc || !(scc2->tty && scc2->dev && grp2)) + continue; if ((grp1 & 0x3f) == (grp2 & 0x3f)) { @@ -1376,205 +1591,244 @@ } return 0; } - -static inline void dw_slot_timeout(register struct scc_channel *scc) +/* DWAIT and SLOTTIME expired + * + * fulldup == 0: DCD is active or Rand > P-persistence: start t_busy timer + * else key trx and start txdelay + * fulldup == 1: key trx and start txdelay + * fulldup == 2: mintime expired, reset status or key trx and start txdelay + */ + +static void +t_dwait(unsigned long channel) { - scc->t_dwait = TIMER_STOPPED; - scc->t_slot = TIMER_STOPPED; + struct scc_channel *scc = (struct scc_channel *) channel; - if (!scc->kiss.fulldup) + if (scc->stat.tx_state == TXS_WAIT) + { + if (scc->tx_queue == NULL) + { + scc->stat.tx_state = TXS_IDLE; + return; + } + + scc->stat.tx_state = TXS_BUSY; + } + + if (scc->kiss.fulldup == KISS_DUPLEX_HALF) { Rand = Rand * 17 + 31; if ( (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD)) || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) ) { - if (scc->t_mbusy == TIMER_STOPPED) - scc->t_mbusy = TPS * scc->kiss.maxdefer; - - scc->t_slot = scc->kiss.slottime; + scc_start_defer(scc); + scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime); return ; - } } - + if ( !(scc->wreg[R5] & RTS) ) { - scc->t_txdel = scc->kiss.txdelay; scc_key_trx(scc, TX_ON); + scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); } else { - scc->t_txdel = 0; + scc_start_tx_timer(scc, t_txdelay, 0); } } +/* TXDELAY expired + * + * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog. + */ - -static inline void txdel_timeout(register struct scc_channel *scc) +static void +t_txdelay(unsigned long channel) { - scc->t_txdel = TIMER_STOPPED; - - scc->t_maxk = TPS * scc->kiss.maxkeyup; - + struct scc_channel *scc = (struct scc_channel *) channel; + + scc_start_maxkeyup(scc); + if (scc->tx_bp == NULLBUF) + { + disable_irq(scc->irq); scc_txint(scc); + enable_irq(scc->irq); + } } +/* TAILTIME expired + * + * switch off transmitter. If we were stopped by Maxkeyup restart + * transmission after 'mintime' seconds + */ -static inline void tail_timeout(register struct scc_channel *scc) +static void +t_tail(unsigned long channel) { - scc->t_tail = TIMER_STOPPED; + struct scc_channel *scc = (struct scc_channel *) channel; - /* when fulldup is 0 or 1, switch off the transmitter. - * when frames are still queued (because of transmit time limit), - * restart the procedure to get the channel after MINTIME. - * when fulldup is 2, the transmitter remains keyed and we - * continue sending after waiting for waittime. IDLETIME is an - * idle timeout in this case. - */ - - if (scc->kiss.fulldup < 2) - { - if (scc->tx_bp) /* we had a timeout? */ - { - scc->stat.tx_state = TXS_BUSY; - scc->t_dwait = TPS * scc->kiss.mintime; /* try again */ - } - - scc->stat.tx_state = TXS_IDLE; - scc->t_maxk = TIMER_STOPPED; - scc_key_trx(scc, TX_OFF); - return; - } - - if (scc->tx_bp) /* maxkeyup expired */ /* ?! */ - { - scc->stat.tx_state = TXS_BUSY; - scc->t_txdel = TPS * scc->kiss.waittime; - } - else - - scc->t_idle = TPS * scc->kiss.idletime; + del_timer(&scc->tx_wdog); + scc_key_trx(scc, TX_OFF); + + if (scc->stat.tx_state == TXS_TIMEOUT) /* we had a timeout? */ + { + scc->stat.tx_state = TXS_WAIT; + + if (scc->tx_bp != NULLBUF) + scc_enqueue_buffer(&scc->tx_queue, scc->tx_bp); + + scc->tx_bp = NULLBUF; + + if (scc->kiss.mintime != TIMER_OFF) /* try it again */ + scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); + else + scc_start_tx_timer(scc, t_dwait, 0); + return; + } + + scc->stat.tx_state = TXS_IDLE; + +#ifdef CONFIG_SCC_DEV + if (scc->stat.is_netdev) + scc->dev->tbusy = 0; +#endif } -static inline void busy_timeout(register struct scc_channel *scc) +/* BUSY timeout + * + * throw away send buffers if DCD remains active too long. + */ + +static void +t_busy(unsigned long channel) { -#ifdef THROW_AWAY_AFTER_BUSY_TIMEOUT - register struct mbuf *bp; /* not tested */ + struct scc_channel *scc = (struct scc_channel *) channel; + struct mbuf *bp; + + del_timer(&scc->tx_t); - while (bp = scc_dequeue_buffer(&scc->tx_queue)) + while ((bp = scc_dequeue_buffer(&scc->tx_queue)) != NULLBUF) scc_enqueue_buffer(&scc->tx_buffer_pool, bp); + + scc->stat.tx_queued = 0; + scc->stat.txerrs++; scc->tx_queue = NULLBUF; scc->stat.tx_state = TXS_IDLE; - -#else - scc->t_txdel = scc->kiss.txdelay; /* brute force ... */ + +#ifdef CONFIG_SCC_DEV + if (scc->stat.is_netdev) + scc->dev->tbusy = 0; #endif - scc->t_mbusy = TIMER_STOPPED; - } +/* MAXKEYUP timeout + * + * this is our watchdog. + */ -static inline void maxk_idle_timeout(register struct scc_channel *scc) -{ - scc->t_maxk = TIMER_STOPPED; - scc->t_idle = TIMER_STOPPED; - - scc->stat.tx_state = TXS_BUSY; - scc->t_tail = scc->kiss.tailtime; -} - -static void -scc_tx_timer(unsigned long channel) +static void +t_maxkeyup(unsigned long channel) { - register struct scc_channel *scc; + struct scc_channel *scc = (struct scc_channel *) channel; unsigned long flags; - - scc = (struct scc_channel *) channel; - - if (scc->tty && scc->init) - { - save_flags(flags); cli(); - - /* KISS-TNC emulation */ - - if (Expired(t_dwait)) dw_slot_timeout(scc) ; else - if (Expired(t_slot)) dw_slot_timeout(scc) ; else - if (Expired(t_txdel)) txdel_timeout(scc) ; else - if (Expired(t_tail)) tail_timeout(scc) ; - - /* watchdogs */ - - if (Expired(t_mbusy)) busy_timeout(scc); - if (Expired(t_maxk)) maxk_idle_timeout(scc); - if (Expired(t_idle)) maxk_idle_timeout(scc); - - restore_flags(flags); + save_flags(flags); + cli(); + + if (scc->stat.is_netdev) + { +#ifdef CONFIG_SCC_DEV + /* + * let things settle down before we start to + * accept new data. + */ + scc->dev->tbusy = 1; + scc->dev->trans_start = jiffies; +#endif } + + del_timer(&scc->tx_t); + + cl(scc, R1, TxINT_ENAB); /* force an ABORT, but don't */ + cl(scc, R15, TxUIE); /* count it. */ + OutReg(scc->ctrl, R0, RES_Tx_P); + + restore_flags(flags); + + scc->stat.txerrs++; + scc->stat.tx_state = TXS_TIMEOUT; + scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); +} + +/* IDLE timeout + * + * in fulldup mode 2 it keys down the transmitter after 'idle' seconds + * of inactivity. We will not restart transmission before 'mintime' + * expires. + */ + +static void +t_idle(unsigned long channel) +{ + struct scc_channel *scc = (struct scc_channel *) channel; - scc->tx_t.expires = jiffies + HZ/TPS; - add_timer(&scc->tx_t); + del_timer(&scc->tx_wdog); + scc_key_trx(scc, TX_OFF); + + if (scc->kiss.mintime != TIMER_OFF) + scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); + scc->stat.tx_state = TXS_WAIT; } - + +#ifdef CONFIG_SCC_TTY static void scc_rx_timer(unsigned long channel) { - register struct scc_channel *scc; + struct scc_channel *scc; scc = (struct scc_channel *) channel; if (scc->rx_queue && scc->throttled) { - scc->rx_t.expires = jiffies + HZ/TPS; + scc->rx_t.expires = jiffies + HZ/25; /* 40 msec */ add_timer(&scc->rx_t); return; } kiss_encode(scc); - + if (scc->rx_queue && !scc->throttled) { - printk("z8530drv: warning: %s should be throttled\n", - kdevname(scc->tty->device)); - - scc->rx_t.expires = jiffies + HZ/TPS; + printk(KERN_DEBUG "z8530drv: warning, %s should be throttled\n", + SCC_DEVNAME); + + scc->rx_t.expires = jiffies + HZ/25; /* 40 msec */ add_timer(&scc->rx_t); } } +#endif static void scc_init_timer(struct scc_channel *scc) { unsigned long flags; - save_flags(flags); cli(); - - Stop_Timer(t_dwait); - Stop_Timer(t_slot); - Stop_Timer(t_txdel); - Stop_Timer(t_tail); - Stop_Timer(t_mbusy); - Stop_Timer(t_maxk); - Stop_Timer(t_idle); + save_flags(flags); + cli(); scc->stat.tx_state = TXS_IDLE; - if (scc->tx_t.next) - del_timer(&scc->tx_t); - - scc->tx_t.data = (unsigned long) scc; - scc->tx_t.function = scc_tx_timer; - scc->tx_t.expires = jiffies + HZ/TPS; - add_timer(&scc->tx_t); - +#ifdef CONFIG_SCC_TTY scc->rx_t.data = (unsigned long) scc; scc->rx_t.function = scc_rx_timer; +#endif restore_flags(flags); } @@ -1586,54 +1840,111 @@ /* - * this will set the "kiss" parameters through kiss itself + * this will set the "kiss" parameters through KISS commands or ioctl() */ - -static void -kiss_set_param(struct scc_channel *scc,char cmd, unsigned int val) + +#define CAST(x) (unsigned long)(x) + +static unsigned int +kiss_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg) { + int dcd; + + switch (cmd) + { + case PARAM_TXDELAY: scc->kiss.txdelay=arg; break; + case PARAM_PERSIST: scc->kiss.persist=arg; break; + case PARAM_SLOTTIME: scc->kiss.slottime=arg; break; + case PARAM_TXTAIL: scc->kiss.tailtime=arg; break; + case PARAM_FULLDUP: scc->kiss.fulldup=arg; break; + case PARAM_DTR: break; /* does someone need this? */ + case PARAM_GROUP: scc->kiss.group=arg; break; + case PARAM_IDLE: scc->kiss.idletime=arg; break; + case PARAM_MIN: scc->kiss.mintime=arg; break; + case PARAM_MAXKEY: scc->kiss.maxkeyup=arg; break; + case PARAM_WAIT: scc->kiss.waittime=arg; break; + case PARAM_MAXDEFER: scc->kiss.maxdefer=arg; break; + case PARAM_TX: scc->kiss.tx_inhibit=arg; break; + + case PARAM_SOFTDCD: + scc->kiss.softdcd=arg; + if (arg) + or(scc, R15, SYNCIE); + else + cl(scc, R15, SYNCIE); + break; + + case PARAM_SPEED: + if (arg < 256) + scc->modem.speed=arg*100; + else + scc->modem.speed=arg; + + if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */ + set_speed(scc); + break; + + case PARAM_RTS: + if ( !(scc->wreg[R5] & RTS) ) + { + if (arg != TX_OFF) + scc_key_trx(scc, TX_ON); + scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); + } else { + if (arg == TX_OFF) + { + scc->stat.tx_state = TXS_BUSY; + scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); + } + } + break; + + case PARAM_HWEVENT: + dcd = (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD)); + scc_notify(scc, dcd? HWEV_DCD_ON:HWEV_DCD_OFF); + break; + + default: return -EINVAL; + } + + return 0; +} -#define VAL val=val*TPS/100 -#define SVAL val? val:TIMER_STOPPED - switch(cmd){ - case PARAM_TXDELAY: - scc->kiss.txdelay = VAL; break; - case PARAM_PERSIST: - scc->kiss.persist = val; break; - case PARAM_SLOTTIME: - scc->kiss.slottime = VAL; break; - case PARAM_TXTAIL: - scc->kiss.tailtime = VAL; break; - case PARAM_FULLDUP: - scc->kiss.fulldup = val; break; - case PARAM_WAIT: - scc->kiss.waittime = VAL; break; - case PARAM_MAXKEY: - scc->kiss.maxkeyup = SVAL; break; - case PARAM_MIN: - scc->kiss.mintime = SVAL; break; - case PARAM_IDLE: - scc->kiss.idletime = val; break; - case PARAM_MAXDEFER: - scc->kiss.maxdefer = SVAL; break; - case PARAM_GROUP: - scc->kiss.group = val; break; - case PARAM_TX: - scc->kiss.tx_inhibit = val; - case PARAM_SOFTDCD: - scc->kiss.softdcd = val; + +static unsigned long +kiss_get_param(struct scc_channel *scc, unsigned int cmd) +{ + switch (cmd) + { + case PARAM_TXDELAY: return CAST(scc->kiss.txdelay); + case PARAM_PERSIST: return CAST(scc->kiss.persist); + case PARAM_SLOTTIME: return CAST(scc->kiss.slottime); + case PARAM_TXTAIL: return CAST(scc->kiss.tailtime); + case PARAM_FULLDUP: return CAST(scc->kiss.fulldup); + case PARAM_SOFTDCD: return CAST(scc->kiss.softdcd); + case PARAM_DTR: return CAST((scc->wreg[R5] & DTR)? 1:0); + case PARAM_RTS: return CAST((scc->wreg[R5] & RTS)? 1:0); + case PARAM_SPEED: return CAST(scc->modem.speed); + case PARAM_GROUP: return CAST(scc->kiss.group); + case PARAM_IDLE: return CAST(scc->kiss.idletime); + case PARAM_MIN: return CAST(scc->kiss.mintime); + case PARAM_MAXKEY: return CAST(scc->kiss.maxkeyup); + case PARAM_WAIT: return CAST(scc->kiss.waittime); + case PARAM_MAXDEFER: return CAST(scc->kiss.maxdefer); + case PARAM_TX: return CAST(scc->kiss.tx_inhibit); + default: return NO_SUCH_PARAM; } - return; -#undef VAL -#undef SVAL } +#undef CAST +#undef SVAL /* interpret frame: strip CRC and decode KISS */ -static void kiss_interpret_frame(struct scc_channel * scc) +static void +kiss_interpret_frame(struct scc_channel * scc) { unsigned char kisscmd; unsigned long flags; @@ -1642,14 +1953,6 @@ bp = scc->kiss_decode_bp; bp->rw_ptr = bp->data; -#ifdef DEBUG_BUFFERS - if (bp == NULLBUF) - { - printk("kiss_interpret_frame(): weird --- nothing to do.\n"); - return; - } -#endif - if (bp->cnt < 2) { scc_enqueue_buffer(&scc->tx_buffer_pool, bp); @@ -1657,15 +1960,8 @@ return; } - - - if (scc->kiss.not_slip) - { - kisscmd = *bp->rw_ptr; - bp->rw_ptr++; - } else { - kisscmd = 0; - } + kisscmd = *bp->rw_ptr; + bp->rw_ptr++; if (kisscmd & 0xa0) { @@ -1679,10 +1975,8 @@ } } - kisscmd &= 0x1f; - - + if (kisscmd) { kiss_set_param(scc, kisscmd, *bp->rw_ptr); @@ -1692,32 +1986,47 @@ } scc_enqueue_buffer(&scc->tx_queue, bp); /* enqueue frame */ + scc->kiss_decode_bp = NULLBUF; scc->stat.txframes++; scc->stat.tx_queued++; - scc->kiss_decode_bp = NULLBUF; - save_flags(flags); cli(); + save_flags(flags); + cli(); + + /* + * start transmission if the trx state is idle or + * t_idle hasn't expired yet. Use dwait/persistance/slottime + * algorithm for normal halfduplex operation. + */ - if(scc->stat.tx_state == TXS_IDLE) - { /* when transmitter is idle */ + if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2) + { scc->stat.tx_state = TXS_BUSY; - scc->t_dwait = (scc->kiss.fulldup? 0:scc->kiss.waittime); + if (scc->kiss.fulldup == KISS_DUPLEX_HALF) + scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime); + else + scc_start_tx_timer(scc, t_dwait, 0); } - + restore_flags(flags); } -static inline void kiss_store_byte(struct scc_channel *scc, unsigned char ch) +#ifdef CONFIG_SCC_TTY +static inline void +kiss_store_byte(struct scc_channel *scc, unsigned char ch) { - register struct mbuf *bp = scc->kiss_decode_bp; + struct mbuf *bp = scc->kiss_decode_bp; + static int k = 0; if (bp != NULLBUF) { if (bp->cnt > scc->stat.bufsize) - printk("kiss_decode(): frame too long\n"); - else { + if (!k++) + printk(KERN_NOTICE "z8530drv: KISS frame too long\n"); + } else { + k = 0; *bp->rw_ptr = ch; bp->rw_ptr++; bp->cnt++; @@ -1725,7 +2034,8 @@ } } -static inline int kiss_decode(struct scc_channel *scc, unsigned char ch) +static inline int +kiss_decode(struct scc_channel *scc, unsigned char ch) { switch (scc->stat.tx_kiss_state) { @@ -1779,7 +2089,7 @@ /* ----> Encode received data and write it to the flip-buffer <---- */ static void -kiss_encode(register struct scc_channel *scc) +kiss_encode(struct scc_channel *scc) { struct mbuf *bp; struct tty_struct * tty = scc->tty; @@ -1825,10 +2135,6 @@ if (scc->stat.rx_kiss_state == KISS_IDLE) { tty_insert_flip_char(tty, FEND, 0); - - if (scc->kiss.not_slip) - tty_insert_flip_char(tty, 0, 0); - scc->stat.rx_kiss_state = KISS_RXFRAME; } @@ -1852,6 +2158,7 @@ queue_task(&tty->flip.tqueue, &tq_timer); /* kick it... */ } +#endif /* CONFIG_SCC_TTY */ /* ******************************************************************* */ @@ -1868,7 +2175,7 @@ char *flag; - printk("Init Z8530 driver: %u channels, IRQ", Nchips*2); + printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2); flag=" "; for (k = 0; k < 16; k++) @@ -1885,9 +2192,7 @@ { scc=&SCC_Info[2*chip]; if (!scc->ctrl) continue; - - save_flags(flags); cli(); /* because of 2-step accesses */ - + /* Special SCC cards */ if(scc->brand & EAGLE) /* this is an EAGLE card */ @@ -1901,12 +2206,15 @@ /* some general init we can do now */ + save_flags(flags); + cli(); + Outb(scc->ctrl, 0); OutReg(scc->ctrl,R9,FHWRES); /* force hardware reset */ udelay(100); /* give it 'a bit' more time than required */ wr(scc, R2, chip*16); /* interrupt vector */ wr(scc, R9, VIS); /* vector includes status */ - + restore_flags(flags); } @@ -1920,18 +2228,17 @@ /* ******************************************************************** */ - /* scc_paranoia_check(): warn user if something went wrong */ -static inline int scc_paranoia_check(struct scc_channel *scc, kdev_t device, - const char *routine) +static __inline__ int +scc_paranoia_check(struct scc_channel *scc, kdev_t device, const char *routine) { #ifdef SCC_PARANOIA_CHECK static const char *badmagic = - "Warning: bad magic number for Z8530 SCC struct (%s) in %s\n"; + KERN_ALERT "Warning: bad magic number for Z8530 SCC struct (%s) in %s\n"; static const char *badinfo = - "Warning: Z8530 not found for (%s) in %s\n"; + KERN_CRIT "Warning: Z8530 not found for (%s) in %s (forgot to run sccinit?)\n"; if (!scc->init) { @@ -1949,10 +2256,11 @@ return 0; } - +#ifdef CONFIG_SCC_TTY /* ----> this one is called whenever you open the device <---- */ -int scc_open(struct tty_struct *tty, struct file * filp) +int +scc_open(struct tty_struct *tty, struct file * filp) { struct scc_channel *scc; int chan; @@ -1976,28 +2284,30 @@ if (scc->magic != SCC_MAGIC) { - printk("ERROR: scc_open(): bad magic number for device (%s)", + printk(KERN_ALERT "z8530drv: scc_open() found bad magic number for device (%s)", kdevname(tty->device)); return -ENODEV; - } - + } + MOD_INC_USE_COUNT; - - if(scc->tty != NULL) + + if(scc->tty != NULL || scc->stat.is_netdev) { scc->tty_opened++; return 0; } + + scc->tty = tty; - scc->tty = tty; + if(!scc->init) + return 0; + alloc_buffer_pool(scc); - - if(!scc->init) return 0; scc->throttled = 0; - scc->stat.tx_kiss_state = KISS_IDLE; /* don't change this... */ - scc->stat.rx_kiss_state = KISS_IDLE; /* ...or this */ + scc->stat.tx_kiss_state = KISS_IDLE; + scc->stat.rx_kiss_state = KISS_IDLE; init_channel(scc); return 0; @@ -2014,10 +2324,11 @@ if (!scc || (scc->magic != SCC_MAGIC)) return; + MOD_DEC_USE_COUNT; - if(scc->tty_opened) + if(scc->tty_opened || scc->stat.is_netdev) { scc->tty_opened--; return; @@ -2028,21 +2339,26 @@ if (!Driver_Initialized) return; - save_flags(flags); cli(); + save_flags(flags); + cli(); Outb(scc->ctrl,0); /* Make sure pointer is written */ wr(scc,R1,0); /* disable interrupts */ wr(scc,R3,0); scc->tty = NULL; - + del_timer(&scc->tx_t); + del_timer(&scc->tx_wdog); del_timer(&scc->rx_t); - free_buffer_pool(scc); - restore_flags(flags); + if (!scc->init) + return; + + free_buffer_pool(scc); + scc->throttled = 0; tty->stopped = 0; } @@ -2068,135 +2384,144 @@ if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */ set_speed(scc); } - +#endif /* CONFIG_SCC_TTY */ /* ----> ioctl-routine of the driver <---- */ -/* perform ioctl on SCC (sdlc) channel - * this is used for AX.25 mode, and will set the "kiss" parameters - */ + +/* sub routines to read/set KISS parameters */ -/* TIOCMGET - get modem status arg: (unsigned long *) arg - * TIOCMBIS - set PTT arg: --- - * TIOCMBIC - reset PTT arg: --- - * TIOCMBIC - set PTT arg: --- - * TIOCSCCINI - initialize driver arg: --- - * TIOCCHANINI - initialize channel arg: (struct scc_modem *) arg - * TIOCGKISS - get level 1 parameter arg: (struct ioctl_command *) arg - * TIOCSKISS - set level 1 parameter arg: (struct ioctl_command *) arg - * TIOCSCCSTAT - get driver status arg: (struct scc_stat *) arg +/* generic ioctl() functions for both TTY or network device mode: + * + * TIOCSCCCFG - configure driver arg: (struct scc_hw_config *) arg + * TIOCSCCINI - initialize driver arg: --- + * TIOCSCCCHANINI - initialize channel arg: (struct scc_modem *) arg + * TIOCSCCGKISS - get level 1 parameter arg: (struct scc_kiss_cmd *) arg + * TIOCSCCSKISS - set level 1 parameter arg: (struct scc_kiss_cmd *) arg + * TIOCSCCSTAT - get driver status arg: (struct scc_stat *) arg */ - static int -scc_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) +scc_ioctl(struct scc_channel *scc, unsigned int cmd, void *arg) { - struct scc_channel * scc = tty->driver_data; - unsigned long flags, r; - unsigned int result; - unsigned int value; - struct ioctl_command kiss_cmd; + unsigned long flags; + struct scc_kiss_cmd kiss_cmd; struct scc_mem_config memcfg; struct scc_hw_config hwcfg; int error, chan; - - if (scc->magic != SCC_MAGIC) - { - printk("ERROR: scc_ioctl(): bad magic number for device %s", - kdevname(tty->device)); - - return -ENODEV; - } - - r = NO_SUCH_PARAM; + unsigned char device_name[10]; if (!Driver_Initialized) { if (cmd == TIOCSCCCFG) { int found = 1; - + if (!suser()) return -EPERM; if (!arg) return -EFAULT; - + + error = verify_area(VERIFY_READ, arg, sizeof(struct scc_hw_config)); + if (error) return error; + if (Nchips >= MAXSCC) return -EINVAL; - - memcpy_fromfs(&hwcfg, (void *) arg, sizeof(hwcfg)); - + + memcpy_fromfs(&hwcfg, arg, sizeof(hwcfg)); + if (hwcfg.irq == 2) hwcfg.irq = 9; - + if (!Ivec[hwcfg.irq].used && hwcfg.irq) { if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL)) - printk("z8530drv: Warning --- could not get IRQ %d\n", hwcfg.irq); + printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq); else Ivec[hwcfg.irq].used = 1; } - + if (hwcfg.vector_latch) Vector_Latch = hwcfg.vector_latch; - + if (hwcfg.clock == 0) hwcfg.clock = DEFAULT_CLOCK; #ifndef DONT_CHECK - save_flags(flags); cli(); - + disable_irq(hwcfg.irq); + check_region(scc->ctrl, 1); Outb(hwcfg.ctrl_a, 0); udelay(5); OutReg(hwcfg.ctrl_a,R13,0x55); /* is this chip really there? */ udelay(5); - - if (InReg(hwcfg.ctrl_a,R13) != 0x55 ) + + if (InReg(hwcfg.ctrl_a,R13) != 0x55) found = 0; - - restore_flags(flags); + + enable_irq(hwcfg.irq); #endif if (found) { SCC_Info[2*Nchips ].ctrl = hwcfg.ctrl_a; SCC_Info[2*Nchips ].data = hwcfg.data_a; + SCC_Info[2*Nchips ].irq = hwcfg.irq; SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b; SCC_Info[2*Nchips+1].data = hwcfg.data_b; + SCC_Info[2*Nchips+1].irq = hwcfg.irq; - SCC_ctrl[2*Nchips ] = hwcfg.ctrl_a; - SCC_ctrl[2*Nchips+1] = hwcfg.ctrl_b; + SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a; + SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b; + SCC_ctrl[Nchips].irq = hwcfg.irq; } - + + for (chan = 0; chan < 2; chan++) { + sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan); + SCC_Info[2*Nchips+chan].special = hwcfg.special; SCC_Info[2*Nchips+chan].clock = hwcfg.clock; SCC_Info[2*Nchips+chan].brand = hwcfg.brand; SCC_Info[2*Nchips+chan].option = hwcfg.option; SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc; - + #ifdef DONT_CHECK - printk("%s%i: data port = 0x%3.3x control port = 0x%3.3x\n", - scc_driver.name, 2*Nchips+chan, + printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x\n", + device_name, SCC_Info[2*Nchips+chan].data, SCC_Info[2*Nchips+chan].ctrl); #else - printk("%s%i: data port = 0x%3.3x control port = 0x%3.3x -- %s\n", - scc_driver.name, 2*Nchips+chan, + printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x -- %s\n", + device_name, chan? hwcfg.data_b : hwcfg.data_a, chan? hwcfg.ctrl_b : hwcfg.ctrl_a, found? "found" : "missing"); #endif - + if (found) { request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl"); request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data"); +#ifdef CONFIG_SCC_DEV + if (Nchips+chan != 0) + scc_net_setup(&SCC_Info[2*Nchips+chan], device_name); +#endif } } if (found) Nchips++; +#ifdef SCC_DEBUG + printk(KERN_INFO "Available modes:" +#ifdef CONFIG_SCC_DEV + " dev" +#endif +#ifdef CONFIG_SCC_TTY + " tty" +#endif + "\n"); +#endif + return 0; } @@ -2209,9 +2534,6 @@ return -EINVAL; z8530_init(); - - scc->tty=tty; - alloc_buffer_pool(scc); return 0; } @@ -2220,263 +2542,268 @@ if (!scc->init) { - if (cmd == TIOCCHANINI) + if (cmd == TIOCSCCCHANINI) { - if (!arg) - return -EFAULT; - - if (!suser()) - return -EPERM; - - memcpy_fromfs(&scc->modem, (void *) arg, sizeof(struct scc_modem)); + if (!suser()) return -EPERM; + if (!arg) return -EINVAL; + error = verify_area(VERIFY_READ, arg, sizeof(struct scc_modem)); + if (error) return error; + + scc->stat.rxbuffers = RXBUFFERS; + scc->stat.txbuffers = TXBUFFERS; + scc->stat.bufsize = BUFSIZE; + + memcpy_fromfs(&scc->modem, arg, sizeof(struct scc_modem)); /* default KISS Params */ if (scc->modem.speed < 4800) { - scc->kiss.txdelay = 36*TPS/100; /* 360 ms */ - scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */ - scc->kiss.slottime = 16*TPS/100; /* 160 ms */ - scc->kiss.tailtime = 4; /* minimal reasonable value */ - scc->kiss.fulldup = 0; /* CSMA */ - scc->kiss.waittime = 50*TPS/100; /* 500 ms */ - scc->kiss.maxkeyup = 10; /* 10 s */ - scc->kiss.mintime = 3; /* 3 s */ - scc->kiss.idletime = 30; /* 30 s */ - scc->kiss.maxdefer = 120; /* 2 min */ - scc->kiss.not_slip = 1; /* KISS mode */ - scc->kiss.softdcd = 0; /* hardware dcd */ + scc->kiss.txdelay = 36; /* 360 ms */ + scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */ + scc->kiss.slottime = 16; /* 160 ms */ + scc->kiss.tailtime = 4; /* minimal reasonable value */ + scc->kiss.fulldup = 0; /* CSMA */ + scc->kiss.waittime = 50; /* 500 ms */ + scc->kiss.maxkeyup = 10; /* 10 s */ + scc->kiss.mintime = 3; /* 3 s */ + scc->kiss.idletime = 30; /* 30 s */ + scc->kiss.maxdefer = 120; /* 2 min */ + scc->kiss.softdcd = 0; /* hardware dcd */ } else { - scc->kiss.txdelay = 10*TPS/100; /* 100 ms */ - scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */ - scc->kiss.slottime = 8*TPS/100; /* 160 ms */ - scc->kiss.tailtime = 1; /* minimal reasonable value */ - scc->kiss.fulldup = 0; /* CSMA */ - scc->kiss.waittime = 50*TPS/100; /* 500 ms */ - scc->kiss.maxkeyup = 7; /* 7 s */ - scc->kiss.mintime = 3; /* 3 s */ - scc->kiss.idletime = 30; /* 30 s */ - scc->kiss.maxdefer = 120; /* 2 min */ - scc->kiss.not_slip = 1; /* KISS mode */ - scc->kiss.softdcd = 0; /* hardware dcd */ + scc->kiss.txdelay = 10; /* 100 ms */ + scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */ + scc->kiss.slottime = 8; /* 160 ms */ + scc->kiss.tailtime = 1; /* minimal reasonable value */ + scc->kiss.fulldup = 0; /* CSMA */ + scc->kiss.waittime = 50; /* 500 ms */ + scc->kiss.maxkeyup = 7; /* 7 s */ + scc->kiss.mintime = 3; /* 3 s */ + scc->kiss.idletime = 30; /* 30 s */ + scc->kiss.maxdefer = 120; /* 2 min */ + scc->kiss.softdcd = 0; /* hardware dcd */ } - scc->init = 1; + scc->init = 1; return 0; } return -EINVAL; } - - switch(cmd){ - case TCSBRK: - return 0; - case TIOCMGET: - error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(unsigned int *)); - if (error) - return error; - - save_flags(flags); cli(); - - result = ((scc->wreg[R5] & RTS) ? TIOCM_RTS : 0) - | ((scc->wreg[R5] & DTR) ? TIOCM_DTR : 0) - | ((InReg(scc->ctrl,R0) & DCD) ? TIOCM_CAR : 0) - | ((InReg(scc->ctrl,R0) & CTS) ? TIOCM_CTS : 0); + + switch(cmd) + { + case TIOCSCCSMEM: + case TIOCCHANMEM_OLD: + if (!suser()) return -EPERM; + if (!arg) return -EINVAL; + error = verify_area(VERIFY_READ, arg, sizeof(struct scc_mem_config)); + if (error) return error; + + memcpy_fromfs(&memcfg, arg, sizeof(struct scc_mem_config)); + + save_flags(flags); + cli(); + + free_buffer_pool(scc); + scc->stat.rxbuffers = memcfg.rxbuffers; + scc->stat.txbuffers = memcfg.txbuffers; + scc->stat.bufsize = memcfg.bufsize; + if (scc->tty || (scc->dev && (scc->dev->flags & IFF_UP))) + alloc_buffer_pool(scc); - restore_flags(flags); + restore_flags(flags); + return 0; + + case TIOCSCCGSTAT: + case TIOCSCCSTAT_OLD: + if (!arg) return -EINVAL; + error = verify_area(VERIFY_WRITE, arg, sizeof(struct scc_mem_config)); + if (error) return error; - put_user(result,(unsigned int *) arg); - return 0; - case TIOCMBIS: - case TIOCMBIC: - case TIOCMSET: - switch (cmd) { - case TIOCMBIS: - scc->wreg[R5] |= DTR; - scc->wreg[R5] |= RTS; + memcpy_tofs(arg, &scc->stat, sizeof(struct scc_stat)); + return 0; + + case TIOCSCCGKISS: + case TIOCGKISS_OLD: + if (!arg) return -EINVAL; + error = verify_area(VERIFY_WRITE, arg, sizeof(struct scc_mem_config)); + if (error) return error; + + memcpy_fromfs(&kiss_cmd, arg, sizeof(struct scc_kiss_cmd)); + kiss_cmd.param = kiss_get_param(scc, kiss_cmd.command); + memcpy_tofs(arg, &kiss_cmd, sizeof(struct scc_kiss_cmd)); + return 0; break; - case TIOCMBIC: - scc->wreg[R5] &= ~DTR; - scc->wreg[R5] &= ~RTS; + + case TIOCSCCSKISS: + case TIOCSKISS_OLD: + if (!suser()) return -EPERM; + if (!arg) return -EINVAL; + error = verify_area(VERIFY_READ, arg, sizeof(struct scc_mem_config)); + if (error) return error; + + memcpy_fromfs(&kiss_cmd, arg, sizeof(struct scc_kiss_cmd)); + return kiss_set_param(scc, kiss_cmd.command, kiss_cmd.param); break; - case TIOCMSET: - value = get_user((unsigned int *) arg); - - if(value & TIOCM_DTR) - scc->wreg[R5] |= DTR; - else - scc->wreg[R5] &= ~DTR; - if(value & TIOCM_RTS) - scc->wreg[R5] |= RTS; - else - scc->wreg[R5] &= ~RTS; - break; - } - - save_flags(flags); cli(); + + default: + return -ENOIOCTLCMD; - if(scc->stat.tx_state == TXS_IDLE && !Running(t_idle)) - maxk_idle_timeout(scc); + } + + return 0; +} + +#ifdef CONFIG_SCC_TTY +/* addtional ioctl() for TTY driver mode: + * + * TIOCMGET - get modem status arg: (unsigned long *) arg + * TIOCMBIS - set PTT arg: --- + * TIOCMBIC - reset PTT arg: --- + * TIOCMBIC - set PTT arg: --- + */ + +static int +scc_tty_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) +{ + struct scc_channel * scc = tty->driver_data; + unsigned long flags; + unsigned int result; + unsigned int value; + int error; + + if (scc->magic != SCC_MAGIC) + { + printk(KERN_ALERT "z8530drv: scc_ioctl() found bad magic number for device %s", + SCC_DEVNAME); - restore_flags(flags); - - return 0; - - case TCGETS: - error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct termios)); - if (error) - return error; - if (!arg) - return -EFAULT; + return -ENODEV; + } + + if (!Driver_Initialized && cmd == TIOCSCCINI) + scc->tty=tty; + + switch(cmd) + { + case TIOCSCCCFG: + case TIOCSCCINI: + case TIOCSCCCHANINI: + case TIOCSCCSMEM: + case TIOCSCCSKISS: + case TIOCSCCGKISS: + + case TIOCSCCCFG_OLD: + case TIOCSCCINI_OLD: + case TIOCCHANINI_OLD: + case TIOCCHANMEM_OLD: + case TIOCSKISS_OLD: + case TIOCGKISS_OLD: + case TIOCSCCSTAT_OLD: + return scc_ioctl(scc, cmd, (void *) arg); + + case TCSBRK: + return 0; - memcpy_tofs((void *) arg, scc->tty->termios, sizeof(struct termios)); - return 0; - - case TCSETS: - case TCSETSF: /* should flush first, but... */ - case TCSETSW: /* should wait 'till flush, but... */ - if (!arg) - return -EFAULT; - - memcpy_fromfs(scc->tty->termios, (void *) arg, sizeof(struct termios)); - scc_change_speed(scc); - return 0; + case TIOCMGET: + error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(unsigned int *)); + if (error) + return error; + + save_flags(flags); + cli(); + + result = ((scc->wreg[R5] & RTS) ? TIOCM_RTS : 0) + | ((scc->wreg[R5] & DTR) ? TIOCM_DTR : 0) + | ((InReg(scc->ctrl,R0) & DCD) ? TIOCM_CAR : 0) + | ((InReg(scc->ctrl,R0) & CTS) ? TIOCM_CTS : 0); - case TIOCCHANMEM: - if (!arg) - return -EFAULT; - - memcpy_fromfs(&memcfg, (void *) arg, sizeof(struct scc_mem_config)); - - save_flags(flags); cli(); - - free_buffer_pool(scc); - scc->stat.rxbuffers = memcfg.rxbuffers; - scc->stat.txbuffers = memcfg.txbuffers; - scc->stat.bufsize = memcfg.bufsize; - alloc_buffer_pool(scc); - - restore_flags(flags); - return 0; - - - case TIOCSCCSTAT: - error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(struct scc_stat)); - if (error) - return error; - - if (!arg) - return -EFAULT; + restore_flags(flags); - memcpy_tofs((void *) arg, &scc->stat, sizeof(struct scc_stat)); - return 0; - -#define TICKS (100/TPS) -#define CAST(x) (unsigned long)(x) -#define Val kiss_cmd.param -#define VAL kiss_cmd.param*TPS/100 -#define SVAL kiss_cmd.param? kiss_cmd.param:TIMER_STOPPED - - case TIOCGKISS: - error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(struct ioctl_command)); - if (error) - return error; - - if (!arg) - return -EFAULT; - - memcpy_fromfs(&kiss_cmd, (void *) arg, sizeof(struct ioctl_command)); - - switch (kiss_cmd.command) - { - case PARAM_TXDELAY: r = CAST(scc->kiss.txdelay*TICKS); break; - case PARAM_PERSIST: r = CAST(scc->kiss.persist); break; - case PARAM_SLOTTIME: r = CAST(scc->kiss.slottime*TICKS); break; - case PARAM_TXTAIL: r = CAST(scc->kiss.tailtime*TICKS); break; - case PARAM_FULLDUP: r = CAST(scc->kiss.fulldup); break; - case PARAM_SOFTDCD: r = CAST(scc->kiss.softdcd); break; - case PARAM_DTR: r = CAST((scc->wreg[R5] & DTR)? 1:0); break; - case PARAM_RTS: r = CAST((scc->wreg[R5] & RTS)? 1:0); break; - case PARAM_SPEED: r = CAST(scc->modem.speed); break; - case PARAM_GROUP: r = CAST(scc->kiss.group); break; - case PARAM_IDLE: r = CAST(scc->kiss.idletime); break; - case PARAM_MIN: r = CAST(scc->kiss.mintime); break; - case PARAM_MAXKEY: r = CAST(scc->kiss.maxkeyup); break; - case PARAM_WAIT: r = CAST(scc->kiss.waittime); break; - case PARAM_MAXDEFER: r = CAST(scc->kiss.maxdefer); break; - case PARAM_TX: r = CAST(scc->kiss.tx_inhibit); break; - case PARAM_SLIP: r = CAST(!scc->kiss.not_slip); break; - default: r = NO_SUCH_PARAM; - } - - kiss_cmd.param = r; + put_user(result,(unsigned int *) arg); + return 0; + + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + switch (cmd) + { + case TIOCMBIS: + scc->wreg[R5] |= DTR; + scc->wreg[R5] |= RTS; + break; + case TIOCMBIC: + scc->wreg[R5] &= ~DTR; + scc->wreg[R5] &= ~RTS; + break; + case TIOCMSET: + error = verify_area(VERIFY_READ, (void *) arg,sizeof(unsigned int *)); + if (error) + return error; + value = get_user((unsigned int *) arg); + + if(value & TIOCM_DTR) + scc->wreg[R5] |= DTR; + else + scc->wreg[R5] &= ~DTR; + if(value & TIOCM_RTS) + scc->wreg[R5] |= RTS; + else + scc->wreg[R5] &= ~RTS; + break; + } + + OutReg(scc->ctrl, R5, scc->wreg[R5]); + return 0; - memcpy_tofs((void *) arg, &kiss_cmd, sizeof(struct ioctl_command)); - return 0; - break; + case TCGETS: + error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct termios)); + if (error) + return error; + if (!arg) + return -EFAULT; + + memcpy_tofs((void *) arg, scc->tty->termios, sizeof(struct termios)); + return 0; - case TIOCSKISS: - if (!arg) - return -EFAULT; - - memcpy_fromfs(&kiss_cmd, (void *) arg, sizeof(struct ioctl_command)); - - switch (kiss_cmd.command) - { - case PARAM_TXDELAY: scc->kiss.txdelay=VAL; break; - case PARAM_PERSIST: scc->kiss.persist=Val; break; - case PARAM_SLOTTIME: scc->kiss.slottime=VAL; break; - case PARAM_TXTAIL: scc->kiss.tailtime=VAL; break; - case PARAM_FULLDUP: scc->kiss.fulldup=Val; break; - case PARAM_SOFTDCD: scc->kiss.softdcd=Val; break; - case PARAM_DTR: break; /* does someone need this? */ - case PARAM_RTS: break; /* or this? */ - case PARAM_SPEED: scc->modem.speed=Val; break; - case PARAM_GROUP: scc->kiss.group=Val; break; - case PARAM_IDLE: scc->kiss.idletime=Val; break; - case PARAM_MIN: scc->kiss.mintime=SVAL; break; - case PARAM_MAXKEY: scc->kiss.maxkeyup=SVAL; break; - case PARAM_WAIT: scc->kiss.waittime=Val; break; - case PARAM_MAXDEFER: scc->kiss.maxdefer=SVAL; break; - case PARAM_TX: scc->kiss.tx_inhibit=Val; break; - case PARAM_SLIP: scc->kiss.not_slip=!Val; break; - default: return -ENOIOCTLCMD; - } + case TCSETS: + case TCSETSF: /* should flush first, but... */ + case TCSETSW: /* should wait 'till flush, but... */ + if (!arg) + return -EFAULT; - return 0; - break; -#undef TICKS -#undef CAST -#undef VAL -#undef SVAL -#undef Val + error = verify_area(VERIFY_READ, (void *) arg, sizeof(struct termios)); + if (error) + return error; + memcpy_fromfs(scc->tty->termios, (void *) arg, sizeof(struct termios)); + scc_change_speed(scc); + return 0; - default: - return -ENOIOCTLCMD; - } + } + + return -ENOIOCTLCMD; } /* ----> tx routine: decode KISS data and scc_enqueue it <---- */ /* send raw frame to SCC. used for AX.25 */ -int scc_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count) +int +scc_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count) { struct scc_channel * scc = tty->driver_data; unsigned char *p; - unsigned long flags; int cnt, cnt2; - if (!tty) return count; + if (!tty) + return -EINVAL; if (scc_paranoia_check(scc, tty->device, "scc_write")) - return 0; + return -EINVAL; if (scc->kiss.tx_inhibit) return count; - - save_flags(flags); cli(); - + cnt2 = count; while (cnt2) @@ -2493,8 +2820,8 @@ else memcpy(scc_wbuf, buf, cnt); - /* Strange thing. The timeout of the slip driver is */ - /* very small, thus we'll wake him up now. */ + /* The timeout of the slip driver is very small, */ + /* thus we'll wake him up now. */ if (cnt2 == 0) { @@ -2510,34 +2837,28 @@ while(cnt--) if (kiss_decode(scc, *p++)) - { - scc->stat.nospace++; - restore_flags(flags); - return 0; - } + return -ENOMEM; } /* while cnt2 */ - restore_flags(flags); - return count; } /* put a single char into the buffer */ -static void scc_put_char(struct tty_struct * tty, unsigned char ch) +static void +scc_put_char(struct tty_struct * tty, unsigned char ch) { struct scc_channel *scc = tty->driver_data; - unsigned char ch2; if (scc_paranoia_check(scc, tty->device, "scc_put_char")) return; - - ch2 = ch; - scc_write(tty, 0, &ch2, 1); /* that's all */ + + kiss_decode(scc, ch); } -static void scc_flush_chars(struct tty_struct * tty) +static void +scc_flush_chars(struct tty_struct * tty) { struct scc_channel *scc = tty->driver_data; @@ -2547,7 +2868,8 @@ } -static int scc_write_room(struct tty_struct *tty) +static int +scc_write_room(struct tty_struct *tty) { struct scc_channel *scc = tty->driver_data; @@ -2557,7 +2879,8 @@ return BUFSIZE; } -static int scc_chars_in_buffer(struct tty_struct *tty) +static int +scc_chars_in_buffer(struct tty_struct *tty) { struct scc_channel *scc = tty->driver_data; @@ -2567,7 +2890,8 @@ return 0; } -static void scc_flush_buffer(struct tty_struct *tty) +static void +scc_flush_buffer(struct tty_struct *tty) { struct scc_channel *scc = tty->driver_data; @@ -2579,41 +2903,43 @@ (tty->ldisc.write_wakeup)(tty); } -static void scc_throttle(struct tty_struct *tty) +static void +scc_throttle(struct tty_struct *tty) { struct scc_channel *scc = tty->driver_data; if (scc_paranoia_check(scc, tty->device, "scc_throttle")) return; -#ifdef DEBUG - printk("scc: scc_throttle() called for device %d\n", MINOR(tty->device)); +#ifdef SCC_DEBUG + printk(KERN_DEBUG "z8530drv: scc_throttle() called for device %d\n", MINOR(tty->device)); #endif scc->throttled = 1; del_timer(&(scc->rx_t)); - scc->rx_t.expires = jiffies + HZ/TPS; + scc->rx_t.expires = jiffies + HZ/25; add_timer(&scc->rx_t); } -static void scc_unthrottle(struct tty_struct *tty) +static void +scc_unthrottle(struct tty_struct *tty) { struct scc_channel *scc = tty->driver_data; if (scc_paranoia_check(scc, tty->device, "scc_unthrottle")) return; -#ifdef DEBUG - printk("scc: scc_unthrottle() called for device %d\n", MINOR(tty->device)); +#ifdef SCC_DEBUG + printk(KERN_DEBUG "z8350drv: scc_unthrottle() called for device %d\n", MINOR(tty->device)); #endif scc->throttled = 0; del_timer(&(scc->rx_t)); - scc_tx_timer(scc->rx_t.data); } /* experimental, the easiest way to stop output is a fake scc_throttle */ -static void scc_start(struct tty_struct *tty) +static void +scc_start(struct tty_struct *tty) { struct scc_channel *scc = tty->driver_data; @@ -2623,7 +2949,8 @@ scc_unthrottle(tty); } -static void scc_stop(struct tty_struct *tty) +static void +scc_stop(struct tty_struct *tty) { struct scc_channel *scc = tty->driver_data; @@ -2657,20 +2984,521 @@ scc_change_speed(scc); } +#endif /* CONFIG_SCC_TTY */ + +#ifdef CONFIG_SCC_DEV +/* ******************************************************************** */ +/* * Network driver routines * */ +/* ******************************************************************** */ + +static unsigned char ax25_bcast[AX25_ADDR_LEN] = +{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1}; +static unsigned char ax25_nocall[AX25_ADDR_LEN] = +{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1}; + +static int +scc_net_init(struct device *dev) +{ + return 0; /* dummy */ +} + +/* ----> open network device <---- */ + +static int +scc_net_open(struct device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + + if (scc == NULL || scc->magic != SCC_MAGIC) + return -ENODEV; + + if (scc->tty != NULL) + return -EBUSY; + + if (!scc->init) + return -EINVAL; + + scc->stat.is_netdev = 1; + + MOD_INC_USE_COUNT; + + alloc_buffer_pool(scc); + + scc->stat.tx_kiss_state = KISS_IDLE; /* unused */ + scc->stat.rx_kiss_state = KISS_IDLE; /* unused */ + + init_channel(scc); + + dev->tbusy = 0; + dev->start = 1; + + return 0; +} + +/* ----> close network device <---- */ + +static int +scc_net_close(struct device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + unsigned long flags; + + if (scc == NULL || scc->magic != SCC_MAGIC) + return -ENODEV; + + if (!scc->stat.is_netdev) + return -ENXIO; + + MOD_DEC_USE_COUNT; + + scc->stat.is_netdev = 0; + + save_flags(flags); + cli(); + + Outb(scc->ctrl,0); /* Make sure pointer is written */ + wr(scc,R1,0); /* disable interrupts */ + wr(scc,R3,0); + + del_timer(&scc->tx_t); + del_timer(&scc->tx_wdog); +#ifdef CONFIG_SCC_TTY + del_timer(&scc->rx_t); +#endif + restore_flags(flags); + + free_buffer_pool(scc); + + dev->tbusy = 1; + dev->start = 0; + + return 0; +} + +/* ----> receive frame, called from scc_rxint() <---- */ + +static void +scc_net_rx(struct scc_channel *scc, struct mbuf *bp) +{ + struct sk_buff *skb; + + if ( bp == NULL || bp->cnt == 0 || + scc == NULL || scc->magic != SCC_MAGIC) + return; + + skb = dev_alloc_skb(bp->cnt+1); + + if (skb == NULL) + { + scc->dev_stat.rx_dropped++; + return; + } + + scc->dev_stat.rx_packets++; + + skb->dev = scc->dev; + skb->protocol = htons(ETH_P_AX25); + + memcpy(skb_put(skb, bp->cnt), bp->data, bp->cnt); + skb->mac.raw = skb->data; + netif_rx(skb); + return; +} + +/* ----> transmit frame <---- */ + +static int +scc_net_tx(struct sk_buff *skb, struct device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + struct mbuf *bp; + long ticks; + + /* + * We have our own queues, dev->tbusy is set only by t_maxkeyup + * to avoid running the driver into maxkeup again and again. + * + * Hope I havn't outsmart me _again_ ;-) + */ + + if (dev->tbusy) + { + ticks = (signed) (jiffies - dev->trans_start); + + if (ticks < scc->kiss.maxdefer*HZ || scc == NULL) + return 1; + + /* + * Arrgh... Seems to be a _very_ busy channel. + * throw away transmission queue. + */ + + del_timer(&scc->tx_wdog); + t_busy((unsigned long) scc); + + dev->tbusy=0; + dev->trans_start = jiffies; + } + + if (skb == NULL) + { + dev_tint(dev); + return 0; + } + + if (scc == NULL || scc->magic != SCC_MAGIC) + { + dev_kfree_skb(skb, FREE_WRITE); + return 0; + } + + scc->dev_stat.tx_packets++; + bp = scc_get_buffer(scc, BT_TRANSMIT); + + if (bp == NULLBUF || skb->len > scc->stat.bufsize || skb->len == 0) + { + scc->dev_stat.tx_dropped++; + if (bp) scc_enqueue_buffer(&scc->tx_buffer_pool, bp); + dev_kfree_skb(skb, FREE_WRITE); + return 0; + } + + memcpy(bp->data, skb->data, skb->len); + bp->cnt = skb->len; + + dev_kfree_skb(skb, FREE_WRITE); + + scc->kiss_decode_bp = bp; + kiss_interpret_frame(scc); + dev->trans_start = jiffies; + return 0; +} + +/* ----> set interface callsign <---- */ + +static int +scc_net_set_mac_address(struct device *dev, void *addr) +{ + struct sockaddr *sa = (struct sockaddr *) addr; + memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); + return 0; +} + +/* ----> rebuild header <---- */ + +static int +scc_net_rebuild_header(void *buff, struct device *dev, unsigned long raddr, struct sk_buff *skb) +{ + return ax25_rebuild_header(buff, dev, raddr, skb); +} + +/* ----> "hard" header <---- */ + +static int +scc_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, + void *daddr, void *saddr, unsigned len) +{ + return ax25_encapsulate(skb, dev, type, daddr, saddr, len); +} + +/* ----> get statistics <---- */ + +static struct enet_statistics * +scc_net_get_stats(struct device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + + if (scc == NULL || scc->magic != SCC_MAGIC) + return NULL; + + scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over; + scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under; + scc->dev_stat.rx_fifo_errors = scc->stat.rx_over; + scc->dev_stat.tx_fifo_errors = scc->stat.tx_under; + + return &scc->dev_stat; +} + +/* ----> ioctl for network devices <---- */ + +static int +scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + unsigned int ncmd = 0; + int res; + + if (scc == NULL || scc->magic != SCC_MAGIC) + return -EINVAL; + + if (!Driver_Initialized && cmd == SIOCSCCINI) + { + scc->dev = dev; + scc->stat.is_netdev = 1; + } + + switch (cmd) + { + case SIOCSCCRESERVED: return -EINVAL; /* unused */ + case SIOCSCCCFG: ncmd = TIOCSCCCFG; break; + case SIOCSCCINI: ncmd = TIOCSCCINI; break; + case SIOCSCCCHANINI: ncmd = TIOCSCCCHANINI; break; + case SIOCSCCSMEM: ncmd = TIOCSCCSMEM; break; + case SIOCSCCGKISS: ncmd = TIOCSCCGKISS; break; + case SIOCSCCSKISS: ncmd = TIOCSCCSKISS; break; + case SIOCSCCGSTAT: ncmd = TIOCSCCGSTAT; break; + default: return -EINVAL; + } + + res = scc_ioctl(scc, ncmd, (void *) ifr->ifr_data); + +#ifdef CONFIG_SCC_TTY + if (!(dev->flags & IFF_UP)) + scc->stat.is_netdev = 0; +#endif + + return res; + +} + + +/* ----> initialize interface <---- */ + +static int +scc_net_setup(struct scc_channel *scc, unsigned char *name) +{ + int k; + unsigned char *buf; + struct device *dev; + + if (dev_get(name) != NULL) + { + printk(KERN_INFO "Z8530drv: device %s already exists.\n", name); + return -EEXIST; + } + + if ((scc->dev = (struct device *) kmalloc(sizeof(struct device), GFP_KERNEL)) == NULL) + return -ENOMEM; + +#ifndef CONFIG_SCC_TTY + scc->stat.is_netdev = 1; +#endif + dev = scc->dev; + memset(dev, 0, sizeof(struct device)); + + buf = (unsigned char *) kmalloc(10, GFP_KERNEL); + strcpy(buf, name); + + dev->priv = (void *) scc; + dev->name = buf; + dev->init = scc_net_init; + + if (register_netdev(dev) != 0) + { + kfree(dev); + return -EIO; + } + + for (k=0; k < DEV_NUMBUFFS; k++) + skb_queue_head_init(&dev->buffs[k]); + + dev->open = scc_net_open; + dev->stop = scc_net_close; + + dev->hard_start_xmit = scc_net_tx; + dev->hard_header = scc_net_header; + dev->rebuild_header = scc_net_rebuild_header; + dev->set_mac_address = scc_net_set_mac_address; + dev->get_stats = scc_net_get_stats; + dev->do_ioctl = scc_net_ioctl; + + memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); + memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN); + + dev->flags = 0; + dev->family = AF_INET; + dev->pa_addr = 0; + dev->pa_brdaddr = 0; + dev->pa_mask = 0; + dev->pa_alen = 4; + + dev->type = ARPHRD_AX25; + dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; + dev->mtu = AX25_DEF_PACLEN; + dev->addr_len = AX25_ADDR_LEN; + + return 0; +} +#endif /* CONFIG_SCC_DEV */ + +/* ******************************************************************** */ +/* * dump statistics to /proc/net/z8530drv * */ +/* ******************************************************************** */ + + +static int +scc_net_get_info(char *buffer, char **start, off_t offset, int length, int dummy) +{ + struct scc_channel *scc; + char *txstate, *clksrc, *brand; + struct scc_kiss *kiss; + struct scc_stat *stat; + int len = 0; + off_t pos = 0; + off_t begin = 0; + int k; + + if (!Driver_Initialized) + { + len += sprintf(buffer, "Driver not initialized.\n"); + goto done; + } + + if (!Nchips) + { + len += sprintf(buffer, "Z8530 chips not found\n"); + goto done; + } + + for (k = 0; k < Nchips*2; k++) + { + scc = &SCC_Info[k]; + stat = &scc->stat; + kiss = &scc->kiss; + + if (!scc->init) + continue; + + switch(stat->tx_state) + { + case TXS_IDLE: txstate = "idle"; break; + case TXS_BUSY: txstate = "busy"; break; + case TXS_ACTIVE: txstate = "active"; break; + case TXS_NEWFRAME: txstate = "new"; break; + case TXS_IDLE2: txstate = "keyed"; break; + case TXS_WAIT: txstate = "wait"; break; + case TXS_TIMEOUT: txstate = "timeout"; break; + default: txstate = "???"; + } + switch(scc->modem.clocksrc) + { + case CLK_DPLL: clksrc = "dpll"; break; + case CLK_EXTERNAL: clksrc = "ext"; break; + case CLK_DIVIDER: clksrc = "div"; break; + default: clksrc = "???"; + } + switch(scc->brand) + { + case PA0HZP: brand="pa0hzp"; break; + case EAGLE: brand="eagle"; break; + case PC100: brand="pc1100"; break; + case PRIMUS: brand="primus"; break; + case DRSI: brand="drsi"; break; + case BAYCOM: brand="baycom"; break; + default: brand="???"; + } + + len += sprintf(buffer+len, "Device : %s%d\n", SCC_DriverName, k); + len += sprintf(buffer+len, "Hardware : %3.3x %3.3x %d %lu %s %s %3.3x %3.3x %d\n", + scc->data, scc->ctrl, scc->irq, scc->clock, brand, + scc->enhanced? "enh":"norm", Vector_Latch, scc->special, + scc->option); + len += sprintf(buffer+len, "Received : %lu %lu %d\n", + stat->rxframes, stat->rxerrs, stat->rx_over); + len += sprintf(buffer+len, "Transmitted: %lu %lu %d %s\n", + stat->txframes, stat->txerrs, stat->tx_under, txstate); + len += sprintf(buffer+len, "Interrupts : %lu %lu %lu %lu\n", + stat->rxints, stat->txints, stat->exints, stat->spints); + len += sprintf(buffer+len, "Buffers : %d %d/%d %d/%d %d\n", + stat->bufsize, stat->rx_queued, stat->rxbuffers, + stat->tx_queued, stat->txbuffers, stat->nospace); + len += sprintf(buffer+len, "MODEM : %lu %s %s %s\n", + scc->modem.speed, scc->modem.nrz? "nrz":"nrzi", + clksrc, kiss->softdcd? "soft":"hard"); + len += sprintf(buffer+len, "Mode : %s\n", + stat->is_netdev? "dev":"tty"); +#define K(x) kiss->x + len += sprintf(buffer+len, "KISS params: %d %d %d %d %d %d %d %d %d %d %d %d\n", + K(txdelay), K(persist), K(slottime), K(tailtime), + K(fulldup), K(waittime), K(mintime), K(maxkeyup), + K(idletime), K(maxdefer), K(tx_inhibit), K(group)); +#undef K +#ifdef SCC_DEBUG + { + int reg; + + len += sprintf(buffer+len, "Z8530 wregs: "); + for (reg = 0; reg < 16; reg++) + len += sprintf(buffer+len, "%2.2x ", scc->wreg[reg]); + len += sprintf(buffer+len, "\n"); + + len += sprintf(buffer+len, "Z8530 rregs: %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1)); + for (reg = 3; reg < 8; reg++) + len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg)); + len += sprintf(buffer+len, "XX "); + for (reg = 9; reg < 16; reg++) + len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg)); + len += sprintf(buffer+len, "\n"); + } +#endif + len += sprintf(buffer+len, "\n"); + + pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + + if (pos > offset + length) + break; + } + +done: + + *start = buffer + (offset - begin); + len -= (offset - begin); + + if (len > length) len = length; + + return len; +} + +#ifdef CONFIG_INET +struct proc_dir_entry scc_proc_dir_entry = +{ + PROC_NET_Z8530, 8, "z8530drv", S_IFREG | S_IRUGO, 1, 0, 0, 0, + &proc_net_inode_operations, scc_net_get_info +}; + +#define scc_net_procfs_init() proc_net_register(&scc_proc_dir_entry); +#define scc_net_procfs_remove() proc_net_unregister(PROC_NET_Z8530); +#else +#define scc_net_procfs_init() +#define scc_net_procfs_remove() +#endif + + /* ******************************************************************** */ /* * Init SCC driver * */ /* ******************************************************************** */ -int scc_init (void) +int scc_init (void) { int chip, chan, k; +#ifdef CONFIG_SCC_DEV + char devname[10]; +#endif + printk(KERN_INFO BANNER); + +#ifdef CONFIG_SCC_TTY memset(&scc_std_termios, 0, sizeof(struct termios)); memset(&scc_driver, 0, sizeof(struct tty_driver)); scc_driver.magic = TTY_DRIVER_MAGIC; - scc_driver.name = "scc"; + scc_driver.name = SCC_DriverName; scc_driver.major = Z8530_MAJOR; scc_driver.minor_start = 0; scc_driver.num = MAXSCC*2; @@ -2699,17 +3527,19 @@ scc_driver.throttle = scc_throttle; scc_driver.unthrottle = scc_unthrottle; - scc_driver.ioctl = scc_ioctl; + scc_driver.ioctl = scc_tty_ioctl; scc_driver.set_termios = scc_set_termios; scc_driver.set_ldisc = scc_set_ldisc; - printk(BANNER); if (tty_register_driver(&scc_driver)) { - printk("Failed to register Z8530 SCC driver\n"); + printk(KERN_ERR "Failed to register Z8530 SCC driver\n"); return -EIO; } +#endif + + memset(&SCC_ctrl, 0, sizeof(SCC_ctrl)); /* pre-init channel information */ @@ -2719,16 +3549,19 @@ memset((char *) &SCC_Info[2*chip+1], 0, sizeof(struct scc_channel)); for (chan = 0; chan < 2; chan++) - { SCC_Info[2*chip+chan].magic = SCC_MAGIC; - SCC_Info[2*chip+chan].stat.rxbuffers = RXBUFFERS; - SCC_Info[2*chip+chan].stat.txbuffers = TXBUFFERS; - SCC_Info[2*chip+chan].stat.bufsize = BUFSIZE; - } } - + for (k = 0; k < 16; k++) Ivec[k].used = 0; +#ifdef CONFIG_SCC_DEV + sprintf(devname,"%s0", SCC_DriverName); + + if (scc_net_setup(SCC_Info, devname)) + printk(KERN_WARNING "z8530drv: cannot setup network device\n"); +#endif + scc_net_procfs_init(); + return 0; } @@ -2743,9 +3576,9 @@ int result = 0; result = scc_init(); - + if (result == 0) - printk("Copyright 1993,1995 Joerg Reuter DL1BKE (jreuter@lykos.tng.oche.de)\n"); + printk(KERN_INFO "Copyright 1993,1996 Joerg Reuter DL1BKE (jreuter@lykos.tng.oche.de)\n"); return result; } @@ -2754,19 +3587,26 @@ { long flags; io_port ctrl; - int k, errno; + int k; struct scc_channel *scc; - save_flags(flags); cli(); - if ( (errno = tty_unregister_driver(&scc_driver)) ) + save_flags(flags); + cli(); + +#ifdef CONFIG_SCC_TTY + if ( (k = tty_unregister_driver(&scc_driver)) ) { - printk("Failed to unregister Z8530 SCC driver (%d)", -errno); + printk(KERN_ERR "z8530drv: failed to unregister tty driver: (%d)\n", -k); restore_flags(flags); return; } - +#endif + + if (Nchips == 0) + unregister_netdev(SCC_Info[0].dev); + for (k = 0; k < Nchips; k++) - if ( (ctrl = SCC_ctrl[k*2]) ) + if ( (ctrl = SCC_ctrl[k].chan_A) ) { Outb(ctrl, 0); OutReg(ctrl,R9,FHWRES); /* force hardware reset */ @@ -2780,6 +3620,11 @@ { release_region(scc->ctrl, 1); release_region(scc->data, 1); + if (scc->dev) + { + unregister_netdev(scc->dev); + kfree(scc->dev); + } } } @@ -2787,5 +3632,7 @@ if (Ivec[k].used) free_irq(k, NULL); restore_flags(flags); + + scc_net_procfs_remove(); } #endif diff -u --recursive --new-file v2.0.30/linux/drivers/char/specialix.c linux/drivers/char/specialix.c --- v2.0.30/linux/drivers/char/specialix.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/specialix.c Tue Aug 12 13:06:54 1997 @@ -0,0 +1,2330 @@ +/* + * specialix.c -- specialix IO8+ multiport serial driver. + * + * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net) + * + * Specialix pays for the development and support of this driver. + * Please DO contact io8-linux@specialix.co.uk if you require + * support. But please read the documentation (specialix.txt) + * first. + * + * This driver was developped in the BitWizard linux device + * driver service. If you require a linux device driver for your + * product, please contact devices@BitWizard.nl for a quote. + * + * This code is firmly based on the riscom/8 serial driver, + * written by Dmitry Gorodchanin. The specialix IO8+ card + * programming information was obtained from the CL-CD1865 Data + * Book, and Specialix document number 6200059: IO8+ Hardware + * Functional Specification. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + * Revision history: + * + * Revision 1.0: April 1st 1997. + * Initial release for alpha testing. + * Revision 1.1: April 14th 1997. + * Incorporated Richard Hudsons suggestions, + * removed some debugging printk's. + * Revision 1.2: April 15th 1997. + * Ported to 2.1.x kernels. + * Revision 1.3: April 17th 1997 + * Backported to 2.0. (Compatibility macros). + * Revision 1.4: April 18th 1997 + * Fixed DTR/RTS bug that caused the card to indicate + * "don't send data" to a modem after the password prompt. + * Fixed bug for premature (fake) interrupts. + * Revision 1.5: April 19th 1997 + * fixed a minor typo in the header file, cleanup a little. + * performance warnings are now MAXed at once per minute. + * Revision 1.6: May 23 1997 + * Changed the specialix=... format to include interrupt. + * Revision 1.7: May 27 1997 + * Made many more debug printk's a compile time option. + * + */ + +#define VERSION "1.7" + + +/* + * There is a bunch of documentation about the card, jumpers, config + * settings, restrictions, cables, device names and numbers in + * ../../Documentation/specialix.txt + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* ************************************************************** */ +/* * This section can be removed when 2.0 becomes outdated.... * */ +/* ************************************************************** */ + +#if LINUX_VERSION_CODE < 131328 /* Less than 2.1.0 */ +#define TWO_ZERO +#else +#if LINUX_VERSION_CODE < 131368 /* less than 2.1.40 */ +/* This has not been extensively tested yet. Sorry. */ +#warning "You're on your own between 2.1.0 and 2.1.40.... " +#warning "Please use 2.1.40 or higher." +#endif +#endif + + +#ifdef TWO_ZERO +#define Get_user(a,b) a = get_user(b) +#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c) +#define copy_to_user(a,b,c) memcpy_tofs(a,b,c) +#define queue_task queue_task_irq_off +#else +#define Get_user(a,b) get_user(a,b) +#endif + +/* ************************************************************** */ +/* * End of compatibility section.. * */ +/* ************************************************************** */ + + +#ifndef TWO_ZERO +#include +#endif + +#include "specialix_io8.h" +#include "cd1865.h" + + + +/* Configurable options: */ + +/* Am I paranoid or not ? ;-) */ +#define SPECIALIX_PARANOIA_CHECK + +/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help) + When the IRQ routine leaves the chip in a state that is keeps on + requiring attention, the timer doesn't help either. */ +#undef SPECIALIX_TIMER + +/* + * The following defines are mostly for testing purposes. But if you need + * some nice reporting in your syslog, you can define them also. + */ +#undef SX_REPORT_FIFO +#undef SX_REPORT_OVERRUN + + + +#ifdef CONFIG_SPECIALIX_RTSCTS +#define SX_CRTSCTS(bla) 1 +#else +#define SX_CRTSCTS(tty) C_CRTSCTS(tty) +#endif + + +/* Used to be outb (0xff, 0x80); */ +#define short_pause() udelay (1) + + +#define SPECIALIX_LEGAL_FLAGS \ + (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \ + ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \ + ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP) + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +DECLARE_TASK_QUEUE(tq_specialix); + + + +#define SPECIALIX_TYPE_NORMAL 1 +#define SPECIALIX_TYPE_CALLOUT 2 + +static struct specialix_board * IRQ_to_board[16] = { NULL, } ; +static struct tty_driver specialix_driver, specialix_callout_driver; +static int specialix_refcount = 0; +static struct tty_struct * specialix_table[SX_NBOARD * SX_NPORT] = { NULL, }; +static struct termios * specialix_termios[SX_NBOARD * SX_NPORT] = { NULL, }; +static struct termios * specialix_termios_locked[SX_NBOARD * SX_NPORT] = { NULL, }; +static unsigned char * tmp_buf = NULL; +static struct semaphore tmp_buf_sem = MUTEX; + +static unsigned long baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 0, +}; + +static struct specialix_board sx_board[SX_NBOARD] = { + { 0, SX_IOBASE1, 9, }, + { 0, SX_IOBASE2, 11, }, + { 0, SX_IOBASE3, 12, }, + { 0, SX_IOBASE4, 15, }, +}; + +static struct specialix_port sx_port[SX_NBOARD * SX_NPORT] = { + { 0, }, +}; + + +#ifdef SPECIALIX_TIMER +static struct timer_list missed_irq_timer; +static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs); +#endif + + + +static inline int sx_paranoia_check(struct specialix_port const * port, + kdev_t device, const char *routine) +{ +#ifdef SPECIALIX_PARANOIA_CHECK + static const char *badmagic = + KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n"; + static const char *badinfo = + KERN_ERR "sx: Warning: null specialix port for device %s in %s\n"; + + if (!port) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (port->magic != SPECIALIX_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + + +/* + * + * Service functions for specialix IO8+ driver. + * + */ + +/* Get board number from pointer */ +extern inline int board_No (struct specialix_board * bp) +{ + return bp - sx_board; +} + + +/* Get port number from pointer */ +extern inline int port_No (struct specialix_port const * port) +{ + return SX_PORT(port - sx_port); +} + + +/* Get pointer to board from pointer to port */ +extern inline struct specialix_board * port_Board(struct specialix_port const * port) +{ + return &sx_board[SX_BOARD(port - sx_port)]; +} + + +/* Input Byte from CL CD186x register */ +extern inline unsigned char sx_in(struct specialix_board * bp, unsigned short reg) +{ + bp->reg = reg | 0x80; + outb (reg | 0x80, bp->base + SX_ADDR_REG); + return inb (bp->base + SX_DATA_REG); +} + + +/* Output Byte to CL CD186x register */ +extern inline void sx_out(struct specialix_board * bp, unsigned short reg, + unsigned char val) +{ + bp->reg = reg | 0x80; + outb (reg | 0x80, bp->base + SX_ADDR_REG); + outb (val, bp->base + SX_DATA_REG); +} + + +/* Input Byte from CL CD186x register */ +extern inline unsigned char sx_in_off(struct specialix_board * bp, unsigned short reg) +{ + bp->reg = reg; + outb (reg, bp->base + SX_ADDR_REG); + return inb (bp->base + SX_DATA_REG); +} + + +/* Output Byte to CL CD186x register */ +extern inline void sx_out_off(struct specialix_board * bp, unsigned short reg, + unsigned char val) +{ + bp->reg = reg; + outb (reg, bp->base + SX_ADDR_REG); + outb (val, bp->base + SX_DATA_REG); +} + + +/* Wait for Channel Command Register ready */ +extern inline void sx_wait_CCR(struct specialix_board * bp) +{ + unsigned long delay; + + for (delay = SX_CCR_TIMEOUT; delay; delay--) + if (!sx_in(bp, CD186x_CCR)) + return; + + printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp)); +} + + +/* Wait for Channel Command Register ready */ +extern inline void sx_wait_CCR_off(struct specialix_board * bp) +{ + unsigned long delay; + + for (delay = SX_CCR_TIMEOUT; delay; delay--) + if (!sx_in_off(bp, CD186x_CCR)) + return; + + printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp)); +} + + +/* + * specialix IO8+ IO range functions. + */ + +extern inline int sx_check_io_range(struct specialix_board * bp) +{ + return check_region (bp->base, SX_IO_SPACE); +} + + +extern inline void sx_request_io_range(struct specialix_board * bp) +{ + request_region(bp->base, SX_IO_SPACE, "specialix IO8+" ); +} + + +extern inline void sx_release_io_range(struct specialix_board * bp) +{ + release_region(bp->base, SX_IO_SPACE); +} + + +/* Must be called with enabled interrupts */ +extern inline void sx_long_delay(unsigned long delay) +{ + unsigned long i; + + for (i = jiffies + delay; i > jiffies; ) ; +} + + + +/* Set the IRQ using the RTS lines that run to the PAL on the board.... */ +int sx_set_irq ( struct specialix_board *bp) +{ + int virq; + int i; + + switch (bp->irq) { + /* In the same order as in the docs... */ + case 15: virq = 0;break; + case 12: virq = 1;break; + case 11: virq = 2;break; + case 9: virq = 3;break; + default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq); + return 0; + } + + for (i=0;i<2;i++) { + sx_out(bp, CD186x_CAR, i); + sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0); + } + return 1; +} + + +/* Reset and setup CD186x chip */ +static int sx_init_CD186x(struct specialix_board * bp) +{ + unsigned long flags; + int scaler; + int rv = 1; + + save_flags(flags); cli(); + + sx_wait_CCR_off(bp); /* Wait for CCR ready */ + sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */ + sti(); + sx_long_delay(HZ/20); /* Delay 0.05 sec */ + cli(); + sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */ + sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */ + sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */ + sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */ + sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */ + /* Set RegAckEn */ + sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN); + + /* Setting up prescaler. We need 4 ticks per 1 ms */ + scaler = SX_OSCFREQ/SPECIALIX_TPS; + + sx_out_off(bp, CD186x_PPRH, scaler >> 8); + sx_out_off(bp, CD186x_PPRL, scaler & 0xff); + + if (!sx_set_irq (bp)) { + /* Figure out how to pass this along... */ + printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq); + rv = 0; + } + + restore_flags(flags); + return rv; +} + + +int read_cross_byte (struct specialix_board *bp, int reg, int bit) +{ + int i; + int t; + + for (i=0, t=0;i<8;i++) { + sx_out_off (bp, CD186x_CAR, i); + if (sx_in_off (bp, reg) & bit) + t |= 1 << i; + } + return t; +} + + +#ifdef SPECIALIX_TIMER +void missed_irq (unsigned long data) +{ + if (sx_in ((struct specialix_board *)data, CD186x_SRSR) & + (SRSR_RREQint | + SRSR_TREQint | + SRSR_MREQint)) { + printk (KERN_INFO "Missed interrupt... Calling int from timer. \n"); + sx_interrupt (((struct specialix_board *)data)->irq, + NULL, NULL); + } + missed_irq_timer.expires = jiffies + HZ; + add_timer (&missed_irq_timer); +} +#endif + + + +/* Main probing routine, also sets irq. */ +static int sx_probe(struct specialix_board *bp) +{ + unsigned char val1, val2; +#if 0 + int irqs = 0; + int retries; +#endif + int rev; + int chip; + + if (sx_check_io_range(bp)) + return 1; + + /* Are the I/O ports here ? */ + sx_out_off(bp, CD186x_PPRL, 0x5a); + short_pause (); + val1 = sx_in_off(bp, CD186x_PPRL); + + sx_out_off(bp, CD186x_PPRL, 0xa5); + short_pause (); + val2 = sx_in_off(bp, CD186x_PPRL); + + + if ((val1 != 0x5a) || (val2 != 0xa5)) { + printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n", + board_No(bp), bp->base); + return 1; + } + + /* Check the DSR lines that Specialix uses as board + identification */ + val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR); + val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS); +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "sx%d: DSR lines are: %02x, rts lines are: %02x\n", + board_No(bp), val1, val2); +#endif + if (val1 != 0xb2) { + printk(KERN_INFO "sx%d: specialix IO8+ ID at 0x%03x not found.\n", + board_No(bp), bp->base); + return 1; + } + + +#if 0 + /* It's time to find IRQ for this board */ + for (retries = 0; retries < 5 && irqs <= 0; retries++) { + irqs = probe_irq_on(); + sx_init_CD186x(bp); /* Reset CD186x chip */ + sx_out(bp, CD186x_CAR, 2); /* Select port 2 */ + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */ + sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */ + sx_long_delay(HZ/20); + irqs = probe_irq_off(irqs); + +#if SPECIALIX_DEBUG > 2 + printk (KERN_DEBUG "SRSR = %02x, ", sx_in(bp, CD186x_SRSR)); + printk ( "TRAR = %02x, ", sx_in(bp, CD186x_TRAR)); + printk ( "GIVR = %02x, ", sx_in(bp, CD186x_GIVR)); + printk ( "GICR = %02x, ", sx_in(bp, CD186x_GICR)); + printk ( "\n"); +#endif + /* Reset CD186x again */ + if (!sx_init_CD186x(bp)) { + /* Hmmm. This is dead code anyway. */ + } +#if SPECIALIX_DEBUG > 2 + printk (KERN_DEBUG "val1 = %02x, val2 = %02x, val3 = %02x.\n", + val1, val2, val3); +#endif + + } + +#if 0 + if (irqs <= 0) { + printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n", + board_No(bp), bp->base); + return 1; + } +#endif + printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs); + if (irqs > 0) + bp->irq = irqs; +#endif + /* Reset CD186x again */ + if (!sx_init_CD186x(bp)) { + return -EIO; + } + + sx_request_io_range(bp); + bp->flags |= SX_BOARD_PRESENT; + + /* Chip revcode pkgtype + GFRCR SRCR bit 7 + CD180 rev B 0x81 0 + CD180 rev C 0x82 0 + CD1864 rev A 0x82 1 + CD1865 rev A 0x83 1 -- Do not use!!! Does not work. + CD1865 rev B 0x84 1 + -- Thanks to Gwen Wang, Cirrus Logic. + */ + + switch (sx_in_off(bp, CD186x_GFRCR)) { + case 0x82:chip = 1864;rev='A';break; + case 0x83:chip = 1865;rev='A';break; + case 0x84:chip = 1865;rev='B';break; + case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */ + default:chip=-1;rev='x'; + } + +#if SPECIALIX_DEBUG > 2 + printk (KERN_DEBUG " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) ); +#endif + +#ifdef SPECIALIX_TIMER + init_timer (&missed_irq_timer); + missed_irq_timer.function = missed_irq; + missed_irq_timer.data = (unsigned long) bp; + missed_irq_timer.expires = jiffies + HZ; + add_timer (&missed_irq_timer); +#endif + + printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n", + board_No(bp), + bp->base, bp->irq, + chip, rev); + + return 0; +} + +/* + * + * Interrupt processing routines. + * */ + +extern inline void sx_mark_event(struct specialix_port * port, int event) +{ + /* + * I'm not quite happy with current scheme all serial + * drivers use their own BH routine. + * It seems this easily can be done with one BH routine + * serving for all serial drivers. + * For now I must introduce another one - SPECIALIX_BH. + * Still hope this will be changed in near future. + * -- Dmitry. + */ + set_bit(event, &port->event); + queue_task(&port->tqueue, &tq_specialix); + mark_bh(SPECIALIX_BH); +} + + +extern inline struct specialix_port * sx_get_port(struct specialix_board * bp, + unsigned char const * what) +{ + unsigned char channel; + struct specialix_port * port; + + channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF; + if (channel < CD186x_NCH) { + port = &sx_port[board_No(bp) * SX_NPORT + channel]; + if (port->flags & ASYNC_INITIALIZED) { + return port; + } + } + printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n", + board_No(bp), what, channel); + return NULL; +} + + +extern inline void sx_receive_exc(struct specialix_board * bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char status; + unsigned char ch; + + if (!(port = sx_get_port(bp, "Receive"))) + return; + + tty = port->tty; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n", + board_No(bp), port_No(port)); + return; + } + +#ifdef SX_REPORT_OVERRUN + status = sx_in(bp, CD186x_RCSR); + if (status & RCSR_OE) { + port->overrun++; +#if SPECIALIX_DEBUG + printk(KERN_DEBUG "sx%d: port %d: Overrun. Total %ld overruns.\n", + board_No(bp), port_No(port), port->overrun); +#endif + } + status &= port->mark_mask; +#else + status = sx_in(bp, CD186x_RCSR) & port->mark_mask; +#endif + ch = sx_in(bp, CD186x_RDR); + if (!status) { + return; + } + if (status & RCSR_TOUT) { + printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n", + board_No(bp), port_No(port)); + return; + + } else if (status & RCSR_BREAK) { +#ifdef SPECIALIX_DEBUG + printk(KERN_DEBUG "sx%d: port %d: Handling break...\n", + board_No(bp), port_No(port)); +#endif + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + if (port->flags & ASYNC_SAK) + do_SAK(tty); + + } else if (status & RCSR_PE) + *tty->flip.flag_buf_ptr++ = TTY_PARITY; + + else if (status & RCSR_FE) + *tty->flip.flag_buf_ptr++ = TTY_FRAME; + + else if (status & RCSR_OE) + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; + + else + *tty->flip.flag_buf_ptr++ = 0; + + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + queue_task(&tty->flip.tqueue, &tq_timer); +} + + +extern inline void sx_receive(struct specialix_board * bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char count; + + if (!(port = sx_get_port(bp, "Receive"))) + return; + + tty = port->tty; + + count = sx_in(bp, CD186x_RDCR); + +#ifdef SX_REPORT_FIFO + port->hits[count > 8 ? 9 : count]++; +#endif + + while (count--) { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n", + board_No(bp), port_No(port)); + break; + } + *tty->flip.char_buf_ptr++ = sx_in(bp, CD186x_RDR); + *tty->flip.flag_buf_ptr++ = 0; + tty->flip.count++; + } + queue_task(&tty->flip.tqueue, &tq_timer); +} + + +extern inline void sx_transmit(struct specialix_board * bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char count; + + + if (!(port = sx_get_port(bp, "Transmit"))) + return; + + tty = port->tty; + + if (port->IER & IER_TXEMPTY) { + /* FIFO drained */ + sx_out(bp, CD186x_CAR, port_No(port)); + port->IER &= ~IER_TXEMPTY; + sx_out(bp, CD186x_IER, port->IER); + return; + } + + if ((port->xmit_cnt <= 0 && !port->break_length) + || tty->stopped || tty->hw_stopped) { + sx_out(bp, CD186x_CAR, port_No(port)); + port->IER &= ~IER_TXRDY; + sx_out(bp, CD186x_IER, port->IER); + return; + } + + if (port->break_length) { + if (port->break_length > 0) { + if (port->COR2 & COR2_ETC) { + sx_out(bp, CD186x_TDR, CD186x_C_ESC); + sx_out(bp, CD186x_TDR, CD186x_C_SBRK); + port->COR2 &= ~COR2_ETC; + } + count = MIN(port->break_length, 0xff); + sx_out(bp, CD186x_TDR, CD186x_C_ESC); + sx_out(bp, CD186x_TDR, CD186x_C_DELAY); + sx_out(bp, CD186x_TDR, count); + if (!(port->break_length -= count)) + port->break_length--; + } else { + sx_out(bp, CD186x_TDR, CD186x_C_ESC); + sx_out(bp, CD186x_TDR, CD186x_C_EBRK); + sx_out(bp, CD186x_COR2, port->COR2); + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_CORCHG2); + port->break_length = 0; + } + return; + } + + count = CD186x_NFIFO; + do { + sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]); + port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1); + if (--port->xmit_cnt <= 0) + break; + } while (--count > 0); + + if (port->xmit_cnt <= 0) { + sx_out(bp, CD186x_CAR, port_No(port)); + port->IER &= ~IER_TXRDY; + sx_out(bp, CD186x_IER, port->IER); + } + if (port->xmit_cnt <= port->wakeup_chars) + sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); +} + + +extern inline void sx_check_modem(struct specialix_board * bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char mcr; + +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "Modem intr. "); +#endif + if (!(port = sx_get_port(bp, "Modem"))) + return; + + tty = port->tty; + + mcr = sx_in(bp, CD186x_MCR); + printk ("mcr = %02x.\n", mcr); + + if ((mcr & MCR_CDCHG)) { +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "CD just changed... "); +#endif + if (sx_in(bp, CD186x_MSVR) & MSVR_CD) { +#ifdef SPECIALIX_DEBUG + printk ( "Waking up guys in open.\n"); +#endif + wake_up_interruptible(&port->open_wait); + } + else if (!((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_CALLOUT_NOHUP))) { +#ifdef SPECIALIX_DEBUG + printk ( "Sending HUP.\n"); +#endif + queue_task(&port->tqueue_hangup, + &tq_scheduler); + } else { +#ifdef SPECIALIX_DEBUG + printk ( "Don't need to send HUP.\n"); +#endif + } + } + +#ifdef SPECIALIX_BRAIN_DAMAGED_CTS + if (mcr & MCR_CTSCHG) { + if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) { + tty->hw_stopped = 0; + port->IER |= IER_TXRDY; + if (port->xmit_cnt <= port->wakeup_chars) + sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); + } else { + tty->hw_stopped = 1; + port->IER &= ~IER_TXRDY; + } + sx_out(bp, CD186x_IER, port->IER); + } + if (mcr & MCR_DSSXHG) { + if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) { + tty->hw_stopped = 0; + port->IER |= IER_TXRDY; + if (port->xmit_cnt <= port->wakeup_chars) + sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); + } else { + tty->hw_stopped = 1; + port->IER &= ~IER_TXRDY; + } + sx_out(bp, CD186x_IER, port->IER); + } +#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */ + + /* Clear change bits */ + sx_out(bp, CD186x_MCR, 0); +} + + +/* The main interrupt processing routine */ +static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ + unsigned char status; + unsigned char ack; + struct specialix_board *bp; + unsigned long loop = 0; + int saved_reg; + + bp = IRQ_to_board[irq]; + + if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) { +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "sx: False interrupt. irq %d.\n", irq); +#endif + return; + } + + saved_reg = bp->reg; + + while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) & + (SRSR_RREQint | + SRSR_TREQint | + SRSR_MREQint)))) { + if (status & SRSR_RREQint) { + ack = sx_in(bp, CD186x_RRAR); + + if (ack == (SX_ID | GIVR_IT_RCV)) + sx_receive(bp); + else if (ack == (SX_ID | GIVR_IT_REXC)) + sx_receive_exc(bp); + else + printk(KERN_ERR "sx%d: Bad receive ack 0x%02x.\n", + board_No(bp), ack); + + } else if (status & SRSR_TREQint) { + ack = sx_in(bp, CD186x_TRAR); + + if (ack == (SX_ID | GIVR_IT_TX)) + sx_transmit(bp); + else + printk(KERN_ERR "sx%d: Bad transmit ack 0x%02x.\n", + board_No(bp), ack); + } else if (status & SRSR_MREQint) { + ack = sx_in(bp, CD186x_MRAR); + + if (ack == (SX_ID | GIVR_IT_MODEM)) + sx_check_modem(bp); + else + printk(KERN_ERR "sx%d: Bad modem ack 0x%02x.\n", + board_No(bp), ack); + + } + + sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */ + } + bp->reg = saved_reg; + outb (bp->reg, bp->base + SX_ADDR_REG); +} + + +/* + * Routines for open & close processing. + */ + + +/* Called with disabled interrupts */ +extern inline int sx_setup_board(struct specialix_board * bp) +{ + int error; + + if (bp->flags & SX_BOARD_ACTIVE) + return 0; + + error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", NULL); + + if (error) + return error; + + IRQ_to_board[bp->irq] = bp; + (void) sx_in (bp, 0); /* Turn ON interrupts. */ + + bp->flags |= SX_BOARD_ACTIVE; + + MOD_INC_USE_COUNT; + return 0; +} + + +/* Called with disabled interrupts */ +extern inline void sx_shutdown_board(struct specialix_board *bp) +{ + if (!(bp->flags & SX_BOARD_ACTIVE)) + return; + + bp->flags &= ~SX_BOARD_ACTIVE; + + free_irq(bp->irq, NULL); + (void) sx_in_off (bp, 0); /* Turn off interrupts. */ + + IRQ_to_board[bp->irq] = NULL; + + MOD_DEC_USE_COUNT; +} + + +/* + * Setting up port characteristics. + * Must be called with disabled interrupts + */ +static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port) +{ + struct tty_struct *tty; + unsigned long baud; + long tmp; + unsigned char cor1 = 0, cor3 = 0; + unsigned char mcor1 = 0, mcor2 = 0; + static int again=0; + + if (!(tty = port->tty) || !tty->termios) + return; + + port->IER = 0; + port->COR2 = 0; + /* Select port on the board */ + sx_out(bp, CD186x_CAR, port_No(port)); + + /* The Specialix board doens't implement the RTS lines. + They are used to set the IRQ level. Don't touch them. */ + if (SX_CRTSCTS(tty)) + port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS); + else + port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS); +#ifdef DEBUG_SPECIALIX + printk (KERN_DEBUG "sx: got MSVR=%02x.\n", port->MSVR); +#endif + baud = C_BAUD(tty); + + if (baud & CBAUDEX) { + baud &= ~CBAUDEX; + if (baud < 1 || baud > 2) + port->tty->termios->c_cflag &= ~CBAUDEX; + else + baud += 15; + } + if (baud == 15) { + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + baud ++; + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + baud += 2; + } + + + if (!baud_table[baud]) { + /* Drop DTR & exit */ +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "Dropping DTR... Hmm....\n"); +#endif + if (!SX_CRTSCTS (tty)) { + port -> MSVR &= ~ MSVR_DTR; + sx_out(bp, CD186x_MSVR, port->MSVR ); + } +#ifdef DEBUG_SPECIALIX + else + printk (KERN_DEBUG "Can't drop DTR: no DTR.\n"); +#endif + return; + } else { + /* Set DTR on */ + if (!SX_CRTSCTS (tty)) { + port ->MSVR |= MSVR_DTR; + } + } + + /* + * Now we must calculate some speed depended things + */ + + /* Set baud rate for port */ + tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] + + CD186x_TPC/2) / CD186x_TPC); + if ((tmp < 0x10) && (again < jiffies)) { + again = jiffies + HZ * 60; + /* Page 48 of version 2.0 of the CL-CD1865 databook */ + if (tmp >= 12) { + printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n" + "Performance degradation is possible.\n", + port_No (port), tmp); + } else { + printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n" + "Warning: overstressing Cirrus chip. " + "This might not work.\n", + port_No (port), tmp); + } + } + + sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff); + sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff); + sx_out(bp, CD186x_RBPRL, tmp & 0xff); + sx_out(bp, CD186x_TBPRL, tmp & 0xff); + + baud = (baud_table[baud] + 5) / 10; /* Estimated CPS */ + + /* Two timer ticks seems enough to wakeup something like SLIP driver */ + tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO; + port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ? + SERIAL_XMIT_SIZE - 1 : tmp); + + /* Receiver timeout will be transmission time for 1.5 chars */ + tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud; + tmp = (tmp > 0xff) ? 0xff : tmp; + sx_out(bp, CD186x_RTPR, tmp); + + switch (C_CSIZE(tty)) { + case CS5: + cor1 |= COR1_5BITS; + break; + case CS6: + cor1 |= COR1_6BITS; + break; + case CS7: + cor1 |= COR1_7BITS; + break; + case CS8: + cor1 |= COR1_8BITS; + break; + } + + if (C_CSTOPB(tty)) + cor1 |= COR1_2SB; + + cor1 |= COR1_IGNORE; + if (C_PARENB(tty)) { + cor1 |= COR1_NORMPAR; + if (C_PARODD(tty)) + cor1 |= COR1_ODDP; + if (I_INPCK(tty)) + cor1 &= ~COR1_IGNORE; + } + /* Set marking of some errors */ + port->mark_mask = RCSR_OE | RCSR_TOUT; + if (I_INPCK(tty)) + port->mark_mask |= RCSR_FE | RCSR_PE; + if (I_BRKINT(tty) || I_PARMRK(tty)) + port->mark_mask |= RCSR_BREAK; + if (I_IGNPAR(tty)) + port->mark_mask &= ~(RCSR_FE | RCSR_PE); + if (I_IGNBRK(tty)) { + port->mark_mask &= ~RCSR_BREAK; + if (I_IGNPAR(tty)) + /* Real raw mode. Ignore all */ + port->mark_mask &= ~RCSR_OE; + } + /* Enable Hardware Flow Control */ + if (C_CRTSCTS(tty)) { +#ifdef SPECIALIX_BRAIN_DAMAGED_CTS + port->IER |= IER_DSR | IER_CTS; + mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD; + mcor2 |= MCOR2_DSROD | MCOR2_CTSOD; + tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR)); +#else + port->COR2 |= COR2_CTSAE; +#endif + } + /* Enable Software Flow Control. FIXME: I'm not sure about this */ + /* Some people reported that it works, but I still doubt it */ + if (I_IXON(tty)) { + port->COR2 |= COR2_TXIBE; + cor3 |= (COR3_FCT | COR3_SCDE); + if (I_IXANY(tty)) + port->COR2 |= COR2_IXM; + sx_out(bp, CD186x_SCHR1, START_CHAR(tty)); + sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty)); + sx_out(bp, CD186x_SCHR3, START_CHAR(tty)); + sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty)); + } + if (!C_CLOCAL(tty)) { + /* Enable CD check */ + port->IER |= IER_CD; + mcor1 |= MCOR1_CDZD; + mcor2 |= MCOR2_CDOD; + } + + if (C_CREAD(tty)) + /* Enable receiver */ + port->IER |= IER_RXD; + + /* Set input FIFO size (1-8 bytes) */ + cor3 |= SPECIALIX_RXFIFO; + /* Setting up CD186x channel registers */ + sx_out(bp, CD186x_COR1, cor1); + sx_out(bp, CD186x_COR2, port->COR2); + sx_out(bp, CD186x_COR3, cor3); + /* Make CD186x know about registers change */ + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3); + /* Setting up modem option registers */ +#ifdef DEBUG_SPECIALIX + printk ("Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2); +#endif + sx_out(bp, CD186x_MCOR1, mcor1); + sx_out(bp, CD186x_MCOR2, mcor2); + /* Enable CD186x transmitter & receiver */ + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN); + /* Enable interrupts */ + sx_out(bp, CD186x_IER, port->IER); + /* And finally set the modem lines... */ + sx_out(bp, CD186x_MSVR, port->MSVR); +} + + +/* Must be called with interrupts enabled */ +static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port) +{ + unsigned long flags; + + if (port->flags & ASYNC_INITIALIZED) + return 0; + + if (!port->xmit_buf) { + /* We may sleep in get_free_page() */ + unsigned long tmp; + + if (!(tmp = get_free_page(GFP_KERNEL))) + return -ENOMEM; + + if (port->xmit_buf) { + free_page(tmp); + return -ERESTARTSYS; + } + port->xmit_buf = (unsigned char *) tmp; + } + + save_flags(flags); cli(); + + if (port->tty) + clear_bit(TTY_IO_ERROR, &port->tty->flags); + + if (port->count == 1) + bp->count++; + + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + sx_change_speed(bp, port); + port->flags |= ASYNC_INITIALIZED; + + restore_flags(flags); + return 0; +} + + +/* Must be called with interrupts disabled */ +static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port) +{ + struct tty_struct *tty; + + if (!(port->flags & ASYNC_INITIALIZED)) + return; + +#ifdef SX_REPORT_OVERRUN + printk(KERN_INFO "sx%d: port %d: Total %ld overruns were detected.\n", + board_No(bp), port_No(port), port->overrun); +#endif +#ifdef SX_REPORT_FIFO + { + int i; + + printk(KERN_INFO "sx%d: port %d: FIFO hits [ ", + board_No(bp), port_No(port)); + for (i = 0; i < 10; i++) { + printk("%ld ", port->hits[i]); + } + printk("].\n"); + } +#endif + if (port->xmit_buf) { + free_page((unsigned long) port->xmit_buf); + port->xmit_buf = NULL; + } + + /* Select port */ + sx_out(bp, CD186x_CAR, port_No(port)); + + if (!(tty = port->tty) || C_HUPCL(tty)) { + /* Drop DTR */ + sx_out(bp, CD186x_MSVDTR, 0); + } + + /* Reset port */ + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_SOFTRESET); + /* Disable all interrupts from this port */ + port->IER = 0; + sx_out(bp, CD186x_IER, port->IER); + + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); + port->flags &= ~ASYNC_INITIALIZED; + + if (--bp->count < 0) { + printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d\n", + board_No(bp), bp->count); + bp->count = 0; + } + + /* + * If this is the last opened port on the board + * shutdown whole board + */ + if (!bp->count) + sx_shutdown_board(bp); +} + + +static int block_til_ready(struct tty_struct *tty, struct file * filp, + struct specialix_port *port) +{ + struct wait_queue wait = { current, NULL }; + struct specialix_board *bp = port_Board(port); + int retval; + int do_clocal = 0; + int CD; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { + interruptible_sleep_on(&port->close_wait); + if (port->flags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SPECIALIX_TYPE_CALLOUT) { + if (port->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_SESSION_LOCKOUT) && + (port->session != current->session)) + return -EBUSY; + if ((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_PGRP_LOCKOUT) && + (port->pgrp != current->pgrp)) + return -EBUSY; + port->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (port->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (port->flags & ASYNC_CALLOUT_ACTIVE) { + if (port->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (C_CLOCAL(tty)) + do_clocal = 1; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&port->open_wait, &wait); + cli(); + if (!tty_hung_up_p(filp)) + port->count--; + sti(); + port->blocked_open++; + while (1) { + cli(); + sx_out(bp, CD186x_CAR, port_No(port)); + CD = sx_in(bp, CD186x_MSVR) & MSVR_CD; + if (!(port->flags & ASYNC_CALLOUT_ACTIVE)) { + if (SX_CRTSCTS (tty)) { + /* Activate RTS */ + port->MSVR |= MSVR_DTR; + sx_out (bp, CD186x_MSVR, port->MSVR); + } else { + /* Activate DTR */ + port->MSVR |= MSVR_DTR; + sx_out (bp, CD186x_MSVR, port->MSVR); + } + } + sti(); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || + !(port->flags & ASYNC_INITIALIZED)) { + if (port->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + break; + } + if (!(port->flags & ASYNC_CALLOUT_ACTIVE) && + !(port->flags & ASYNC_CLOSING) && + (do_clocal || CD)) + break; + if (current->signal & ~current->blocked) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&port->open_wait, &wait); + if (!tty_hung_up_p(filp)) + port->count++; + port->blocked_open--; + if (retval) + return retval; + + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + + +static int sx_open(struct tty_struct * tty, struct file * filp) +{ + int board; + int error; + struct specialix_port * port; + struct specialix_board * bp; + unsigned long flags; + + board = SX_BOARD(MINOR(tty->device)); + + if (board > SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) + return -ENODEV; + + bp = &sx_board[board]; + port = sx_port + board * SX_NPORT + SX_PORT(MINOR(tty->device)); + +#ifdef DEBUG_SPECIALIX + printk (KERN_DEBUG "Board = %d, bp = %p, port = %p, portno = %d.\n", + board, bp, port, SX_PORT(MINOR(tty->device))); +#endif + + if (sx_paranoia_check(port, tty->device, "sx_open")) + return -ENODEV; + + if ((error = sx_setup_board(bp))) + return error; + + port->count++; + tty->driver_data = port; + port->tty = tty; + + if ((error = sx_setup_port(bp, port))) + return error; + + if ((error = block_til_ready(tty, filp, port))) + return error; + + if ((port->count == 1) && (port->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SPECIALIX_TYPE_NORMAL) + *tty->termios = port->normal_termios; + else + *tty->termios = port->callout_termios; + save_flags(flags); cli(); + sx_change_speed(bp, port); + restore_flags(flags); + } + + port->session = current->session; + port->pgrp = current->pgrp; + return 0; +} + + +static void sx_close(struct tty_struct * tty, struct file * filp) +{ + struct specialix_port *port = (struct specialix_port *) tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + unsigned long timeout; + + if (!port || sx_paranoia_check(port, tty->device, "close")) + return; + + save_flags(flags); cli(); + if (tty_hung_up_p(filp)) { + restore_flags(flags); + return; + } + + bp = port_Board(port); + if ((tty->count == 1) && (port->count != 1)) { + printk(KERN_ERR "sx%d: sx_close: bad port count;" + " tty->count is 1, port count is %d\n", + board_No(bp), port->count); + port->count = 1; + } + if (--port->count < 0) { + printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n", + board_No(bp), port_No(port), port->count); + port->count = 0; + } + if (port->count) { + restore_flags(flags); + return; + } + port->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (port->flags & ASYNC_NORMAL_ACTIVE) + port->normal_termios = *tty->termios; + if (port->flags & ASYNC_CALLOUT_ACTIVE) + port->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, port->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + port->IER &= ~IER_RXD; + if (port->flags & ASYNC_INITIALIZED) { + port->IER &= ~IER_TXRDY; + port->IER |= IER_TXEMPTY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + timeout = jiffies+HZ; + while(port->IER & IER_TXEMPTY) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + port->timeout; + schedule(); + if (jiffies > timeout) { + printk (KERN_INFO "Timeout waiting for close\n"); + break; + } + } + + } + sx_shutdown_port(bp, port); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + port->event = 0; + port->tty = 0; + if (port->blocked_open) { + if (port->close_delay) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + port->close_delay; + schedule(); + } + wake_up_interruptible(&port->open_wait); + } + port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&port->close_wait); + restore_flags(flags); +} + + +static int sx_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + int c, total = 0; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_write")) + return 0; + + bp = port_Board(port); + + if (!tty || !port->xmit_buf || !tmp_buf) + return 0; + + if (from_user) + down(&tmp_buf_sem); + + save_flags(flags); + while (1) { + cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); + if (c <= 0) + break; + + if (from_user) { + copy_from_user(tmp_buf, buf, c); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); + memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c); + } else + memcpy(port->xmit_buf + port->xmit_head, buf, c); + port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + port->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; + } + if (from_user) + up(&tmp_buf_sem); + if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped && + !(port->IER & IER_TXRDY)) { + port->IER |= IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + } + restore_flags(flags); + return total; +} + + +static void sx_put_char(struct tty_struct * tty, unsigned char ch) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_put_char")) + return; + + if (!tty || !port->xmit_buf) + return; + + save_flags(flags); cli(); + + if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { + restore_flags(flags); + return; + } + + port->xmit_buf[port->xmit_head++] = ch; + port->xmit_head &= SERIAL_XMIT_SIZE - 1; + port->xmit_cnt++; + restore_flags(flags); +} + + +static void sx_flush_chars(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_flush_chars")) + return; + + if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !port->xmit_buf) + return; + + save_flags(flags); cli(); + port->IER |= IER_TXRDY; + sx_out(port_Board(port), CD186x_CAR, port_No(port)); + sx_out(port_Board(port), CD186x_IER, port->IER); + restore_flags(flags); +} + + +static int sx_write_room(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + int ret; + + if (sx_paranoia_check(port, tty->device, "sx_write_room")) + return 0; + + ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; +} + + +static int sx_chars_in_buffer(struct tty_struct *tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + + if (sx_paranoia_check(port, tty->device, "sx_chars_in_buffer")) + return 0; + + return port->xmit_cnt; +} + + +static void sx_flush_buffer(struct tty_struct *tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_flush_buffer")) + return; + + save_flags(flags); cli(); + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + restore_flags(flags); + + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + + +static int sx_get_modem_info(struct specialix_port * port, unsigned int *value) +{ + struct specialix_board * bp; + unsigned char status; + unsigned int result; + unsigned long flags; + + bp = port_Board(port); + save_flags(flags); cli(); + sx_out(bp, CD186x_CAR, port_No(port)); + status = sx_in(bp, CD186x_MSVR); + restore_flags(flags); +#ifdef DEBUG_SPECIALIX + printk (KERN_DEBUG "Got msvr[%d] = %02x, car = %d.\n", + port_No(port), status, sx_in (bp, CD186x_CAR)); + printk (KERN_DEBUG "sx_port = %p, port = %p\n", sx_port, port); +#endif + if (SX_CRTSCTS(port->tty)) { + result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */ + | ((status & MSVR_DTR) ? TIOCM_RTS : 0) + | ((status & MSVR_CD) ? TIOCM_CAR : 0) + |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */ + | ((status & MSVR_CTS) ? TIOCM_CTS : 0); + } else { + result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */ + | ((status & MSVR_DTR) ? TIOCM_DTR : 0) + | ((status & MSVR_CD) ? TIOCM_CAR : 0) + |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */ + | ((status & MSVR_CTS) ? TIOCM_CTS : 0); + } + put_user(result,(unsigned long *) value); + return 0; +} + + +static int sx_set_modem_info(struct specialix_port * port, unsigned int cmd, + unsigned int *value) +{ + int error; + unsigned int arg; + unsigned long flags; + struct specialix_board *bp = port_Board(port); + + error = verify_area(VERIFY_READ, value, sizeof(int)); + if (error) + return error; + + Get_user(arg, (unsigned long *) value); + switch (cmd) { + case TIOCMBIS: + /* if (arg & TIOCM_RTS) + port->MSVR |= MSVR_RTS; */ + /* if (arg & TIOCM_DTR) + port->MSVR |= MSVR_DTR; */ + + if (SX_CRTSCTS(port->tty)) { + if (arg & TIOCM_RTS) + port->MSVR |= MSVR_DTR; + } else { + if (arg & TIOCM_DTR) + port->MSVR |= MSVR_DTR; + } + break; + case TIOCMBIC: + /* if (arg & TIOCM_RTS) + port->MSVR &= ~MSVR_RTS; */ + /* if (arg & TIOCM_DTR) + port->MSVR &= ~MSVR_DTR; */ + if (SX_CRTSCTS(port->tty)) { + if (arg & TIOCM_RTS) + port->MSVR &= ~MSVR_DTR; + } else { + if (arg & TIOCM_DTR) + port->MSVR &= ~MSVR_DTR; + } + break; + case TIOCMSET: + /* port->MSVR = (arg & TIOCM_RTS) ? (port->MSVR | MSVR_RTS) : + (port->MSVR & ~MSVR_RTS); */ + /* port->MSVR = (arg & TIOCM_DTR) ? (port->MSVR | MSVR_DTR) : + (port->MSVR & ~MSVR_DTR); */ + if (SX_CRTSCTS(port->tty)) { + port->MSVR = (arg & TIOCM_RTS) ? + (port->MSVR | MSVR_DTR) : + (port->MSVR & ~MSVR_DTR); + } else { + port->MSVR = (arg & TIOCM_DTR) ? + (port->MSVR | MSVR_DTR): + (port->MSVR & ~MSVR_DTR); + } + break; + default: + return -EINVAL; + } + save_flags(flags); cli(); + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_MSVR, port->MSVR); + restore_flags(flags); + return 0; +} + + +extern inline void sx_send_break(struct specialix_port * port, unsigned long length) +{ + struct specialix_board *bp = port_Board(port); + unsigned long flags; + + save_flags(flags); cli(); + port->break_length = SPECIALIX_TPS / HZ * length; + port->COR2 |= COR2_ETC; + port->IER |= IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_COR2, port->COR2); + sx_out(bp, CD186x_IER, port->IER); + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_CORCHG2); + sx_wait_CCR(bp); + restore_flags(flags); +} + + +extern inline int sx_set_serial_info(struct specialix_port * port, + struct serial_struct * newinfo) +{ + struct serial_struct tmp; + struct specialix_board *bp = port_Board(port); + int change_speed; + unsigned long flags; + int error; + + error = verify_area(VERIFY_READ, (void *) newinfo, sizeof(tmp)); + if (error) + return error; + + copy_from_user(&tmp, newinfo, sizeof(tmp)); + +#if 0 + if ((tmp.irq != bp->irq) || + (tmp.port != bp->base) || + (tmp.type != PORT_CIRRUS) || + (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) || + (tmp.custom_divisor != 0) || + (tmp.xmit_fifo_size != CD186x_NFIFO) || + (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) + return -EINVAL; +#endif + + change_speed = ((port->flags & ASYNC_SPD_MASK) != + (tmp.flags & ASYNC_SPD_MASK)); + + if (!suser()) { + if ((tmp.close_delay != port->close_delay) || + (tmp.closing_wait != port->closing_wait) || + ((tmp.flags & ~ASYNC_USR_MASK) != + (port->flags & ~ASYNC_USR_MASK))) + return -EPERM; + port->flags = ((port->flags & ~ASYNC_USR_MASK) | + (tmp.flags & ASYNC_USR_MASK)); + } else { + port->flags = ((port->flags & ~ASYNC_FLAGS) | + (tmp.flags & ASYNC_FLAGS)); + port->close_delay = tmp.close_delay; + port->closing_wait = tmp.closing_wait; + } + if (change_speed) { + save_flags(flags); cli(); + sx_change_speed(bp, port); + restore_flags(flags); + } + return 0; +} + + +extern inline int sx_get_serial_info(struct specialix_port * port, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + struct specialix_board *bp = port_Board(port); + int error; + + error = verify_area(VERIFY_WRITE, (void *) retinfo, sizeof(tmp)); + if (error) + return error; + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = PORT_CIRRUS; + tmp.line = port - sx_port; + tmp.port = bp->base; + tmp.irq = bp->irq; + tmp.flags = port->flags; + tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC; + tmp.close_delay = port->close_delay * HZ/100; + tmp.closing_wait = port->closing_wait * HZ/100; + tmp.xmit_fifo_size = CD186x_NFIFO; + copy_to_user(retinfo, &tmp, sizeof(tmp)); + return 0; +} + + +static int sx_ioctl(struct tty_struct * tty, struct file * filp, + unsigned int cmd, unsigned long arg) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + int error; + int retval; + + if (sx_paranoia_check(port, tty->device, "sx_ioctl")) + return -ENODEV; + + switch (cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (!arg) + sx_send_break(port, HZ/4); /* 1/4 second */ + return 0; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + sx_send_break(port, arg ? arg*(HZ/10) : HZ/4); + return 0; + case TIOCGSOFTCAR: + error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long)); + if (error) + return error; + put_user(C_CLOCAL(tty) ? 1 : 0, + (unsigned long *) arg); + return 0; + case TIOCSSOFTCAR: + Get_user(arg, (unsigned long *) arg); + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + return 0; + case TIOCMGET: + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int)); + if (error) + return error; + return sx_get_modem_info(port, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return sx_set_modem_info(port, cmd, (unsigned int *) arg); + case TIOCGSERIAL: + return sx_get_serial_info(port, (struct serial_struct *) arg); + case TIOCSSERIAL: + return sx_set_serial_info(port, (struct serial_struct *) arg); + default: + return -ENOIOCTLCMD; + } + return 0; +} + + +static void sx_throttle(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_throttle")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + + /* Use DTR instead of RTS ! */ + if (SX_CRTSCTS (tty)) + port->MSVR &= ~MSVR_DTR; + else { + /* Auch!!! I think the system shouldn't call this then. */ + /* Or maybe we're supposed (allowed?) to do our side of hw + handshake anyway, even when hardware handshake is off. + When you see this in your logs, please report.... */ + printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n", + port_No (port)); + } + sx_out(bp, CD186x_CAR, port_No(port)); + if (I_IXOFF(tty)) { + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_SSCH2); + sx_wait_CCR(bp); + } + sx_out(bp, CD186x_MSVR, port->MSVR); + restore_flags(flags); +} + + +static void sx_unthrottle(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_unthrottle")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + /* XXXX Use DTR INSTEAD???? */ + if (SX_CRTSCTS(tty)) { + port->MSVR |= MSVR_DTR; + } /* Else clause: see remark in "sx_throttle"... */ + + sx_out(bp, CD186x_CAR, port_No(port)); + if (I_IXOFF(tty)) { + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_SSCH1); + sx_wait_CCR(bp); + } + sx_out(bp, CD186x_MSVR, port->MSVR); + restore_flags(flags); +} + + +static void sx_stop(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_stop")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + port->IER &= ~IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + restore_flags(flags); +} + + +static void sx_start(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_start")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) { + port->IER |= IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + } + restore_flags(flags); +} + + +/* + * This routine is called from the scheduler tqueue when the interrupt + * routine has signalled that a hangup has occurred. The path of + * hangup processing is: + * + * serial interrupt routine -> (scheduler tqueue) -> + * do_sx_hangup() -> tty->hangup() -> sx_hangup() + * + */ +static void do_sx_hangup(void *private_) +{ + struct specialix_port *port = (struct specialix_port *) private_; + struct tty_struct *tty; + + tty = port->tty; + if (!tty) + return; + + tty_hangup(tty); +} + + +static void sx_hangup(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + + if (sx_paranoia_check(port, tty->device, "sx_hangup")) + return; + + bp = port_Board(port); + + sx_shutdown_port(bp, port); + port->event = 0; + port->count = 0; + port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + port->tty = 0; + wake_up_interruptible(&port->open_wait); +} + + +static void sx_set_termios(struct tty_struct * tty, struct termios * old_termios) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_set_termios")) + return; + + if (tty->termios->c_cflag == old_termios->c_cflag && + tty->termios->c_iflag == old_termios->c_iflag) + return; + + save_flags(flags); cli(); + sx_change_speed(port_Board(port), port); + restore_flags(flags); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + sx_start(tty); + } +} + + +static void do_specialix_bh(void) +{ + run_task_queue(&tq_specialix); +} + + +static void do_softint(void *private_) +{ + struct specialix_port *port = (struct specialix_port *) private_; + struct tty_struct *tty; + + if(!(tty = port->tty)) + return; + + if (clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + } +} + + +static int sx_init_drivers(void) +{ + int error; + int i; + + + if (!(tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL))) { + printk(KERN_ERR "sx: Couldn't get free page.\n"); + return 1; + } + init_bh(SPECIALIX_BH, do_specialix_bh); + memset(IRQ_to_board, 0, sizeof(IRQ_to_board)); + memset(&specialix_driver, 0, sizeof(specialix_driver)); + specialix_driver.magic = TTY_DRIVER_MAGIC; + specialix_driver.name = "ttyW"; + specialix_driver.major = SPECIALIX_NORMAL_MAJOR; + specialix_driver.num = SX_NBOARD * SX_NPORT; + specialix_driver.type = TTY_DRIVER_TYPE_SERIAL; + specialix_driver.subtype = SPECIALIX_TYPE_NORMAL; + specialix_driver.init_termios = tty_std_termios; + specialix_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + specialix_driver.flags = TTY_DRIVER_REAL_RAW; + specialix_driver.refcount = &specialix_refcount; + specialix_driver.table = specialix_table; + specialix_driver.termios = specialix_termios; + specialix_driver.termios_locked = specialix_termios_locked; + + specialix_driver.open = sx_open; + specialix_driver.close = sx_close; + specialix_driver.write = sx_write; + specialix_driver.put_char = sx_put_char; + specialix_driver.flush_chars = sx_flush_chars; + specialix_driver.write_room = sx_write_room; + specialix_driver.chars_in_buffer = sx_chars_in_buffer; + specialix_driver.flush_buffer = sx_flush_buffer; + specialix_driver.ioctl = sx_ioctl; + specialix_driver.throttle = sx_throttle; + specialix_driver.unthrottle = sx_unthrottle; + specialix_driver.set_termios = sx_set_termios; + specialix_driver.stop = sx_stop; + specialix_driver.start = sx_start; + specialix_driver.hangup = sx_hangup; + + specialix_callout_driver = specialix_driver; + specialix_callout_driver.name = "cuw"; + specialix_callout_driver.major = SPECIALIX_CALLOUT_MAJOR; + specialix_callout_driver.subtype = SPECIALIX_TYPE_CALLOUT; + + if ((error = tty_register_driver(&specialix_driver))) { + free_page((unsigned long)tmp_buf); + printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n", + error); + return 1; + } + if ((error = tty_register_driver(&specialix_callout_driver))) { + free_page((unsigned long)tmp_buf); + tty_unregister_driver(&specialix_driver); + printk(KERN_ERR "sx: Couldn't register specialix IO8+ callout driver, error = %d\n", + error); + return 1; + } + + memset(sx_port, 0, sizeof(sx_port)); + for (i = 0; i < SX_NPORT * SX_NBOARD; i++) { + sx_port[i].callout_termios = specialix_callout_driver.init_termios; + sx_port[i].normal_termios = specialix_driver.init_termios; + sx_port[i].magic = SPECIALIX_MAGIC; + sx_port[i].tqueue.routine = do_softint; + sx_port[i].tqueue.data = &sx_port[i]; + sx_port[i].tqueue_hangup.routine = do_sx_hangup; + sx_port[i].tqueue_hangup.data = &sx_port[i]; + sx_port[i].close_delay = 50 * HZ/100; + sx_port[i].closing_wait = 3000 * HZ/100; + } + + return 0; +} + + +static void sx_release_drivers(void) +{ + free_page((unsigned long)tmp_buf); + tty_unregister_driver(&specialix_driver); + tty_unregister_driver(&specialix_callout_driver); +} + + +#ifndef MODULE +/* + * Called at boot time. + * + * You can specify IO base for up to SX_NBOARD cards, + * using line "specialix=0xiobase1,0xiobase2,.." at LILO prompt. + * Note that there will be no probing at default + * addresses in this case. + * + */ +void specialix_setup(char *str, int * ints) +{ + int i; + + for (i=0;i + +#ifdef __KERNEL__ + +#define SX_NBOARD 4 +/* NOTE: Specialix decoder recognizes 4 addresses, but only two are used.... */ +#define SX_IO_SPACE 4 +/* eight ports per board. */ +#define SX_NPORT 8 +#define SX_BOARD(line) ((line) / SX_NPORT) +#define SX_PORT(line) ((line) & (SX_NPORT - 1)) + + +#define SX_DATA_REG 0 /* Base+0 : Data register */ +#define SX_ADDR_REG 1 /* base+1 : Address register. */ + +#define MHz *1000000 /* I'm ashamed of myself. */ + +/* On-board oscillator frequency */ +#define SX_OSCFREQ (25 MHz/2) +/* There is a 25MHz crystal on the board, but the chip is in /2 mode */ + + +/* Ticks per sec. Used for setting receiver timeout and break length */ +#define SPECIALIX_TPS 4000 + +/* Yeah, after heavy testing I decided it must be 6. + * Sure, You can change it if needed. + */ +#define SPECIALIX_RXFIFO 6 /* Max. receiver FIFO size (1-8) */ + +#define SPECIALIX_MAGIC 0x0907 + +#define SX_CCR_TIMEOUT 10000 /* CCR timeout. You may need to wait upto + 10 milliseconds before the internal + processor is available again after + you give it a command */ + +#define SX_IOBASE1 0x100 +#define SX_IOBASE2 0x180 +#define SX_IOBASE3 0x250 +#define SX_IOBASE4 0x260 + +struct specialix_board { + unsigned long flags; + unsigned short base; + unsigned char irq; + signed char count; + unsigned char DTR; + int reg; +}; + +#define SX_BOARD_PRESENT 0x00000001 +#define SX_BOARD_ACTIVE 0x00000002 + + +struct specialix_port { + int magic; + int baud_base; + int flags; + struct tty_struct * tty; + int count; + int blocked_open; + int event; + int timeout; + int close_delay; + long session; + long pgrp; + unsigned char * xmit_buf; + int custom_divisor; + int xmit_head; + int xmit_tail; + int xmit_cnt; + struct termios normal_termios; + struct termios callout_termios; + struct wait_queue *open_wait; + struct wait_queue *close_wait; + struct tq_struct tqueue; + struct tq_struct tqueue_hangup; + short wakeup_chars; + short break_length; + unsigned short closing_wait; + unsigned char mark_mask; + unsigned char IER; + unsigned char MSVR; + unsigned char COR2; +#ifdef SX_REPORT_OVERRUN + unsigned long overrun; +#endif +#ifdef SX_REPORT_FIFO + unsigned long hits[10]; +#endif +}; + +#endif /* __KERNEL__ */ +#endif /* __LINUX_SPECIALIX_H */ + + + + + + + + + diff -u --recursive --new-file v2.0.30/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- v2.0.30/linux/drivers/char/tty_io.c Thu Oct 31 04:34:58 1996 +++ linux/drivers/char/tty_io.c Wed Aug 13 09:56:45 1997 @@ -11,7 +11,7 @@ * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0. * * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the - * tty_struct and tty_queue structures. Previously there was a array + * tty_struct and tty_queue structures. Previously there was an array * of 256 tty_struct's which was statically allocated, and the * tty_queue structures were allocated at boot time. Both are now * dynamically allocated only when the tty is open. @@ -44,6 +44,9 @@ * * Restrict vt switching via ioctl() * -- grif@cs.ucr.edu, 5-Dec-95 + * + * Rewrote init_dev and release_dev to eliminate races. + * -- Bill Hawes , June 97 */ #include @@ -830,14 +833,27 @@ (unsigned int)count); } +/* Semaphore to protect creating and releasing a tty */ +static struct semaphore tty_sem = MUTEX; +static void down_tty_sem(int index) +{ + down(&tty_sem); +} +static void up_tty_sem(int index) +{ + up(&tty_sem); +} +static void release_mem(struct tty_struct *tty, int idx); + /* - * This is so ripe with races that you should *really* not touch this - * unless you know exactly what you are doing. All the changes have to be - * made atomically, or there may be incorrect pointers all over the place. + * Rewritten to remove races and properly clean up after a failed open. + * The new code protects the open with a semaphore, so it's really + * quite straightforward. The semaphore locking can probably be + * relaxed for the (most common) case of reopening a tty. */ static int init_dev(kdev_t device, struct tty_struct **ret_tty) { - struct tty_struct *tty, **tty_loc, *o_tty, **o_tty_loc; + struct tty_struct *tty, *o_tty; struct termios *tp, **tp_loc, *o_tp, **o_tp_loc; struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; struct tty_driver *driver; @@ -849,163 +865,221 @@ return -ENODEV; idx = MINOR(device) - driver->minor_start; - tty = o_tty = NULL; + + /* + * Check whether we need to acquire the tty semaphore to avoid + * race conditions. For now, play it safe. + */ + down_tty_sem(idx); + + /* check whether we're reopening an existing tty */ + tty = driver->table[idx]; + if(tty) goto fast_track; + + /* + * First time open is complex, especially for PTY devices. + * This code guarantees that either everything succeeds and the + * TTY is ready for operation, or else the table slots are vacated + * and the allocated memory released. (Except that the termios + * and locked termios may be retained.) + */ + + o_tty = NULL; tp = o_tp = NULL; ltp = o_ltp = NULL; - o_tty_loc = NULL; - o_tp_loc = o_ltp_loc = NULL; - tty_loc = &driver->table[idx]; - tp_loc = &driver->termios[idx]; - ltp_loc = &driver->termios_locked[idx]; + tty = (struct tty_struct*) get_free_page(GFP_KERNEL); + if(!tty) + goto fail_no_mem; + initialize_tty_struct(tty); + tty->device = device; + tty->driver = *driver; -repeat: - retval = -EIO; - if (driver->type == TTY_DRIVER_TYPE_PTY && - driver->subtype == PTY_TYPE_MASTER && - *tty_loc && (*tty_loc)->count) - goto end_init; - retval = -ENOMEM; - if (!*tty_loc && !tty) { - if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL))) - goto end_init; - initialize_tty_struct(tty); - tty->device = device; - tty->driver = *driver; - goto repeat; - } - if (!*tp_loc && !tp) { + tp_loc = &driver->termios[idx]; + if (!*tp_loc) { tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!tp) - goto end_init; + goto free_mem_out; *tp = driver->init_termios; - goto repeat; } - if (!*ltp_loc && !ltp) { + + ltp_loc = &driver->termios_locked[idx]; + if (!*ltp_loc) { ltp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!ltp) - goto end_init; + goto free_mem_out; memset(ltp, 0, sizeof(struct termios)); - goto repeat; } + if (driver->type == TTY_DRIVER_TYPE_PTY) { - o_tty_loc = &driver->other->table[idx]; - o_tp_loc = &driver->other->termios[idx]; - o_ltp_loc = &driver->other->termios_locked[idx]; + o_tty = (struct tty_struct *) get_free_page(GFP_KERNEL); + if (!o_tty) + goto free_mem_out; + initialize_tty_struct(o_tty); + o_tty->device = (kdev_t) MKDEV(driver->other->major, + driver->other->minor_start + idx); + o_tty->driver = *driver->other; - if (!*o_tty_loc && !o_tty) { - kdev_t o_device; - - o_tty = (struct tty_struct *) - get_free_page(GFP_KERNEL); - if (!o_tty) - goto end_init; - o_device = MKDEV(driver->other->major, - driver->other->minor_start + idx); - initialize_tty_struct(o_tty); - o_tty->device = o_device; - o_tty->driver = *driver->other; - goto repeat; - } - if (!*o_tp_loc && !o_tp) { + o_tp_loc = &driver->other->termios[idx]; + if (!*o_tp_loc) { o_tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_tp) - goto end_init; + goto free_mem_out; *o_tp = driver->other->init_termios; - goto repeat; } - if (!*o_ltp_loc && !o_ltp) { + + o_ltp_loc = &driver->other->termios_locked[idx]; + if (!*o_ltp_loc) { o_ltp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_ltp) - goto end_init; + goto free_mem_out; memset(o_ltp, 0, sizeof(struct termios)); - goto repeat; } - + + /* + * Everything allocated ... set up the o_tty structure. + */ + driver->other->table[idx] = o_tty; + if (!*o_tp_loc) + *o_tp_loc = o_tp; + if (!*o_ltp_loc) + *o_ltp_loc = o_ltp; + o_tty->termios = *o_tp_loc; + o_tty->termios_locked = *o_ltp_loc; + (*driver->other->refcount)++; + if (driver->subtype == PTY_TYPE_MASTER) + o_tty->count++; + + /* Establish the links in both directions */ + tty->link = o_tty; + o_tty->link = tty; } - /* Now we have allocated all the structures: update all the pointers.. */ - if (!*tp_loc) { + + /* + * All structures have been allocated, so now we install them. + * Failures after this point use release_mem to clean up, so + * there's no need to null out the local pointers. + */ + driver->table[idx] = tty; + if (!*tp_loc) *tp_loc = tp; - tp = NULL; - } - if (!*ltp_loc) { + if (!*ltp_loc) *ltp_loc = ltp; - ltp = NULL; + tty->termios = *tp_loc; + tty->termios_locked = *ltp_loc; + (*driver->refcount)++; + tty->count++; + + /* + * Structures all installed ... call the ldisc open routines. + * If we fail here just call release_mem to clean up. No need + * to decrement the use counts, as release_mem doesn't care. + */ + if (tty->ldisc.open) { + retval = (tty->ldisc.open)(tty); + if (retval) + goto release_mem_out; } - if (!*tty_loc) { - tty->termios = *tp_loc; - tty->termios_locked = *ltp_loc; - *tty_loc = tty; - (*driver->refcount)++; - (*tty_loc)->count++; - if (tty->ldisc.open) { - retval = (tty->ldisc.open)(tty); - if (retval < 0) { - (*tty_loc)->count--; - tty = NULL; - goto end_init; - } - } - tty = NULL; - } else { - if ((*tty_loc)->flags & (1 << TTY_CLOSING)) { - printk("Attempt to open closing tty %s.\n", - tty_name(*tty_loc)); - printk("Ack!!!! This should never happen!!\n"); - return -EINVAL; + if (o_tty && o_tty->ldisc.open) { + retval = (o_tty->ldisc.open)(o_tty); + if (retval) { + if (tty->ldisc.close) + (tty->ldisc.close)(tty); + goto release_mem_out; } - (*tty_loc)->count++; } - if (driver->type == TTY_DRIVER_TYPE_PTY) { - if (!*o_tp_loc) { - *o_tp_loc = o_tp; - o_tp = NULL; - } - if (!*o_ltp_loc) { - *o_ltp_loc = o_ltp; - o_ltp = NULL; - } - if (!*o_tty_loc) { - o_tty->termios = *o_tp_loc; - o_tty->termios_locked = *o_ltp_loc; - *o_tty_loc = o_tty; - (*driver->other->refcount)++; - if (o_tty->ldisc.open) { - retval = (o_tty->ldisc.open)(o_tty); - if (retval < 0) { - (*tty_loc)->count--; - o_tty = NULL; - goto end_init; - } - } - o_tty = NULL; - } - (*tty_loc)->link = *o_tty_loc; - (*o_tty_loc)->link = *tty_loc; - if (driver->subtype == PTY_TYPE_MASTER) - (*o_tty_loc)->count++; + goto success; + + /* + * This fast open can be used if the tty is already open. + * No memory is allocated, and the only failures are from + * attempting to open a closing tty or attempting multiple + * opens on a pty master. + */ +fast_track: + retval = -EIO; + if (test_bit(TTY_CLOSING, &tty->flags)) + goto end_init; + + if (driver->type == TTY_DRIVER_TYPE_PTY && + driver->subtype == PTY_TYPE_MASTER) { + /* + * special case for PTY masters: only one open permitted, + * and the slave side open count is incremented as well. + */ + if (tty->count) + goto end_init; + tty->link->count++; } - (*tty_loc)->driver = *driver; - *ret_tty = *tty_loc; + tty->count++; + tty->driver = *driver; /* N.B. why do this every time?? */ + +success: retval = 0; + *ret_tty = tty; + + /* All paths come through here to release the semaphore */ end_init: - if (tty) - free_page((unsigned long) tty); - if (o_tty) - free_page((unsigned long) o_tty); - if (tp) - kfree_s(tp, sizeof(struct termios)); + up_tty_sem(idx); + return retval; + + /* Release locally allocated memory ... nothing placed in slots */ +free_mem_out: if (o_tp) kfree_s(o_tp, sizeof(struct termios)); + if (o_tty) + free_page((unsigned long) o_tty); if (ltp) kfree_s(ltp, sizeof(struct termios)); - if (o_ltp) - kfree_s(o_ltp, sizeof(struct termios)); - return retval; + if (tp) + kfree_s(tp, sizeof(struct termios)); + free_page((unsigned long) tty); + +fail_no_mem: + retval = -ENOMEM; + goto end_init; + + /* call the tty release_mem routine to clean out this slot */ +release_mem_out: + printk("init_dev: ldisc open failed, clearing slot %d\n", idx); + release_mem(tty, idx); + goto end_init; +} + +/* + * Releases memory associated with a tty structure, and clears out the + * driver table slots. + */ +static void release_mem(struct tty_struct *tty, int idx) +{ + struct tty_struct *o_tty; + struct termios *tp; + + if ((o_tty = tty->link) != NULL) { + o_tty->driver.table[idx] = NULL; + if (o_tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { + tp = o_tty->driver.termios[idx]; + o_tty->driver.termios[idx] = NULL; + kfree_s(tp, sizeof(struct termios)); + } + o_tty->magic = 0; + (*o_tty->driver.refcount)--; + free_page((unsigned long) o_tty); + } + + tty->driver.table[idx] = NULL; + if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { + tp = tty->driver.termios[idx]; + tty->driver.termios[idx] = NULL; + kfree_s(tp, sizeof(struct termios)); + } + tty->magic = 0; + (*tty->driver.refcount)--; + free_page((unsigned long) tty); } /* @@ -1016,8 +1090,7 @@ static void release_dev(struct file * filp) { struct tty_struct *tty, *o_tty; - struct termios *tp, *o_tp, *ltp, *o_ltp; - struct task_struct **p; + int pty_master, tty_closing, o_tty_closing, do_sleep; int idx; tty = (struct tty_struct *)filp->private_data; @@ -1028,10 +1101,11 @@ tty_fasync(filp->f_inode, filp, 0); - tp = tty->termios; - ltp = tty->termios_locked; - idx = MINOR(tty->device) - tty->driver.minor_start; + pty_master = (tty->driver.type == TTY_DRIVER_TYPE_PTY && + tty->driver.subtype == PTY_TYPE_MASTER); + o_tty = tty->link; + #ifdef TTY_PARANOIA_CHECK if (idx < 0 || idx >= tty->driver.num) { printk("release_dev: bad idx when trying to free (%s)\n", @@ -1043,15 +1117,15 @@ idx, kdevname(tty->device)); return; } - if (tp != tty->driver.termios[idx]) { - printk("release_dev: driver.termios[%d] not termios for (" - "%s)\n", + if (tty->termios != tty->driver.termios[idx]) { + printk("release_dev: driver.termios[%d] not termios " + "for (%s)\n", idx, kdevname(tty->device)); return; } - if (ltp != tty->driver.termios_locked[idx]) { - printk("release_dev: driver.termios_locked[%d] not termios_locked for (" - "%s)\n", + if (tty->termios_locked != tty->driver.termios_locked[idx]) { + printk("release_dev: driver.termios_locked[%d] not " + "termios_locked for (%s)\n", idx, kdevname(tty->device)); return; } @@ -1062,10 +1136,6 @@ tty->count); #endif - o_tty = tty->link; - o_tp = (o_tty) ? o_tty->termios : NULL; - o_ltp = (o_tty) ? o_tty->termios_locked : NULL; - #ifdef TTY_PARANOIA_CHECK if (tty->driver.other) { if (o_tty != tty->driver.other->table[idx]) { @@ -1074,34 +1144,90 @@ idx, kdevname(tty->device)); return; } - if (o_tp != tty->driver.other->termios[idx]) { - printk("release_dev: other->termios[%d] not o_termios for (" - "%s)\n", + if (o_tty->termios != tty->driver.other->termios[idx]) { + printk("release_dev: other->termios[%d] not o_termios " + "for (%s)\n", idx, kdevname(tty->device)); return; } - if (o_ltp != tty->driver.other->termios_locked[idx]) { - printk("release_dev: other->termios_locked[%d] not o_termios_locked for (" - "%s)\n", + if (o_tty->termios_locked != + tty->driver.other->termios_locked[idx]) { + printk("release_dev: other->termios_locked[%d] not " + "o_termios_locked for (%s)\n", idx, kdevname(tty->device)); return; } - if (o_tty->link != tty) { printk("release_dev: bad pty pointers\n"); return; } } #endif - + /* + * Sanity check: if tty->count is going to zero, there shouldn't be + * any waiters on tty->read_wait or tty->write_wait. We test the + * wait queues and kick everyone out _before_ actually starting to + * close. This ensures that we won't block while releasing the tty + * structure. + * + * The test for the o_tty closing is necessary, since the master and + * slave sides may close in any order. If the slave side closes out + * first, its count will be one, since the master side holds an open. + * Thus this test wouldn't be triggered at the time the slave closes, + * so we do it now. + * + * Note that it's possible for the tty to be opened again while we're + * flushing out waiters. By recalculating the closing flags before + * each iteration we avoid any problems. + */ + while (1) { + tty_closing = tty->count <= 1; + o_tty_closing = o_tty && + (o_tty->count <= (pty_master ? 1 : 0)); + do_sleep = 0; + + if (tty_closing) { + if (waitqueue_active(&tty->read_wait)) { + wake_up(&tty->read_wait); + do_sleep++; + } + if (waitqueue_active(&tty->write_wait)) { + wake_up(&tty->write_wait); + do_sleep++; + } + } + if (o_tty_closing) { + if (waitqueue_active(&o_tty->read_wait)) { + wake_up(&o_tty->read_wait); + do_sleep++; + } + if (waitqueue_active(&o_tty->write_wait)) { + wake_up(&o_tty->write_wait); + do_sleep++; + } + } + if (!do_sleep) + break; + + printk("release_dev: %s: read/write wait queue active!\n", + tty_name(tty)); + schedule(); + } + + /* + * The closing flags are now consistent with the open counts on + * both sides, and we've completed the last operation that could + * block, so it's safe to proceed with closing. + */ + if (tty->driver.close) tty->driver.close(tty, filp); - if (tty->driver.type == TTY_DRIVER_TYPE_PTY && - tty->driver.subtype == PTY_TYPE_MASTER) { - if (--tty->link->count < 0) { + + if (pty_master) { + if (--o_tty->count < 0) { printk("release_dev: bad pty slave count (%d) for %s\n", - tty->count, tty_name(tty)); - tty->link->count = 0; + o_tty->count, tty_name(o_tty)); + o_tty->count = 0; } } if (--tty->count < 0) { @@ -1109,41 +1235,48 @@ tty->count, tty_name(tty)); tty->count = 0; } - if (tty->count) - return; /* - * We're committed; at this point, we must not block! + * Perform some housekeeping before deciding whether to return. + * + * Set the TTY_CLOSING flag if this was the last open. In the + * case of a pty we may have to wait around for the other side + * to close, and TTY_CLOSING makes sure we can't be reopened. */ - if (o_tty) { - if (o_tty->count) - return; - tty->driver.other->table[idx] = NULL; - tty->driver.other->termios[idx] = NULL; - kfree_s(o_tp, sizeof(struct termios)); + if(tty_closing) + set_bit(TTY_CLOSING, &tty->flags); + if(o_tty_closing) + set_bit(TTY_CLOSING, &o_tty->flags); + + /* + * If _either_ side is closing, make sure there aren't any + * processes that still think tty or o_tty is their controlling + * tty. Also, clear redirect if it points to either tty. + */ + if (tty_closing || o_tty_closing) { + struct task_struct *p; + + for_each_task(p) { + if (p->tty == tty || (o_tty && p->tty == o_tty)) + p->tty = NULL; + } + + if (redirect == tty || (o_tty && redirect == o_tty)) + redirect = NULL; } + + /* check whether both sides are closing ... */ + if (!tty_closing || (o_tty && !o_tty_closing)) + return; + filp->private_data = 0; #ifdef TTY_DEBUG_HANGUP printk("freeing tty structure..."); #endif - tty->flags |= (1 << TTY_CLOSING); - - /* - * Make sure there aren't any processes that still think this - * tty is their controlling tty. - */ - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { - if (*p == 0) - continue; - if ((*p)->tty == tty) - (*p)->tty = NULL; - if (o_tty && (*p)->tty == o_tty) - (*p)->tty = NULL; - } /* - * Shutdown the current line discipline, and reset it to - * N_TTY. + * Shutdown the current line discipline, and reset it to N_TTY. + * N.B. why reset ldisc when we're releasing the memory?? */ if (tty->ldisc.close) (tty->ldisc.close)(tty); @@ -1155,41 +1288,34 @@ o_tty->ldisc = ldiscs[N_TTY]; } - tty->driver.table[idx] = NULL; - if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { - tty->driver.termios[idx] = NULL; - kfree_s(tp, sizeof(struct termios)); - } - if (tty == redirect || o_tty == redirect) - redirect = NULL; /* * Make sure that the tty's task queue isn't activated. If it - * is, take it out of the linked list. + * is, take it out of the linked list. The tqueue isn't used by + * pty's, so skip the test for them. */ - cli(); - if (tty->flip.tqueue.sync) { - struct tq_struct *tq, *prev; + if (tty->driver.type != TTY_DRIVER_TYPE_PTY) { + cli(); + if (tty->flip.tqueue.sync) { + struct tq_struct *tq, *prev; - for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) { - if (tq == &tty->flip.tqueue) { - if (prev) - prev->next = tq->next; - else - tq_timer = tq->next; - break; + for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) { + if (tq == &tty->flip.tqueue) { + if (prev) + prev->next = tq->next; + else + tq_timer = tq->next; + break; + } } } + sti(); } - sti(); - tty->magic = 0; - (*tty->driver.refcount)--; - free_page((unsigned long) tty); - filp->private_data = 0; - if (o_tty) { - o_tty->magic = 0; - (*o_tty->driver.refcount)--; - free_page((unsigned long) o_tty); - } + + /* + * The release_mem function takes care of the details of clearing + * the slots and preserving the termios structure. + */ + release_mem(tty, idx); } /* @@ -1274,11 +1400,6 @@ return 0; } -/* - * Note that releasing a pty master also releases the child, so - * we have to make the redirection checks after that and on both - * sides of a pty. - */ static void tty_release(struct inode * inode, struct file * filp) { release_dev(filp); @@ -1930,6 +2051,9 @@ #endif #ifdef CONFIG_BAYCOM baycom_init(); +#endif +#ifdef CONFIG_SPECIALIX + specialix_init(); #endif pty_init(); vcs_init(); diff -u --recursive --new-file v2.0.30/linux/drivers/char/tty_ioctl.c linux/drivers/char/tty_ioctl.c --- v2.0.30/linux/drivers/char/tty_ioctl.c Sun Sep 17 22:54:08 1995 +++ linux/drivers/char/tty_ioctl.c Wed Aug 13 15:40:05 1997 @@ -376,7 +376,6 @@ { struct tty_struct * real_tty; int retval; - int opt = 0; if (tty->driver.type == TTY_DRIVER_TYPE_PTY && tty->driver.subtype == PTY_TYPE_MASTER) @@ -414,19 +413,19 @@ sizeof (struct termios)); return 0; case TCSETSF: - opt |= TERMIOS_FLUSH; + return set_termios(real_tty, arg, TERMIOS_FLUSH); case TCSETSW: - opt |= TERMIOS_WAIT; + return set_termios(real_tty, arg, TERMIOS_WAIT); case TCSETS: - return set_termios(real_tty, arg, opt); + return set_termios(real_tty, arg, 0); case TCGETA: return get_termio(real_tty,(struct termio *) arg); case TCSETAF: - opt |= TERMIOS_FLUSH; + return set_termios(real_tty, arg, TERMIOS_FLUSH | TERMIOS_TERMIO); case TCSETAW: - opt |= TERMIOS_WAIT; + return set_termios(real_tty, arg, TERMIOS_WAIT | TERMIOS_TERMIO); case TCSETA: - return set_termios(real_tty, arg, opt|TERMIOS_TERMIO); + return set_termios(real_tty, arg, TERMIOS_TERMIO); case TCXONC: retval = tty_check_change(tty); if (retval) diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/Config.in linux/drivers/isdn/Config.in --- v2.0.30/linux/drivers/isdn/Config.in Sun May 19 05:29:29 1996 +++ linux/drivers/isdn/Config.in Mon Aug 4 17:33:59 1997 @@ -11,4 +11,21 @@ bool 'Support audio via ISDN' CONFIG_ISDN_AUDIO dep_tristate 'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN dep_tristate 'PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN -dep_tristate 'Teles/NICCY1016PC/Creatix support' CONFIG_ISDN_DRV_TELES $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 + 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 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 ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2 +fi +if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then + dep_tristate 'Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN +fi +dep_tristate 'AVM-B1 with CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN +if [ "$CONFIG_ISDN_DRV_AVMB1" != "n" ]; then + bool 'Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON +fi diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/Makefile linux/drivers/isdn/Makefile --- v2.0.30/linux/drivers/isdn/Makefile Sun May 19 05:29:29 1996 +++ linux/drivers/isdn/Makefile Mon Aug 4 17:33:59 1997 @@ -1,6 +1,6 @@ SUB_DIRS := MOD_SUB_DIRS := -ALL_SUB_DIRS := icn teles pcbit +ALL_SUB_DIRS := icn pcbit hisax avmb1 L_OBJS := LX_OBJS := @@ -13,8 +13,8 @@ ifeq ($(CONFIG_ISDN),y) L_TARGET := isdn.a - L_OBJS += isdn_net.o isdn_tty.o isdn_cards.o - LX_OBJS += isdn_common.o + L_OBJS += isdn_common.o isdn_net.o isdn_tty.o isdn_cards.o + LX_OBJS += isdn_syms.o ifdef CONFIG_ISDN_PPP L_OBJS += isdn_ppp.o endif @@ -25,8 +25,8 @@ ifeq ($(CONFIG_ISDN),m) M_OBJS += isdn.o O_TARGET += isdn.o - O_OBJS += isdn_net.o isdn_tty.o - OX_OBJS += isdn_common.o + O_OBJS += isdn_common.o isdn_net.o isdn_tty.o + OX_OBJS += isdn_syms.o ifdef CONFIG_ISDN_PPP O_OBJS += isdn_ppp.o endif @@ -36,13 +36,13 @@ endif endif -ifeq ($(CONFIG_ISDN_DRV_TELES),y) - L_OBJS += teles/teles.o - SUB_DIRS += teles - MOD_SUB_DIRS += teles +ifeq ($(CONFIG_ISDN_DRV_HISAX),y) + L_OBJS += hisax/hisax.o + SUB_DIRS += hisax + MOD_SUB_DIRS += hisax else - ifeq ($(CONFIG_ISDN_DRV_TELES),m) - MOD_SUB_DIRS += teles + ifeq ($(CONFIG_ISDN_DRV_HISAX),m) + MOD_SUB_DIRS += hisax endif endif @@ -63,6 +63,36 @@ else ifeq ($(CONFIG_ISDN_DRV_PCBIT),m) MOD_SUB_DIRS += pcbit + endif +endif + +ifeq ($(CONFIG_ISDN_DRV_SC),y) + L_OBJS += sc/sc.o + SUB_DIRS += sc + MOD_SUB_DIRS += sc +else + ifeq ($(CONFIG_ISDN_DRV_SC),m) + MOD_SUB_DIRS += sc + endif +endif + +ifeq ($(CONFIG_ISDN_DRV_AVMB1),y) + L_OBJS += avmb1/avmb1.o + SUB_DIRS += avmb1 + MOD_SUB_DIRS += avmb1 +else + ifeq ($(CONFIG_ISDN_DRV_AVMB1),m) + MOD_SUB_DIRS += avmb1 + endif +endif + +ifeq ($(CONFIG_ISDN_DRV_LOOP),y) + L_OBJS += isdnloop/isdnloop.o + SUB_DIRS += isdnloop + MOD_SUB_DIRS += isdnloop +else + ifeq ($(CONFIG_ISDN_DRV_LOOP),m) + MOD_SUB_DIRS += isdnloop endif endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/Makefile linux/drivers/isdn/avmb1/Makefile --- v2.0.30/linux/drivers/isdn/avmb1/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/avmb1/Makefile Mon Aug 4 17:33:59 1997 @@ -0,0 +1,77 @@ +# +# $Id: Makefile,v 1.4 1997/03/30 17:10:40 calle Exp $ +# +# Makefile for the CAPI and AVM-B1 device drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now inherited from the +# parent makes.. +# +# $Log: Makefile,v $ +# Revision 1.4 1997/03/30 17:10:40 calle +# added support for AVM-B1-PCI card. +# +# Revision 1.3 1997/03/22 02:00:57 fritz +# -Reworked toplevel Makefile. From now on, no different Makefiles +# for standalone- and in-kernel-compilation are needed any more. +# -Added local Rules.make for above reason. +# -Experimental changes in teles3.c for enhanced IRQ-checking with +# 2.1.X and SMP kernels. +# -Removed diffstd-script, same functionality is in stddiff -r. +# -Enhanced scripts std2kern and stddiff. +# +# Revision 1.1 1997/03/05 21:26:14 fritz +# Renamed, according naming conventions in CVS tree. +# +# Revision 1.1 1997/03/04 21:50:26 calle +# Frirst version in isdn4linux +# +# Revision 2.2 1997/02/12 09:31:39 calle +# +# Revision 1.1 1997/01/31 10:32:20 calle +# Initial revision +# +# + +# +# Objects that don't export a symtab +# +L_OBJS := # used as component of an L_TARGET +O_OBJS := # used as component of an O_TARGET +M_OBJS := # used as module +# +# Objects that do export a symtab +# +LX_OBJS := # used as component of an L_TARGET +OX_OBJS := # used as component of an O_TARGET +MX_OBJS := # used as module +# +# Targets, created by linking others +# +O_TARGET := # used for .o targets (from O and OX objects) +L_TARGET := # used for .a targets (from L and LX objects) + +ifeq ($(CONFIG_ISDN_DRV_AVMB1),y) + O_TARGET += avmb1.o + O_OBJS += capi.o b1lli.o + OX_OBJS += capiutil.o b1capi.o capidrv.o + ifdef CONFIG_PCI + OX_OBJS += b1pci.o + endif +else + ifeq ($(CONFIG_ISDN_DRV_AVMB1),m) + O_TARGET += kernelcapi.o + O_OBJS += b1lli.o + OX_OBJS += b1capi.o + M_OBJS += capi.o kernelcapi.o + MX_OBJS += capiutil.o capidrv.o + ifdef CONFIG_PCI + MX_OBJS += b1pci.o + endif + endif +endif + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/b1capi.c linux/drivers/isdn/avmb1/b1capi.c --- v2.0.30/linux/drivers/isdn/avmb1/b1capi.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/avmb1/b1capi.c Mon Aug 4 17:33:59 1997 @@ -0,0 +1,946 @@ +/* + * $Id: b1capi.c,v 1.4 1997/05/27 15:17:45 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 1997/05/27 15:17:45 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.3 1997/05/18 09:24:09 calle + * added verbose disconnect reason reporting to avmb1. + * some fixes in capi20 interface. + * changed info messages for B1-PCI + * + * Revision 1.2 1997/03/05 21:20:41 fritz + * Removed include of config.h (mkdep stated this is unneded). + * + * Revision 1.1 1997/03/04 21:50:27 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "compat.h" +#include "capicmd.h" +#include "capiutil.h" + +static char *revision = "$Revision: 1.4 $"; + +/* ------------------------------------------------------------- */ + +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(loaddebug, "0-1i"); +#endif + +/* ------------------------------------------------------------- */ + +struct msgidqueue { + struct msgidqueue *next; + __u16 msgid; +}; + +typedef struct avmb1_ncci { + struct avmb1_ncci *next; + __u16 applid; + __u32 ncci; + __u32 winsize; + struct msgidqueue *msgidqueue; + struct msgidqueue *msgidlast; + struct msgidqueue *msgidfree; + struct msgidqueue msgidpool[CAPI_MAXDATAWINDOW]; +} avmb1_ncci; + +typedef struct avmb1_appl { + __u16 applid; + capi_register_params rparam; + int releasing; + __u32 param; + void (*signal) (__u16 applid, __u32 param); + struct sk_buff_head recv_queue; + struct avmb1_ncci *nccilist; +} avmb1_appl; + +/* ------------------------------------------------------------- */ + +static struct capi_version driver_version = {2, 0, 0, 9}; +static char driver_serial[CAPI_SERIAL_LEN] = "4711"; +static char capi_manufakturer[64] = "AVM Berlin"; + +#define APPL(a) (&applications[(a)-1]) +#define VALID_APPLID(a) ((a) && (a) <= CAPI_MAXAPPL && APPL(a)->applid == a) +#define APPL_IS_FREE(a) (APPL(a)->applid == 0) +#define APPL_MARK_FREE(a) do{ APPL(a)->applid=0; MOD_DEC_USE_COUNT; }while(0); +#define APPL_MARK_USED(a) do{ APPL(a)->applid=(a); MOD_INC_USE_COUNT; }while(0); + +#define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f) + +#define VALID_CARD(c) ((c) > 0 && (c) <= ncards) +#define CARD(c) (&cards[(c)-1]) +#define CARDNR(cp) ((cards-(cp))+1) + +static avmb1_appl applications[CAPI_MAXAPPL]; +static avmb1_card cards[CAPI_MAXCONTR]; +static int ncards = 0; +static struct sk_buff_head recv_queue; +static struct capi_interface_user *capi_users = 0; +static long notify_up_set = 0; +static long notify_down_set = 0; + +static struct tq_struct tq_state_notify; +static struct tq_struct tq_recv_notify; + +/* -------- util functions ------------------------------------ */ + +static inline int capi_cmd_valid(__u8 cmd) +{ + switch (cmd) { + case CAPI_ALERT: + case CAPI_CONNECT: + case CAPI_CONNECT_ACTIVE: + case CAPI_CONNECT_B3_ACTIVE: + case CAPI_CONNECT_B3: + case CAPI_CONNECT_B3_T90_ACTIVE: + case CAPI_DATA_B3: + case CAPI_DISCONNECT_B3: + case CAPI_DISCONNECT: + case CAPI_FACILITY: + case CAPI_INFO: + case CAPI_LISTEN: + case CAPI_MANUFACTURER: + case CAPI_RESET_B3: + case CAPI_SELECT_B_PROTOCOL: + return 1; + } + return 0; +} + +static inline int capi_subcmd_valid(__u8 subcmd) +{ + switch (subcmd) { + case CAPI_REQ: + case CAPI_CONF: + case CAPI_IND: + case CAPI_RESP: + return 1; + } + return 0; +} + +/* -------- NCCI Handling ------------------------------------- */ + +static inline void mq_init(avmb1_ncci * np) +{ + int i; + np->msgidqueue = 0; + np->msgidlast = 0; + memset(np->msgidpool, 0, sizeof(np->msgidpool)); + np->msgidfree = &np->msgidpool[0]; + for (i = 1; i < np->winsize; i++) { + np->msgidpool[i].next = np->msgidfree; + np->msgidfree = &np->msgidpool[i]; + } +} + +static inline int mq_enqueue(avmb1_ncci * np, __u16 msgid) +{ + struct msgidqueue *mq; + if ((mq = np->msgidfree) == 0) + return 0; + np->msgidfree = mq->next; + mq->msgid = msgid; + mq->next = 0; + if (np->msgidlast) + np->msgidlast->next = mq; + np->msgidlast = mq; + if (!np->msgidqueue) + np->msgidqueue = mq; + return 1; +} + +static inline int mq_dequeue(avmb1_ncci * np, __u16 msgid) +{ + struct msgidqueue **pp; + for (pp = &np->msgidqueue; *pp; pp = &(*pp)->next) { + if ((*pp)->msgid == msgid) { + struct msgidqueue *mq = *pp; + *pp = mq->next; + if (mq == np->msgidlast) + np->msgidlast = 0; + mq->next = np->msgidfree; + np->msgidfree = mq; + return 1; + } + } + return 0; +} + +void avmb1_handle_new_ncci(avmb1_card * card, + __u16 appl, __u32 ncci, __u32 winsize) +{ + avmb1_ncci *np; + if (!VALID_APPLID(appl)) { + printk(KERN_ERR "avmb1_handle_new_ncci: illegal appl %d\n", appl); + return; + } + if ((np = (avmb1_ncci *) kmalloc(sizeof(avmb1_ncci), GFP_ATOMIC)) == 0) { + printk(KERN_ERR "avmb1_handle_new_ncci: alloc failed ncci 0x%x\n", ncci); + return; + } + if (winsize > CAPI_MAXDATAWINDOW) { + printk(KERN_ERR "avmb1_handle_new_ncci: winsize %d too big, set to %d\n", + winsize, CAPI_MAXDATAWINDOW); + winsize = CAPI_MAXDATAWINDOW; + } + np->applid = appl; + np->ncci = ncci; + np->winsize = winsize; + mq_init(np); + np->next = APPL(appl)->nccilist; + APPL(appl)->nccilist = np; + printk(KERN_INFO "b1capi: appl %d ncci 0x%x up\n", appl, ncci); + +} + +void avmb1_handle_free_ncci(avmb1_card * card, + __u16 appl, __u32 ncci) +{ + if (!VALID_APPLID(appl)) { + printk(KERN_ERR "avmb1_handle_free_ncci: illegal appl %d\n", appl); + return; + } + if (ncci != 0xffffffff) { + avmb1_ncci **pp; + for (pp = &APPL(appl)->nccilist; *pp; pp = &(*pp)->next) { + if ((*pp)->ncci == ncci) { + avmb1_ncci *np = *pp; + *pp = np->next; + kfree(np); + printk(KERN_INFO "b1capi: appl %d ncci 0x%x down\n", appl, ncci); + return; + } + } + printk(KERN_ERR "avmb1_handle_free_ncci: ncci 0x%x not found\n", ncci); + } else { + avmb1_ncci **pp, **nextpp; + for (pp = &APPL(appl)->nccilist; *pp; pp = nextpp) { + if (NCCI2CTRL((*pp)->ncci) == card->cnr) { + avmb1_ncci *np = *pp; + *pp = np->next; + printk(KERN_INFO "b1capi: appl %d ncci 0x%x down!\n", appl, np->ncci); + kfree(np); + nextpp = pp; + } else { + nextpp = &(*pp)->next; + } + } + APPL(appl)->releasing--; + if (APPL(appl)->releasing == 0) { + APPL(appl)->signal = 0; + APPL_MARK_FREE(appl); + printk(KERN_INFO "b1capi: appl %d down\n", appl); + } + } +} + +static avmb1_ncci *find_ncci(avmb1_appl * app, __u32 ncci) +{ + avmb1_ncci *np; + for (np = app->nccilist; np; np = np->next) { + if (np->ncci == ncci) + return np; + } + return 0; +} + +/* -------- Receiver ------------------------------------------ */ + + +static void recv_handler(void *dummy) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&recv_queue)) != 0) { + __u16 appl = CAPIMSG_APPID(skb->data); + struct avmb1_ncci *np; + if (!VALID_APPLID(appl)) { + printk(KERN_ERR "b1capi: recv_handler: applid %d ? (%s)\n", + appl, capi_message2str(skb->data)); + kfree_skb(skb, FREE_READ); + continue; + } + if (APPL(appl)->signal == 0) { + printk(KERN_ERR "b1capi: recv_handler: applid %d has no signal function\n", + appl); + kfree_skb(skb, FREE_READ); + continue; + } + if ( CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3 + && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF + && (np = find_ncci(APPL(appl), CAPIMSG_NCCI(skb->data))) != 0 + && mq_dequeue(np, CAPIMSG_MSGID(skb->data)) == 0) { + printk(KERN_ERR "b1capi: msgid %hu ncci 0x%x not on queue\n", + CAPIMSG_MSGID(skb->data), np->ncci); + } + skb_queue_tail(&APPL(appl)->recv_queue, skb); + (APPL(appl)->signal) (APPL(appl)->applid, APPL(appl)->param); + } +} + + +void avmb1_handle_capimsg(avmb1_card * card, __u16 appl, struct sk_buff *skb) +{ + if (card->cardstate != CARD_RUNNING) { + printk(KERN_INFO "b1capi: controller %d not active, got: %s", + card->cnr, capi_message2str(skb->data)); + goto error; + return; + } + skb_queue_tail(&recv_queue, skb); + queue_task(&tq_recv_notify, &tq_immediate); + mark_bh(IMMEDIATE_BH); + return; + + error: + kfree_skb(skb, FREE_READ); +} + +void avmb1_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmb1_card *card; + + card = (avmb1_card *) devptr; + + if (!card) { + printk(KERN_WARNING "avmb1_interrupt: wrong device\n"); + return; + } + if (card->interrupt) { + printk(KERN_ERR "avmb1_interrupt: reentering interrupt hander\n"); + return; + } + + card->interrupt = 1; + + B1_handle_interrupt(card); + + card->interrupt = 0; +} + +/* -------- Notifier ------------------------------------------ */ + +static void notify_up(__u16 contr) +{ + struct capi_interface_user *p; + + for (p = capi_users; p; p = p->next) { + if (p->callback) + (*p->callback) (KCI_CONTRUP, contr, + (capi_profile *) + CARD(contr)->version[VER_PROFILE]); + } +} + +static void notify_down(__u16 contr) +{ + struct capi_interface_user *p; + for (p = capi_users; p; p = p->next) { + if (p->callback) + (*p->callback) (KCI_CONTRDOWN, contr, 0); + } +} + +static void notify_handler(void *dummy) +{ + __u16 contr; + + for (contr=1; VALID_CARD(contr); contr++) + if (test_and_clear_bit(contr, ¬ify_up_set)) + notify_up(contr); + for (contr=1; VALID_CARD(contr); contr++) + if (test_and_clear_bit(contr, ¬ify_down_set)) + notify_down(contr); +} + +/* -------- card ready callback ------------------------------- */ + +void avmb1_card_ready(avmb1_card * card) +{ + __u16 appl; + + 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->cardstate = CARD_RUNNING; + + + for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { + if (VALID_APPLID(appl) && !APPL(appl)->releasing) { + B1_send_register(card->port, appl, + 1024 * (APPL(appl)->rparam.level3cnt+1), + APPL(appl)->rparam.level3cnt, + APPL(appl)->rparam.datablkcnt, + APPL(appl)->rparam.datablklen); + } + } + + set_bit(CARDNR(card), ¬ify_up_set); + queue_task(&tq_state_notify, &tq_scheduler); +} + +/* ------------------------------------------------------------- */ + +int avmb1_addcard(int port, int irq) +{ + struct avmb1_card *card; + int irqval; + + + card = &cards[ncards]; + memset(card, 0, sizeof(avmb1_card)); + sprintf(card->name, "avmb1-%d", ncards + 1); + + request_region(port, AVMB1_PORTLEN, card->name); + + 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; + } + ncards++; + card->cnr = ncards; + card->port = port; + card->irq = irq; + card->cardstate = CARD_DETECTED; + return 0; +} + +int avmb1_probecard(int port, int irq) +{ + 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 ((rc = B1_detect(port)) != 0) { + printk(KERN_NOTICE "b1capi: NO card at 0x%x (%d)\n", port, rc); + return -EIO; + } + B1_reset(port); + printk(KERN_NOTICE "b1capi: AVM-B1-Controller detected at 0x%x\n", port); + + return 0; +} + +/* ------------------------------------------------------------- */ +/* -------- CAPI2.0 Interface ---------------------------------- */ +/* ------------------------------------------------------------- */ + +static int capi_installed(void) +{ + int i; + for (i = 0; i < ncards; i++) { + if (cards[i].cardstate == CARD_RUNNING) + return 1; + } + return 0; +} + +static __u16 capi_register(capi_register_params * rparam, __u16 * applidp) +{ + int i; + int appl; + + if (rparam->datablklen < 128) + return CAPI_LOGBLKSIZETOSMALL; + + for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { + if (APPL_IS_FREE(appl)) + break; + } + if (appl > CAPI_MAXAPPL) + return CAPI_TOOMANYAPPLS; + + APPL_MARK_USED(appl); + skb_queue_head_init(&APPL(appl)->recv_queue); + + memcpy(&APPL(appl)->rparam, rparam, sizeof(capi_register_params)); + + for (i = 0; i < ncards; i++) { + if (cards[i].cardstate != CARD_RUNNING) + continue; + 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); + } + *applidp = appl; + printk(KERN_INFO "b1capi: appl %d up\n", appl); + + return CAPI_NOERROR; +} + +static __u16 capi_release(__u16 applid) +{ + 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++) { + if (cards[i].cardstate != CARD_RUNNING) + continue; + APPL(applid)->releasing++; + B1_send_release(cards[i].port, applid); + } + if (APPL(applid)->releasing == 0) { + APPL(applid)->signal = 0; + APPL_MARK_FREE(applid); + printk(KERN_INFO "b1capi: appl %d down\n", applid); + } + return CAPI_NOERROR; +} + +static __u16 capi_put_message(__u16 applid, struct sk_buff *skb) +{ + avmb1_ncci *np; + int contr; + if (ncards == 0) + return CAPI_REGNOTINSTALLED; + if (!VALID_APPLID(applid)) + return CAPI_ILLAPPNR; + if (skb->len < 12 + || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data)) + || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data))) + return CAPI_ILLCMDORSUBCMDORMSGTOSMALL; + contr = CAPIMSG_CONTROLLER(skb->data); + if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) { + contr = 1; + if (CARD(contr)->cardstate != CARD_RUNNING) + return CAPI_REGNOTINSTALLED; + } + if (CARD(contr)->blocked) + return CAPI_SENDQUEUEFULL; + + if ( CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3 + && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_REQ + && (np = find_ncci(APPL(applid), CAPIMSG_NCCI(skb->data))) != 0 + && mq_enqueue(np, CAPIMSG_MSGID(skb->data)) == 0) + return CAPI_SENDQUEUEFULL; + + B1_send_message(CARD(contr)->port, skb); + return CAPI_NOERROR; +} + +static __u16 capi_get_message(__u16 applid, struct sk_buff **msgp) +{ + struct sk_buff *skb; + + if (!VALID_APPLID(applid)) + return CAPI_ILLAPPNR; + if ((skb = skb_dequeue(&APPL(applid)->recv_queue)) == 0) + return CAPI_RECEIVEQUEUEEMPTY; + *msgp = skb; + return CAPI_NOERROR; +} + +static __u16 capi_set_signal(__u16 applid, + void (*signal) (__u16 applid, __u32 param), + __u32 param) +{ + if (!VALID_APPLID(applid)) + return CAPI_ILLAPPNR; + APPL(applid)->signal = signal; + APPL(applid)->param = param; + return CAPI_NOERROR; +} + +static __u16 capi_get_manufacturer(__u16 contr, __u8 buf[CAPI_MANUFACTURER_LEN]) +{ + if (contr == 0) { + strncpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN); + return CAPI_NOERROR; + } + if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) + return 0x2002; + + strncpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN); + return CAPI_NOERROR; +} + +static __u16 capi_get_version(__u16 contr, struct capi_version *verp) +{ + if (contr == 0) { + *verp = driver_version; + return CAPI_NOERROR; + } + if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) + return 0x2002; + + memcpy((void *) verp, CARD(contr)->version[VER_SERIAL], + sizeof(capi_version)); + return CAPI_NOERROR; +} + +static __u16 capi_get_serial(__u16 contr, __u8 serial[CAPI_SERIAL_LEN]) +{ + if (contr == 0) { + strncpy(serial, driver_serial, 8); + return CAPI_NOERROR; + } + if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) + return 0x2002; + + memcpy((void *) serial, CARD(contr)->version[VER_SERIAL], + CAPI_SERIAL_LEN); + serial[CAPI_SERIAL_LEN - 1] = 0; + return CAPI_NOERROR; +} + +static __u16 capi_get_profile(__u16 contr, struct capi_profile *profp) +{ + if (contr == 0) { + profp->ncontroller = ncards; + return CAPI_NOERROR; + } + if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) + return 0x2002; + + memcpy((void *) profp, CARD(contr)->version[VER_PROFILE], + sizeof(struct capi_profile)); + return CAPI_NOERROR; +} + +static int capi_manufacturer(unsigned int cmd, void *data) +{ + unsigned long flags; + avmb1_loaddef ldef; + avmb1_carddef cdef; + avmb1_resetdef rdef; + 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; + + if ((rc = avmb1_probecard(cdef.port, cdef.irq)) != 0) + return rc; + + return avmb1_addcard(cdef.port, cdef.irq); + + case AVMB1_LOAD: + + 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); + return -EINVAL; + } + + card = CARD(ldef.contr); + save_flags(flags); + cli(); + if (card->cardstate != CARD_DETECTED) { + restore_flags(flags); + if (loaddebug) + printk(KERN_DEBUG "b1capi: load: contr=%d not in detect state\n", ldef.contr); + return -EBUSY; + } + card->cardstate = CARD_LOADING; + restore_flags(flags); + + if (loaddebug) { + printk(KERN_DEBUG "b1capi: load: reseting contr %d\n", + ldef.contr); + } + + B1_reset(card->port); + 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 (loaddebug) { + printk(KERN_DEBUG "b1capi: load: ready contr %d: checking\n", + ldef.contr); + } + + if (!B1_loaded(card->port)) { + card->cardstate = CARD_DETECTED; + printk(KERN_ERR "b1capi: failed to load t4file.\n"); + return -EIO; + } + /* + * enable interrupt + */ + + card->cardstate = CARD_INITSTATE; + save_flags(flags); + cli(); + B1_assign_irq(card->port, card->irq); + B1_enable_irq(card->port); + restore_flags(flags); + + if (loaddebug) { + printk(KERN_DEBUG "b1capi: load: irq enabled contr %d\n", + ldef.contr); + } + + /* + * init card + */ + B1_send_init(card->port, AVM_NAPPS, AVM_NNCCI, card->cnr - 1); + + if (loaddebug) { + printk(KERN_DEBUG "b1capi: load: waiting for init reply contr %d\n", + ldef.contr); + } + + while (card->cardstate != CARD_RUNNING) { + + current->timeout = jiffies + HZ / 10; /* 0.1 sec */ + current->state = TASK_INTERRUPTIBLE; + schedule(); + + if (current->signal & ~current->blocked) + 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; + + card = CARD(rdef.contr); + + if (card->cardstate == CARD_RUNNING) + return -EBUSY; + + B1_reset(card->port); + B1_reset(card->port); + + card->cardstate = CARD_DETECTED; + return 0; + } + return -EINVAL; +} + +struct capi_interface avmb1_interface = +{ + capi_installed, + capi_register, + capi_release, + capi_put_message, + capi_get_message, + capi_set_signal, + capi_get_manufacturer, + capi_get_version, + capi_get_serial, + capi_get_profile, + capi_manufacturer +}; + +/* ------------------------------------------------------------- */ +/* -------- Exported Functions --------------------------------- */ +/* ------------------------------------------------------------- */ + +struct capi_interface *attach_capi_interface(struct capi_interface_user *userp) +{ + struct capi_interface_user *p; + + for (p = capi_users; p; p = p->next) { + if (p == userp) { + printk(KERN_ERR "b1capi: double attach from %s\n", + userp->name); + return 0; + } + } + userp->next = capi_users; + capi_users = userp; + MOD_INC_USE_COUNT; + + return &avmb1_interface; +} + +int detach_capi_interface(struct capi_interface_user *userp) +{ + struct capi_interface_user **pp; + + for (pp = &capi_users; *pp; pp = &(*pp)->next) { + if (*pp == userp) { + *pp = userp->next; + userp->next = 0; + MOD_DEC_USE_COUNT; + return 0; + } + } + printk(KERN_ERR "b1capi: double detach from %s\n", userp->name); + return -1; +} + +/* ------------------------------------------------------------- */ +/* -------- Init & Cleanup ------------------------------------- */ +/* ------------------------------------------------------------- */ + +#ifdef HAS_NEW_SYMTAB +EXPORT_SYMBOL(attach_capi_interface); +EXPORT_SYMBOL(detach_capi_interface); +EXPORT_SYMBOL(avmb1_addcard); +EXPORT_SYMBOL(avmb1_probecard); +#else +static struct symbol_table capidev_syms = +{ +#include + X(attach_capi_interface), + X(detach_capi_interface), + X(avmb1_addcard), + X(avmb1_probecard), +#include +}; +#endif + + +/* + * init / exit functions + */ + +#ifdef MODULE +#define avmb1_init init_module +#endif + +int avmb1_init(void) +{ + char *p; + char rev[10]; + + +#ifndef HAS_NEW_SYMTAB + /* No symbols to export, hide all symbols */ + register_symtab(&capidev_syms); +#endif + skb_queue_head_init(&recv_queue); + /* init_bh(CAPI_BH, do_capi_bh); */ + + tq_state_notify.routine = notify_handler; + tq_state_notify.data = 0; + + tq_recv_notify.routine = recv_handler; + tq_recv_notify.data = 0; + + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else + 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); + } +#else + printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: started\n", rev); +#endif + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + char rev[10]; + char *p; + int i; + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else { + 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); + + } + printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: unloaded\n", rev); +} +#endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/b1lli.c linux/drivers/isdn/avmb1/b1lli.c --- v2.0.30/linux/drivers/isdn/avmb1/b1lli.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/avmb1/b1lli.c Mon Aug 4 17:33:59 1997 @@ -0,0 +1,594 @@ +/* + * $Id: b1lli.c,v 1.1 1997/03/04 21:50:28 calle 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 1997/03/04 21:50:28 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" +#include "capicmd.h" +#include "capiutil.h" + +/* + * LLI Messages to the ISDN-ControllerISDN Controller + */ + +#define SEND_POLL 0x72 /* + * after load <- RECEIVE_POLL + */ +#define SEND_INIT 0x11 /* + * first message <- RECEIVE_INIT + * int32 NumApplications int32 + * NumNCCIs int32 BoardNumber + */ +#define SEND_REGISTER 0x12 /* + * register an application int32 + * ApplIDId int32 NumMessages + * int32 NumB3Connections int32 + * NumB3Blocks int32 B3Size + * + * AnzB3Connection != 0 && + * AnzB3Blocks >= 1 && B3Size >= 1 + */ +#define SEND_RELEASE 0x14 /* + * deregister an application int32 + * ApplID + */ +#define SEND_MESSAGE 0x15 /* + * send capi-message int32 length + * capi-data ... + */ +#define SEND_DATA_B3_REQ 0x13 /* + * send capi-data-message int32 + * MsgLength capi-data ... int32 + * B3Length data .... + */ + +/* + * LLI Messages from the ISDN-ControllerISDN Controller + */ + +#define RECEIVE_POLL 0x32 /* + * <- after SEND_POLL + */ +#define RECEIVE_INIT 0x27 /* + * <- after SEND_INIT int32 length + * byte total length b1struct board + * driver revision b1struct card + * type b1struct reserved b1struct + * serial number b1struct driver + * capability b1struct d-channel + * protocol b1struct CAPI-2.0 + * profile b1struct capi version + */ +#define RECEIVE_MESSAGE 0x21 /* + * <- after SEND_MESSAGE int32 + * AppllID int32 Length capi-data + * .... + */ +#define RECEIVE_DATA_B3_IND 0x22 /* + * received data int32 AppllID + * int32 Length capi-data ... + * int32 B3Length data ... + */ +#define RECEIVE_START 0x23 /* + * Handshake + */ +#define RECEIVE_STOP 0x24 /* + * Handshake + */ +#define RECEIVE_NEW_NCCI 0x25 /* + * int32 AppllID int32 NCCI int32 + * WindowSize + */ +#define RECEIVE_FREE_NCCI 0x26 /* + * int32 AppllID int32 NCCI + */ +#define RECEIVE_RELEASE 0x26 /* + * int32 AppllID int32 0xffffffff + */ + +/* + * port offsets + */ + +#define B1_READ 0x00 +#define B1_WRITE 0x01 +#define B1_INSTAT 0x02 +#define B1_OUTSTAT 0x03 +#define B1_RESET 0x10 +#define B1_ANALYSE 0x04 + + + +static inline unsigned char b1outp(unsigned short base, + unsigned short offset, + unsigned char value) +{ + outb(value, base + offset); + return inb(base + B1_ANALYSE); +} + +static int irq_table[16] = +{0, + 0, + 0, + 192, /* irq 3 */ + 32, /* irq 4 */ + 160, /* irq 5 */ + 96, /* irq 6 */ + 224, /* irq 7 */ + 0, + 64, /* irq 9 */ + 80, /* irq 10 */ + 208, /* irq 11 */ + 48, /* irq 12 */ + 0, + 0, + 112, /* irq 15 */ +}; + +int B1_valid_irq(unsigned irq) +{ + return irq_table[irq] != 0; +} + +unsigned char B1_assign_irq(unsigned short base, unsigned irq) +{ + return b1outp(base, B1_RESET, irq_table[irq]); +} + +unsigned char B1_enable_irq(unsigned short base) +{ + return b1outp(base, B1_INSTAT, 0x02); +} + +unsigned char B1_disable_irq(unsigned short base) +{ + return b1outp(base, B1_INSTAT, 0x00); +} + +void B1_reset(unsigned short base) +{ + b1outp(base, B1_RESET, 0); + udelay(55 * 2 * 1000); /* 2 TIC's */ + + b1outp(base, B1_RESET, 1); + udelay(55 * 2 * 1000); /* 2 TIC's */ + + b1outp(base, B1_RESET, 0); + udelay(55 * 2 * 1000); /* 2 TIC's */ +} + +int B1_detect(unsigned short base) +{ + /* + * 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); + if ((inb(base + B1_INSTAT) & 0xfe) != 0x2 + || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2) + return 2; + + /* + * Statusregister 0000 000x + */ + b1outp(base, B1_INSTAT, 0x0); /* disable irq */ + b1outp(base, B1_OUTSTAT, 0x0); + if ((inb(base + B1_INSTAT) & 0xfe) + || (inb(base + B1_OUTSTAT) & 0xfe)) + return 3; + + return 0; +} + +static inline int B1_rx_full(unsigned short base) +{ + return inb(base + B1_INSTAT) & 0x1; +} + +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) +{ + while (!B1_tx_empty(base)); + b1outp(base, B1_WRITE, val); +} + +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); +} + +static inline unsigned int B1_get_slice(unsigned short base, + unsigned char *dp) +{ + unsigned int len, i; + + len = i = B1_get_word(base); + while (i-- > 0) + *dp++ = B1_get_byte(base); + return len; +} + +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++); +} + +extern int loaddebug; + +int B1_load_t4file(unsigned short base, avmb1_t4file * t4file) +{ + /* + * Data is in user space !!! + */ + unsigned char buf[256]; + unsigned char *dp; + int i, left, retval; + + + dp = t4file->data; + left = t4file->len; + while (left > sizeof(buf)) { + retval = copy_from_user(buf, dp, sizeof(buf)); + if (retval) + return -EFAULT; + if (loaddebug) + printk(KERN_DEBUG "b1capi: loading: %d bytes ..", sizeof(buf)); + for (i = 0; i < sizeof(buf); i++) + 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: loading: %d bytes ..", left); + for (i = 0; i < left; i++) + B1_put_byte(base, buf[i]); + if (loaddebug) + printk("ok\n"); + } + return 0; +} + +int B1_loaded(unsigned short base) +{ + int i; + unsigned char ans; + + if (loaddebug) + printk(KERN_DEBUG "b1capi: loaded: wait 1 ..\n"); + for (i = jiffies + 10 * HZ; i > jiffies;) { + if (B1_tx_empty(base)) + break; + } + if (!B1_tx_empty(base)) { + printk(KERN_ERR "b1lli: B1_loaded: timeout tx\n"); + return 0; + } + B1_put_byte(base, SEND_POLL); + printk(KERN_DEBUG "b1capi: loaded: wait 2 ..\n"); + for (i = jiffies + 10 * HZ; i > jiffies;) { + if (B1_rx_full(base)) { + if ((ans = B1_get_byte(base)) == RECEIVE_POLL) { + if (loaddebug) + printk(KERN_DEBUG "b1capi: loaded: ok\n"); + return 1; + } + printk(KERN_ERR "b1lli: B1_loaded: got 0x%x ???\n", ans); + return 0; + } + } + printk(KERN_ERR "b1lli: B1_loaded: timeout rx\n"); + return 0; +} + +/* + * ------------------------------------------------------------------- + */ +static inline void parse_version(avmb1_card * card) +{ + int i, j; + for (j = 0; j < AVM_MAXVERSION; j++) + card->version[j] = "\0\0" + 1; + for (i = 0, j = 0; + j < AVM_MAXVERSION && i < card->versionlen; + j++, i += card->versionbuf[i] + 1) + card->version[j] = &card->versionbuf[i + 1]; +} +/* + * ------------------------------------------------------------------- + */ + +void B1_send_init(unsigned short port, + unsigned int napps, unsigned int nncci, unsigned int cardnr) +{ + unsigned long flags; + + save_flags(flags); + cli(); + B1_put_byte(port, SEND_INIT); + B1_put_word(port, napps); + B1_put_word(port, nncci); + B1_put_word(port, cardnr); + restore_flags(flags); +} + +void B1_send_register(unsigned short port, + __u16 appid, __u32 nmsg, + __u32 nb3conn, __u32 nb3blocks, __u32 b3bsize) +{ + unsigned long flags; + + save_flags(flags); + cli(); + B1_put_byte(port, SEND_REGISTER); + B1_put_word(port, appid); + B1_put_word(port, nmsg); + B1_put_word(port, nb3conn); + B1_put_word(port, nb3blocks); + B1_put_word(port, b3bsize); + restore_flags(flags); +} + +void B1_send_release(unsigned short port, + __u16 appid) +{ + unsigned long flags; + + save_flags(flags); + cli(); + B1_put_byte(port, SEND_RELEASE); + B1_put_word(port, appid); + restore_flags(flags); +} + +extern int showcapimsgs; + +void B1_send_message(unsigned short port, struct sk_buff *skb) +{ + unsigned long flags; + __u16 len = CAPIMSG_LEN(skb->data); + __u8 cmd = CAPIMSG_COMMAND(skb->data); + __u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data); + __u32 contr = CAPIMSG_CONTROL(skb->data); + + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + __u16 dlen = CAPIMSG_DATALEN(skb->data); + + if (showcapimsgs > 2) { + if (showcapimsgs & 1) { + printk(KERN_DEBUG "b1lli: Put [0x%lx] id#%d %s len=%u\n", + (unsigned long) contr, + CAPIMSG_APPID(skb->data), + capi_cmd2str(cmd, subcmd), len); + } else { + printk(KERN_DEBUG "b1lli: Put %s\n", capi_message2str(skb->data)); + } + + } + save_flags(flags); + cli(); + B1_put_byte(port, SEND_DATA_B3_REQ); + B1_put_slice(port, skb->data, len); + B1_put_slice(port, skb->data + len, dlen); + restore_flags(flags); + } else { + if (showcapimsgs) { + + if (showcapimsgs & 1) { + printk(KERN_DEBUG "b1lli: Put [0x%lx] id#%d %s len=%u\n", + (unsigned long) contr, + CAPIMSG_APPID(skb->data), + capi_cmd2str(cmd, subcmd), len); + } else { + printk(KERN_DEBUG "b1lli: Put %s\n", capi_message2str(skb->data)); + } + } + save_flags(flags); + cli(); + B1_put_byte(port, SEND_MESSAGE); + B1_put_slice(port, skb->data, len); + restore_flags(flags); + } + dev_kfree_skb(skb, FREE_WRITE); +} + +/* + * ------------------------------------------------------------------- + */ + +void B1_handle_interrupt(avmb1_card * card) +{ + unsigned char b1cmd; + struct sk_buff *skb; + + unsigned ApplId; + unsigned MsgLen; + unsigned DataB3Len; + unsigned NCCI; + unsigned WindowSize; + + if (!B1_rx_full(card->port)) + return; + + b1cmd = B1_get_byte(card->port); + + switch (b1cmd) { + + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) B1_get_word(card->port); + MsgLen = B1_get_slice(card->port, card->msgbuf); + DataB3Len = B1_get_slice(card->port, card->databuf); + + if (showcapimsgs > 2) { + __u8 cmd = CAPIMSG_COMMAND(card->msgbuf); + __u8 subcmd = CAPIMSG_SUBCOMMAND(card->msgbuf); + __u32 contr = CAPIMSG_CONTROL(card->msgbuf); + CAPIMSG_SETDATA(card->msgbuf, card->databuf); + if (showcapimsgs & 1) { + printk(KERN_DEBUG "b1lli: Got [0x%lx] id#%d %s len=%u/%u\n", + (unsigned long) contr, + CAPIMSG_APPID(card->msgbuf), + capi_cmd2str(cmd, subcmd), + MsgLen, DataB3Len); + } else { + printk(KERN_DEBUG "b1lli: Got %s\n", capi_message2str(card->msgbuf)); + } + } + if (!(skb = dev_alloc_skb(DataB3Len + MsgLen))) { + printk(KERN_ERR "b1lli: incoming packet dropped\n"); + } else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len); + CAPIMSG_SETDATA(skb->data, skb->data + MsgLen); + avmb1_handle_capimsg(card, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) B1_get_word(card->port); + MsgLen = B1_get_slice(card->port, card->msgbuf); + if (showcapimsgs) { + __u8 cmd = CAPIMSG_COMMAND(card->msgbuf); + __u8 subcmd = CAPIMSG_SUBCOMMAND(card->msgbuf); + __u32 contr = CAPIMSG_CONTROL(card->msgbuf); + if (showcapimsgs & 1) { + printk(KERN_DEBUG "b1lli: Got [0x%lx] id#%d %s len=%u\n", + (unsigned long) contr, + CAPIMSG_APPID(card->msgbuf), + capi_cmd2str(cmd, subcmd), + MsgLen); + } else { + printk(KERN_DEBUG "b1lli: Got %s\n", capi_message2str(card->msgbuf)); + } + + } + if (!(skb = dev_alloc_skb(MsgLen))) { + printk(KERN_ERR "b1lli: incoming packet dropped\n"); + } else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + avmb1_handle_capimsg(card, ApplId, skb); + } + break; + + case RECEIVE_NEW_NCCI: + + ApplId = B1_get_word(card->port); + NCCI = B1_get_word(card->port); + WindowSize = B1_get_word(card->port); + + if (showcapimsgs) + printk(KERN_DEBUG "b1lli: NEW_NCCI app %u ncci 0x%x\n", ApplId, NCCI); + + avmb1_handle_new_ncci(card, ApplId, NCCI, WindowSize); + + break; + + case RECEIVE_FREE_NCCI: + + ApplId = B1_get_word(card->port); + NCCI = B1_get_word(card->port); + + if (showcapimsgs) + printk(KERN_DEBUG "b1lli: FREE_NCCI app %u ncci 0x%x\n", ApplId, NCCI); + + avmb1_handle_free_ncci(card, ApplId, NCCI); + break; + + case RECEIVE_START: + if (card->blocked) + printk(KERN_DEBUG "b1lli: RESTART\n"); + card->blocked = 0; + break; + + case RECEIVE_STOP: + printk(KERN_DEBUG "b1lli: STOP\n"); + card->blocked = 1; + break; + + case RECEIVE_INIT: + + 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", + card->version[VER_CARDTYPE], + card->version[VER_DRIVER], + card->version[VER_PROTO]); + avmb1_card_ready(card); + break; + default: + printk(KERN_ERR "b1lli: B1_handle_interrupt: 0x%x ???\n", b1cmd); + break; + } +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/b1pci.c linux/drivers/isdn/avmb1/b1pci.c --- v2.0.30/linux/drivers/isdn/avmb1/b1pci.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/avmb1/b1pci.c Mon Aug 4 17:33:59 1997 @@ -0,0 +1,121 @@ +/* + * $Id: b1pci.c,v 1.2 1997/05/18 09:24:13 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 1997/05/18 09:24:13 calle + * added verbose disconnect reason reporting to avmb1. + * some fixes in capi20 interface. + * changed info messages for B1-PCI + * + * Revision 1.1 1997/03/30 17:10:42 calle + * added support for AVM-B1-PCI card. + * + */ + +#include +#include +#include +#include +#include +#include +#include "compat.h" +#include +#include + +#ifndef PCI_VENDOR_ID_AVM +#define PCI_VENDOR_ID_AVM 0x1244 +#endif + +#ifndef PCI_DEVICE_ID_AVM_B1 +#define PCI_DEVICE_ID_AVM_B1 0x700 +#endif + +static char *revision = "$Revision: 1.2 $"; + +/* ------------------------------------------------------------- */ + +#ifdef HAS_NEW_SYMTAB +MODULE_AUTHOR("Carsten Paeth "); +#endif + +/* ------------------------------------------------------------- */ + +/* ------------------------------------------------------------- */ +/* -------- Init & Cleanup ------------------------------------- */ +/* ------------------------------------------------------------- */ + +/* + * init / exit functions + */ + +#ifdef MODULE +#define b1pci_init init_module +#endif + +int b1pci_init(void) +{ + char *p; + char rev[10]; + int rc; + int pci_index; + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else + strcpy(rev, " ??? "); + + +#ifdef CONFIG_PCI + if (!pcibios_present()) { + printk(KERN_ERR "b1pci: no PCI-BIOS present\n"); + return -EIO; + } + + printk(KERN_INFO "b1pci: revision %s\n", rev); + + for (pci_index = 0; pci_index < 8; pci_index++) { + unsigned char pci_bus, pci_device_fn; + unsigned int ioaddr; + unsigned char irq; + + if (pcibios_find_device (PCI_VENDOR_ID_AVM, + PCI_DEVICE_ID_AVM_B1, 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); + /* Strip the I/O address out of the returned value */ + ioaddr &= PCI_BASE_ADDRESS_IO_MASK; + 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) { + 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) + return rc; + } + return 0; +#else + printk(KERN_ERR "b1pci: kernel not compiled with PCI.\n"); + return -EIO; +#endif +} + +#ifdef MODULE +void cleanup_module(void) +{ +} +#endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/capi.c linux/drivers/isdn/avmb1/capi.c --- v2.0.30/linux/drivers/isdn/avmb1/capi.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/avmb1/capi.c Mon Aug 4 17:33:59 1997 @@ -0,0 +1,540 @@ +/* + * $Id: capi.c,v 1.4 1997/05/27 15:17:50 fritz Exp $ + * + * CAPI 2.0 Interface for Linux + * + * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: capi.c,v $ + * Revision 1.4 1997/05/27 15:17:50 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.3 1997/05/18 09:24:14 calle + * added verbose disconnect reason reporting to avmb1. + * some fixes in capi20 interface. + * changed info messages for B1-PCI + * + * Revision 1.2 1997/03/05 21:17:59 fritz + * Added capi_poll for compiling under 2.1.27 + * + * Revision 1.1 1997/03/04 21:50:29 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= 0x020117) +#include +#endif +#include +#include + +#include "compat.h" +#include "capiutil.h" +#include "capicmd.h" +#include "capidev.h" + +#ifdef HAS_NEW_SYMTAB +MODULE_AUTHOR("Carsten Paeth (calle@calle.in-berlin.de)"); +#endif + +/* -------- driver information -------------------------------------- */ + +int capi_major = 68; /* allocated */ + +#ifdef HAS_NEW_SYMTAB +MODULE_PARM(capi_major, "i"); +#endif + +/* -------- global variables ---------------------------------------- */ + +static struct capidev capidevs[CAPI_MAXMINOR + 1]; +struct capi_interface *capifuncs; + +/* -------- function called by lower level -------------------------- */ + +static void capi_signal(__u16 applid, __u32 minor) +{ + struct capidev *cdev; + struct sk_buff *skb = 0; + + if (minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) { + printk(KERN_ERR "BUG: capi_signal: illegal minor %d\n", minor); + return; + } + cdev = &capidevs[minor]; + (void) (*capifuncs->capi_get_message) (applid, &skb); + if (skb) { + skb_queue_tail(&cdev->recv_queue, skb); + wake_up_interruptible(&cdev->recv_wait); + } else { + printk(KERN_ERR "BUG: capi_signal: no skb\n"); + } +} + +/* -------- file_operations ----------------------------------------- */ + +#if LINUX_VERSION_CODE < 0x020100 +static int capi_lseek(struct inode *inode, struct file *file, + off_t offset, int origin) +{ + return -ESPIPE; +} +#else +static long long capi_llseek(struct inode *inode, struct file *file, + long long offset, int origin) +{ + return -ESPIPE; +} +#endif + +#if LINUX_VERSION_CODE < 0x020100 +static int capi_read(struct inode *inode, struct file *file, + char *buf, int count) +#else +static long capi_read(struct inode *inode, struct file *file, + char *buf, unsigned long count) +#endif +{ + unsigned int minor = MINOR(inode->i_rdev); + struct capidev *cdev; + struct sk_buff *skb; + int retval; + size_t copied; + + if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) + return -ENODEV; + + cdev = &capidevs[minor]; + + if ((skb = skb_dequeue(&cdev->recv_queue)) == 0) { + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + for (;;) { + interruptible_sleep_on(&cdev->recv_wait); + if ((skb = skb_dequeue(&cdev->recv_queue)) != 0) + break; + if (current->signal & ~current->blocked) + break; + } + if (skb == 0) + return -ERESTARTNOHAND; + } + if (skb->len > count) { + skb_queue_head(&cdev->recv_queue, skb); + return -EMSGSIZE; + } + if (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3 + && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) + CAPIMSG_SETDATA(skb->data, buf + CAPIMSG_LEN(skb->data)); + retval = copy_to_user(buf, skb->data, skb->len); + if (retval) { + skb_queue_head(&cdev->recv_queue, skb); + return retval; + } + copied = skb->len; + + + kfree_skb(skb, FREE_READ); + + return copied; +} + +#if LINUX_VERSION_CODE < 0x020100 +static int capi_write(struct inode *inode, struct file *file, + const char *buf, int count) +#else +static long capi_write(struct inode *inode, struct file *file, + const char *buf, unsigned long count) +#endif +{ + unsigned int minor = MINOR(inode->i_rdev); + struct capidev *cdev; + struct sk_buff *skb; + int retval; + __u8 cmd; + __u8 subcmd; + __u16 mlen; + + if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) + return -ENODEV; + + cdev = &capidevs[minor]; + + skb = alloc_skb(count, GFP_USER); + + if ((retval = copy_from_user(skb_put(skb, count), buf, count))) { + dev_kfree_skb(skb, FREE_WRITE); + return retval; + } + cmd = CAPIMSG_COMMAND(skb->data); + subcmd = CAPIMSG_SUBCOMMAND(skb->data); + mlen = CAPIMSG_LEN(skb->data); + if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) { + __u16 dlen = CAPIMSG_DATALEN(skb->data); + if (mlen + dlen != count) { + dev_kfree_skb(skb, FREE_WRITE); + return -EINVAL; + } + } else if (mlen != count) { + dev_kfree_skb(skb, FREE_WRITE); + return -EINVAL; + } + CAPIMSG_SETAPPID(skb->data, cdev->applid); + + cdev->errcode = (*capifuncs->capi_put_message) (cdev->applid, skb); + + if (cdev->errcode) { + dev_kfree_skb(skb, FREE_WRITE); + return -EIO; + } + return count; +} + +#if (LINUX_VERSION_CODE < 0x020117) +static int capi_select(struct inode *inode, struct file *file, + int sel_type, select_table * wait) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct capidev *cdev; + + if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) + return -ENODEV; + + cdev = &capidevs[minor]; + + switch (sel_type) { + case SEL_IN: + if (!skb_queue_empty(&cdev->recv_queue)) + return 1; + /* fall througth */ + case SEL_EX: + /* error conditions ? */ + + select_wait(&cdev->recv_wait, wait); + return 0; + case SEL_OUT: + /* + if (!queue_full()) + return 1; + select_wait(&cdev->send_wait, wait); + return 0; + */ + return 1; + } + return 1; +} +#else +static unsigned int +capi_poll(struct file *file, poll_table * wait) +{ + unsigned int mask = 0; + unsigned int minor = MINOR(file->f_inode->i_rdev); + struct capidev *cdev; + + if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) + return POLLERR; + + cdev = &capidevs[minor]; + poll_wait(&(cdev->recv_wait), wait); + mask = POLLOUT | POLLWRNORM; + if (!skb_queue_empty(&cdev->recv_queue)) + mask |= POLLIN | POLLRDNORM; + return mask; +} +#endif + +static int capi_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct capidev *cdev; + capi_ioctl_struct data; + int retval; + + + if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open) + return -ENODEV; + + cdev = &capidevs[minor]; + + switch (cmd) { + case CAPI_REGISTER: + { + if (!minor) + return -EINVAL; + retval = copy_from_user((void *) &data.rparams, + (void *) arg, sizeof(struct capi_register_params)); + if (retval) + return -EFAULT; + if (cdev->is_registered) + return -EEXIST; + cdev->errcode = (*capifuncs->capi_register) (&data.rparams, + &cdev->applid); + if (cdev->errcode) + return -EIO; + (void) (*capifuncs->capi_set_signal) (cdev->applid, capi_signal, minor); + cdev->is_registered = 1; + } + return 0; + + case CAPI_GET_VERSION: + { + retval = copy_from_user((void *) &data.contr, + (void *) arg, + sizeof(data.contr)); + if (retval) + return -EFAULT; + cdev->errcode = (*capifuncs->capi_get_version) (data.contr, &data.version); + if (cdev->errcode) + return -EIO; + retval = copy_to_user((void *) arg, + (void *) &data.version, + sizeof(data.version)); + if (retval) + return -EFAULT; + } + return 0; + + case CAPI_GET_SERIAL: + { + retval = copy_from_user((void *) &data.contr, + (void *) arg, + sizeof(data.contr)); + if (retval) + return -EFAULT; + cdev->errcode = (*capifuncs->capi_get_serial) (data.contr, data.serial); + if (cdev->errcode) + return -EIO; + retval = copy_to_user((void *) arg, + (void *) data.serial, + sizeof(data.serial)); + if (retval) + return -EFAULT; + } + return 0; + case CAPI_GET_PROFILE: + { + retval = copy_from_user((void *) &data.contr, + (void *) arg, + sizeof(data.contr)); + if (retval) + return -EFAULT; + + if (data.contr == 0) { + cdev->errcode = (*capifuncs->capi_get_profile) (data.contr, &data.profile); + if (cdev->errcode) + return -EIO; + + retval = copy_to_user((void *) arg, + (void *) &data.profile.ncontroller, + sizeof(data.profile.ncontroller)); + + } else { + cdev->errcode = (*capifuncs->capi_get_profile) (data.contr, &data.profile); + if (cdev->errcode) + return -EIO; + + retval = copy_to_user((void *) arg, + (void *) &data.profile, + sizeof(data.profile)); + } + if (retval) + return -EFAULT; + } + return 0; + + case CAPI_GET_MANUFACTURER: + { + retval = copy_from_user((void *) &data.contr, + (void *) arg, + sizeof(data.contr)); + if (retval) + return -EFAULT; + cdev->errcode = (*capifuncs->capi_get_manufacturer) (data.contr, data.manufacturer); + if (cdev->errcode) + return -EIO; + + retval = copy_to_user((void *) arg, (void *) data.manufacturer, + sizeof(data.manufacturer)); + if (retval) + return -EFAULT; + + } + return 0; + case CAPI_GET_ERRCODE: + data.errcode = cdev->errcode; + cdev->errcode = CAPI_NOERROR; + if (arg) { + retval = copy_to_user((void *) arg, + (void *) &data.errcode, + sizeof(data.errcode)); + if (retval) + return -EFAULT; + } + return data.errcode; + + case CAPI_INSTALLED: + if ((*capifuncs->capi_installed) ()) + return 0; + return -ENXIO; + + case CAPI_MANUFACTURER_CMD: + { + struct capi_manufacturer_cmd mcmd; + if (minor) + return -EINVAL; + if (!suser()) + return -EPERM; + retval = copy_from_user((void *) &mcmd, (void *) arg, + sizeof(mcmd)); + if (retval) + return -EFAULT; + return (*capifuncs->capi_manufacturer) (mcmd.cmd, mcmd.data); + } + return 0; + } + return -EINVAL; +} + +static int capi_open(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + + if (minor >= CAPI_MAXMINOR) + return -ENXIO; + + if (minor) { + if (capidevs[minor].is_open) + return -EEXIST; + + capidevs[minor].is_open = 1; + skb_queue_head_init(&capidevs[minor].recv_queue); + MOD_INC_USE_COUNT; + + } else { + + if (!capidevs[minor].is_open) { + capidevs[minor].is_open = 1; + MOD_INC_USE_COUNT; + } + } + + + return 0; +} + +static CLOSETYPE +capi_release(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct capidev *cdev; + struct sk_buff *skb; + + if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open) { + printk(KERN_ERR "capi20: release minor %d ???\n", minor); + return CLOSEVAL; + } + cdev = &capidevs[minor]; + + if (minor) { + + if (cdev->is_registered) + (*capifuncs->capi_release) (cdev->applid); + + cdev->is_registered = 0; + cdev->applid = 0; + + while ((skb = skb_dequeue(&cdev->recv_queue)) != 0) + kfree_skb(skb, FREE_READ); + } + cdev->is_open = 0; + + MOD_DEC_USE_COUNT; + return CLOSEVAL; +} + +static struct file_operations capi_fops = +{ +#if LINUX_VERSION_CODE < 0x020100 + capi_lseek, +#else + capi_llseek, +#endif + capi_read, + capi_write, + NULL, /* capi_readdir */ +#if (LINUX_VERSION_CODE < 0x020117) + capi_select, +#else + capi_poll, +#endif + capi_ioctl, + NULL, /* capi_mmap */ + capi_open, + capi_release, + NULL, /* capi_fsync */ + NULL, /* capi_fasync */ +}; + + +/* -------- init function and module interface ---------------------- */ + +#ifdef MODULE +#define capi_init init_module +#endif + +static struct capi_interface_user cuser = { + "capi20", + 0, +}; + +int capi_init(void) +{ + memset(capidevs, 0, sizeof(capidevs)); + + if (register_chrdev(capi_major, "capi20", &capi_fops)) { + printk(KERN_ERR "capi20: unable to get major %d\n", capi_major); + return -EIO; + } + printk(KERN_NOTICE "capi20: started up with major %d\n", capi_major); + + if ((capifuncs = attach_capi_interface(&cuser)) == 0) { + unregister_chrdev(capi_major, "capi20"); + return -EIO; + } + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + unregister_chrdev(capi_major, "capi20"); + (void) detach_capi_interface(&cuser); +} + +#endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/capicmd.h linux/drivers/isdn/avmb1/capicmd.h --- v2.0.30/linux/drivers/isdn/avmb1/capicmd.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/avmb1/capicmd.h Mon Aug 4 17:33:59 1997 @@ -0,0 +1,119 @@ +/* + * $Id: capicmd.h,v 1.1 1997/03/04 21:50:30 calle Exp $ + * + * CAPI 2.0 Interface for Linux + * + * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: capicmd.h,v $ + * Revision 1.1 1997/03/04 21:50:30 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + * + */ +#ifndef __CAPICMD_H__ +#define __CAPICMD_H__ + +/*----- CAPI commands -----*/ +#define CAPI_ALERT 0x01 +#define CAPI_CONNECT 0x02 +#define CAPI_CONNECT_ACTIVE 0x03 +#define CAPI_CONNECT_B3_ACTIVE 0x83 +#define CAPI_CONNECT_B3 0x82 +#define CAPI_CONNECT_B3_T90_ACTIVE 0x88 +#define CAPI_DATA_B3 0x86 +#define CAPI_DISCONNECT_B3 0x84 +#define CAPI_DISCONNECT 0x04 +#define CAPI_FACILITY 0x80 +#define CAPI_INFO 0x08 +#define CAPI_LISTEN 0x05 +#define CAPI_MANUFACTURER 0xff +#define CAPI_RESET_B3 0x87 +#define CAPI_SELECT_B_PROTOCOL 0x41 + +/*----- CAPI subcommands -----*/ + +#define CAPI_REQ 0x80 +#define CAPI_CONF 0x81 +#define CAPI_IND 0x82 +#define CAPI_RESP 0x83 + +/*----- CAPI combined commands -----*/ + +#define CAPICMD(cmd,subcmd) (((cmd)<<8)|(subcmd)) + +#define CAPI_DISCONNECT_REQ CAPICMD(CAPI_DISCONNECT,CAPI_REQ) +#define CAPI_DISCONNECT_CONF CAPICMD(CAPI_DISCONNECT,CAPI_CONF) +#define CAPI_DISCONNECT_IND CAPICMD(CAPI_DISCONNECT,CAPI_IND) +#define CAPI_DISCONNECT_RESP CAPICMD(CAPI_DISCONNECT,CAPI_RESP) + +#define CAPI_ALERT_REQ CAPICMD(CAPI_ALERT,CAPI_REQ) +#define CAPI_ALERT_CONF CAPICMD(CAPI_ALERT,CAPI_CONF) + +#define CAPI_CONNECT_REQ CAPICMD(CAPI_CONNECT,CAPI_REQ) +#define CAPI_CONNECT_CONF CAPICMD(CAPI_CONNECT,CAPI_CONF) +#define CAPI_CONNECT_IND CAPICMD(CAPI_CONNECT,CAPI_IND) +#define CAPI_CONNECT_RESP CAPICMD(CAPI_CONNECT,CAPI_RESP) + +#define CAPI_CONNECT_ACTIVE_REQ CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_REQ) +#define CAPI_CONNECT_ACTIVE_CONF CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_CONF) +#define CAPI_CONNECT_ACTIVE_IND CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_IND) +#define CAPI_CONNECT_ACTIVE_RESP CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_RESP) + +#define CAPI_SELECT_B_PROTOCOL_REQ CAPICMD(CAPI_SELECT_B_PROTOCOL,CAPI_REQ) +#define CAPI_SELECT_B_PROTOCOL_CONF CAPICMD(CAPI_SELECT_B_PROTOCOL,CAPI_CONF) + +#define CAPI_CONNECT_B3_ACTIVE_REQ CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_REQ) +#define CAPI_CONNECT_B3_ACTIVE_CONF CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_CONF) +#define CAPI_CONNECT_B3_ACTIVE_IND CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_IND) +#define CAPI_CONNECT_B3_ACTIVE_RESP CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_RESP) + +#define CAPI_CONNECT_B3_REQ CAPICMD(CAPI_CONNECT_B3,CAPI_REQ) +#define CAPI_CONNECT_B3_CONF CAPICMD(CAPI_CONNECT_B3,CAPI_CONF) +#define CAPI_CONNECT_B3_IND CAPICMD(CAPI_CONNECT_B3,CAPI_IND) +#define CAPI_CONNECT_B3_RESP CAPICMD(CAPI_CONNECT_B3,CAPI_RESP) + + +#define CAPI_CONNECT_B3_T90_ACTIVE_IND CAPICMD(CAPI_CONNECT_B3_T90_ACTIVE,CAPI_IND) +#define CAPI_CONNECT_B3_T90_ACTIVE_RESP CAPICMD(CAPI_CONNECT_B3_T90_ACTIVE,CAPI_RESP) + +#define CAPI_DATA_B3_REQ CAPICMD(CAPI_DATA_B3,CAPI_REQ) +#define CAPI_DATA_B3_CONF CAPICMD(CAPI_DATA_B3,CAPI_CONF) +#define CAPI_DATA_B3_IND CAPICMD(CAPI_DATA_B3,CAPI_IND) +#define CAPI_DATA_B3_RESP CAPICMD(CAPI_DATA_B3,CAPI_RESP) + +#define CAPI_DISCONNECT_B3_REQ CAPICMD(CAPI_DISCONNECT_B3,CAPI_REQ) +#define CAPI_DISCONNECT_B3_CONF CAPICMD(CAPI_DISCONNECT_B3,CAPI_CONF) +#define CAPI_DISCONNECT_B3_IND CAPICMD(CAPI_DISCONNECT_B3,CAPI_IND) +#define CAPI_DISCONNECT_B3_RESP CAPICMD(CAPI_DISCONNECT_B3,CAPI_RESP) + +#define CAPI_RESET_B3_REQ CAPICMD(CAPI_RESET_B3,CAPI_REQ) +#define CAPI_RESET_B3_CONF CAPICMD(CAPI_RESET_B3,CAPI_CONF) +#define CAPI_RESET_B3_IND CAPICMD(CAPI_RESET_B3,CAPI_IND) +#define CAPI_RESET_B3_RESP CAPICMD(CAPI_RESET_B3,CAPI_RESP) + +#define CAPI_LISTEN_REQ CAPICMD(CAPI_LISTEN,CAPI_REQ) +#define CAPI_LISTEN_CONF CAPICMD(CAPI_LISTEN,CAPI_CONF) + +#define CAPI_MANUFACTURER_REQ CAPICMD(CAPI_MANUFACTURER,CAPI_REQ) +#define CAPI_MANUFACTURER_CONF CAPICMD(CAPI_MANUFACTURER,CAPI_CONF) +#define CAPI_MANUFACTURER_IND CAPICMD(CAPI_MANUFACTURER,CAPI_IND) +#define CAPI_MANUFACTURER_RESP CAPICMD(CAPI_MANUFACTURER,CAPI_RESP) + +#define CAPI_FACILITY_REQ CAPICMD(CAPI_FACILITY,CAPI_REQ) +#define CAPI_FACILITY_CONF CAPICMD(CAPI_FACILITY,CAPI_CONF) +#define CAPI_FACILITY_IND CAPICMD(CAPI_FACILITY,CAPI_IND) +#define CAPI_FACILITY_RESP CAPICMD(CAPI_FACILITY,CAPI_RESP) + +#define CAPI_INFO_REQ CAPICMD(CAPI_INFO,CAPI_REQ) +#define CAPI_INFO_CONF CAPICMD(CAPI_INFO,CAPI_CONF) +#define CAPI_INFO_IND CAPICMD(CAPI_INFO,CAPI_IND) +#define CAPI_INFO_RESP CAPICMD(CAPI_INFO,CAPI_RESP) + +#endif /* __CAPICMD_H__ */ diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/capidev.h linux/drivers/isdn/avmb1/capidev.h --- v2.0.30/linux/drivers/isdn/avmb1/capidev.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/avmb1/capidev.h Mon Aug 4 17:33:59 1997 @@ -0,0 +1,29 @@ +/* + * $Id: capidev.h,v 1.1 1997/03/04 21:50:30 calle Exp $ + * + * CAPI 2.0 Interface for Linux + * + * (c) Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: capidev.h,v $ + * Revision 1.1 1997/03/04 21:50:30 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + */ + +struct capidev { + int is_open; + int is_registered; + __u16 applid; + struct sk_buff_head recv_queue; + struct wait_queue *recv_wait; + __u16 errcode; +}; + +#define CAPI_MAXMINOR CAPI_MAXAPPL diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/capidrv.c linux/drivers/isdn/avmb1/capidrv.c --- v2.0.30/linux/drivers/isdn/avmb1/capidrv.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/avmb1/capidrv.c Mon Aug 4 17:33:59 1997 @@ -0,0 +1,1764 @@ +/* + * $Id: capidrv.c,v 1.3 1997/05/18 09:24:15 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 1997/05/18 09:24:15 calle + * added verbose disconnect reason reporting to avmb1. + * some fixes in capi20 interface. + * changed info messages for B1-PCI + * + * Revision 1.2 1997/03/05 21:19:59 fritz + * Removed include of config.h (mkdep stated this is unneded). + * + * Revision 1.1 1997/03/04 21:50:31 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" +#include "capiutil.h" +#include "capicmd.h" +#include "capidrv.h" + +static char *revision = "$Revision: 1.3 $"; +int debugmode = 0; + +#ifdef HAS_NEW_SYMTAB +MODULE_AUTHOR("Carsten Paeth "); +MODULE_PARM(debugmode, "i"); +#endif + +/* -------- type definitions ----------------------------------------- */ + + +struct capidrv_contr { + + struct capidrv_contr *next; + + __u32 contrnr; + char name[20]; + + /* + * for isdn4linux + */ + isdn_if interface; + int myid; + + /* + * LISTEN state + */ + int state; + __u32 cipmask; + __u32 cipmask2; + + /* + * ID of capi message sent + */ + __u16 msgid; + + /* + * B-Channels + */ + int nbchan; + struct capidrv_bchan { + struct capidrv_contr *contr; + __u8 msn[ISDN_MSNLEN]; + int l2; + int l3; + __u8 num[ISDN_MSNLEN]; + __u8 mynum[ISDN_MSNLEN]; + int si1; + int si2; + int incoming; + int disconnecting; + struct capidrv_plci { + struct capidrv_plci *next; + __u32 plci; + __u32 ncci; /* ncci for CONNECT_ACTIVE_IND */ + __u16 msgid; /* to identfy CONNECT_CONF */ + int chan; + int state; + struct capidrv_ncci { + struct capidrv_ncci *next; + struct capidrv_plci *plcip; + __u32 ncci; + __u16 msgid; /* to identfy CONNECT_B3_CONF */ + int chan; + int state; + int oldstate; + /* */ + __u16 datahandle; + } *ncci_list; + } *plcip; + struct capidrv_ncci *nccip; + } *bchans; + + struct capidrv_plci *plci_list; +}; + +struct capidrv_data { + __u16 appid; + int ncontr; + struct capidrv_contr *contr_list; +}; + +typedef struct capidrv_plci capidrv_plci; +typedef struct capidrv_ncci capidrv_ncci; +typedef struct capidrv_contr capidrv_contr; +typedef struct capidrv_data capidrv_data; +typedef struct capidrv_bchan capidrv_bchan; + +/* -------- data definitions ----------------------------------------- */ + +static capidrv_data global; +static struct capi_interface *capifuncs; + +/* -------- convert functions ---------------------------------------- */ + +static inline __u32 b1prot(int l2, int l3) +{ + switch (l2) { + case ISDN_PROTO_L2_X75I: + case ISDN_PROTO_L2_X75UI: + case ISDN_PROTO_L2_X75BUI: + return 0; + case ISDN_PROTO_L2_HDLC: + default: + return 0; + case ISDN_PROTO_L2_TRANS: + return 1; + } +} + +static inline __u32 b2prot(int l2, int l3) +{ + switch (l2) { + case ISDN_PROTO_L2_X75I: + case ISDN_PROTO_L2_X75UI: + case ISDN_PROTO_L2_X75BUI: + default: + return 0; + case ISDN_PROTO_L2_HDLC: + return 1; + case ISDN_PROTO_L2_TRANS: + return 0; + } +} + +static inline __u32 b3prot(int l2, int l3) +{ + switch (l2) { + case ISDN_PROTO_L2_X75I: + case ISDN_PROTO_L2_X75UI: + case ISDN_PROTO_L2_X75BUI: + case ISDN_PROTO_L2_HDLC: + case ISDN_PROTO_L2_TRANS: + default: + return 0; + } +} + +static inline __u16 si2cip(__u8 si1, __u8 si2) +{ + static const __u8 cip[17][5] = + { + /* 0 1 2 3 4 */ + {0, 0, 0, 0, 0}, /*0 */ + {16, 16, 4, 26, 16}, /*1 */ + {17, 17, 17, 4, 4}, /*2 */ + {2, 2, 2, 2, 2}, /*3 */ + {18, 18, 18, 18, 18}, /*4 */ + {2, 2, 2, 2, 2}, /*5 */ + {0, 0, 0, 0, 0}, /*6 */ + {2, 2, 2, 2, 2}, /*7 */ + {2, 2, 2, 2, 2}, /*8 */ + {21, 21, 21, 21, 21}, /*9 */ + {19, 19, 19, 19, 19}, /*10 */ + {0, 0, 0, 0, 0}, /*11 */ + {0, 0, 0, 0, 0}, /*12 */ + {0, 0, 0, 0, 0}, /*13 */ + {0, 0, 0, 0, 0}, /*14 */ + {22, 22, 22, 22, 22}, /*15 */ + {27, 27, 27, 28, 27} /*16 */ + }; + if (si1 > 16) + si1 = 0; + if (si2 > 4) + si2 = 0; + + return (__u16) cip[si1][si2]; +} + +static inline __u8 cip2si1(__u16 cipval) +{ + static const __u8 si[32] = + {7, 1, 7, 7, 1, 1, 7, 7, /*0-7 */ + 7, 1, 0, 0, 0, 0, 0, 0, /*8-15 */ + 1, 2, 4, 10, 9, 9, 15, 7, /*16-23 */ + 7, 7, 1, 16, 16, 0, 0, 0}; /*24-31 */ + + if (cipval > 31) + cipval = 0; /* .... */ + return si[cipval]; +} + +static inline __u8 cip2si2(__u16 cipval) +{ + static const __u8 si[32] = + {0, 0, 0, 0, 2, 3, 0, 0, /*0-7 */ + 0, 3, 0, 0, 0, 0, 0, 0, /*8-15 */ + 1, 2, 0, 0, 9, 0, 0, 0, /*16-23 */ + 0, 0, 3, 2, 3, 0, 0, 0}; /*24-31 */ + + if (cipval > 31) + cipval = 0; /* .... */ + return si[cipval]; +} + + +/* -------- controller managment ------------------------------------- */ + +static inline capidrv_contr *findcontrbydriverid(int driverid) +{ + capidrv_contr *p = global.contr_list; + + while (p) { + if (p->myid == driverid) + return p; + p = p->next; + } + return (capidrv_contr *) 0; +} + +static capidrv_contr *findcontrbynumber(__u32 contr) +{ + capidrv_contr *p = global.contr_list; + + while (p) { + if (p->contrnr == contr) + return p; + p = p->next; + } + return (capidrv_contr *) 0; +} + + +/* -------- plci management ------------------------------------------ */ + +static capidrv_plci *new_plci(capidrv_contr * card, int chan) +{ + capidrv_plci *plcip; + + plcip = (capidrv_plci *) kmalloc(sizeof(capidrv_plci), GFP_ATOMIC); + + if (plcip == 0) + return 0; + + memset(plcip, 0, sizeof(capidrv_plci)); + plcip->state = ST_PLCI_NONE; + plcip->plci = 0; + plcip->msgid = 0; + plcip->chan = chan; + plcip->next = card->plci_list; + card->plci_list = plcip; + card->bchans[chan].plcip = plcip; + + return plcip; +} + +static capidrv_plci *find_plci_by_plci(capidrv_contr * card, __u32 plci) +{ + capidrv_plci *p; + for (p = card->plci_list; p; p = p->next) + if (p->plci == plci) + return p; + return 0; +} + +static capidrv_plci *find_plci_by_msgid(capidrv_contr * card, __u16 msgid) +{ + capidrv_plci *p; + for (p = card->plci_list; p; p = p->next) + if (p->msgid == msgid) + return p; + return 0; +} + +static capidrv_plci *find_plci_by_ncci(capidrv_contr * card, __u32 ncci) +{ + capidrv_plci *p; + for (p = card->plci_list; p; p = p->next) + if (p->plci == (ncci & 0xffff)) + return p; + return 0; +} + +static void free_plci(capidrv_contr * card, capidrv_plci * plcip) +{ + capidrv_plci **pp; + + for (pp = &card->plci_list; *pp; pp = &(*pp)->next) { + if (*pp == plcip) { + *pp = (*pp)->next; + card->bchans[plcip->chan].plcip = 0; + card->bchans[plcip->chan].disconnecting = 0; + card->bchans[plcip->chan].incoming = 0; + kfree(plcip); + return; + } + } + printk(KERN_ERR "capidrv: free_plci %p (0x%x) not found, Huh?\n", + plcip, plcip->plci); +} + +/* -------- ncci management ------------------------------------------ */ + +static inline capidrv_ncci *new_ncci(capidrv_contr * card, + capidrv_plci * plcip, + __u32 ncci) +{ + capidrv_ncci *nccip; + + nccip = (capidrv_ncci *) kmalloc(sizeof(capidrv_ncci), GFP_ATOMIC); + + if (nccip == 0) + return 0; + + memset(nccip, 0, sizeof(capidrv_ncci)); + nccip->ncci = ncci; + nccip->state = ST_NCCI_NONE; + nccip->plcip = plcip; + nccip->chan = plcip->chan; + nccip->datahandle = 0; + + nccip->next = plcip->ncci_list; + plcip->ncci_list = nccip; + + card->bchans[plcip->chan].nccip = nccip; + + return nccip; +} + +static inline capidrv_ncci *find_ncci(capidrv_contr * card, __u32 ncci) +{ + capidrv_plci *plcip; + capidrv_ncci *p; + + if ((plcip = find_plci_by_ncci(card, ncci)) == 0) + return 0; + + for (p = plcip->ncci_list; p; p = p->next) + if (p->ncci == ncci) + return p; + return 0; +} + +static inline capidrv_ncci *find_ncci_by_msgid(capidrv_contr * card, + __u32 ncci, __u16 msgid) +{ + capidrv_plci *plcip; + capidrv_ncci *p; + + if ((plcip = find_plci_by_ncci(card, ncci)) == 0) + return 0; + + for (p = plcip->ncci_list; p; p = p->next) + if (p->msgid == msgid) + return p; + return 0; +} + +static void free_ncci(capidrv_contr * card, struct capidrv_ncci *nccip) +{ + struct capidrv_ncci **pp; + + for (pp = &(nccip->plcip->ncci_list); *pp; pp = &(*pp)->next) { + if (*pp == nccip) { + *pp = (*pp)->next; + break; + } + } + card->bchans[nccip->chan].nccip = 0; + kfree(nccip); +} + +/* -------- convert and send capi message ---------------------------- */ + +static void send_message(capidrv_contr * card, _cmsg * cmsg) +{ + struct sk_buff *skb; + size_t len; + capi_cmsg2message(cmsg, cmsg->buf); + len = CAPIMSG_LEN(cmsg->buf); + skb = dev_alloc_skb(len); + SET_SKB_FREE(skb); + memcpy(skb_put(skb, len), cmsg->buf, len); + (*capifuncs->capi_put_message) (global.appid, skb); +} + +/* -------- state machine -------------------------------------------- */ + +struct listenstatechange { + int actstate; + int nextstate; + int event; +}; + +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}, + {}, +}; + +static void listen_change_state(capidrv_contr * card, int event) +{ + struct listenstatechange *p = listentable; + 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); + card->state = p->nextstate; + return; + } + p++; + } + printk(KERN_ERR "capidrv: listen_change_state state=%d event=%d ????\n", + card->state, event); + +} + +/* ------------------------------------------------------------------ */ + +static void p0(capidrv_contr * card, capidrv_plci * plci) +{ + isdn_ctrl cmd; + + card->bchans[plci->chan].contr = 0; + cmd.command = ISDN_STAT_DHUP; + cmd.driver = card->myid; + cmd.arg = plci->chan; + card->interface.statcallb(&cmd); + free_plci(card, plci); +} + +/* ------------------------------------------------------------------ */ + +struct plcistatechange { + int actstate; + int nextstate; + int event; + void (*changefunc) (capidrv_contr * card, capidrv_plci * plci); +}; + +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}, + /* 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}, + /* 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}, + /* 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}, + /* 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}, + /* 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}, + /* 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}, + /* P-5 */ +{ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + /* P-6 */ + {ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0}, + {}, +}; + +static void plci_change_state(capidrv_contr * card, capidrv_plci * plci, int event) +{ + struct plcistatechange *p = plcitable; + 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); + plci->state = p->nextstate; + if (p->changefunc) + p->changefunc(card, plci); + return; + } + p++; + } + printk(KERN_ERR "capidrv: plci_change_state:0x%x state=%d event=%d ????\n", + plci->plci, plci->state, event); +} + +/* ------------------------------------------------------------------ */ + +static _cmsg cmsg; + +static void n0(capidrv_contr * card, capidrv_ncci * ncci) +{ + isdn_ctrl cmd; + + capi_fill_DISCONNECT_REQ(&cmsg, + global.appid, + card->msgid++, + ncci->plcip->plci, + 0, /* BChannelinformation */ + 0, /* Keypadfacility */ + 0, /* Useruserdata */ + 0 /* Facilitydataarray */ + ); + send_message(card, &cmsg); + plci_change_state(card, ncci->plcip, EV_PLCI_DISCONNECT_REQ); + + cmd.command = ISDN_STAT_BHUP; + cmd.driver = card->myid; + cmd.arg = ncci->chan; + card->interface.statcallb(&cmd); + free_ncci(card, ncci); +} + +/* ------------------------------------------------------------------ */ + +struct nccistatechange { + int actstate; + int nextstate; + int event; + void (*changefunc) (capidrv_contr * card, capidrv_ncci * ncci); +}; + +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}, + /* 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}, + /* 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_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 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}, + /* 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}, + /* N-3 */ + {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}, + /* 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}, + /* N-5 */ + {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) +{ + struct nccistatechange *p = nccitable; + 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); + if (p->nextstate == ST_NCCI_PREVIOUS) { + ncci->state = ncci->oldstate; + ncci->oldstate = p->actstate; + } else { + ncci->oldstate = p->actstate; + ncci->state = p->nextstate; + } + if (p->changefunc) + p->changefunc(card, ncci); + return; + } + p++; + } + printk(KERN_ERR "capidrv: ncci_change_state:0x%x state=%d event=%d ????\n", + ncci->ncci, ncci->state, event); +} + +/* ------------------------------------------------------------------- */ + +static inline int new_bchan(capidrv_contr * card) +{ + int i; + for (i = 0; i < card->nbchan; i++) { + if (card->bchans[i].plcip == 0) { + card->bchans[i].disconnecting = 0; + return i; + } + } + return -1; +} + +/* ------------------------------------------------------------------- */ + +static void handle_controller(_cmsg * cmsg) +{ + capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f); + + if (!card) { + printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrController & 0x7f); + return; + } + switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) { + + 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); + if (cmsg->Info) { + listen_change_state(card, EV_LISTEN_CONF_ERROR); + } else if (card->cipmask == 0) { + listen_change_state(card, EV_LISTEN_CONF_EMPTY); + } else { + listen_change_state(card, EV_LISTEN_CONF_OK); + } + break; + + case CAPI_MANUFACTURER_IND: /* Controller */ + goto ignored; + case CAPI_MANUFACTURER_CONF: /* Controller */ + goto ignored; + case CAPI_FACILITY_IND: /* Controller/plci/ncci */ + goto ignored; + case CAPI_FACILITY_CONF: /* Controller/plci/ncci */ + goto ignored; + case CAPI_INFO_IND: /* Controller/plci */ + goto ignored; + case CAPI_INFO_CONF: /* Controller/plci */ + goto ignored; + + default: + printk(KERN_ERR "capidrv: got %s from controller 0x%x ???", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrController); + } + return; + + ignored: + printk(KERN_INFO "capidrv: %s from controller 0x%x ignored\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrController); +} + +static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg) +{ + capidrv_plci *plcip; + capidrv_bchan *bchan; + isdn_ctrl cmd; + int chan; + + if ((chan = new_bchan(card)) == -1) { + printk(KERN_ERR "capidrv: incoming call on not existing bchan ?\n"); + return; + } + bchan = &card->bchans[chan]; + if ((plcip = new_plci(card, chan)) == 0) { + printk(KERN_ERR "capidrv: incoming call: no memory, sorry.\n"); + return; + } + bchan->incoming = 1; + plcip->plci = cmsg->adr.adrPLCI; + plci_change_state(card, plcip, EV_PLCI_CONNECT_IND); + + cmd.command = ISDN_STAT_ICALL; + cmd.driver = card->myid; + cmd.arg = chan; + memset(&cmd.parm.setup, 0, sizeof(cmd.parm.setup)); + strncpy(cmd.parm.setup.phone, + cmsg->CallingPartyNumber + 3, + cmsg->CallingPartyNumber[0] - 2); + strncpy(cmd.parm.setup.eazmsn, + cmsg->CalledPartyNumber + 2, + cmsg->CalledPartyNumber[0] - 1); + cmd.parm.setup.si1 = cip2si1(cmsg->CIPValue); + cmd.parm.setup.si2 = cip2si2(cmsg->CIPValue); + 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", + cmd.parm.setup.phone, + cmd.parm.setup.si1, + cmd.parm.setup.si2, + cmd.parm.setup.eazmsn); + + switch (card->interface.statcallb(&cmd)) { + case 0: + /* No device matching this call. + * and isdn_common.c has send a HANGUP command + * which is ignored in state ST_PLCI_INCOMING, + * so we send RESP to ignore the call + */ + capi_cmsg_answer(cmsg); + 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", + cmd.parm.setup.phone, + cmd.parm.setup.si1, + cmd.parm.setup.si2, + cmd.parm.setup.eazmsn); + break; + case 1: + /* At least one device matching this call (RING on ttyI) + * HL-driver may send ALERTING on the D-channel in this + * case. + * really means: RING on ttyI or a net interface + * accepted this call already. + * + * If the call was accepted, state has already changed, + * and CONNECT_RESP already sent. + */ + if (plcip->state == ST_PLCI_INCOMING) { + printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s tty alerting\n", + cmd.parm.setup.phone, + cmd.parm.setup.si1, + cmd.parm.setup.si2, + cmd.parm.setup.eazmsn); + capi_fill_ALERT_REQ(cmsg, + global.appid, + card->msgid++, + plcip->plci, /* adr */ + 0, /* BChannelinformation */ + 0, /* Keypadfacility */ + 0, /* Useruserdata */ + 0 /* Facilitydataarray */ + ); + plcip->msgid = cmsg->Messagenumber; + send_message(card, cmsg); + } else { + printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s on netdev\n", + cmd.parm.setup.phone, + cmd.parm.setup.si1, + cmd.parm.setup.si2, + cmd.parm.setup.eazmsn); + } + break; + + case 2: /* Call will be rejected. */ + capi_cmsg_answer(cmsg); + cmsg->Reject = 2; /* reject call, normal call clearing */ + send_message(card, cmsg); + plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT); + break; + + default: + /* An error happened. (Invalid parameters for example.) */ + capi_cmsg_answer(cmsg); + cmsg->Reject = 8; /* reject call, + destination out of order */ + send_message(card, cmsg); + plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT); + break; + } + return; +} + +static void handle_plci(_cmsg * cmsg) +{ + capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f); + capidrv_plci *plcip; + isdn_ctrl cmd; + + if (!card) { + printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrController & 0x7f); + return; + } + switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) { + + case CAPI_DISCONNECT_IND: /* plci */ + if (cmsg->Reason) { + printk(KERN_INFO "capidrv: %s reason 0x%x (%s) for plci 0x%x\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->Reason, capi_info2str(cmsg->Reason), cmsg->adr.adrPLCI); + } + if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) { + capi_cmsg_answer(cmsg); + send_message(card, cmsg); + goto notfound; + } + card->bchans[plcip->chan].disconnecting = 1; + plci_change_state(card, plcip, EV_PLCI_DISCONNECT_IND); + capi_cmsg_answer(cmsg); + send_message(card, cmsg); + plci_change_state(card, plcip, EV_PLCI_DISCONNECT_RESP); + break; + + case CAPI_DISCONNECT_CONF: /* plci */ + if (cmsg->Info) { + printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->Info, capi_info2str(cmsg->Info), + cmsg->adr.adrPLCI); + } + if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) + goto notfound; + + card->bchans[plcip->chan].disconnecting = 1; + break; + + case CAPI_ALERT_CONF: /* plci */ + if (cmsg->Info) { + printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->Info, capi_info2str(cmsg->Info), + cmsg->adr.adrPLCI); + } + break; + + case CAPI_CONNECT_IND: /* plci */ + handle_incoming_call(card, cmsg); + break; + + case CAPI_CONNECT_CONF: /* plci */ + if (cmsg->Info) { + printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->Info, capi_info2str(cmsg->Info), + cmsg->adr.adrPLCI); + } + if (!(plcip = find_plci_by_msgid(card, cmsg->Messagenumber))) + goto notfound; + + plcip->plci = cmsg->adr.adrPLCI; + if (cmsg->Info) { + plci_change_state(card, plcip, EV_PLCI_CONNECT_CONF_ERROR); + } else { + plci_change_state(card, plcip, EV_PLCI_CONNECT_CONF_OK); + } + break; + + case CAPI_CONNECT_ACTIVE_IND: /* plci */ + + if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) + goto notfound; + + if (card->bchans[plcip->chan].incoming) { + capi_cmsg_answer(cmsg); + send_message(card, cmsg); + plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND); + } else { + capidrv_ncci *nccip; + capi_cmsg_answer(cmsg); + send_message(card, cmsg); + + nccip = new_ncci(card, plcip, cmsg->adr.adrPLCI); + + if (!nccip) { + printk(KERN_ERR "capidrv: no mem for ncci, sorry\n"); + break; /* $$$$ */ + } + capi_fill_CONNECT_B3_REQ(cmsg, + global.appid, + card->msgid++, + plcip->plci, /* adr */ + 0 /* NCPI */ + ); + nccip->msgid = cmsg->Messagenumber; + send_message(card, cmsg); + cmd.command = ISDN_STAT_DCONN; + cmd.driver = card->myid; + cmd.arg = plcip->chan; + card->interface.statcallb(&cmd); + plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND); + ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_REQ); + } + break; + + case CAPI_INFO_IND: /* Controller/plci */ + + if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) + goto notfound; + + if (cmsg->InfoNumber == 0x4000) { + if (cmsg->InfoElement[0] == 4) { + cmd.command = ISDN_STAT_CINF; + cmd.driver = card->myid; + cmd.arg = plcip->chan; + sprintf(cmd.parm.num, "%lu", + (unsigned long) + ((__u32) cmsg->InfoElement[1] + | ((__u32) (cmsg->InfoElement[2]) << 8) + | ((__u32) (cmsg->InfoElement[3]) << 16) + | ((__u32) (cmsg->InfoElement[4]) << 24))); + card->interface.statcallb(&cmd); + break; + } + } + printk(KERN_ERR "capidrv: %s\n", capi_cmsg2str(cmsg)); + break; + + case CAPI_CONNECT_ACTIVE_CONF: /* plci */ + goto ignored; + case CAPI_SELECT_B_PROTOCOL_CONF: /* plci */ + goto ignored; + case CAPI_FACILITY_IND: /* Controller/plci/ncci */ + goto ignored; + case CAPI_FACILITY_CONF: /* Controller/plci/ncci */ + goto ignored; + + case CAPI_INFO_CONF: /* Controller/plci */ + goto ignored; + + default: + printk(KERN_ERR "capidrv: got %s for plci 0x%x ???", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrPLCI); + } + return; + ignored: + printk(KERN_INFO "capidrv: %s for plci 0x%x ignored\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrPLCI); + return; + notfound: + printk(KERN_ERR "capidrv: %s: plci 0x%x not found\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrPLCI); + return; +} + +static void handle_ncci(_cmsg * cmsg) +{ + capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f); + capidrv_plci *plcip; + capidrv_ncci *nccip; + isdn_ctrl cmd; + + if (!card) { + printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrController & 0x7f); + return; + } + switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) { + + case CAPI_CONNECT_B3_ACTIVE_IND: /* ncci */ + if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) + goto notfound; + + capi_cmsg_answer(cmsg); + send_message(card, cmsg); + ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_ACTIVE_IND); + + cmd.command = ISDN_STAT_BCONN; + cmd.driver = card->myid; + cmd.arg = nccip->chan; + card->interface.statcallb(&cmd); + + printk(KERN_INFO "capidrv: chan %d up with ncci 0x%x\n", + nccip->chan, nccip->ncci); + break; + + case CAPI_CONNECT_B3_ACTIVE_CONF: /* ncci */ + goto ignored; + + case CAPI_CONNECT_B3_IND: /* ncci */ + + plcip = find_plci_by_ncci(card, cmsg->adr.adrNCCI); + if (plcip) { + nccip = new_ncci(card, plcip, cmsg->adr.adrNCCI); + if (nccip) { + ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_IND); + capi_fill_CONNECT_B3_RESP(cmsg, + global.appid, + card->msgid++, + nccip->ncci, /* adr */ + 0, /* Reject */ + 0 /* NCPI */ + ); + send_message(card, cmsg); + ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_RESP); + break; + } + printk(KERN_ERR "capidrv: no mem for ncci, sorry\n"); + } else { + printk(KERN_ERR "capidrv: %s: plci for ncci 0x%x not found\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrNCCI); + } + capi_fill_CONNECT_B3_RESP(cmsg, + global.appid, + card->msgid++, + cmsg->adr.adrNCCI, + 2, /* Reject */ + 0 /* NCPI */ + ); + send_message(card, cmsg); + break; + + case CAPI_CONNECT_B3_CONF: /* ncci */ + + if (!(nccip = find_ncci_by_msgid(card, + cmsg->adr.adrNCCI, + cmsg->Messagenumber))) + goto notfound; + + nccip->ncci = cmsg->adr.adrNCCI; + if (cmsg->Info) { + printk(KERN_INFO "capidrv: %s info 0x%x (%s) for ncci 0x%x\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->Info, capi_info2str(cmsg->Info), + cmsg->adr.adrNCCI); + } + + if (cmsg->Info) + ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_CONF_ERROR); + else + ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_CONF_OK); + break; + + case CAPI_CONNECT_B3_T90_ACTIVE_IND: /* ncci */ + capi_cmsg_answer(cmsg); + send_message(card, cmsg); + break; + + case CAPI_DATA_B3_IND: /* ncci */ + /* handled in handle_data() */ + goto ignored; + + case CAPI_DATA_B3_CONF: /* ncci */ + if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) + goto notfound; + + cmd.command = ISDN_STAT_BSENT; + cmd.driver = card->myid; + cmd.arg = nccip->chan; + card->interface.statcallb(&cmd); + + break; + + case CAPI_DISCONNECT_B3_IND: /* ncci */ + if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) + goto notfound; + + card->bchans[nccip->chan].disconnecting = 1; + ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_IND); + capi_cmsg_answer(cmsg); + send_message(card, cmsg); + ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_RESP); + break; + + case CAPI_DISCONNECT_B3_CONF: /* ncci */ + 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", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->Info, capi_info2str(cmsg->Info), + cmsg->adr.adrNCCI); + ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_CONF_ERROR); + } + break; + + case CAPI_RESET_B3_IND: /* ncci */ + capi_cmsg_answer(cmsg); + send_message(card, cmsg); + break; + + case CAPI_RESET_B3_CONF: /* ncci */ + goto ignored; /* $$$$ */ + + case CAPI_FACILITY_IND: /* Controller/plci/ncci */ + goto ignored; + case CAPI_FACILITY_CONF: /* Controller/plci/ncci */ + goto ignored; + + default: + printk(KERN_ERR "capidrv: got %s for ncci 0x%x ???", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrNCCI); + } + return; + ignored: + printk(KERN_INFO "capidrv: %s for ncci 0x%x ignored\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrNCCI); + return; + notfound: + printk(KERN_ERR "capidrv: %s: ncci 0x%x not found\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrNCCI); +} + + +static void handle_data(_cmsg * cmsg, struct sk_buff *skb) +{ + capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f); + capidrv_ncci *nccip; + + if (!card) { + printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrController & 0x7f); + return; + } + if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) { + printk(KERN_ERR "capidrv: %s: ncci 0x%x not found\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrNCCI); + kfree_skb(skb, FREE_READ); + return; + } + (void) skb_pull(skb, CAPIMSG_LEN(skb->data)); + card->interface.rcvcallb_skb(card->myid, nccip->chan, skb); + capi_cmsg_answer(cmsg); + send_message(card, cmsg); +} + +static _cmsg s_cmsg; + +static void capidrv_signal(__u16 applid, __u32 dummy) +{ + struct sk_buff *skb = 0; + + 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)); + + if (s_cmsg.Command == CAPI_DATA_B3 + && s_cmsg.Subcommand == CAPI_IND) { + handle_data(&s_cmsg, skb); + continue; + } + kfree_skb(skb, FREE_READ); + if ((s_cmsg.adr.adrController & 0xffffff00) == 0) + handle_controller(&s_cmsg); + else if ((s_cmsg.adr.adrPLCI & 0xffff0000) == 0) + handle_plci(&s_cmsg); + else + handle_ncci(&s_cmsg); + } +} + +/* ------------------------------------------------------------------- */ + +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); + return -EINVAL; + } + return -EINVAL; +} + +static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) +{ + isdn_ctrl cmd; + struct capidrv_bchan *bchan; + struct capidrv_plci *plcip; + + if (c->command == ISDN_CMD_IOCTL) + return capidrv_ioctl(c, card); + + switch (c->command) { + case ISDN_CMD_DIAL:{ + __u8 calling[ISDN_MSNLEN + 3]; + __u8 called[ISDN_MSNLEN + 2]; + + if (debugmode) + printk(KERN_DEBUG "capidrv: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n", + c->arg, + c->parm.setup.phone, + c->parm.setup.si1, + c->parm.setup.si2, + c->parm.setup.eazmsn); + + 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", + c->arg, + c->parm.setup.phone, + c->parm.setup.si1, + c->parm.setup.si2, + c->parm.setup.eazmsn, + bchan->plcip->plci); + return 0; + } + bchan->si1 = c->parm.setup.si1; + bchan->si2 = c->parm.setup.si2; + + 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); + + capi_fill_CONNECT_REQ(&cmdcmsg, + global.appid, + card->msgid++, + 1, /* adr */ + si2cip(bchan->si1, bchan->si2), /* cipvalue */ + called, /* CalledPartyNumber */ + calling, /* CallingPartyNumber */ + 0, /* CalledPartySubaddress */ + 0, /* CallingPartySubaddress */ + b1prot(bchan->l2, bchan->l3), /* B1protocol */ + b2prot(bchan->l2, bchan->l3), /* B2protocol */ + b3prot(bchan->l2, bchan->l3), /* B3protocol */ + 0, /* B1configuration */ + 0, /* B2configuration */ + 0, /* B3configuration */ + 0, /* BC */ + 0, /* LLC */ + 0, /* HLC */ + 0, /* BChannelinformation */ + 0, /* Keypadfacility */ + 0, /* Useruserdata */ + 0 /* Facilitydataarray */ + ); + if ((plcip = new_plci(card, (c->arg % card->nbchan))) == 0) { + cmd.command = ISDN_STAT_DHUP; + cmd.driver = card->myid; + cmd.arg = (c->arg % card->nbchan); + card->interface.statcallb(&cmd); + return -1; + } + plcip->msgid = cmdcmsg.Messagenumber; + plci_change_state(card, plcip, EV_PLCI_CONNECT_REQ); + send_message(card, &cmdcmsg); + return 0; + } + + 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]; + + capi_fill_CONNECT_RESP(&cmdcmsg, + global.appid, + card->msgid++, + bchan->plcip->plci, /* adr */ + 0, /* Reject */ + b1prot(bchan->l2, bchan->l3), /* B1protocol */ + b2prot(bchan->l2, bchan->l3), /* B2protocol */ + b3prot(bchan->l2, bchan->l3), /* B3protocol */ + 0, /* B1configuration */ + 0, /* B2configuration */ + 0, /* B3configuration */ + 0, /* ConnectedNumber */ + 0, /* ConnectedSubaddress */ + 0, /* LLC */ + 0, /* BChannelinformation */ + 0, /* Keypadfacility */ + 0, /* Useruserdata */ + 0 /* Facilitydataarray */ + ); + capi_cmsg2message(&cmdcmsg, cmdcmsg.buf); + plci_change_state(card, bchan->plcip, EV_PLCI_CONNECT_RESP); + send_message(card, &cmdcmsg); + return 0; + + case ISDN_CMD_ACCEPTB: + if (debugmode) + printk(KERN_DEBUG "capidrv: ISDN_CMD_ACCEPTB(ch=%ld)\n", + c->arg); + return -ENOSYS; + + case ISDN_CMD_HANGUP: + if (debugmode) + printk(KERN_DEBUG "capidrv: ISDN_CMD_HANGUP(ch=%ld)\n", + c->arg); + bchan = &card->bchans[c->arg % card->nbchan]; + + if (bchan->disconnecting) { + if (debugmode) + printk(KERN_DEBUG "capidrv: chan %ld already disconnecting ...\n", + c->arg); + return 0; + } + if (bchan->nccip) { + bchan->disconnecting = 1; + capi_fill_DISCONNECT_B3_REQ(&cmdcmsg, + global.appid, + card->msgid++, + bchan->nccip->ncci, + 0 /* NCPI */ + ); + ncci_change_state(card, bchan->nccip, EV_NCCI_DISCONNECT_B3_REQ); + send_message(card, &cmdcmsg); + } else if (bchan->plcip) { + bchan->disconnecting = 1; + if (bchan->plcip->state == ST_PLCI_INCOMING) { + /* just ignore, we a called from isdn_status_callback(), + * which will return 0 or 2, this is handled by the + * CONNECT_IND handler + */ + } else { + capi_fill_DISCONNECT_REQ(&cmdcmsg, + global.appid, + card->msgid++, + bchan->plcip->plci, + 0, /* BChannelinformation */ + 0, /* Keypadfacility */ + 0, /* Useruserdata */ + 0 /* Facilitydataarray */ + ); + plci_change_state(card, bchan->plcip, EV_PLCI_DISCONNECT_REQ); + send_message(card, &cmdcmsg); + } + } +/* ready */ + + case ISDN_CMD_SETL2: + if (debugmode) + printk(KERN_DEBUG "capidrv: set L2 on chan %ld to %ld\n", + (c->arg & 0xff), (c->arg >> 8)); + bchan = &card->bchans[c->arg % 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", + (c->arg & 0xff), (c->arg >> 8)); + bchan = &card->bchans[c->arg % 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", + c->parm.num, c->arg); + bchan = &card->bchans[c->arg % card->nbchan]; + strncpy(bchan->msn, c->parm.num, ISDN_MSNLEN); + return 0; + + case ISDN_CMD_CLREAZ: + if (debugmode) + printk(KERN_DEBUG "capidrv: clearing EAZ on chan %ld\n", 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); + MOD_INC_USE_COUNT; + break; + + case ISDN_CMD_UNLOCK: + if (debugmode > 1) + printk(KERN_DEBUG "capidrv: ISDN_CMD_UNLOCK (%ld)\n", c->arg); + MOD_DEC_USE_COUNT; + break; + +/* never called */ + case ISDN_CMD_GETL2: + if (debugmode) + printk(KERN_DEBUG "capidrv: ISDN_CMD_GETL2\n"); + return -ENODEV; + case ISDN_CMD_GETL3: + if (debugmode) + printk(KERN_DEBUG "capidrv: ISDN_CMD_GETL3\n"); + return -ENODEV; + case ISDN_CMD_GETEAZ: + if (debugmode) + printk(KERN_DEBUG "capidrv: ISDN_CMD_GETEAZ\n"); + return -ENODEV; + case ISDN_CMD_SETSIL: + if (debugmode) + printk(KERN_DEBUG "capidrv: ISDN_CMD_SETSIL\n"); + return -ENODEV; + case ISDN_CMD_GETSIL: + if (debugmode) + printk(KERN_DEBUG "capidrv: ISDN_CMD_GETSIL\n"); + return -ENODEV; + default: + printk(KERN_ERR "capidrv: ISDN_CMD_%d, Huh?\n", c->command); + return -EINVAL; + } + return 0; +} + +static int if_command(isdn_ctrl * c) +{ + capidrv_contr *card = findcontrbydriverid(c->driver); + + if (card) + return capidrv_command(c, card); + + printk(KERN_ERR + "capidrv: if_command %d called with invalid driverId %d!\n", + c->command, c->driver); + return -ENODEV; +} + +static _cmsg sendcmsg; + +static int if_sendbuf(int id, int channel, struct sk_buff *skb) +{ + capidrv_contr *card = findcontrbydriverid(id); + capidrv_bchan *bchan; + capidrv_ncci *nccip; + int len = skb->len; + size_t msglen; + __u16 errcode; + + if (!card) { + printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n", + 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); + return 0; + } + capi_fill_DATA_B3_REQ(&sendcmsg, global.appid, card->msgid++, + nccip->ncci, /* adr */ + (__u32) skb->data, /* Data */ + skb->len, /* DataLength */ + nccip->datahandle++, /* DataHandle */ + 0 /* Flags */ + ); + capi_cmsg2message(&sendcmsg, sendcmsg.buf); + msglen = CAPIMSG_LEN(sendcmsg.buf); + 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"); + return 0; + } +#if 0 + printk(KERN_DEBUG "capidrv: only %d bytes headroom\n", + skb_headroom(skb)); +#endif + SET_SKB_FREE(nskb); + memcpy(skb_put(nskb, msglen), sendcmsg.buf, msglen); + memcpy(skb_put(nskb, skb->len), skb->data, skb->len); + errcode = (*capifuncs->capi_put_message) (global.appid, nskb); + switch (errcode) { + case CAPI_NOERROR: + dev_kfree_skb(skb, FREE_WRITE); + return len; + case CAPI_SENDQUEUEFULL: + dev_kfree_skb(nskb, FREE_WRITE); + return 0; + default: + return -1; + } + } else { + memcpy(skb_push(skb, msglen), sendcmsg.buf, msglen); + errcode = (*capifuncs->capi_put_message) (global.appid, skb); + switch (errcode) { + case CAPI_NOERROR: + return len; + case CAPI_SENDQUEUEFULL: + return 0; + default: + return -1; + } + } +} + +static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) +{ + capidrv_contr *card; + isdn_ctrl cmd; + char id[20]; + int i; + + sprintf(id, "capidrv-%d", contr); + if (!(card = (capidrv_contr *) kmalloc(sizeof(capidrv_contr), GFP_ATOMIC))) { + printk(KERN_WARNING + "capidrv: (%s) Could not allocate contr-struct.\n", id); + return -1; + } + memset(card, 0, sizeof(capidrv_contr)); + strcpy(card->name, id); + card->contrnr = contr; + card->nbchan = profp->nbchannel; + card->bchans = (capidrv_bchan *) kmalloc(sizeof(capidrv_bchan) * card->nbchan, GFP_ATOMIC); + if (!card->bchans) { + printk(KERN_WARNING + "capidrv: (%s) Could not allocate bchan-structs.\n", id); + kfree(card); + return -1; + } + card->interface.channels = profp->nbchannel; + card->interface.maxbufsize = 2048; + card->interface.command = if_command; + card->interface.writebuf_skb = if_sendbuf; + card->interface.writecmd = 0; + card->interface.readstat = 0; + card->interface.features = ISDN_FEATURE_L2_X75I | + ISDN_FEATURE_L2_X75UI | + ISDN_FEATURE_L2_X75BUI | + ISDN_FEATURE_L2_HDLC | + ISDN_FEATURE_L2_TRANS | + ISDN_FEATURE_L3_TRANS | + ISDN_FEATURE_P_UNKNOWN; + card->interface.hl_hdrlen = 22; /* len of DATA_B3_REQ */ + strncpy(card->interface.id, id, sizeof(card->interface.id) - 1); + card->next = global.contr_list; + global.contr_list = card; + global.ncontr++; + + if (!register_isdn(&card->interface)) { + global.contr_list = global.contr_list->next; + printk(KERN_ERR "capidrv: Unable to register contr %s\n", id); + kfree(card->bchans); + kfree(card); + return -1; + } + card->myid = card->interface.channels; + + memset(card->bchans, 0, sizeof(capidrv_bchan) * card->nbchan); + for (i = 0; i < card->nbchan; i++) { + card->bchans[i].contr = card; + } + + cmd.driver = card->myid; + cmd.command = ISDN_STAT_RUN; + card->interface.statcallb(&cmd); + + card->cipmask = 1; /* any */ + card->cipmask2 = 0; + + capi_fill_LISTEN_REQ(&cmdcmsg, global.appid, + card->msgid++, + contr, /* controller */ + 1 << 6, /* Infomask */ + card->cipmask, + card->cipmask2, + 0, 0); + send_message(card, &cmdcmsg); + listen_change_state(card, EV_LISTEN_REQ); + + printk(KERN_INFO "%s: now up (%d B channels)\n", + card->name, card->nbchan); + + return 0; +} + +static int capidrv_delcontr(__u16 contr) +{ + capidrv_contr **pp, *card; + isdn_ctrl cmd; + int i; + + for (pp = &global.contr_list; *pp; pp = &(*pp)->next) { + if ((*pp)->contrnr == contr) + break; + } + if (!*pp) { + printk(KERN_ERR "capidrv: delcontr: no contr %u\n", contr); + return -1; + } + card = *pp; + *pp = (*pp)->next; + global.ncontr--; + + for (i = 0; i < card->nbchan; i++) { + if (card->bchans[i].nccip) + free_ncci(card, card->bchans[i].nccip); + if (card->bchans[i].plcip) + free_plci(card, card->bchans[i].plcip); + if (card->plci_list) + printk(KERN_ERR "capidrv: bug in free_plci()\n"); + } + kfree(card->bchans); + + cmd.command = ISDN_STAT_UNLOAD; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + + printk(KERN_INFO "%s: now down.\n", card->name); + + kfree(card); + + return 0; +} + + +static void lower_callback(unsigned int cmd, __u16 contr, void *data) +{ + switch (cmd) { + case KCI_CONTRUP: + (void) capidrv_addcontr(contr, (capi_profile *) data); + break; + case KCI_CONTRDOWN: + (void) capidrv_delcontr(contr); + break; + } +} + +static struct capi_interface_user cuser = { + "capidrv", + lower_callback +}; + +#ifdef MODULE +#define capidrv_init init_module +#endif + +int capidrv_init(void) +{ + struct capi_register_params rparam; + capi_profile profile; + char rev[10]; + char *p; + __u32 ncontr, contr; + __u16 errcode; + + capifuncs = attach_capi_interface(&cuser); + + if (!capifuncs) + return -EIO; + +#ifndef HAS_NEW_SYMTAB + /* No symbols to export, hide all symbols */ + register_symtab(NULL); +#endif + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else + strcpy(rev, " ??? "); + + rparam.level3cnt = 2; + rparam.datablkcnt = 8; + rparam.datablklen = 2048; + errcode = (*capifuncs->capi_register) (&rparam, &global.appid); + if (errcode) { + detach_capi_interface(&cuser); + return -EIO; + } + + errcode = (*capifuncs->capi_get_profile) (0, &profile); + if (errcode != CAPI_NOERROR) { + (void) (*capifuncs->capi_release) (global.appid); + detach_capi_interface(&cuser); + return -EIO; + } + + (void) (*capifuncs->capi_set_signal) (global.appid, capidrv_signal, 0); + + ncontr = profile.ncontroller; + for (contr = 1; contr <= ncontr; contr++) { + errcode = (*capifuncs->capi_get_profile) (contr, &profile); + if (errcode != CAPI_NOERROR) + continue; + (void) capidrv_addcontr(contr, &profile); + } + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + capidrv_contr *card, *next; + char rev[10]; + char *p; + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else { + strcpy(rev, " ??? "); + } + + for (card = global.contr_list; card; card = next) { + next = card->next; + capidrv_delcontr(card->contrnr); + } + + (void) (*capifuncs->capi_release) (global.appid); + detach_capi_interface(&cuser); + + printk(KERN_NOTICE "capidrv: Rev%s: unloaded\n", rev); +} + +#endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/capidrv.h linux/drivers/isdn/avmb1/capidrv.h --- v2.0.30/linux/drivers/isdn/avmb1/capidrv.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/avmb1/capidrv.h Mon Aug 4 17:33:59 1997 @@ -0,0 +1,111 @@ +/* + * $Id: capidrv.h,v 1.1 1997/03/04 21:50:33 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 1997/03/04 21:50:33 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + */ +#ifndef __CAPIDRV_H__ +#define __CAPIDRV_H__ + +/* + * LISTEN state machine + */ +#define ST_LISTEN_NONE 0 /* L-0 */ +#define ST_LISTEN_WAIT_CONF 1 /* L-0.1 */ +#define ST_LISTEN_ACTIVE 2 /* L-1 */ +#define ST_LISTEN_ACTIVE_WAIT_CONF 3 /* L-1.1 */ + + +#define EV_LISTEN_REQ 1 /* L-0 -> L-0.1 + L-1 -> L-1.1 */ +#define EV_LISTEN_CONF_ERROR 2 /* L-0.1 -> L-0 + L-1.1 -> L-1 */ +#define EV_LISTEN_CONF_EMPTY 3 /* L-0.1 -> L-0 + L-1.1 -> L-0 */ +#define EV_LISTEN_CONF_OK 4 /* L-0.1 -> L-1 + L-1.1 -> L.1 */ + +/* + * per plci state machine + */ +#define ST_PLCI_NONE 0 /* P-0 */ +#define ST_PLCI_OUTGOING 1 /* P-0.1 */ +#define ST_PLCI_ALLOCATED 2 /* P-1 */ +#define ST_PLCI_ACTIVE 3 /* P-ACT */ +#define ST_PLCI_INCOMING 4 /* P-2 */ +#define ST_PLCI_FACILITY_IND 5 /* P-3 */ +#define ST_PLCI_ACCEPTING 6 /* P-4 */ +#define ST_PLCI_DISCONNECTING 7 /* P-5 */ +#define ST_PLCI_DISCONNECTED 8 /* P-6 */ + +#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 */ +#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 */ +#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 */ +#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 */ + +/* + * per ncci state machine + */ +#define ST_NCCI_PREVIOUS -1 +#define ST_NCCI_NONE 0 /* N-0 */ +#define ST_NCCI_OUTGOING 1 /* N-0.1 */ +#define ST_NCCI_INCOMING 2 /* N-1 */ +#define ST_NCCI_ALLOCATED 3 /* N-2 */ +#define ST_NCCI_ACTIVE 4 /* N-ACT */ +#define ST_NCCI_RESETING 5 /* N-3 */ +#define ST_NCCI_DISCONNECTING 6 /* N-4 */ +#define ST_NCCI_DISCONNECTED 7 /* N-5 */ + +#define EV_NCCI_CONNECT_B3_REQ 1 /* N-0 -> N-0.1 */ +#define EV_NCCI_CONNECT_B3_IND 2 /* N-0 -> N.1 */ +#define EV_NCCI_CONNECT_B3_CONF_OK 3 /* N-0.1 -> N.2 */ +#define EV_NCCI_CONNECT_B3_CONF_ERROR 4 /* N-0.1 -> N.0 */ +#define EV_NCCI_CONNECT_B3_REJECT 5 /* N-1 -> N-4 */ +#define EV_NCCI_CONNECT_B3_RESP 6 /* N-1 -> N-2 */ +#define EV_NCCI_CONNECT_B3_ACTIVE_IND 7 /* N-2 -> N-ACT */ +#define EV_NCCI_RESET_B3_REQ 8 /* N-ACT -> N-3 */ +#define EV_NCCI_RESET_B3_IND 9 /* N-3 -> N-ACT */ +#define EV_NCCI_DISCONNECT_B3_IND 10 /* N-4 -> N.5 */ +#define EV_NCCI_DISCONNECT_B3_CONF_ERROR 11 /* N-4 -> previous */ +#define EV_NCCI_DISCONNECT_B3_REQ 12 /* N-1 -> N-4 + N-2 -> N-4 + N-3 -> N-4 + N-ACT -> N-4 */ +#define EV_NCCI_DISCONNECT_B3_RESP 13 /* N-5 -> N-0 */ + +#endif /* __CAPIDRV_H__ */ diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/capiutil.c linux/drivers/isdn/avmb1/capiutil.c --- v2.0.30/linux/drivers/isdn/avmb1/capiutil.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/avmb1/capiutil.c Mon Aug 4 17:33:59 1997 @@ -0,0 +1,974 @@ +/* + * $Id: capiutil.c,v 1.3 1997/05/18 09:24:18 calle Exp $ + * + * CAPI 2.0 convert capi message to capi message struct + * + * From CAPI 2.0 Development Kit AVM 1995 (msg.c) + * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: capiutil.c,v $ + * Revision 1.3 1997/05/18 09:24:18 calle + * added verbose disconnect reason reporting to avmb1. + * some fixes in capi20 interface. + * changed info messages for B1-PCI + * + * Revision 1.2 1997/03/05 21:22:13 fritz + * Fix: Symbols have to be exported unconditionally. + * + * Revision 1.1 1997/03/04 21:50:34 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" +#include "capiutil.h" + +/* from CAPI2.0 DDK AVM Berlin GmbH */ + +#ifndef CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON +char *capi_info2str(__u16 reason) +{ + return ".."; +} +#else +char *capi_info2str(__u16 reason) +{ + switch (reason) { + +/*-- informative values (corresponding message was processed) -----*/ + case 0x0001: + return "NCPI not supported by current protocol, NCPI ignored"; + case 0x0002: + return "Flags not supported by current protocol, flags ignored"; + case 0x0003: + return "Alert already sent by another application"; + +/*-- error information concerning CAPI_REGISTER -----*/ + case 0x1001: + return "Too many applications"; + case 0x1002: + return "Logical block size to small, must be at least 128 Bytes"; + case 0x1003: + return "Buffer exceeds 64 kByte"; + case 0x1004: + return "Message buffer size too small, must be at least 1024 Bytes"; + case 0x1005: + return "Max. number of logical connections not supported"; + case 0x1006: + return "Reserved"; + case 0x1007: + return "The message could not be accepted because of an internal busy condition"; + case 0x1008: + return "OS resource error (no memory ?)"; + case 0x1009: + return "CAPI not installed"; + case 0x100A: + return "Controller does not support external equipment"; + case 0x100B: + return "Controller does only support external equipment"; + +/*-- error information concerning message exchange functions -----*/ + case 0x1101: + return "Illegal application number"; + case 0x1102: + return "Illegal command or subcommand or message length less than 12 bytes"; + case 0x1103: + return "The message could not be accepted because of a queue full condition !! The error code does not imply that CAPI cannot receive messages directed to another controller, PLCI or NCCI"; + case 0x1104: + return "Queue is empty"; + case 0x1105: + return "Queue overflow, a message was lost !! This indicates a configuration error. The only recovery from this error is to perform a CAPI_RELEASE"; + case 0x1106: + return "Unknown notification parameter"; + case 0x1107: + return "The Message could not be accepted because of an internal busy condition"; + case 0x1108: + return "OS Resource error (no memory ?)"; + case 0x1109: + return "CAPI not installed"; + case 0x110A: + return "Controller does not support external equipment"; + case 0x110B: + return "Controller does only support external equipment"; + +/*-- error information concerning resource / coding problems -----*/ + case 0x2001: + return "Message not supported in current state"; + case 0x2002: + return "Illegal Controller / PLCI / NCCI"; + case 0x2003: + return "Out of PLCI"; + case 0x2004: + return "Out of NCCI"; + case 0x2005: + return "Out of LISTEN"; + case 0x2006: + return "Out of FAX resources (protocol T.30)"; + case 0x2007: + return "Illegal message parameter coding"; + +/*-- error information concerning requested services -----*/ + case 0x3001: + return "B1 protocol not supported"; + case 0x3002: + return "B2 protocol not supported"; + case 0x3003: + return "B3 protocol not supported"; + case 0x3004: + return "B1 protocol parameter not supported"; + case 0x3005: + return "B2 protocol parameter not supported"; + case 0x3006: + return "B3 protocol parameter not supported"; + case 0x3007: + return "B protocol combination not supported"; + case 0x3008: + return "NCPI not supported"; + case 0x3009: + return "CIP Value unknown"; + case 0x300A: + return "Flags not supported (reserved bits)"; + case 0x300B: + return "Facility not supported"; + case 0x300C: + return "Data length not supported by current protocol"; + case 0x300D: + return "Reset procedure not supported by current protocol"; + +/*-- informations about the clearing of a physical connection -----*/ + case 0x3301: + return "Protocol error layer 1 (broken line or B-channel removed by signalling protocol)"; + case 0x3302: + return "Protocol error layer 2"; + case 0x3303: + return "Protocol error layer 3"; + case 0x3304: + return "Another application got that call"; +/*-- T.30 specific reasons -----*/ + case 0x3311: + return "Connecting not successful (remote station is no FAX G3 machine)"; + case 0x3312: + return "Connecting not successful (training error)"; + case 0x3313: + return "Disconnected before transfer (remote station does not support transfer mode, e.g. resolution)"; + case 0x3314: + return "Disconnected during transfer (remote abort)"; + case 0x3315: + return "Disconnected during transfer (remote procedure error, e.g. unsuccessful repetition of T.30 commands)"; + case 0x3316: + return "Disconnected during transfer (local tx data underrun)"; + case 0x3317: + return "Disconnected during transfer (local rx data overflow)"; + case 0x3318: + return "Disconnected during transfer (local abort)"; + case 0x3319: + return "Illegal parameter coding (e.g. SFF coding error)"; + +/*-- disconnect causes from the network according to ETS 300 102-1/Q.931 -----*/ + case 0x3481: return "Unallocated (unassigned) number"; + case 0x3482: return "No route to specified transit network"; + case 0x3483: return "No route to destination"; + case 0x3486: return "Channel unacceptable"; + case 0x3487: + return "Call awarded and being delivered in an established channel"; + case 0x3490: return "Normal call clearing"; + case 0x3491: return "User busy"; + case 0x3492: return "No user responding"; + case 0x3493: return "No answer from user (user alerted)"; + case 0x3495: return "Call rejected"; + case 0x3496: return "Number changed"; + case 0x349A: return "Non-selected user clearing"; + case 0x349B: return "Destination out of order"; + case 0x349C: return "Invalid number format"; + case 0x349D: return "Facility rejected"; + case 0x349E: return "Response to STATUS ENQUIRY"; + case 0x349F: return "Normal, unspecified"; + case 0x34A2: return "No circuit / channel available"; + case 0x34A6: return "Network out of order"; + case 0x34A9: return "Temporary failure"; + case 0x34AA: return "Switching equipment congestion"; + case 0x34AB: return "Access information discarded"; + case 0x34AC: return "Requested circuit / channel not available"; + case 0x34AF: return "Resources unavailable, unspecified"; + case 0x34B1: return "Quality of service unavailable"; + case 0x34B2: return "Requested facility not subscribed"; + case 0x34B9: return "Bearer capability not authorized"; + case 0x34BA: return "Bearer capability not presently available"; + case 0x34BF: return "Service or option not available, unspecified"; + case 0x34C1: return "Bearer capability not implemented"; + case 0x34C2: return "Channel type not implemented"; + case 0x34C5: return "Requested facility not implemented"; + case 0x34C6: return "Only restricted digital information bearer capability is available"; + case 0x34CF: return "Service or option not implemented, unspecified"; + case 0x34D1: return "Invalid call reference value"; + case 0x34D2: return "Identified channel does not exist"; + case 0x34D3: return "A suspended call exists, but this call identity does not"; + case 0x34D4: return "Call identity in use"; + case 0x34D5: return "No call suspended"; + case 0x34D6: return "Call having the requested call identity has been cleared"; + case 0x34D8: return "Incompatible destination"; + case 0x34DB: return "Invalid transit network selection"; + case 0x34DF: return "Invalid message, unspecified"; + case 0x34E0: return "Mandatory information element is missing"; + case 0x34E1: return "Message type non-existent or not implemented"; + case 0x34E2: return "Message not compatible with call state or message type non-existent or not implemented"; + case 0x34E3: return "Information element non-existent or not implemented"; + case 0x34E4: return "Invalid information element contents"; + case 0x34E5: return "Message not compatible with call state"; + case 0x34E6: return "Recovery on timer expiry"; + case 0x34EF: return "Protocol error, unspecified"; + case 0x34FF: return "Interworking, unspecified"; + + default: return "No additional information"; + } +} +#endif + +typedef struct { + int typ; + size_t off; +} _cdef; + +#define _CBYTE 1 +#define _CWORD 2 +#define _CDWORD 3 +#define _CSTRUCT 4 +#define _CMSTRUCT 5 +#define _CEND 6 + +static _cdef cdef[] = +{ + /*00 */ + {_CEND}, + /*01 */ + {_CEND}, + /*02 */ + {_CEND}, + /*03 */ + {_CDWORD, offsetof(_cmsg, adr.adrController)}, + /*04 */ + {_CMSTRUCT, offsetof(_cmsg, AdditionalInfo)}, + /*05 */ + {_CSTRUCT, offsetof(_cmsg, B1configuration)}, + /*06 */ + {_CWORD, offsetof(_cmsg, B1protocol)}, + /*07 */ + {_CSTRUCT, offsetof(_cmsg, B2configuration)}, + /*08 */ + {_CWORD, offsetof(_cmsg, B2protocol)}, + /*09 */ + {_CSTRUCT, offsetof(_cmsg, B3configuration)}, + /*0a */ + {_CWORD, offsetof(_cmsg, B3protocol)}, + /*0b */ + {_CSTRUCT, offsetof(_cmsg, BC)}, + /*0c */ + {_CSTRUCT, offsetof(_cmsg, BChannelinformation)}, + /*0d */ + {_CMSTRUCT, offsetof(_cmsg, BProtocol)}, + /*0e */ + {_CSTRUCT, offsetof(_cmsg, CalledPartyNumber)}, + /*0f */ + {_CSTRUCT, offsetof(_cmsg, CalledPartySubaddress)}, + /*10 */ + {_CSTRUCT, offsetof(_cmsg, CallingPartyNumber)}, + /*11 */ + {_CSTRUCT, offsetof(_cmsg, CallingPartySubaddress)}, + /*12 */ + {_CDWORD, offsetof(_cmsg, CIPmask)}, + /*13 */ + {_CDWORD, offsetof(_cmsg, CIPmask2)}, + /*14 */ + {_CWORD, offsetof(_cmsg, CIPValue)}, + /*15 */ + {_CDWORD, offsetof(_cmsg, Class)}, + /*16 */ + {_CSTRUCT, offsetof(_cmsg, ConnectedNumber)}, + /*17 */ + {_CSTRUCT, offsetof(_cmsg, ConnectedSubaddress)}, + /*18 */ + {_CDWORD, offsetof(_cmsg, Data)}, + /*19 */ + {_CWORD, offsetof(_cmsg, DataHandle)}, + /*1a */ + {_CWORD, offsetof(_cmsg, DataLength)}, + /*1b */ + {_CSTRUCT, offsetof(_cmsg, FacilityConfirmationParameter)}, + /*1c */ + {_CSTRUCT, offsetof(_cmsg, Facilitydataarray)}, + /*1d */ + {_CSTRUCT, offsetof(_cmsg, FacilityIndicationParameter)}, + /*1e */ + {_CSTRUCT, offsetof(_cmsg, FacilityRequestParameter)}, + /*1f */ + {_CWORD, offsetof(_cmsg, FacilitySelector)}, + /*20 */ + {_CWORD, offsetof(_cmsg, Flags)}, + /*21 */ + {_CDWORD, offsetof(_cmsg, Function)}, + /*22 */ + {_CSTRUCT, offsetof(_cmsg, HLC)}, + /*23 */ + {_CWORD, offsetof(_cmsg, Info)}, + /*24 */ + {_CSTRUCT, offsetof(_cmsg, InfoElement)}, + /*25 */ + {_CDWORD, offsetof(_cmsg, InfoMask)}, + /*26 */ + {_CWORD, offsetof(_cmsg, InfoNumber)}, + /*27 */ + {_CSTRUCT, offsetof(_cmsg, Keypadfacility)}, + /*28 */ + {_CSTRUCT, offsetof(_cmsg, LLC)}, + /*29 */ + {_CSTRUCT, offsetof(_cmsg, ManuData)}, + /*2a */ + {_CDWORD, offsetof(_cmsg, ManuID)}, + /*2b */ + {_CSTRUCT, offsetof(_cmsg, NCPI)}, + /*2c */ + {_CWORD, offsetof(_cmsg, Reason)}, + /*2d */ + {_CWORD, offsetof(_cmsg, Reason_B3)}, + /*2e */ + {_CWORD, offsetof(_cmsg, Reject)}, + /*2f */ + {_CSTRUCT, offsetof(_cmsg, Useruserdata)} +}; + +static unsigned char *cpars[] = +{ + /*00 */ 0, + /*01 ALERT_REQ */ (unsigned char *) "\x03\x04\x0c\x27\x2f\x1c\x01\x01", + /*02 CONNECT_REQ */ (unsigned char *) "\x03\x14\x0e\x10\x0f\x11\x0d\x06\x08\x0a\x05\x07\x09\x01\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01", + /*03 */ 0, + /*04 DISCONNECT_REQ */ (unsigned char *) "\x03\x04\x0c\x27\x2f\x1c\x01\x01", + /*05 LISTEN_REQ */ (unsigned char *) "\x03\x25\x12\x13\x10\x11\x01", + /*06 */ 0, + /*07 */ 0, + /*08 INFO_REQ */ (unsigned char *) "\x03\x0e\x04\x0c\x27\x2f\x1c\x01\x01", + /*09 FACILITY_REQ */ (unsigned char *) "\x03\x1f\x1e\x01", + /*0a SELECT_B_PROTOCOL_REQ */ (unsigned char *) "\x03\x0d\x06\x08\x0a\x05\x07\x09\x01\x01", + /*0b CONNECT_B3_REQ */ (unsigned char *) "\x03\x2b\x01", + /*0c */ 0, + /*0d DISCONNECT_B3_REQ */ (unsigned char *) "\x03\x2b\x01", + /*0e */ 0, + /*0f DATA_B3_REQ */ (unsigned char *) "\x03\x18\x1a\x19\x20\x01", + /*10 RESET_B3_REQ */ (unsigned char *) "\x03\x2b\x01", + /*11 */ 0, + /*12 */ 0, + /*13 ALERT_CONF */ (unsigned char *) "\x03\x23\x01", + /*14 CONNECT_CONF */ (unsigned char *) "\x03\x23\x01", + /*15 */ 0, + /*16 DISCONNECT_CONF */ (unsigned char *) "\x03\x23\x01", + /*17 LISTEN_CONF */ (unsigned char *) "\x03\x23\x01", + /*18 MANUFACTURER_REQ */ (unsigned char *) "\x03\x2a\x15\x21\x29\x01", + /*19 */ 0, + /*1a INFO_CONF */ (unsigned char *) "\x03\x23\x01", + /*1b FACILITY_CONF */ (unsigned char *) "\x03\x23\x1f\x1b\x01", + /*1c SELECT_B_PROTOCOL_CONF */ (unsigned char *) "\x03\x23\x01", + /*1d CONNECT_B3_CONF */ (unsigned char *) "\x03\x23\x01", + /*1e */ 0, + /*1f DISCONNECT_B3_CONF */ (unsigned char *) "\x03\x23\x01", + /*20 */ 0, + /*21 DATA_B3_CONF */ (unsigned char *) "\x03\x19\x23\x01", + /*22 RESET_B3_CONF */ (unsigned char *) "\x03\x23\x01", + /*23 */ 0, + /*24 */ 0, + /*25 */ 0, + /*26 CONNECT_IND */ (unsigned char *) "\x03\x14\x0e\x10\x0f\x11\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01", + /*27 CONNECT_ACTIVE_IND */ (unsigned char *) "\x03\x16\x17\x28\x01", + /*28 DISCONNECT_IND */ (unsigned char *) "\x03\x2c\x01", + /*29 */ 0, + /*2a MANUFACTURER_CONF */ (unsigned char *) "\x03\x2a\x15\x21\x29\x01", + /*2b */ 0, + /*2c INFO_IND */ (unsigned char *) "\x03\x26\x24\x01", + /*2d FACILITY_IND */ (unsigned char *) "\x03\x1f\x1d\x01", + /*2e */ 0, + /*2f CONNECT_B3_IND */ (unsigned char *) "\x03\x2b\x01", + /*30 CONNECT_B3_ACTIVE_IND */ (unsigned char *) "\x03\x2b\x01", + /*31 DISCONNECT_B3_IND */ (unsigned char *) "\x03\x2d\x2b\x01", + /*32 */ 0, + /*33 DATA_B3_IND */ (unsigned char *) "\x03\x18\x1a\x19\x20\x01", + /*34 RESET_B3_IND */ (unsigned char *) "\x03\x2b\x01", + /*35 CONNECT_B3_T90_ACTIVE_IND */ (unsigned char *) "\x03\x2b\x01", + /*36 */ 0, + /*37 */ 0, + /*38 CONNECT_RESP */ (unsigned char *) "\x03\x2e\x0d\x06\x08\x0a\x05\x07\x09\x01\x16\x17\x28\x04\x0c\x27\x2f\x1c\x01\x01", + /*39 CONNECT_ACTIVE_RESP */ (unsigned char *) "\x03\x01", + /*3a DISCONNECT_RESP */ (unsigned char *) "\x03\x01", + /*3b */ 0, + /*3c MANUFACTURER_IND */ (unsigned char *) "\x03\x2a\x15\x21\x29\x01", + /*3d */ 0, + /*3e INFO_RESP */ (unsigned char *) "\x03\x01", + /*3f FACILITY_RESP */ (unsigned char *) "\x03\x1f\x01", + /*40 */ 0, + /*41 CONNECT_B3_RESP */ (unsigned char *) "\x03\x2e\x2b\x01", + /*42 CONNECT_B3_ACTIVE_RESP */ (unsigned char *) "\x03\x01", + /*43 DISCONNECT_B3_RESP */ (unsigned char *) "\x03\x01", + /*44 */ 0, + /*45 DATA_B3_RESP */ (unsigned char *) "\x03\x19\x01", + /*46 RESET_B3_RESP */ (unsigned char *) "\x03\x01", + /*47 CONNECT_B3_T90_ACTIVE_RESP */ (unsigned char *) "\x03\x01", + /*48 */ 0, + /*49 */ 0, + /*4a */ 0, + /*4b */ 0, + /*4c */ 0, + /*4d */ 0, + /*4e MANUFACTURER_RESP */ (unsigned char *) "\x03\x2a\x15\x21\x29\x01", +}; + +/*-------------------------------------------------------*/ + +#define byteTLcpy(x,y) *(__u8 *)(x)=*(__u8 *)(y); +#define wordTLcpy(x,y) *(__u16 *)(x)=*(__u16 *)(y); +#define dwordTLcpy(x,y) memcpy(x,y,4); +#define structTLcpy(x,y,l) memcpy (x,y,l) +#define structTLcpyovl(x,y,l) memmove (x,y,l) + +#define byteTRcpy(x,y) *(__u8 *)(y)=*(__u8 *)(x); +#define wordTRcpy(x,y) *(__u16 *)(y)=*(__u16 *)(x); +#define dwordTRcpy(x,y) memcpy(y,x,4); +#define structTRcpy(x,y,l) memcpy (y,x,l) +#define structTRcpyovl(x,y,l) memmove (y,x,l) + +/*-------------------------------------------------------*/ +static unsigned command_2_index(unsigned c, unsigned sc) +{ + if (c & 0x80) + c = 0x9 + (c & 0x0f); + else if (c <= 0x0f); + else if (c == 0x41) + c = 0x9 + 0x1; + else if (c == 0xff) + c = 0x00; + return (sc & 3) * (0x9 + 0x9) + c; +} + +/*-------------------------------------------------------*/ +#define TYP (cdef[cmsg->par[cmsg->p]].typ) +#define OFF (((__u8 *)cmsg)+cdef[cmsg->par[cmsg->p]].off) + +static void jumpcstruct(_cmsg * cmsg) +{ + unsigned layer; + for (cmsg->p++, layer = 1; layer;) { + /* $$$$$ assert (cmsg->p); */ + cmsg->p++; + switch (TYP) { + case _CMSTRUCT: + layer++; + break; + case _CEND: + layer--; + break; + } + } +} +/*-------------------------------------------------------*/ +static void pars_2_message(_cmsg * cmsg) +{ + + for (; TYP != _CEND; cmsg->p++) { + switch (TYP) { + case _CBYTE: + byteTLcpy(cmsg->m + cmsg->l, OFF); + cmsg->l++; + break; + case _CWORD: + wordTLcpy(cmsg->m + cmsg->l, OFF); + cmsg->l += 2; + break; + case _CDWORD: + dwordTLcpy(cmsg->m + cmsg->l, OFF); + cmsg->l += 4; + break; + case _CSTRUCT: + if (*(__u8 **) OFF == 0) { + *(cmsg->m + cmsg->l) = '\0'; + cmsg->l++; + } else if (**(_cstruct *) OFF != 0xff) { + structTLcpy(cmsg->m + cmsg->l, *(_cstruct *) OFF, 1 + **(_cstruct *) OFF); + cmsg->l += 1 + **(_cstruct *) OFF; + } else { + _cstruct s = *(_cstruct *) OFF; + structTLcpy(cmsg->m + cmsg->l, s, 3 + *(__u16 *) (s + 1)); + cmsg->l += 3 + *(__u16 *) (s + 1); + } + break; + case _CMSTRUCT: +/*----- Metastruktur 0 -----*/ + if (*(_cmstruct *) OFF == CAPI_DEFAULT) { + *(cmsg->m + cmsg->l) = '\0'; + cmsg->l++; + jumpcstruct(cmsg); + } +/*----- Metastruktur wird composed -----*/ + else { + unsigned _l = cmsg->l; + unsigned _ls; + cmsg->l++; + cmsg->p++; + pars_2_message(cmsg); + _ls = cmsg->l - _l - 1; + if (_ls < 255) + (cmsg->m + _l)[0] = (__u8) _ls; + else { + structTLcpyovl(cmsg->m + _l + 3, cmsg->m + _l + 1, _ls); + (cmsg->m + _l)[0] = 0xff; + wordTLcpy(cmsg->m + _l + 1, &_ls); + } + } + break; + } + } +} + +/*-------------------------------------------------------*/ +unsigned capi_cmsg2message(_cmsg * cmsg, __u8 * msg) +{ + cmsg->m = msg; + cmsg->l = 8; + cmsg->p = 0; + cmsg->par = cpars[command_2_index(cmsg->Command, cmsg->Subcommand)]; + + pars_2_message(cmsg); + + wordTLcpy(msg + 0, &cmsg->l); + byteTLcpy(cmsg->m + 4, &cmsg->Command); + byteTLcpy(cmsg->m + 5, &cmsg->Subcommand); + wordTLcpy(cmsg->m + 2, &cmsg->ApplId); + wordTLcpy(cmsg->m + 6, &cmsg->Messagenumber); + + return 0; +} + +/*-------------------------------------------------------*/ +static void message_2_pars(_cmsg * cmsg) +{ + for (; TYP != _CEND; cmsg->p++) { + + switch (TYP) { + case _CBYTE: + byteTRcpy(cmsg->m + cmsg->l, OFF); + cmsg->l++; + break; + case _CWORD: + wordTRcpy(cmsg->m + cmsg->l, OFF); + cmsg->l += 2; + break; + case _CDWORD: + dwordTRcpy(cmsg->m + cmsg->l, OFF); + cmsg->l += 4; + break; + case _CSTRUCT: + *(__u8 **) OFF = cmsg->m + cmsg->l; + + if (cmsg->m[cmsg->l] != 0xff) + cmsg->l += 1 + cmsg->m[cmsg->l]; + else + cmsg->l += 3 + *(__u16 *) (cmsg->m + cmsg->l + 1); + break; + case _CMSTRUCT: +/*----- Metastruktur 0 -----*/ + if (cmsg->m[cmsg->l] == '\0') { + *(_cmstruct *) OFF = CAPI_DEFAULT; + cmsg->l++; + jumpcstruct(cmsg); + } else { + unsigned _l = cmsg->l; + *(_cmstruct *) OFF = CAPI_COMPOSE; + cmsg->l = (cmsg->m + _l)[0] == 255 ? cmsg->l + 3 : cmsg->l + 1; + cmsg->p++; + message_2_pars(cmsg); + } + break; + } + } +} + +/*-------------------------------------------------------*/ +unsigned capi_message2cmsg(_cmsg * cmsg, __u8 * msg) +{ + memset(cmsg, 0, sizeof(_cmsg)); + cmsg->m = msg; + cmsg->l = 8; + cmsg->p = 0; + byteTRcpy(cmsg->m + 4, &cmsg->Command); + byteTRcpy(cmsg->m + 5, &cmsg->Subcommand); + cmsg->par = cpars[command_2_index(cmsg->Command, cmsg->Subcommand)]; + + message_2_pars(cmsg); + + wordTRcpy(msg + 0, &cmsg->l); + wordTRcpy(cmsg->m + 2, &cmsg->ApplId); + wordTRcpy(cmsg->m + 6, &cmsg->Messagenumber); + + return 0; +} + +/*-------------------------------------------------------*/ +unsigned capi_cmsg_header(_cmsg * cmsg, __u16 _ApplId, + __u8 _Command, __u8 _Subcommand, + __u16 _Messagenumber, __u32 _Controller) +{ + memset(cmsg, 0, sizeof(_cmsg)); + cmsg->ApplId = _ApplId; + cmsg->Command = _Command; + cmsg->Subcommand = _Subcommand; + cmsg->Messagenumber = _Messagenumber; + cmsg->adr.adrController = _Controller; + return 0; +} + +/*-------------------------------------------------------*/ + +static char *mnames[] = +{ + 0, + "ALERT_REQ", + "CONNECT_REQ", + 0, + "DISCONNECT_REQ", + "LISTEN_REQ", + 0, + 0, + "INFO_REQ", + "FACILITY_REQ", + "SELECT_B_PROTOCOL_REQ", + "CONNECT_B3_REQ", + 0, + "DISCONNECT_B3_REQ", + 0, + "DATA_B3_REQ", + "RESET_B3_REQ", + 0, + 0, + "ALERT_CONF", + "CONNECT_CONF", + 0, + "DISCONNECT_CONF", + "LISTEN_CONF", + "MANUFACTURER_REQ", + 0, + "INFO_CONF", + "FACILITY_CONF", + "SELECT_B_PROTOCOL_CONF", + "CONNECT_B3_CONF", + 0, + "DISCONNECT_B3_CONF", + 0, + "DATA_B3_CONF", + "RESET_B3_CONF", + 0, + 0, + 0, + "CONNECT_IND", + "CONNECT_ACTIVE_IND", + "DISCONNECT_IND", + 0, + "MANUFACTURER_CONF", + 0, + "INFO_IND", + "FACILITY_IND", + 0, + "CONNECT_B3_IND", + "CONNECT_B3_ACTIVE_IND", + "DISCONNECT_B3_IND", + 0, + "DATA_B3_IND", + "RESET_B3_IND", + "CONNECT_B3_T90_ACTIVE_IND", + 0, + 0, + "CONNECT_RESP", + "CONNECT_ACTIVE_RESP", + "DISCONNECT_RESP", + 0, + "MANUFACTURER_IND", + 0, + "INFO_RESP", + "FACILITY_RESP", + 0, + "CONNECT_B3_RESP", + "CONNECT_B3_ACTIVE_RESP", + "DISCONNECT_B3_RESP", + 0, + "DATA_B3_RESP", + "RESET_B3_RESP", + "CONNECT_B3_T90_ACTIVE_RESP", + 0, + 0, + 0, + 0, + 0, + 0, + "MANUFACTURER_RESP" +}; + +char *capi_cmd2str(__u8 cmd, __u8 subcmd) +{ + return mnames[command_2_index(cmd, subcmd)]; +} + + +/*-------------------------------------------------------*/ +/*-------------------------------------------------------*/ + +static char *pnames[] = +{ + /*00 */ 0, + /*01 */ 0, + /*02 */ 0, + /*03 */ "Controller/PLCI/NCCI", + /*04 */ "AdditionalInfo", + /*05 */ "B1configuration", + /*06 */ "B1protocol", + /*07 */ "B2configuration", + /*08 */ "B2protocol", + /*09 */ "B3configuration", + /*0a */ "B3protocol", + /*0b */ "BC", + /*0c */ "BChannelinformation", + /*0d */ "BProtocol", + /*0e */ "CalledPartyNumber", + /*0f */ "CalledPartySubaddress", + /*10 */ "CallingPartyNumber", + /*11 */ "CallingPartySubaddress", + /*12 */ "CIPmask", + /*13 */ "CIPmask2", + /*14 */ "CIPValue", + /*15 */ "Class", + /*16 */ "ConnectedNumber", + /*17 */ "ConnectedSubaddress", + /*18 */ "Data", + /*19 */ "DataHandle", + /*1a */ "DataLength", + /*1b */ "FacilityConfirmationParameter", + /*1c */ "Facilitydataarray", + /*1d */ "FacilityIndicationParameter", + /*1e */ "FacilityRequestParameter", + /*1f */ "FacilitySelector", + /*20 */ "Flags", + /*21 */ "Function", + /*22 */ "HLC", + /*23 */ "Info", + /*24 */ "InfoElement", + /*25 */ "InfoMask", + /*26 */ "InfoNumber", + /*27 */ "Keypadfacility", + /*28 */ "LLC", + /*29 */ "ManuData", + /*2a */ "ManuID", + /*2b */ "NCPI", + /*2c */ "Reason", + /*2d */ "Reason_B3", + /*2e */ "Reject", + /*2f */ "Useruserdata" +}; + + +static char buf[8192]; +static char *p = 0; + +#include + +/*-------------------------------------------------------*/ +static void bufprint(char *fmt,...) +{ + va_list f; + va_start(f, fmt); + vsprintf(p, fmt, f); + va_end(f); + p += strlen(p); +} + +static void printstructlen(__u8 * m, unsigned len) +{ + unsigned hex = 0; + for (; len; len--, m++) + if (isalnum(*m) || *m == ' ') { + if (hex) + bufprint(">"); + bufprint("%c", *m); + hex = 0; + } else { + if (!hex) + bufprint("<%02x", *m); + else + bufprint(" %02x", *m); + hex = 1; + } + if (hex) + bufprint(">"); +} + +static void printstruct(__u8 * m) +{ + unsigned len; + if (m[0] != 0xff) { + len = m[0]; + m += 1; + } else { + len = ((__u16 *) (m + 1))[0]; + m += 3; + } + printstructlen(m, len); +} + +/*-------------------------------------------------------*/ +#define NAME (pnames[cmsg->par[cmsg->p]]) + +static void protocol_message_2_pars(_cmsg * cmsg, int level) +{ + for (; TYP != _CEND; cmsg->p++) { + int slen = 29 + 3 - level; + int i; + + bufprint(" "); + for (i = 0; i < level - 1; i++) + bufprint(" "); + + switch (TYP) { + case _CBYTE: + bufprint("%-*s = 0x%x\n", slen, NAME, *(__u8 *) (cmsg->m + cmsg->l)); + cmsg->l++; + break; + case _CWORD: + bufprint("%-*s = 0x%x\n", slen, NAME, *(__u16 *) (cmsg->m + cmsg->l)); + cmsg->l += 2; + break; + case _CDWORD: + if (strcmp(NAME, "Data") == 0) { + bufprint("%-*s = ", slen, NAME); + printstructlen((__u8 *) * (__u32 *) (cmsg->m + cmsg->l), + *(__u16 *) (cmsg->m + cmsg->l + sizeof(__u32))); + bufprint("\n"); + } else + bufprint("%-*s = 0x%lx\n", slen, NAME, *(__u32 *) (cmsg->m + cmsg->l)); + cmsg->l += 4; + break; + case _CSTRUCT: + bufprint("%-*s = ", slen, NAME); + if (cmsg->m[cmsg->l] == '\0') + bufprint("default"); + else + printstruct(cmsg->m + cmsg->l); + bufprint("\n"); + if (cmsg->m[cmsg->l] != 0xff) + cmsg->l += 1 + cmsg->m[cmsg->l]; + else + cmsg->l += 3 + *(__u16 *) (cmsg->m + cmsg->l + 1); + + break; + + case _CMSTRUCT: +/*----- Metastruktur 0 -----*/ + if (cmsg->m[cmsg->l] == '\0') { + bufprint("%-*s = default\n", slen, NAME); + cmsg->l++; + jumpcstruct(cmsg); + } else { + char *name = NAME; + unsigned _l = cmsg->l; + bufprint("%-*s\n", slen, name); + cmsg->l = (cmsg->m + _l)[0] == 255 ? cmsg->l + 3 : cmsg->l + 1; + cmsg->p++; + protocol_message_2_pars(cmsg, level + 1); + } + break; + } + } +} +/*-------------------------------------------------------*/ +char *capi_message2str(__u8 * msg) +{ + + _cmsg cmsg; + p = buf; + p[0] = 0; + + cmsg.m = msg; + cmsg.l = 8; + cmsg.p = 0; + byteTRcpy(cmsg.m + 4, &cmsg.Command); + byteTRcpy(cmsg.m + 5, &cmsg.Subcommand); + cmsg.par = cpars[command_2_index(cmsg.Command, cmsg.Subcommand)]; + + bufprint("%-26s ID=%03d #0x%04x LEN=%04d\n", + mnames[command_2_index(cmsg.Command, cmsg.Subcommand)], + ((unsigned short *) msg)[1], + ((unsigned short *) msg)[3], + ((unsigned short *) msg)[0]); + + protocol_message_2_pars(&cmsg, 1); + return buf; +} + +char *capi_cmsg2str(_cmsg * cmsg) +{ + p = buf; + p[0] = 0; + cmsg->l = 8; + cmsg->p = 0; + bufprint("%s ID=%03d #0x%04x LEN=%04d\n", + mnames[command_2_index(cmsg->Command, cmsg->Subcommand)], + ((__u16 *) cmsg->m)[1], + ((__u16 *) cmsg->m)[3], + ((__u16 *) cmsg->m)[0]); + protocol_message_2_pars(cmsg, 1); + return buf; +} + + +#ifdef HAS_NEW_SYMTAB +EXPORT_SYMBOL(capi_cmsg2message); +EXPORT_SYMBOL(capi_message2cmsg); +EXPORT_SYMBOL(capi_cmsg_header); +EXPORT_SYMBOL(capi_cmd2str); +EXPORT_SYMBOL(capi_cmsg2str); +EXPORT_SYMBOL(capi_message2str); +#else +static struct symbol_table capifunc_syms = +{ +#include + X(capi_cmsg2message), + X(capi_message2cmsg), + X(capi_cmsg_header), + X(capi_cmd2str), + X(capi_cmsg2str), + X(capi_message2str), + X(capi_info2str), +#include +}; +#endif + +#ifdef MODULE + +int init_module(void) +{ +#ifndef HAS_NEW_SYMTAB + register_symtab(&capifunc_syms); +#endif + return 0; +} + +void cleanup_module(void) +{ +} + +#endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/capiutil.h linux/drivers/isdn/avmb1/capiutil.h --- v2.0.30/linux/drivers/isdn/avmb1/capiutil.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/avmb1/capiutil.h Mon Aug 4 17:33:59 1997 @@ -0,0 +1,501 @@ +/* + * $Id: capiutil.h,v 1.2 1997/05/18 09:24:19 calle Exp $ + * + * CAPI 2.0 defines & types + * + * From CAPI 2.0 Development Kit AVM 1995 (capi20.h) + * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: capiutil.h,v $ + * Revision 1.2 1997/05/18 09:24:19 calle + * added verbose disconnect reason reporting to avmb1. + * some fixes in capi20 interface. + * changed info messages for B1-PCI + * + * Revision 1.1 1997/03/04 21:50:35 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + * + */ +#ifndef __CAPIUTIL_H__ +#define __CAPIUTIL_H__ + +#include + +#define CAPIMSG_LEN(m) (m[0] | (m[1] << 8)) +#define CAPIMSG_APPID(m) (m[2] | (m[3] << 8)) +#define CAPIMSG_COMMAND(m) (m[4]) +#define CAPIMSG_SUBCOMMAND(m) (m[5]) +#define CAPIMSG_MSGID(m) (m[6] | (m[7] << 8)) +#define CAPIMSG_CONTROLLER(m) (m[8] & 0x7f) +#define CAPIMSG_CONTROL(m) (m[8]|(m[9]<<8)|(m[10]<<16)|(m[11]<<24)) +#define CAPIMSG_NCCI(m) CAPIMSG_CONTROL(m) +#define CAPIMSG_DATA(m) (m[12]|(m[13]<<8)|(m[14]<<16)|(m[15]<<24)) +#define CAPIMSG_DATALEN(m) (m[16] | (m[17]<<8)) + +#define CAPIMSG_SETAPPID(m, applid) \ + do { \ + ((__u8 *)m)[2] = (__u16)(applid) & 0xff; \ + ((__u8 *)m)[3] = ((__u16)(applid) >> 8) & 0xff; \ + } while (0) + +#define CAPIMSG_SETDATA(m, data) \ + do { \ + ((__u8 *)m)[12] = (__u32)(data) & 0xff; \ + ((__u8 *)m)[13] = ((__u32)(data) >> 8) & 0xff; \ + ((__u8 *)m)[14] = ((__u32)(data) >> 16) & 0xff; \ + ((__u8 *)m)[15] = ((__u32)(data) >> 24) & 0xff; \ + } while (0) + +/*----- basic-type definitions -----*/ + +typedef __u8 *_cstruct; + +typedef enum { + CAPI_COMPOSE, + CAPI_DEFAULT +} _cmstruct; + +/* + The _cmsg structure contains all possible CAPI 2.0 parameter. + All parameters are stored here first. The function CAPI_CMSG_2_MESSAGE + assembles the parameter and builds CAPI2.0 conform messages. + CAPI_MESSAGE_2_CMSG disassembles CAPI 2.0 messages and stores the + parameter in the _cmsg structure + */ + +typedef struct { + /* Header */ + __u16 ApplId; + __u8 Command; + __u8 Subcommand; + __u16 Messagenumber; + + /* Parameter */ + union { + __u32 adrController; + __u32 adrPLCI; + __u32 adrNCCI; + } adr; + + _cmstruct AdditionalInfo; + _cstruct B1configuration; + __u16 B1protocol; + _cstruct B2configuration; + __u16 B2protocol; + _cstruct B3configuration; + __u16 B3protocol; + _cstruct BC; + _cstruct BChannelinformation; + _cmstruct BProtocol; + _cstruct CalledPartyNumber; + _cstruct CalledPartySubaddress; + _cstruct CallingPartyNumber; + _cstruct CallingPartySubaddress; + __u32 CIPmask; + __u32 CIPmask2; + __u16 CIPValue; + __u32 Class; + _cstruct ConnectedNumber; + _cstruct ConnectedSubaddress; + __u32 Data; + __u16 DataHandle; + __u16 DataLength; + _cstruct FacilityConfirmationParameter; + _cstruct Facilitydataarray; + _cstruct FacilityIndicationParameter; + _cstruct FacilityRequestParameter; + __u16 FacilitySelector; + __u16 Flags; + __u32 Function; + _cstruct HLC; + __u16 Info; + _cstruct InfoElement; + __u32 InfoMask; + __u16 InfoNumber; + _cstruct Keypadfacility; + _cstruct LLC; + _cstruct ManuData; + __u32 ManuID; + _cstruct NCPI; + __u16 Reason; + __u16 Reason_B3; + __u16 Reject; + _cstruct Useruserdata; + + /* intern */ + unsigned l, p; + unsigned char *par; + __u8 *m; + + /* buffer to construct message */ + __u8 buf[180]; + +} _cmsg; + +/* + * capi_cmsg2message() assembles the parameter from _cmsg to a CAPI 2.0 + * conform message + */ +unsigned capi_cmsg2message(_cmsg * cmsg, __u8 * msg); + +/* + * capi_message2cmsg disassembles a CAPI message an writes the parameter + * into _cmsg for easy access + */ +unsigned capi_message2cmsg(_cmsg * cmsg, __u8 * msg); + +/* + * capi_cmsg_header() fills the _cmsg structure with default values, so only + * parameter with non default values must be changed before sending the + * message. + */ +unsigned capi_cmsg_header(_cmsg * cmsg, __u16 _ApplId, + __u8 _Command, __u8 _Subcommand, + __u16 _Messagenumber, __u32 _Controller); + +/* + * capi_info2str generated a readable string for Capi2.0 reasons. + */ +char *capi_info2str(__u16 reason); + +/*-----------------------------------------------------------------------*/ + +/* + * Debugging / Tracing functions + */ +char *capi_cmd2str(__u8 cmd, __u8 subcmd); +char *capi_cmsg2str(_cmsg * cmsg); +char *capi_message2str(__u8 * msg); + +/*-----------------------------------------------------------------------*/ + +static inline void capi_cmsg_answer(_cmsg * cmsg) +{ + cmsg->Subcommand |= 0x01; +} + +/*-----------------------------------------------------------------------*/ + +static inline void capi_fill_CONNECT_B3_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + _cstruct NCPI) +{ + capi_cmsg_header(cmsg, ApplId, 0x82, 0x80, Messagenumber, adr); + cmsg->NCPI = NCPI; +} + +static inline void capi_fill_FACILITY_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u16 FacilitySelector, + _cstruct FacilityRequestParameter) +{ + capi_cmsg_header(cmsg, ApplId, 0x80, 0x80, Messagenumber, adr); + cmsg->FacilitySelector = FacilitySelector; + cmsg->FacilityRequestParameter = FacilityRequestParameter; +} + +static inline void capi_fill_INFO_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + _cstruct CalledPartyNumber, + _cstruct BChannelinformation, + _cstruct Keypadfacility, + _cstruct Useruserdata, + _cstruct Facilitydataarray) +{ + capi_cmsg_header(cmsg, ApplId, 0x08, 0x80, Messagenumber, adr); + cmsg->CalledPartyNumber = CalledPartyNumber; + cmsg->BChannelinformation = BChannelinformation; + cmsg->Keypadfacility = Keypadfacility; + cmsg->Useruserdata = Useruserdata; + cmsg->Facilitydataarray = Facilitydataarray; +} + +static inline void capi_fill_LISTEN_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u32 InfoMask, + __u32 CIPmask, + __u32 CIPmask2, + _cstruct CallingPartyNumber, + _cstruct CallingPartySubaddress) +{ + capi_cmsg_header(cmsg, ApplId, 0x05, 0x80, Messagenumber, adr); + cmsg->InfoMask = InfoMask; + cmsg->CIPmask = CIPmask; + cmsg->CIPmask2 = CIPmask2; + cmsg->CallingPartyNumber = CallingPartyNumber; + cmsg->CallingPartySubaddress = CallingPartySubaddress; +} + +static inline void capi_fill_ALERT_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + _cstruct BChannelinformation, + _cstruct Keypadfacility, + _cstruct Useruserdata, + _cstruct Facilitydataarray) +{ + capi_cmsg_header(cmsg, ApplId, 0x01, 0x80, Messagenumber, adr); + cmsg->BChannelinformation = BChannelinformation; + cmsg->Keypadfacility = Keypadfacility; + cmsg->Useruserdata = Useruserdata; + cmsg->Facilitydataarray = Facilitydataarray; +} + +static inline void capi_fill_CONNECT_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u16 CIPValue, + _cstruct CalledPartyNumber, + _cstruct CallingPartyNumber, + _cstruct CalledPartySubaddress, + _cstruct CallingPartySubaddress, + __u16 B1protocol, + __u16 B2protocol, + __u16 B3protocol, + _cstruct B1configuration, + _cstruct B2configuration, + _cstruct B3configuration, + _cstruct BC, + _cstruct LLC, + _cstruct HLC, + _cstruct BChannelinformation, + _cstruct Keypadfacility, + _cstruct Useruserdata, + _cstruct Facilitydataarray) +{ + + capi_cmsg_header(cmsg, ApplId, 0x02, 0x80, Messagenumber, adr); + cmsg->CIPValue = CIPValue; + cmsg->CalledPartyNumber = CalledPartyNumber; + cmsg->CallingPartyNumber = CallingPartyNumber; + cmsg->CalledPartySubaddress = CalledPartySubaddress; + cmsg->CallingPartySubaddress = CallingPartySubaddress; + cmsg->B1protocol = B1protocol; + cmsg->B2protocol = B2protocol; + cmsg->B3protocol = B3protocol; + cmsg->B1configuration = B1configuration; + cmsg->B2configuration = B2configuration; + cmsg->B3configuration = B3configuration; + cmsg->BC = BC; + cmsg->LLC = LLC; + cmsg->HLC = HLC; + cmsg->BChannelinformation = BChannelinformation; + cmsg->Keypadfacility = Keypadfacility; + cmsg->Useruserdata = Useruserdata; + cmsg->Facilitydataarray = Facilitydataarray; +} + +static inline void capi_fill_DATA_B3_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u32 Data, + __u16 DataLength, + __u16 DataHandle, + __u16 Flags) +{ + + capi_cmsg_header(cmsg, ApplId, 0x86, 0x80, Messagenumber, adr); + cmsg->Data = Data; + cmsg->DataLength = DataLength; + cmsg->DataHandle = DataHandle; + cmsg->Flags = Flags; +} + +static inline void capi_fill_DISCONNECT_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + _cstruct BChannelinformation, + _cstruct Keypadfacility, + _cstruct Useruserdata, + _cstruct Facilitydataarray) +{ + + capi_cmsg_header(cmsg, ApplId, 0x04, 0x80, Messagenumber, adr); + cmsg->BChannelinformation = BChannelinformation; + cmsg->Keypadfacility = Keypadfacility; + cmsg->Useruserdata = Useruserdata; + cmsg->Facilitydataarray = Facilitydataarray; +} + +static inline void capi_fill_DISCONNECT_B3_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + _cstruct NCPI) +{ + + capi_cmsg_header(cmsg, ApplId, 0x84, 0x80, Messagenumber, adr); + cmsg->NCPI = NCPI; +} + +static inline void capi_fill_MANUFACTURER_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u32 ManuID, + __u32 Class, + __u32 Function, + _cstruct ManuData) +{ + + capi_cmsg_header(cmsg, ApplId, 0xff, 0x80, Messagenumber, adr); + cmsg->ManuID = ManuID; + cmsg->Class = Class; + cmsg->Function = Function; + cmsg->ManuData = ManuData; +} + +static inline void capi_fill_RESET_B3_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + _cstruct NCPI) +{ + + capi_cmsg_header(cmsg, ApplId, 0x87, 0x80, Messagenumber, adr); + cmsg->NCPI = NCPI; +} + +static inline void capi_fill_SELECT_B_PROTOCOL_REQ(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u16 B1protocol, + __u16 B2protocol, + __u16 B3protocol, + _cstruct B1configuration, + _cstruct B2configuration, + _cstruct B3configuration) +{ + + capi_cmsg_header(cmsg, ApplId, 0x41, 0x80, Messagenumber, adr); + cmsg->B1protocol = B1protocol; + cmsg->B2protocol = B2protocol; + cmsg->B3protocol = B3protocol; + cmsg->B1configuration = B1configuration; + cmsg->B2configuration = B2configuration; + cmsg->B3configuration = B3configuration; +} + +static inline void capi_fill_CONNECT_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u16 Reject, + __u16 B1protocol, + __u16 B2protocol, + __u16 B3protocol, + _cstruct B1configuration, + _cstruct B2configuration, + _cstruct B3configuration, + _cstruct ConnectedNumber, + _cstruct ConnectedSubaddress, + _cstruct LLC, + _cstruct BChannelinformation, + _cstruct Keypadfacility, + _cstruct Useruserdata, + _cstruct Facilitydataarray) +{ + capi_cmsg_header(cmsg, ApplId, 0x02, 0x83, Messagenumber, adr); + cmsg->Reject = Reject; + cmsg->B1protocol = B1protocol; + cmsg->B2protocol = B2protocol; + cmsg->B3protocol = B3protocol; + cmsg->B1configuration = B1configuration; + cmsg->B2configuration = B2configuration; + cmsg->B3configuration = B3configuration; + cmsg->ConnectedNumber = ConnectedNumber; + cmsg->ConnectedSubaddress = ConnectedSubaddress; + cmsg->LLC = LLC; + cmsg->BChannelinformation = BChannelinformation; + cmsg->Keypadfacility = Keypadfacility; + cmsg->Useruserdata = Useruserdata; + cmsg->Facilitydataarray = Facilitydataarray; +} + +static inline void capi_fill_CONNECT_ACTIVE_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr) +{ + + capi_cmsg_header(cmsg, ApplId, 0x03, 0x83, Messagenumber, adr); +} + +static inline void capi_fill_CONNECT_B3_ACTIVE_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr) +{ + + capi_cmsg_header(cmsg, ApplId, 0x83, 0x83, Messagenumber, adr); +} + +static inline void capi_fill_CONNECT_B3_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u16 Reject, + _cstruct NCPI) +{ + capi_cmsg_header(cmsg, ApplId, 0x82, 0x83, Messagenumber, adr); + cmsg->Reject = Reject; + cmsg->NCPI = NCPI; +} + +static inline void capi_fill_CONNECT_B3_T90_ACTIVE_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr) +{ + + capi_cmsg_header(cmsg, ApplId, 0x88, 0x83, Messagenumber, adr); +} + +static inline void capi_fill_DATA_B3_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u16 DataHandle) +{ + + capi_cmsg_header(cmsg, ApplId, 0x86, 0x83, Messagenumber, adr); + cmsg->DataHandle = DataHandle; +} + +static inline void capi_fill_DISCONNECT_B3_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr) +{ + + capi_cmsg_header(cmsg, ApplId, 0x84, 0x83, Messagenumber, adr); +} + +static inline void capi_fill_DISCONNECT_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr) +{ + + capi_cmsg_header(cmsg, ApplId, 0x04, 0x83, Messagenumber, adr); +} + +static inline void capi_fill_FACILITY_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u16 FacilitySelector) +{ + + capi_cmsg_header(cmsg, ApplId, 0x80, 0x83, Messagenumber, adr); + cmsg->FacilitySelector = FacilitySelector; +} + +static inline void capi_fill_INFO_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr) +{ + + capi_cmsg_header(cmsg, ApplId, 0x08, 0x83, Messagenumber, adr); +} + +static inline void capi_fill_MANUFACTURER_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr, + __u32 ManuID, + __u32 Class, + __u32 Function, + _cstruct ManuData) +{ + + capi_cmsg_header(cmsg, ApplId, 0xff, 0x83, Messagenumber, adr); + cmsg->ManuID = ManuID; + cmsg->Class = Class; + cmsg->Function = Function; + cmsg->ManuData = ManuData; +} + +static inline void capi_fill_RESET_B3_RESP(_cmsg * cmsg, __u16 ApplId, __u16 Messagenumber, + __u32 adr) +{ + + capi_cmsg_header(cmsg, ApplId, 0x87, 0x83, Messagenumber, adr); +} + +#endif /* __CAPIUTIL_H__ */ diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/avmb1/compat.h linux/drivers/isdn/avmb1/compat.h --- v2.0.30/linux/drivers/isdn/avmb1/compat.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/avmb1/compat.h Mon Aug 4 17:33:59 1997 @@ -0,0 +1,30 @@ +/* + * $Id: compat.h,v 1.1 1997/03/04 21:50:36 calle 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 1997/03/04 21:50:36 calle + * Frirst version in isdn4linux + * + * Revision 2.2 1997/02/12 09:31:39 calle + * new version + * + * Revision 1.1 1997/01/31 10:32:20 calle + * Initial revision + * + * + */ +#ifndef __COMPAT_H__ +#define __COMPAT_H__ + +#include +#include + +#if LINUX_VERSION_CODE >= 0x020112 /* 2.1.18 */ +#define HAS_NEW_SYMTAB +#endif + +#endif /* __COMPAT_H__ */ diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/Makefile linux/drivers/isdn/hisax/Makefile --- v2.0.30/linux/drivers/isdn/hisax/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/Makefile Mon Aug 4 17:33:59 1997 @@ -0,0 +1,54 @@ +L_OBJS := +M_OBJS := +O_OBJS := isdnl1.o config.o tei.o isdnl2.o isdnl3.o \ + q931.o callc.o fsm.o + +# EXTRA_CFLAGS += -S + +ifeq ($(CONFIG_HISAX_EURO),y) + O_OBJS += l3dss1.o +endif + +ifeq ($(CONFIG_HISAX_NI1),y) + O_OBJS += l3ni1.o +endif + +ifeq ($(CONFIG_HISAX_1TR6),y) + O_OBJS += l3_1tr6.o +endif + +ifeq ($(CONFIG_HISAX_16_0),y) + O_OBJS += teles0.o +endif + +ifeq ($(CONFIG_HISAX_16_3),y) + O_OBJS += teles3.o +endif + +ifeq ($(CONFIG_HISAX_AVM_A1),y) + O_OBJS += avm_a1.o +endif + +ifeq ($(CONFIG_HISAX_ELSA_PCC),y) + O_OBJS += elsa.o +endif + +ifeq ($(CONFIG_HISAX_ELSA_PCMCIA),y) + O_OBJS += elsa.o +endif + +ifeq ($(CONFIG_HISAX_IX1MICROR2),y) + O_OBJS += ix1_micro.o +endif + +O_TARGET := +ifeq ($(CONFIG_ISDN_DRV_HISAX),y) + O_TARGET += hisax.o +else + ifeq ($(CONFIG_ISDN_DRV_HISAX),m) + O_TARGET += hisax.o + M_OBJS += hisax.o + endif +endif + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/avm_a1.c linux/drivers/isdn/hisax/avm_a1.c --- v2.0.30/linux/drivers/isdn/hisax/avm_a1.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/avm_a1.c Tue Aug 5 09:00:12 1997 @@ -0,0 +1,960 @@ +/* $Id: avm_a1.c,v 1.6 1997/04/13 19:54:07 keil Exp $ + + * avm_a1.c low level stuff for AVM A1 (Fritz) isdn cards + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: avm_a1.c,v $ + * Revision 1.6 1997/04/13 19:54:07 keil + * Change in IRQ check delay for SMP + * + * Revision 1.5 1997/04/06 22:54:10 keil + * Using SKB's + * + * Revision 1.4 1997/01/27 15:50:21 keil + * SMP proof,cosmetics + * + * Revision 1.3 1997/01/21 22:14:20 keil + * cleanups + * + * Revision 1.2 1996/10/27 22:07:31 keil + * cosmetic changes + * + * Revision 1.1 1996/10/13 20:04:49 keil + * Initial revision + * + * + */ +#define __NO_VERSION__ +#include "siemens.h" +#include "hisax.h" +#include "avm_a1.h" +#include "isdnl1.h" +#include + +extern const char *CardType[]; +const char *avm_revision = "$Revision: 1.6 $"; + +#define byteout(addr,val) outb_p(val,addr) +#define bytein(addr) inb_p(addr) + +static inline u_char +readreg(unsigned int adr, u_char off) +{ + return (bytein(adr + off)); +} + +static inline void +writereg(unsigned int adr, u_char off, u_char data) +{ + byteout(adr + off, data); +} + + +static inline void +read_fifo(unsigned int adr, u_char * data, int size) +{ + insb(adr - 0x400, data, size); +} + +static void +write_fifo(unsigned int adr, u_char * data, int size) +{ + outsb(adr - 0x400, 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"); +} + +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); +} + +/* + * fast interrupt here + */ + +static void +hscxreport(struct IsdnCardState *sp, int hscx) +{ + 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); +} + +/* + * HSCX stuff goes here + */ + +static void +hscx_empty_fifo(struct HscxState *hsp, int count) +{ + 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); + } +} + +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) +{ + 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); + } +} + +/* + * 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)) + 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); + } +} + +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); + } + } +} + +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 = 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); + } +} + +static void +avm_a1_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *sp; + u_char val, sval, stat = 0; + char tmp[32]; + + sp = (struct IsdnCardState *) irq2dev_map[intno]; + + if (!sp) { + printk(KERN_WARNING "AVM A1: Spurious interrupt!\n"); + return; + } + while (((sval = bytein(sp->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); + } + if (!(sval & AVM_A1_STAT_HSCX)) { + val = readreg(sp->hscx[1], HSCX_ISTA); + if (val) { + hscx_int_main(sp, val); + stat |= 1; + } + } + if (!(sval & AVM_A1_STAT_ISAC)) { + val = readreg(sp->isac, ISAC_ISTA); + if (val) { + isac_interrupt(sp, 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); + } + 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(sp->hscx[hscx], HSCX_ISTA, 0x00); +} + +inline static void +release_ioregs(struct IsdnCard *card, int mask) +{ + release_region(card->sp->cfg_reg, 8); + if (mask & 1) + release_region(card->sp->isac, 32); + if (mask & 2) + release_region(card->sp->isac - 0x400, 1); + if (mask & 4) + release_region(card->sp->hscx[0], 32); + if (mask & 8) + release_region(card->sp->hscx[0] - 0x400, 1); + if (mask & 0x10) + release_region(card->sp->hscx[1], 32); + if (mask & 0x20) + release_region(card->sp->hscx[1] - 0x400, 1); +} + +void +release_io_avm_a1(struct IsdnCard *card) +{ + release_ioregs(card, 0x3f); +} + +static void +clear_pending_ints(struct IsdnCardState *sp) +{ + 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); + } + } + return (ret); +} + +int +setup_avm_a1(struct IsdnCard *card) +{ + u_char val, verA, verB; + struct IsdnCardState *sp = card->sp; + 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) + 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)) { + 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); + } else { + request_region(sp->cfg_reg, 8, "avm cfg"); + } + if (check_region((sp->isac), 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); + return (0); + } else { + request_region(sp->isac, 32, "HiSax isac"); + } + if (check_region((sp->isac - 0x400), 1)) { + printk(KERN_WARNING + "HiSax: %s isac fifo port %x already in use\n", + CardType[sp->typ], + sp->isac - 0x400); + release_ioregs(card, 1); + return (0); + } else { + request_region(sp->isac - 0x400, 1, "HiSax isac fifo"); + } + if (check_region((sp->hscx[0]), 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); + return (0); + } else { + request_region(sp->hscx[0], 32, "HiSax hscx A"); + } + if (check_region((sp->hscx[0] - 0x400), 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); + return (0); + } else { + request_region(sp->hscx[0] - 0x400, 1, "HiSax hscx A fifo"); + } + if (check_region((sp->hscx[1]), 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); + return (0); + } else { + request_region(sp->hscx[1], 32, "HiSax hscx B"); + } + if (check_region((sp->hscx[1] - 0x400), 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); + return (0); + } else { + request_region(sp->hscx[1] - 0x400, 1, "HiSax hscx B fifo"); + } + save_flags(flags); + byteout(sp->cfg_reg, 0x0); + sti(); + HZDELAY(HZ / 5 + 1); + byteout(sp->cfg_reg, 0x1); + HZDELAY(HZ / 5 + 1); + byteout(sp->cfg_reg, 0x0); + HZDELAY(HZ / 5 + 1); + val = sp->irq; + if (val == 9) + val = 2; + byteout(sp->cfg_reg + 1, val); + HZDELAY(HZ / 5 + 1); + byteout(sp->cfg_reg, 0x0); + HZDELAY(HZ / 5 + 1); + restore_flags(flags); + + val = bytein(sp->cfg_reg); + printk(KERN_INFO "AVM A1: Byte at %x is %x\n", + sp->cfg_reg, val); + val = bytein(sp->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); + 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); + printk(KERN_INFO "AVM A1: Byte at %x is %x\n", + sp->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_WARNING + "AVM A1: wrong HSCX versions check IO address\n"); + release_io_avm_a1(card); + 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.30/linux/drivers/isdn/hisax/avm_a1.h linux/drivers/isdn/hisax/avm_a1.h --- v2.0.30/linux/drivers/isdn/hisax/avm_a1.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/avm_a1.h Mon Aug 4 17:34:00 1997 @@ -0,0 +1,25 @@ +/* $Id: avm_a1.h,v 1.2 1997/01/21 22:14:36 keil Exp $ + * + * avm_a1.h Header for AVM A1 (Fritz) ISDN card + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: avm_a1.h,v $ + * Revision 1.2 1997/01/21 22:14:36 keil + * cleanups + * + * Revision 1.1 1996/10/12 21:42:40 keil + * Initial revision + * + * +*/ + +#define AVM_A1_STAT_ISAC 0x01 +#define AVM_A1_STAT_HSCX 0x02 +#define AVM_A1_STAT_TIMER 0x04 + +extern void avm_a1_report(struct IsdnCardState *sp); +extern void release_io_avm_a1(struct IsdnCard *card); +extern int setup_avm_a1(struct IsdnCard *card); +extern int initavm_a1(struct IsdnCardState *sp); diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/callc.c linux/drivers/isdn/hisax/callc.c --- v2.0.30/linux/drivers/isdn/hisax/callc.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/callc.c Mon Aug 4 17:34:00 1997 @@ -0,0 +1,1975 @@ +/* $Id: callc.c,v 1.30 1997/05/29 10:40:43 keil Exp $ + + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * based on the teles driver from Jan den Ouden + * + * 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.23 1997/03/04 23:07:42 keil + * bugfix dial parameter + * + * Revision 1.22 1997/02/27 13:51:55 keil + * Reset B-channel (dlc) statemachine in every release + * + * Revision 1.21 1997/02/19 09:24:27 keil + * Bugfix: Hangup to LL if a ttyI rings + * + * Revision 1.20 1997/02/17 00:32:47 keil + * Bugfix: No Busy reported to LL + * + * Revision 1.19 1997/02/14 12:23:10 fritz + * Added support for new insmod parameter handling. + * + * Revision 1.18 1997/02/11 01:36:58 keil + * Changed setup-interface (incoming and outgoing), cause reporting + * + * 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.16 1997/01/27 23:17:03 keil + * delete timers while unloading + * + * Revision 1.15 1997/01/27 16:00:38 keil + * D-channel shutdown delay; improved callback + * + * Revision 1.14 1997/01/21 22:16:39 keil + * new statemachine; leased line support; cleanup for 2.0 + * + * Revision 1.13 1996/12/08 19:51:17 keil + * bugfixes from Pekka Sarnila + * + * Revision 1.12 1996/11/26 20:20:03 keil + * fixed warning while compile + * + * Revision 1.11 1996/11/26 18:43:17 keil + * change ioctl 555 --> 55 (555 didn't work) + * + * Revision 1.10 1996/11/26 18:06:07 keil + * fixed missing break statement,ioctl 555 reset modcount + * + * Revision 1.9 1996/11/18 20:23:19 keil + * log writebuf channel not open changed + * + * Revision 1.8 1996/11/06 17:43:17 keil + * more changes for 2.1.X;block fixed ST_PRO_W + * + * Revision 1.7 1996/11/06 15:13:51 keil + * typo 0x64 --->64 in debug code + * + * Revision 1.6 1996/11/05 19:40:33 keil + * X.75 windowsize + * + * Revision 1.5 1996/10/30 10:11:06 keil + * debugging LOCK changed;ST_REL_W EV_HANGUP added + * + * Revision 1.4 1996/10/27 22:20:16 keil + * alerting bugfixes + * no static b-channel<->channel mapping + * + * 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 + * + */ + +#define __NO_VERSION__ +#include "hisax.h" + +#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 $"; + +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 struct Fsm callcfsm = +{NULL, 0, 0}; +static struct Fsm lcfsm = +{NULL, 0, 0}; + +static int chancount = 0; + +/* Flags for remembering action done in l4 */ + +#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 + +#define SETBIT(flg, item) flg |= item +#define RESBIT(flg, item) flg &= (~item) + +/* + * Because of callback it's a good idea to delay the shutdown of the d-channel + */ +#define DREL_TIMER_VALUE 30000 + +/* + * 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; +} + +static void +link_debug(struct Channel *chanp, char *s, int direction) +{ + char tmp[100], tm[32]; + + 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); +} + + +enum { + ST_NULL, /* 0 inactive */ + ST_OUT_WAIT_D, /* 1 outgoing, awaiting d-channel establishment */ + ST_IN_WAIT_D, /* 2 incoming, awaiting d-channel establishment */ + ST_OUT_DIAL, /* 3 outgoing, SETUP send; awaiting confirm */ + ST_IN_WAIT_LL, /* 4 incoming call received; wait for LL confirm */ + ST_IN_ALERT_SEND, /* 5 incoming call received; ALERT send */ + ST_IN_WAIT_CONN_ACK, /* 6 incoming CONNECT send; awaiting CONN_ACK */ + ST_WAIT_BCONN, /* 7 CONNECT/CONN_ACK received, awaiting b-channel prot. estbl. */ + ST_ACTIVE, /* 8 active, b channel prot. established */ + ST_WAIT_BRELEASE, /* 9 call clear. (initiator), awaiting b channel prot. rel. */ + ST_WAIT_BREL_DISC, /* 10 call clear. (receiver), DISCONNECT req. received */ + ST_WAIT_DCOMMAND, /* 11 call clear. (receiver), awaiting DCHANNEL message */ + ST_WAIT_DRELEASE, /* 12 DISCONNECT sent, awaiting RELEASE */ + ST_WAIT_D_REL_CNF, /* 13 RELEASE sent, awaiting RELEASE confirm */ + ST_WAIT_DSHUTDOWN, /* 14 awaiting d-channel shutdown */ +}; + +#define STATE_COUNT (ST_WAIT_DSHUTDOWN +1) + +static char *strState[] = +{ + "ST_NULL", + "ST_OUT_WAIT_D", + "ST_IN_WAIT_D", + "ST_OUT_DIAL", + "ST_IN_WAIT_LL", + "ST_IN_ALERT_SEND", + "ST_IN_WAIT_CONN_ACK", + "ST_WAIT_BCONN", + "ST_ACTIVE", + "ST_WAIT_BRELEASE", + "ST_WAIT_BREL_DISC", + "ST_WAIT_DCOMMAND", + "ST_WAIT_DRELEASE", + "ST_WAIT_D_REL_CNF", + "ST_WAIT_DSHUTDOWN", +}; + +enum { + EV_DIAL, /* 0 */ + EV_SETUP_CNF, /* 1 */ + EV_ACCEPTB, /* 2 */ + EV_DISCONNECT_IND, /* 3 */ + EV_RELEASE_CNF, /* 4 */ + EV_DLEST, /* 5 */ + EV_DLRL, /* 6 */ + EV_SETUP_IND, /* 7 */ + EV_RELEASE_IND, /* 8 */ + EV_ACCEPTD, /* 9 */ + EV_SETUP_CMPL_IND, /* 10 */ + EV_BC_EST, /* 11 */ + EV_WRITEBUF, /* 12 */ + EV_DATAIN, /* 13 */ + EV_HANGUP, /* 14 */ + EV_BC_REL, /* 15 */ + EV_CINF, /* 16 */ + EV_SUSPEND, /* 17 */ + EV_RESUME, /* 18 */ + EV_SHUTDOWN_D, /* 19 */ + EV_NOSETUP_RSP, /* 20 */ + EV_SETUP_ERR, /* 21 */ + EV_CONNECT_ERR, /* 22 */ + EV_RELEASE_ERR, /* 23 */ +}; + +#define EVENT_COUNT (EV_RELEASE_ERR +1) + +static char *strEvent[] = +{ + "EV_DIAL", + "EV_SETUP_CNF", + "EV_ACCEPTB", + "EV_DISCONNECT_IND", + "EV_RELEASE_CNF", + "EV_DLEST", + "EV_DLRL", + "EV_SETUP_IND", + "EV_RELEASE_IND", + "EV_ACCEPTD", + "EV_SETUP_CMPL_IND", + "EV_BC_EST", + "EV_WRITEBUF", + "EV_DATAIN", + "EV_HANGUP", + "EV_BC_REL", + "EV_CINF", + "EV_SUSPEND", + "EV_RESUME", + "EV_SHUTDOWN_D", + "EV_NOSETUP_RSP", + "EV_SETUP_ERR", + "EV_CONNECT_ERR", + "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[] = +{ + "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) + +static char *strLcEvent[] = +{ + "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_D 0 +#define LC_B 1 + +static inline void +l4_deliver_cause(struct Channel *chanp) +{ + isdn_ctrl ic; + + 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); +} + +/* + * Dial out + */ +static void +l4_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) { + 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); + } +} + +static void +l4_do_dialout(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + FsmChangeState(fi, ST_OUT_DIAL); + 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); + } +} + +static void +l4_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); + if (chanp->debug & 1) + link_debug(chanp, "STAT_DCONN", 0); + ic.driver = chanp->sp->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); +} + +static void +l4_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); + if (chanp->debug & 1) + link_debug(chanp, "STAT_BCONN", 0); + SETBIT(chanp->Flags, FLG_LL_BCONN); + ic.driver = chanp->sp->myid; + ic.command = ISDN_STAT_BCONN; + ic.arg = chanp->chan; + chanp->sp->iif.statcallb(&ic); +} + +/* incomming call */ + +static void +l4_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); + } +} + +static void +l4_deliver_call(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + isdn_ctrl ic; + int ret; + char txt[32]; + + /* + * 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)) { + FsmChangeState(fi, ST_IN_WAIT_LL); + SETBIT(chanp->Flags, FLG_ESTAB_D); + SETBIT(chanp->Flags, FLG_CALL_REC); + if (chanp->debug & 1) + link_debug(chanp, "STAT_ICALL", 0); + ic.driver = chanp->sp->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); + 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); + break; + case 2: /* Rejecting Call */ + RESBIT(chanp->Flags, FLG_CALL_REC); + break; + case 0: /* OK, nobody likes this call */ + default: /* statcallb problems */ + chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL); + FsmChangeState(fi, ST_NULL); + chanp->Flags = FLG_ESTAB_D; + FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61); + break; + } + } else { + chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL); + FsmChangeState(fi, ST_NULL); + chanp->Flags = FLG_ESTAB_D; + FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62); + } +} + +static void +l4_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); +} + +static void +l4_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); + if (chanp->debug & 1) + link_debug(chanp, "STAT_DCONN", 0); + ic.driver = chanp->sp->myid; + ic.command = ISDN_STAT_DCONN; + ic.arg = chanp->chan; + chanp->sp->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); +} + +/* Call clearing */ + +static void +l4_reject_call(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); +} + +static void +l4_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 (chanp->debug & 1) + link_debug(chanp, "STAT_BHUP", 0); + RESBIT(chanp->Flags, FLG_LL_BCONN); + ic.driver = chanp->sp->myid; + ic.command = ISDN_STAT_BHUP; + ic.arg = chanp->chan; + chanp->sp->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); +} + +static void +l4_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); +} + +static void +l4_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); + } + FsmChangeState(fi, ST_NULL); + chanp->Flags = FLG_ESTAB_D; + FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60); +} + +static void +l4_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); +} + +static void +l4_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); +} + +static void +l4_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)) + return; + FsmChangeState(fi, ST_WAIT_DRELEASE); + if (chanp->Flags & FLG_LL_BCONN) { + if (chanp->debug & 1) + link_debug(chanp, "STAT_BHUP", 0); + RESBIT(chanp->Flags, FLG_LL_BCONN); + ic.driver = chanp->sp->myid; + ic.command = ISDN_STAT_BHUP; + ic.arg = chanp->chan; + chanp->sp->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); +} + +static void +l4_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 (chanp->debug & 1) + link_debug(chanp, "STAT_BHUP", 0); + RESBIT(chanp->Flags, FLG_LL_BCONN); + ic.driver = chanp->sp->myid; + ic.command = ISDN_STAT_BHUP; + ic.arg = chanp->chan; + chanp->sp->iif.statcallb(&ic); + } + release_ds(chanp); + RESBIT(chanp->Flags, FLG_START_B); +} + + +static void +l4_release_bchan(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + chanp->data_open = 0; + SETBIT(chanp->Flags, FLG_DISC_REC); + FsmChangeState(fi, ST_WAIT_BREL_DISC); + RESBIT(chanp->Flags, FLG_CONNECT_B); + FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); +} + +static void +l4_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); + } + if (chanp->Flags & FLG_LL_BCONN) { + if (chanp->debug & 1) + link_debug(chanp, "STAT_BHUP", 0); + ic.driver = chanp->sp->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); + } + 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); +} + +static void +l4_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); + } + if (chanp->Flags & FLG_LL_BCONN) { + if (chanp->debug & 1) + link_debug(chanp, "STAT_BHUP", 0); + ic.driver = chanp->sp->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); + } + 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); +} + +static void +l4_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) { + if (chanp->debug & 1) + link_debug(chanp, "STAT_BHUP", 0); + ic.driver = chanp->sp->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); + } + 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); +} + +/* processing charge info */ +static void +l4_charge_info(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + isdn_ctrl ic; + + ic.driver = chanp->sp->myid; + ic.command = ISDN_STAT_CINF; + ic.arg = chanp->chan; + sprintf(ic.parm.num, "%d", chanp->para.chargeinfo); + chanp->sp->iif.statcallb(&ic); +} + +/* error procedures */ + +static void +l4_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; + ic.command = ISDN_STAT_NODCH; + ic.arg = chanp->chan; + chanp->sp->iif.statcallb(&ic); + chanp->Flags = 0; + FsmChangeState(fi, ST_NULL); + FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); +} + +static void +l4_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; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->sp->iif.statcallb(&ic); +} + +static void +l4_no_dchan_in(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL); + chanp->Flags = 0; + FsmChangeState(fi, ST_NULL); + FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); +} + +static void +l4_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); + if (chanp->debug & 1) + link_debug(chanp, "STAT_DHUP", 0); + ic.driver = chanp->sp->myid; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->sp->iif.statcallb(&ic); +} + +static void +l4_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 */ +} + +static void +l4_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 */ +} + +static void +l4_active_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 (chanp->Flags & FLG_LL_BCONN) { + if (chanp->debug & 1) + link_debug(chanp, "STAT_BHUP", 0); + ic.driver = chanp->sp->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) { + 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; + } else { + chanp->para.cause = 0x70; + chanp->para.loc = 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->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); +} + +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[] = +{ + {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}, +}; +/* *INDENT-ON* */ + + + + + + + + + + +#define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode)) + +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) +{ + struct PStack *st = &chanp->ds; + struct IsdnCardState *sp; + struct HscxState *hsp; + + sp = st->l1.hardware; + hsp = sp->hs + chanp->hscx; + + close_hscxstate(hsp); + + switch (chanp->l2_active_protocol) { + case (ISDN_PROTO_L2_X75I): + releasestack_isdnl2(st); + break; + case (ISDN_PROTO_L2_HDLC): + case (ISDN_PROTO_L2_TRANS): + 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 *chanp = (struct Channel *) st->l4.userdata; + + 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; + } +} + +static void +cc_l2man(struct PStack *st, int pr, void *arg) +{ + struct Channel *chanp = (struct Channel *) st->l4.userdata; + + 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; + } +} + +static void +dcc_l1man(struct PStack *st, int pr, void *arg) +{ + struct Channel *chanp = (struct Channel *) st->l4.userdata; + + 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); + 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); + break; + case (DL_RELEASE): + FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_RELEASE, NULL); + 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); +} + +static void +ll_handler(struct PStack *st, int pr, void *arg) +{ + struct Channel *chanp = (struct Channel *) st->l4.userdata; + char tmp[64], tm[32]; + + switch (pr) { + case (CC_DISCONNECT_IND): + FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL); + break; + case (CC_RELEASE_CNF): + FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL); + break; + case (CC_SETUP_IND): + FsmEvent(&chanp->fi, EV_SETUP_IND, NULL); + break; + case (CC_RELEASE_IND): + FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL); + break; + case (CC_SETUP_COMPLETE_IND): + FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL); + break; + case (CC_SETUP_CNF): + FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL); + break; + case (CC_INFO_CHARGE): + FsmEvent(&chanp->fi, EV_CINF, NULL); + break; + case (CC_NOSETUP_RSP_ERR): + FsmEvent(&chanp->fi, EV_NOSETUP_RSP, NULL); + break; + case (CC_SETUP_ERR): + FsmEvent(&chanp->fi, EV_SETUP_ERR, NULL); + break; + case (CC_CONNECT_ERR): + FsmEvent(&chanp->fi, EV_CONNECT_ERR, NULL); + break; + case (CC_RELEASE_ERR): + FsmEvent(&chanp->fi, EV_RELEASE_ERR, NULL); + 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); + } + } +} + +static void +init_is(struct Channel *chanp, unsigned int ces) +{ + struct PStack *st = &chanp->is; + struct IsdnCardState *sp = chanp->sp; + char tmp[128]; + + setstack_HiSax(st, sp); + st->l2.sap = 0; + st->l2.tei = 255; + st->l2.ces = ces; + st->l2.extended = !0; + st->l2.laptype = LAPD; + 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); + 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); +} + +static void +callc_debug(struct FsmInst *fi, char *s) +{ + char str[80], tm[32]; + struct Channel *chanp = fi->userdata; + + jiftime(tm, jiffies); + sprintf(str, "%s Channel %d callc %s\n", tm, chanp->chan, s); + HiSax_putstatus(chanp->sp, str); +} + +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); +} + +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); +} + +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) +{ + struct Channel *chanp = csta->channel + chan; + + chanp->sp = csta; + chanp->hscx = hscx; + chanp->chan = chan; + chanp->incoming = 0; + chanp->debug = 0; + chanp->Flags = 0; + chanp->leased = 0; + chanp->impair = 0; + init_is(chanp, ces); + + chanp->fi.fsm = &callcfsm; + chanp->fi.state = ST_NULL; + chanp->fi.debug = 0; + chanp->fi.userdata = chanp; + 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; + 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++); + printk(KERN_INFO "HiSax: 2 channels added\n"); + return (2); +} + +static void +release_is(struct Channel *chanp) +{ + struct PStack *st = &chanp->is; + + releasestack_isdnl2(st); + releasestack_isdnl3(st); + HiSax_rmlist(st->l1.hardware, st); +} + +void +CallcFreeChan(struct IsdnCardState *csta) +{ + int i; + + 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); + } +} + +static void +lldata_handler(struct PStack *st, int pr, void *arg) +{ + struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct sk_buff *skb = arg; + + switch (pr) { + case (DL_DATA): + if (chanp->data_open) + chanp->sp->iif.rcvcallb_skb(chanp->sp->myid, chanp->chan, skb); + else { + SET_SKB_FREE(skb); + dev_kfree_skb(skb, FREE_READ); + } + break; + default: + printk(KERN_WARNING "lldata_handler unknown primitive %d\n", + pr); + break; + } +} + +static void +lltrans_handler(struct PStack *st, int pr, void *arg) +{ + struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct sk_buff *skb = arg; + + switch (pr) { + case (PH_DATA): + if (chanp->data_open) + chanp->sp->iif.rcvcallb_skb(chanp->sp->myid, chanp->chan, skb); + else { + SET_SKB_FREE(skb); + dev_kfree_skb(skb, FREE_READ); + } + break; + default: + printk(KERN_WARNING "lltrans_handler unknown primitive %d\n", + pr); + break; + } +} + +static void +ll_writewakeup(struct PStack *st) +{ + struct Channel *chanp = st->l4.userdata; + isdn_ctrl ic; + + ic.driver = chanp->sp->myid; + ic.command = ISDN_STAT_BSENT; + ic.arg = chanp->chan; + chanp->sp->iif.statcallb(&ic); +} + +static int +init_ds(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)) + return (-1); + + st->l2.extended = 0; + st->l2.laptype = LAPB; + st->l2.orig = !incoming; + st->l2.t200 = 1000; /* 1000 milliseconds */ + st->l2.window = 7; + 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); + setstack_isdnl2(st, tmp); + 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->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): + 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; + break; + } + return (0); +} + +static void +channel_report(struct Channel *chanp) +{ +} + +static void +distr_debug(struct IsdnCardState *csta, int debugflags) +{ + int i; + struct Channel *chanp = csta->channel; + + 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; + } + csta->dlogflag = debugflags & 4; + csta->teistack->l2.l2m.debug = debugflags & 512; +} + +int +HiSax_command(isdn_ctrl * ic) +{ + struct IsdnCardState *csta = hisax_findcard(ic->driver); + struct Channel *chanp; + char tmp[128]; + int i; + unsigned int num; + + if (!csta) { + printk(KERN_ERR + "HiSax: if_command %d called with invalid driverId %d!\n", + 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); + } + chanp->l2_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)", + 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'; + /* this solution is dirty and may be change, if + * we make a callreference based callmanager */ + if (chanp->fi.state == ST_NULL) { + FsmEvent(&chanp->fi, EV_DIAL, NULL); + } else { + FsmDelTimer(&chanp->dial_timer, 70); + FsmAddTimer(&chanp->dial_timer, 50, EV_DIAL, NULL, 71); + } + break; + case (ISDN_CMD_ACCEPTB): + chanp = csta->channel + ic->arg; + if (chanp->debug & 1) + link_debug(chanp, "ACCEPTB", 1); + 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); + 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); + FsmEvent(&chanp->fi, EV_HANGUP, NULL); + break; + case (ISDN_CMD_SUSPEND): + 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); + } + FsmEvent(&chanp->fi, EV_RESUME, ic); + break; + 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); + } +#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); + } +#endif /* MODULE */ + break; + case (ISDN_CMD_IOCTL): + switch (ic->arg) { + case (0): + HiSax_reportcard(csta->cardnr); + for (i = 0; i < 2; i++) + channel_report(&csta->channel[i]); + break; + case (1): + num = *(unsigned int *) ic->parm.num; + distr_debug(csta, num); + sprintf(tmp, "debugging flags card %d set to %x\n", + csta->cardnr + 1, num); + HiSax_putstatus(csta, tmp); + printk(KERN_DEBUG "HiSax: %s", tmp); + 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); + } + break; + case (3): + for (i = 0; i < *(unsigned int *) ic->parm.num; i++) + HiSax_mod_dec_use_count(); + break; + case (4): + for (i = 0; i < *(unsigned int *) ic->parm.num; i++) + 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); + 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): + 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); + 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->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", + (int) ic->arg); + return (-EINVAL); + } + break; + default: + break; + } + + return (0); +} + +int +HiSax_writebuf_skb(int id, int chan, struct sk_buff *skb) +{ + struct IsdnCardState *csta = hisax_findcard(id); + struct Channel *chanp; + struct PStack *st; + int len = skb->len; + unsigned long flags; + struct sk_buff *nskb; + char tmp[64]; + + if (!csta) { + printk(KERN_ERR + "HiSax: if_sendbuf called with invalid driverId!\n"); + return -ENODEV; + } + chanp = csta->channel + chan; + st = &chanp->ds; + if (!chanp->data_open) { + link_debug(chanp, "writebuf: channel not open", 1); + 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); + return -EINVAL; + } + if (len) { + if ((len + csta->hs[chanp->hscx].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); + } + 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); + } + dev_kfree_skb(skb, FREE_WRITE); + } else + len = 0; + restore_flags(flags); + } + return (len); +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/config.c linux/drivers/isdn/hisax/config.c --- v2.0.30/linux/drivers/isdn/hisax/config.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/config.c Mon Aug 4 17:34:00 1997 @@ -0,0 +1,425 @@ +/* $Id: config.c,v 1.15 1997/04/06 22:57:24 keil Exp $ + + * Author Karsten Keil (keil@temic-ech.spacenet.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.14 1997/03/25 23:11:22 keil + * US NI-1 protocol + * + * Revision 1.13 1997/03/23 21:45:49 keil + * Add support for ELSA PCMCIA + * + * Revision 1.12 1997/03/11 21:01:43 keil + * nzproto is only used with modules + * + * Revision 1.11 1997/02/14 12:23:12 fritz + * Added support for new insmod parameter handling. + * + * Revision 1.10 1997/02/14 09:22:09 keil + * Final 2.0 version + * + * Revision 1.9 1997/02/10 11:45:09 fritz + * More changes for Kernel 2.1.X compatibility. + * + * Revision 1.8 1997/02/09 00:28:05 keil + * new interface handling, one interface per card + * default protocol now works again + * + * Revision 1.7 1997/01/27 15:56:57 keil + * Teles PCMCIA ITK ix1 micro added + * + * Revision 1.6 1997/01/21 22:17:56 keil + * new module load syntax + * + * Revision 1.5 1997/01/09 18:28:20 keil + * cosmetic cleanups + * + * Revision 1.4 1996/11/05 19:35:17 keil + * using config.h; some spelling fixes + * + * Revision 1.3 1996/10/23 17:23:28 keil + * default config changes + * + * Revision 1.2 1996/10/23 11:58:48 fritz + * Changed default setup to reflect user's selection of supported + * cards/protocols. + * + * Revision 1.1 1996/10/13 20:04:51 keil + * Initial revision + * + * + * + */ +#include +#include +#include +#include +#include "hisax.h" + +/* + * This structure array contains one entry per card. An entry looks + * like this: + * + * { 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 + * + * + * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1 + * + * + */ + +#ifdef CONFIG_HISAX_ELSA_PCC +#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} +#endif +#ifdef CONFIG_HISAX_AVM_A1 +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_A1 +#define DEFAULT_CFG {10,0x340,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} +#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} +#endif + +#ifdef CONFIG_HISAX_IX1MICROR2 +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_IX1MICROR2 +#define DEFAULT_CFG {5,0x390,0} +#endif + +#ifdef CONFIG_HISAX_1TR6 +#define DEFAULT_PROTO ISDN_PTYPE_1TR6 +#define DEFAULT_PROTO_NAME "1TR6" +#endif +#ifdef CONFIG_HISAX_EURO +#undef DEFAULT_PROTO +#define DEFAULT_PROTO ISDN_PTYPE_EURO +#undef DEFAULT_PROTO_NAME +#define DEFAULT_PROTO_NAME "EURO" +#endif +#ifdef CONFIG_HISAX_NI1 +#undef DEFAULT_PROTO +#define DEFAULT_PROTO ISDN_PTYPE_NI1 +#undef DEFAULT_PROTO_NAME +#define DEFAULT_PROTO_NAME "NI1" +#endif +#ifndef DEFAULT_PROTO +#define DEFAULT_PROTO ISDN_PTYPE_UNKNOWN +#define DEFAULT_PROTO_NAME "UNKNOWN" +#endif +#ifndef DEFAULT_CARD +#error "HiSax: No cards configured" +#endif + +#define FIRST_CARD { \ + DEFAULT_CARD, \ + DEFAULT_PROTO, \ + DEFAULT_CFG, \ + NULL, \ +} + +#define EMPTY_CARD {0, DEFAULT_PROTO, {0, 0, 0}, NULL} + +struct IsdnCard cards[] = +{ + FIRST_CARD, + EMPTY_CARD, + EMPTY_CARD, + EMPTY_CARD, + EMPTY_CARD, + 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" \ +"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ +"\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; +#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; + +#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(id, "s"); +#ifdef CONFIG_HISAX_16_3 /* For Creatix/Teles PnP */ +MODULE_PARM(io0, "1-16i"); +MODULE_PARM(io1, "1-16i"); +#endif +#endif + +#endif + +extern char *l1_revision; +extern char *l2_revision; +extern char *l3_revision; +extern char *l4_revision; +extern char *tei_revision; + +char * +HiSax_getrev(const char *revision) +{ + char *rev; + char *p; + + if ((p = strchr(revision, ':'))) { + rev = p + 2; + p = strchr(rev, '$'); + *--p = 0; + } else + rev = "???"; + return rev; +} + +int nrcards; + +void +HiSax_mod_dec_use_count(void) +{ + MOD_DEC_USE_COUNT; +} + +void +HiSax_mod_inc_use_count(void) +{ + MOD_INC_USE_COUNT; +} + +#ifdef MODULE +#define HiSax_init init_module +#else +void +HiSax_setup(char *str, int *ints) +{ + int i, j, argc; + + argc = ints[0]; + i = 0; + j = 1; + while (argc && (i < 16)) { + if (argc) { + cards[i].typ = ints[j]; + j++; + argc--; + } + if (argc) { + cards[i].protocol = ints[j]; + j++; + argc--; + } + if (argc) { + cards[i].para[0] = ints[j]; + j++; + argc--; + } + if (argc) { + cards[i].para[1] = ints[j]; + j++; + argc--; + } + if (argc) { + cards[i].para[2] = ints[j]; + j++; + argc--; + } + i++; + } + if (strlen(str)) { + strcpy(HiSaxID, str); + HiSax_id = HiSaxID; + } else { + strcpy(HiSaxID, "HiSax"); + HiSax_id = HiSaxID; + } +} +#endif + +int +HiSax_init(void) +{ + int i; + char tmp[64], rev[64]; + char *r = rev; +#ifdef MODULE + int nzproto = 0; +#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); + +#ifdef MODULE + if (id) /* If id= string used */ + HiSax_id = id; + for (i = 0; i < 16; i++) { + cards[i].typ = type[i]; + if (protocol[i]) { + cards[i].protocol = protocol[i]; + nzproto++; + } + switch (type[i]) { + case ISDN_CTYPE_16_0: + cards[i].para[0] = irq[i]; + cards[i].para[1] = mem[i]; + cards[i].para[2] = io[i]; + break; + + case ISDN_CTYPE_8_0: + cards[i].para[0] = irq[i]; + 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 */ + case ISDN_CTYPE_PNP: + cards[i].para[0] = irq[i]; + cards[i].para[1] = io0[i]; + cards[i].para[2] = io1[i]; + break; +#endif + case ISDN_CTYPE_A1: + cards[i].para[0] = irq[i]; + cards[i].para[1] = io[i]; + break; + + 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_IX1MICROR2: + cards[i].para[0] = irq[i]; + cards[i].para[1] = io[i]; + break; + + } + } + if (!nzproto) { + printk(KERN_WARNING "HiSax: Warning - no protocol specified\n"); + printk(KERN_WARNING "HiSax: Note! module load syntax has changed.\n"); + printk(KERN_WARNING "HiSax: using protocol %s\n", DEFAULT_PROTO_NAME); + } +#endif + if (!HiSax_id) + HiSax_id = HiSaxID; + if (!HiSaxID[0]) + strcpy(HiSaxID, "HiSax"); + for (i = 0; i < 16; i++) + if (cards[i].typ > 0) + nrcards++; + printk(KERN_DEBUG "HiSax: Total %d card%s defined\n", + nrcards, (nrcards > 1) ? "s" : ""); + + CallcNew(); + Isdnl2New(); + if (HiSax_inithardware()) { + /* 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"); +#endif + return (0); + } else { + Isdnl2Free(); + CallcFree(); + return -EIO; + } +} + +#ifdef MODULE +void +cleanup_module(void) +{ + HiSax_closehardware(); + printk(KERN_NOTICE "HiSax module removed\n"); +} + +#endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/elsa.c linux/drivers/isdn/hisax/elsa.c --- v2.0.30/linux/drivers/isdn/hisax/elsa.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/elsa.c Tue Aug 5 09:00:12 1997 @@ -0,0 +1,1487 @@ +/* $Id: elsa.c,v 1.14 1997/04/13 19:53:25 keil Exp $ + + * elsa.c low level stuff for Elsa isdn cards + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * Thanks to Elsa GmbH for documents and informations + * + * + * $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.13 1997/04/07 22:58:07 keil + * need include config.h + * + * Revision 1.12 1997/04/06 22:54:14 keil + * Using SKB's + * + * Revision 1.11 1997/03/23 21:45:46 keil + * Add support for ELSA PCMCIA + * + * Revision 1.10 1997/03/12 21:42:19 keil + * Bugfix: IRQ hangs with QS1000 + * + * Revision 1.9 1997/03/04 15:57:39 keil + * bugfix IRQ reset Quickstep, ELSA PC changes, some stuff for new cards + * + * Revision 1.8 1997/01/27 15:51:48 keil + * SMP proof,cosmetics + * + * Revision 1.7 1997/01/21 22:20:48 keil + * Elsa Quickstep support + * + * Revision 1.6 1997/01/09 18:22:46 keil + * one more PCC-8 fix + * + * Revision 1.5 1996/12/08 19:46:14 keil + * PCC-8 correct IRQs; starting ARCOFI support + * + * Revision 1.4 1996/11/18 20:50:54 keil + * with PCF Pro release 16 Byte IO + * + * Revision 1.3 1996/11/18 15:33:04 keil + * PCC and PCFPro support + * + * Revision 1.2 1996/10/27 22:08:03 keil + * cosmetic changes + * + * Revision 1.1 1996/10/13 20:04:52 keil + * Initial revision + * + * + */ + +#define ARCOFI_USE 0 + +#define __NO_VERSION__ +#include +#include "siemens.h" +#include "hisax.h" +#include "elsa.h" +#include "isdnl1.h" +#include + +extern const char *CardType[]; + +const char *Elsa_revision = "$Revision: 1.14 $"; +const char *Elsa_Types[] = +{"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro", + "PCMCIA", "QS 1000", "QS 3000"}; + +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); +} + + +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 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); +} + +static inline u_char +readisac(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); + restore_flags(flags); + return (ret); +} + +static inline void +read_fifo_isac(unsigned int adr, 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); +} + + +static inline void +writeisac(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); + restore_flags(flags); +} + +static inline void +write_fifo_isac(unsigned int adr, 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); +} + +#ifdef CONFIG_HISAX_ELSA_PCC +static inline u_char +readitac(unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(adr + CARD_ALE, off); + ret = bytein(adr + CARD_ITAC); + restore_flags(flags); + return (ret); +} + +static inline void +writeitac(unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(adr + CARD_ALE, off); + byteout(adr + CARD_ITAC, data); + restore_flags(flags); +} + +static inline int +TimerRun(struct IsdnCardState *sp) +{ + 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); +} + +static inline void +elsa_led_handler(struct IsdnCardState *sp) +{ + + 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); +} +#endif + +static inline void +waitforCEC(int adr, int hscx) +{ + int to = 50; + + while ((readhscx(adr, hscx, HSCX_STAR) & 0x04) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "Elsa: waitforCEC timeout\n"); +} + + +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 "Elsa: waitforXFW timeout\n"); +} + +static inline void +writehscxCMDR(int adr, int hscx, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + waitforCEC(adr, hscx); + writehscx(adr, hscx, HSCX_CMDR, 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", 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)); +} + +void +elsa_report(struct IsdnCardState *sp) +{ + 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); +} + +/* + * HSCX stuff goes here + */ + +static void +hscx_empty_fifo(struct HscxState *hsp, int count) +{ + 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); + } +} + +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->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); + 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) +{ + 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) +{ + 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); + 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) +{ + 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->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); + } +} + +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); +} + +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 + + 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 + } + } +} + +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); + } +} + +static void +elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *sp; + u_char val; + + sp = (struct IsdnCardState *) irq2dev_map[intno]; + + if (!sp) { + 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); + } + } +#endif + val = readhscx(sp->cfg_reg, 1, HSCX_ISTA); + Start_HSCX: + if (val) { + hscx_int_main(sp, val); + } + val = readisac(sp->cfg_reg, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(sp, 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"); + goto Start_HSCX; + } + val = readisac(sp->cfg_reg, ISAC_ISTA); + if (val) { + if (sp->debug & L1_DEB_ISAC) + debugl1(sp, "ISAC IntStat after IntRoutine"); + 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); + } +#endif + writehscx(sp->cfg_reg, 0, HSCX_MASK, 0x0); + writehscx(sp->cfg_reg, 1, HSCX_MASK, 0x0); + writeisac(sp->cfg_reg, ISAC_MASK, 0x0); +} + + +static void +initisac(struct IsdnCardState *sp) +{ + unsigned int adr = sp->cfg_reg; + + /* 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; + } + writehscx(sp->cfg_reg, hscx, HSCX_ISTA, 0x00); +} + +void +release_io_elsa(struct IsdnCard *card) +{ + int bytecnt = 8; + + if (card->sp->subtyp == ELSA_PCFPRO) + 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 +} + +static void +clear_pending_ints(struct IsdnCardState *sp) +{ +#ifdef CONFIG_HISAX_ELSA_PCMCIA + int val; + char tmp[64]; + + 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); + } +#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); + } +#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; + 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); + save_flags(flags); + sti(); + HZDELAY(3); + 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); + 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; + } + } 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); + } + sp->mon_flg = 0; +#endif +} + +int +initelsa(struct IsdnCardState *sp) +{ + int ret, irq_cnt, cnt = 3; + 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); + } +#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); + } else { + reset_elsa(sp); + cnt--; + } + } else { + check_arcofi(sp); + cnt = 0; + } + } + sp->counter = 0; + return (ret); +} + +#ifdef CONFIG_HISAX_ELSA_PCC +static unsigned char +probe_elsa_adr(unsigned int adr) +{ + 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)) { + printk(KERN_WARNING + "Elsa: Probing Port 0x%x: already in use\n", + adr); + return (0); + } + save_flags(flags); + cli(); + for (i = 0; i < 16; i++) { + in1 = inb(adr + CARD_CONFIG); /* 'toggelt' bei */ + in2 = inb(adr + CARD_CONFIG); /* jedem Zugriff */ + p16_1 += 0x04 & in1; + p16_2 += 0x04 & in2; + p8_1 += 0x02 & in1; + p8_2 += 0x02 & in2; + pc_1 += 0x01 & in1; + pc_2 += 0x01 & in2; + pfp_1 += 0x40 & in1; + pfp_2 += 0x40 & in2; + } + restore_flags(flags); + printk(KERN_INFO "Elsa: Probing IO 0x%x", adr); + if (65 == ++p16_1 * ++p16_2) { + printk(" PCC-16/PCF found\n"); + return (ELSA_PCC16); + } else if (1025 == ++pfp_1 * ++pfp_2) { + printk(" PCF-Pro found\n"); + return (ELSA_PCFPRO); + } else if (33 == ++p8_1 * ++p8_2) { + printk(" PCC8 found\n"); + return (ELSA_PCC8); + } else if (17 == ++pc_1 * ++pc_2) { + printk(" PC found\n"); + return (ELSA_PC); + } else { + printk(" failed\n"); + return (0); + } +} + +static unsigned int +probe_elsa(struct IsdnCardState *sp) +{ + 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]))) + break; + } + return (CARD_portlist[i]); +} +#endif + +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; + 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 "Elsa: Microlink IO probing\n"); + if (sp->cfg_reg) { + if (!(sp->subtyp = probe_elsa_adr(sp->cfg_reg))) { + printk(KERN_WARNING + "Elsa: no Elsa Microlink at 0x%x\n", + sp->cfg_reg); + 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) { + 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) { + const u_char CARD_IrqTab[8] = + {7, 3, 5, 9, 0, 0, 0, 0}; + sp->irq = CARD_IrqTab[(val & IRQ_INDEX_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]; + } + val = bytein(sp->cfg_reg + CARD_ALE) & 0x7; + if (val < 3) + val |= 8; + val += 'A' - 3; + if (val == 'B' || val == 'C') + val ^= 1; + if ((sp->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) + printk(KERN_WARNING + "Elsa: Microlink S0 bus power bad\n"); + } 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; + 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; + 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 + + switch (sp->subtyp) { + case ELSA_PC: + bytecnt = 8; + break; + case ELSA_PCC8: + bytecnt = 8; + break; + case ELSA_PCFPRO: + bytecnt = 16; + break; + case ELSA_PCC16: + bytecnt = 8; + break; + case ELSA_PCF: + bytecnt = 16; + break; + case ELSA_QS1000: + bytecnt = 8; + break; + case ELSA_PCMCIA: + bytecnt = 8; + break; + default: + printk(KERN_WARNING + "Unknown ELSA subtype %d\n", sp->subtyp); + return (0); + } + + if (check_region((sp->cfg_reg), bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + sp->cfg_reg, + sp->cfg_reg + bytecnt); + return (0); + } else { + request_region(sp->cfg_reg, 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)) { + printk(KERN_WARNING + "Elsa: timer do not start\n"); + release_io_elsa(card); + return (0); + } + } + 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); + } + 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); + } +#endif + +#ifdef CONFIG_HISAX_ELSA_PCC + if (sp->subtyp == ELSA_PC) { + val = readitac(sp->cfg_reg, 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); + } +#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.30/linux/drivers/isdn/hisax/elsa.h linux/drivers/isdn/hisax/elsa.h --- v2.0.30/linux/drivers/isdn/hisax/elsa.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/elsa.h Mon Aug 4 17:34:00 1997 @@ -0,0 +1,90 @@ +/* $Id: elsa.h,v 1.6 1997/03/23 21:45:48 keil Exp $ + * + * elsa.h Header for Elsa ISDN cards + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * Thanks to Elsa GmbH for documents and informations + * + * + * $Log: elsa.h,v $ + * Revision 1.6 1997/03/23 21:45:48 keil + * Add support for ELSA PCMCIA + * + * Revision 1.5 1997/03/04 15:58:13 keil + * ELSA PC changes, some stuff for new cards + * + * Revision 1.4 1997/01/21 22:21:05 keil + * Elsa Quickstep support + * + * Revision 1.3 1996/12/08 19:47:38 keil + * ARCOFI support + * + * Revision 1.2 1996/11/18 15:33:35 keil + * PCC and PCFPro support + * + * Revision 1.1 1996/10/13 20:03:45 keil + * Initial revision + * + * +*/ +#include + +#ifdef CONFIG_HISAX_ELSA_PCMCIA +#define CARD_ISAC 1 +#define CARD_HSCX 2 +#define CARD_ALE 4 +#else +#define CARD_ISAC 0 +#define CARD_ITAC 1 +#define CARD_HSCX 2 +#define CARD_ALE 3 +#define CARD_CONTROL 4 +#define CARD_CONFIG 5 +#define CARD_START_TIMER 6 +#define CARD_TRIG_IRQ 7 +#endif + +#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 + +/* 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 TIMER_RUN 0x02 /* Bit 1 des Config-Reg */ +#define TIMER_RUN_PCC8 0x01 /* Bit 0 des Config-Reg bei PCC */ +#define IRQ_INDEX 0x38 /* Bit 3,4,5 des Config-Reg */ +#define IRQ_INDEX_PCC8 0x30 /* Bit 4,5 des Config-Reg */ +#define IRQ_INDEX_PC 0x0c /* Bit 2,3 des Config-Reg */ + +/* Control-Register (Write) */ +#define LINE_LED 0x02 /* Bit 1 Gelbe LED */ +#define STAT_LED 0x08 /* Bit 3 Gruene LED */ +#define ISDN_RESET 0x20 /* Bit 5 Reset-Leitung */ +#define ENABLE_TIM_INT 0x80 /* Bit 7 Freigabe Timer Interrupt */ + +/* ALE-Register (Read) */ +#define HW_RELEASE 0x07 /* Bit 0-2 Hardwarerkennung */ +#define S0_POWER_BAD 0x08 /* Bit 3 S0-Bus Spannung fehlt */ + +extern void elsa_report(struct IsdnCardState *sp); +extern void release_io_elsa(struct IsdnCard *card); +extern int setup_elsa(struct IsdnCard *card); +extern int initelsa(struct IsdnCardState *sp); diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/fsm.c linux/drivers/isdn/hisax/fsm.c --- v2.0.30/linux/drivers/isdn/hisax/fsm.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/fsm.c Mon Aug 4 17:34:00 1997 @@ -0,0 +1,183 @@ +/* $Id: fsm.c,v 1.4 1997/04/06 22:56:42 keil Exp $ + + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * based on the teles driver from Jan den Ouden + * + * Thanks to Jan den Ouden + * Fritz Elfert + * + * $Log: fsm.c,v $ + * Revision 1.4 1997/04/06 22:56:42 keil + * Some cosmetic changes + * + * Revision 1.3 1997/02/16 01:04:08 fritz + * Bugfix: Changed timer handling caused hang with 2.1.X + * + * Revision 1.2 1997/01/09 20:57:27 keil + * cleanup & FSM_TIMER_DEBUG + * + * Revision 1.1 1996/10/13 20:04:52 keil + * Initial revision + * + * + */ +#define __NO_VERSION__ +#include "hisax.h" + +#define FSM_TIMER_DEBUG 0 + +void +FsmNew(struct Fsm *fsm, + 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; +} + +void +FsmFree(struct Fsm *fsm) +{ + kfree((void *) fsm->jumpmatrix); +} + +int +FsmEvent(struct FsmInst *fi, int event, void *arg) +{ + void (*r) (struct FsmInst *, int, void *); + char str[80]; + + r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; + if (r) { + if (fi->debug) { + sprintf(str, "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", + fi->fsm->strState[fi->state], + fi->fsm->strEvent[event]); + fi->printdebug(fi, str); + } + return (!0); + } +} + +void +FsmChangeState(struct FsmInst *fi, int newstate) +{ + char str[80]; + + fi->state = newstate; + if (fi->debug) { + sprintf(str, "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); + } +#endif + FsmEvent(ft->fi, ft->event, ft->arg); +} + +void +FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft) +{ + ft->fi = fi; + 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); + } +#endif + init_timer(&ft->tl); +} + +void +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); + } +#endif + del_timer(&ft->tl); +} + +int +FsmAddTimer(struct FsmTimer *ft, + int millisec, int event, void *arg, int where) +{ + +#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); + } +#endif + + if (ft->tl.next || ft->tl.prev) { + printk(KERN_WARNING "FsmAddTimer: timer already active!\n"); + ft->fi->printdebug(ft->fi, "FsmAddTimer already active!"); + return -1; + } + init_timer(&ft->tl); + ft->event = event; + ft->arg = arg; + ft->tl.expires = jiffies + (millisec * HZ) / 1000; + add_timer(&ft->tl); + return 0; +} + +int +FsmTimerRunning(struct FsmTimer *ft) +{ + return (ft->tl.next != NULL); +} + +void +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'; +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/hisax.h linux/drivers/isdn/hisax/hisax.h --- v2.0.30/linux/drivers/isdn/hisax/hisax.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/hisax.h Mon Aug 4 17:34:00 1997 @@ -0,0 +1,540 @@ +/* $Id: hisax.h,v 1.13 1997/04/06 22:54:12 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.12 1997/03/23 21:45:45 keil + * Add support for ELSA PCMCIA + * + * Revision 1.11 1997/02/11 01:36:02 keil + * New Param structure + * + * Revision 1.10 1997/02/09 00:23:52 keil + * new interface handling, one interface per card + * + * Revision 1.9 1997/01/27 23:18:44 keil + * prototype for releasestack_isdnl3 + * + * Revision 1.8 1997/01/27 16:02:37 keil + * new cards, callc timers, HZDELAY macro, HiSax_getrev prototype + * + * Revision 1.7 1997/01/21 22:22:14 keil + * changes for 2.0; Elsa Quickstep support + * + * Revision 1.6 1997/01/04 13:48:28 keil + * primitiv for MDL_REMOVE added + * + * Revision 1.5 1996/12/08 19:49:19 keil + * Monitor channel support + * + * Revision 1.4 1996/11/18 15:35:39 keil + * some changes for ELSA cards + * + * Revision 1.3 1996/11/05 19:37:23 keil + * using config.h + * + * Revision 1.2 1996/10/27 22:21:52 keil + * CallFlags for broadcast messages + * + * Revision 1.1 1996/10/13 20:03:46 keil + * Initial revision + * + * + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 + +struct HscxIoctlArg { + int channel; + int mode; + int transbufsize; +}; + +#ifdef __KERNEL__ + +#undef DEBUG_MAGIC + +#define MAX_DFRAME_LEN 3072 +#define HSCX_BUFMAX 4096 +#define MAX_DATA_SIZE (HSCX_BUFMAX - 4) +#define MAX_DATA_MEM (HSCX_BUFMAX * 2) +#define MAX_HEADER_LEN 4 +#define MAX_WINDOW 8 + +/* + * Statemachine + */ +struct Fsm { + int *jumpmatrix; + int state_count, event_count; + char **strEvent, **strState; +}; + +struct FsmInst { + struct Fsm *fsm; + int state; + int debug; + void *userdata; + int userint; + void (*printdebug) (struct FsmInst *, char *); +}; + +struct FsmNode { + int state, event; + void (*routine) (struct FsmInst *, int, void *); +}; + +struct FsmTimer { + struct FsmInst *fi; + struct timer_list tl; + int event; + void *arg; +}; + +struct L3Timer { + struct PStack *st; + struct timer_list tl; + int event; +}; + +struct Layer1 { + void *hardware; + int hscx; + struct PStack **stlistp; + int act_state; + void (*l1l2) (struct PStack *, int, void *); + void (*l1man) (struct PStack *, int, void *); + int hscxmode, hscxchannel, requestpull; +}; + +struct Layer2 { + int sap, tei, ces; + int extended, laptype; + int uihsize, ihsize; + int vs, va, vr; + struct sk_buff_head i_queue; + int window, orig; + int rejexp; + int debug; + struct sk_buff *windowar[MAX_WINDOW]; + int sow; + struct FsmInst l2m; + 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 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; + int debug; + int channr; +}; + +struct Layer4 { + void (*l4l3) (struct PStack *, int, void *); + void *userdata; + void (*l1writewakeup) (struct PStack *); + void (*l2writewakeup) (struct PStack *); +}; + +struct Management { + void (*manl1) (struct PStack *, int, void *); + void (*manl2) (struct PStack *, int, void *); + void (*teil2) (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 + */ + int spv; /* SPV Flag */ +}; + +struct PStack { + struct PStack *next; + struct Layer1 l1; + struct Layer2 l2; + struct Layer3 l3; + struct Layer4 l4; + 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 */ + int tx_cnt; /* B-Channel transmit counter */ + int count; /* Current skb sent count */ + struct sk_buff_head rqueue; /* B-Channel receive Queue */ + struct sk_buff_head squeue; /* B-Channel receive Queue */ + struct PStack *st; + 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]; +}; + +struct Channel { + struct PStack ds, is; + struct IsdnCardState *sp; + int hscx; + 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 data_open; + int outcallref; + int impair; + 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; + unsigned int cfg_reg; + unsigned int membase; + unsigned int isac; + unsigned int hscx[2]; + unsigned int counter; + 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 *); + struct Channel channel[2]; + struct PStack *stlist; + u_char *rcvbuf; + int rcvidx; + struct sk_buff *tx_skb; + int tx_cnt; + int event; + struct tq_struct tqueue; + int ph_active; + 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 debug; + unsigned int CallFlags; +}; + +#define MON0_RX 1 +#define MON1_RX 2 +#define MON0_TX 4 +#define MON1_TX 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_TELESPCMCIA 8 +#define ISDN_CTYPE_IX1MICROR2 9 + +#define ISDN_CTYPE_COUNT 9 + +#ifdef CONFIG_HISAX_16_0 +#define CARD_TELES0 (1<< ISDN_CTYPE_16_0) | (1<< ISDN_CTYPE_8_0) +#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) +#else +#define CARD_TELES3 0 +#endif + +#ifdef CONFIG_HISAX_AVM_A1 +#define CARD_AVM_A1 (1<< ISDN_CTYPE_A1) +#else +#define CARD_AVM_A1 0 +#endif + +#ifdef CONFIG_HISAX_ELSA_PCC +#define CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_QS1000) +#else +#define CARD_ELSA 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" +#else +#undef CARD_ELSA +#define CARD_ELSA (1<< ISDN_CTYPE_ELSA_QS1000) +#endif +#endif + +#ifdef CONFIG_HISAX_IX1MICROR2 +#define CARD_IX1MICROR2 (1 << ISDN_CTYPE_IX1MICROR2) +#else +#define CARD_IX1MICROR2 0 +#endif + +#define SUPORTED_CARDS (CARD_TELES0 | CARD_TELES3 | CARD_AVM_A1 | CARD_ELSA \ + | CARD_IX1MICROR2) + +struct IsdnCard { + int typ; + int protocol; /* EDSS1 or 1TR6 */ + unsigned int para[3]; + struct IsdnCardState *sp; +}; + + +#define LAPD 0 +#define LAPB 1 + +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_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 releasestack_isdnl2(struct PStack *st); +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); + +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); +void FsmDelTimer(struct FsmTimer *ft, int where); +int FsmTimerRunning(struct FsmTimer *ft); +void 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_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 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__ */ + +#define HZDELAY(jiffs) {int tout = jiffs; while (tout--) udelay(1000000/HZ);} + +int ll_run(struct IsdnCardState *csta); +void ll_stop(struct IsdnCardState *csta); +void CallcNew(void); +void CallcFree(void); +int CallcNewChan(struct IsdnCardState *csta); +void CallcFreeChan(struct IsdnCardState *csta); +void Isdnl2New(void); +void Isdnl2Free(void); +void init_tei(struct IsdnCardState *sp, int protocol); +void release_tei(struct IsdnCardState *sp); +char *HiSax_getrev(const char *revision); diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/isdnl1.c linux/drivers/isdn/hisax/isdnl1.c --- v2.0.30/linux/drivers/isdn/hisax/isdnl1.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/isdnl1.c Mon Aug 4 17:34:00 1997 @@ -0,0 +1,1378 @@ +/* $Id: isdnl1.c,v 1.15 1997/05/27 15:17:55 fritz 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) + * + * Thanks to Jan den Ouden + * Fritz Elfert + * Beat Doebeli + * + * + * $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.14 1997/04/07 23:00:08 keil + * GFP_KERNEL ---> GFP_ATOMIC + * + * Revision 1.13 1997/04/06 22:55:50 keil + * Using SKB's + * + * Revision 1.12 1997/03/26 13:43:57 keil + * small cosmetics + * + * Revision 1.11 1997/03/25 23:11:23 keil + * US NI-1 protocol + * + * Revision 1.10 1997/03/13 14:45:05 keil + * using IRQ proof queue_task + * + * Revision 1.9 1997/03/12 21:44:21 keil + * change Interrupt routine from atomic quick to normal + * + * Revision 1.8 1997/02/09 00:24:31 keil + * new interface handling, one interface per card + * + * Revision 1.7 1997/01/27 15:56:03 keil + * PCMCIA Teles card and ITK ix1 micro added + * + * Revision 1.6 1997/01/21 22:20:00 keil + * changes for D-channel log; Elsa Quickstep support + * + * Revision 1.5 1997/01/10 12:51:19 keil + * cleanup; set newversion + * + * Revision 1.4 1996/12/08 19:44:53 keil + * L2FRAME_DEBUG and other changes from Pekka Sarnila + * + * Revision 1.3 1996/11/18 15:34:47 keil + * fix HSCX version code + * + * Revision 1.2 1996/10/27 22:16:54 keil + * ISAC/HSCX version lookup + * + * Revision 1.1 1996/10/13 20:04:53 keil + * Initial revision + * + * + * + */ + +const char *l1_revision = "$Revision: 1.15 $"; + +#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 + +#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; + } +} + +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) +{ + struct PStack *st; + + st = sp->stlist; + while (st) + if (st->l1.act_state) + return (!0); + 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; + } +} + + +static void +act_ivated(struct IsdnCardState *sp) +{ + struct PStack *st; + + st = sp->stlist; + while (st) { + if (st->l1.act_state == 1) { + st->l1.act_state = 2; + st->l1.l1man(st, PH_ACTIVATE, NULL); + } + st = st->next; + } +} + +static void +process_new_ph(struct IsdnCardState *sp) +{ + if (sp->ph_active == 5) + act_ivated(sp); +} + +static void +process_xmt(struct IsdnCardState *sp) +{ + struct PStack *stptr; + + if (sp->tx_skb) + return; + + stptr = sp->stlist; + while (stptr != NULL) + if (stptr->l1.requestpull) { + stptr->l1.requestpull = 0; + stptr->l1.l1l2(stptr, PH_PULL_ACK, NULL); + break; + } else + stptr = stptr->next; +} + +static void +process_rcv(struct IsdnCardState *sp) +{ + struct sk_buff *skb, *nskb; + struct PStack *stptr; + int found, broadc; + char tmp[64]; + + while ((skb = skb_dequeue(&sp->rq))) { +#ifdef L2FRAME_DEBUG /* psa */ + if (sp->debug & L1_DEB_LAPD) + Logl2Frame(sp, 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) + if ((nskb = skb_clone(skb, GFP_ATOMIC))) + stptr->l1.l1l2(stptr, PH_DATA, nskb); + else + printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n"); + stptr = stptr->next; + } + SET_SKB_FREE(skb); + dev_kfree_skb(skb, FREE_READ); + } else { + 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); + 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); + 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) +{ + struct PStack *st = hsp->st; + + if (hsp->tx_skb) + return; + + if (st->l1.requestpull) { + st->l1.requestpull = 0; + st->l1.l1l2(st, PH_PULL_ACK, 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) +{ + struct sk_buff *skb; + +#ifdef DEBUG_MAGIC + if (hsp->magic != 301270) { + printk(KERN_DEBUG "hscx_process_rcv magic not 301270\n"); + return; + } +#endif + while ((skb = skb_dequeue(&hsp->rqueue))) { + hsp->st->l1.l1l2(hsp->st, PH_DATA, skb); + } +} + +static void +hscx_bh(struct HscxState *hsp) +{ + + if (!hsp) + 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); + +} + +/* + * interrupt stuff ends here + */ + +void +HiSax_addlist(struct IsdnCardState *sp, + struct PStack *st) +{ + st->next = sp->stlist; + sp->stlist = st; +} + +void +HiSax_rmlist(struct IsdnCardState *sp, + struct PStack *st) +{ + struct PStack *p; + + if (sp->stlist == st) + sp->stlist = st->next; + else { + p = sp->stlist; + while (p) + if (p->next == st) { + p->next = st->next; + return; + } else + p = p->next; + } +} + +static void +check_ph_act(struct IsdnCardState *sp) +{ + struct PStack *st = sp->stlist; + + while (st) { + if (st->l1.act_state) + return; + st = st->next; + } + if (sp->ph_active == 5) + sp->ph_active = 4; +} + +static void +HiSax_manl1(struct PStack *st, int pr, + void *arg) +{ + struct IsdnCardState *sp = (struct IsdnCardState *) + st->l1.hardware; + long flags; + char tmp[32]; + + 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; + } +} + +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; + +#ifdef DEBUG_MAGIC + if (sp->magic != 301271) { + printk(KERN_DEBUG "isac_discardq magic not 301271\n"); + return; + } +#endif + + while ((skb = skb_dequeue(&sp->sq))) + dev_kfree_skb(skb, FREE_WRITE); +} + +void +setstack_HiSax(struct PStack *st, struct IsdnCardState *sp) +{ + st->l1.hardware = sp; + st->protocol = sp->protocol; + + setstack_tei(st); + + 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; +} + +void +init_hscxstate(struct IsdnCardState *sp, + int hscx) +{ + 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; + + hsp->inuse = 0; + hsp->init = 0; + hsp->active = 0; + +#ifdef DEBUG_MAGIC + hsp->magic = 301270; +#endif +} + +int +get_irq(int cardnr, void *routine) +{ + struct IsdnCard *card = cards + cardnr; + long flags; + + 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); +} + +static void +release_irq(int cardnr) +{ + struct IsdnCard *card = cards + cardnr; + + irq2dev_map[card->sp->irq] = NULL; + free_irq(card->sp->irq, NULL); +} + +void +close_hscxstate(struct HscxState *hs) +{ + 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; +} + +static void +closecard(int cardnr) +{ + struct IsdnCardState *csta = cards[cardnr].sp; + struct sk_buff *skb; + + close_hscxstate(csta->hs + 1); + close_hscxstate(csta->hs); + + 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); +} + +static int +checkcard(int cardnr, char *id) +{ + 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; + + 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); + } + 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); + + 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; + } + if (!ret) { + closecard(cardnr); + return (0); + } + init_tei(sp, sp->protocol); + CallcNewChan(sp); + ll_run(sp); + return (1); +} + +void +HiSax_shiftcards(int idx) +{ + int i; + + for (i = idx; i < 15; i++) + memcpy(&cards[i], &cards[i + 1], sizeof(cards[i])); +} + +int +HiSax_inithardware(void) +{ + 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)) { + 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); + } + } + return foundcards; +} + +void +HiSax_closehardware(void) +{ + int i; + long flags; + + 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); +} + +static void +hscx_l2l1(struct PStack *st, int pr, 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; + } + +} +extern struct IsdnBuffers *tracebuf; + +static void +hscx_l2l1discardq(struct PStack *st, int pr, void *heldby, + int releasetoo) +{ + struct IsdnCardState *sp = (struct IsdnCardState *) + st->l1.hardware; + struct HscxState *hsp = sp->hs + st->l1.hscx; + struct sk_buff *skb; + +#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); +} + +static int +open_hscxstate(struct IsdnCardState *sp, + int hscx) +{ + 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; + + hsp->tx_skb = NULL; + hsp->event = 0; + hsp->rcvidx = 0; + hsp->tx_cnt = 0; + return (0); +} + +static void +hscx_manl1(struct PStack *st, int pr, + void *arg) +{ + struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware; + struct HscxState *hsp = sp->hs + st->l1.hscx; + + 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; + } +} + +int +setstack_hscx(struct PStack *st, struct HscxState *hs) +{ + 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.act_state = 0; + st->l1.requestpull = 0; + + hs->st = st; + return (0); +} + +void +HiSax_reportcard(int cardnr) +{ + 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); +} + +#ifdef L2FRAME_DEBUG /* psa */ + +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 char tmp[20]; + +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); + break; + } else + return "invalid command"; + } + + + return tmp; +} + +void +Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir) +{ + 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); + } +} + +#endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/isdnl1.h linux/drivers/isdn/hisax/isdnl1.h --- v2.0.30/linux/drivers/isdn/hisax/isdnl1.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/isdnl1.h Mon Aug 4 17:34:00 1997 @@ -0,0 +1,51 @@ +/* $Id: isdnl1.h,v 1.4 1997/04/06 22:55:52 keil Exp $ + * + * $Log: isdnl1.h,v $ + * Revision 1.4 1997/04/06 22:55:52 keil + * Using SKB's + * + * Revision 1.3 1996/12/08 19:41:55 keil + * L2FRAME_DEBUG + * + * Revision 1.2 1996/10/27 22:26:27 keil + * ISAC/HSCX version functions + * + * Revision 1.1 1996/10/13 20:03:47 keil + * Initial revision + * + * + * + */ + + +#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); + +#ifdef L2FRAME_DEBUG +extern void Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir); +#endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/isdnl2.c linux/drivers/isdn/hisax/isdnl2.c --- v2.0.30/linux/drivers/isdn/hisax/isdnl2.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/isdnl2.c Mon Aug 4 17:34:00 1997 @@ -0,0 +1,1325 @@ +/* $Id: isdnl2.c,v 1.10 1997/05/06 09:38:13 keil Exp $ + + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * based on the teles driver from Jan den Ouden + * + * 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.9 1997/04/07 23:02:11 keil + * missing braces + * + * Revision 1.8 1997/04/06 22:59:59 keil + * Using SKB's; changing function names; some minor changes + * + * Revision 1.7 1997/02/09 00:25:44 keil + * new interface handling, one interface per card + * + * Revision 1.6 1997/01/21 22:23:42 keil + * D-channel log changed + * + * Revision 1.5 1997/01/04 13:47:06 keil + * handling of MDL_REMOVE added (Thanks to Sim Yskes) + * + * Revision 1.4 1996/12/08 19:51:51 keil + * many fixes from Pekka Sarnila + * + * Revision 1.3 1996/11/05 19:39:12 keil + * X.75 bugfixes Thank to Martin Maurer + * + * Revision 1.2 1996/10/30 10:20:58 keil + * X.75 answer of SABMX fixed to response address (AVM X.75 problem) + * + * Revision 1.1 1996/10/13 20:04:54 keil + * Initial revision + * + * + * + */ +#define __NO_VERSION__ +#include "hisax.h" +#include "isdnl2.h" + +const char *l2_revision = "$Revision: 1.10 $"; + +static void l2m_debug(struct FsmInst *fi, char *s); + +struct Fsm l2fsm = +{NULL, 0, 0, NULL, NULL}; + +enum { + ST_L2_1, + ST_L2_3, + ST_L2_4, + ST_L2_5, + ST_L2_6, + ST_L2_7, + ST_L2_8, +}; + +#define L2_STATE_COUNT (ST_L2_8+1) + +static char *strL2State[] = +{ + "ST_L2_1", + "ST_L2_3", + "ST_L2_4", + "ST_L2_5", + "ST_L2_6", + "ST_L2_7", + "ST_L2_8", +}; + +enum { + EV_L2_UI, + EV_L2_SABMX, + EV_L2_UA, + EV_L2_DISC, + EV_L2_I, + EV_L2_RR, + EV_L2_REJ, + EV_L2_FRMR, + EV_L2_DL_DATA, + EV_L2_DL_ESTABLISH, + EV_L2_MDL_ASSIGN, + EV_L2_MDL_REMOVE, + EV_L2_DL_UNIT_DATA, + EV_L2_DL_RELEASE, + EV_L2_MDL_NOTEIPROC, + EV_L2_T200, + EV_L2_ACK_PULL, + EV_L2_T203, + EV_L2_RNR, +}; + +#define L2_EVENT_COUNT (EV_L2_RNR+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_FRMR", + "EV_L2_DL_DATA", + "EV_L2_DL_ESTABLISH", + "EV_L2_MDL_ASSIGN", + "EV_L2_MDL_REMOVE", + "EV_L2_DL_UNIT_DATA", + "EV_L2_DL_RELEASE", + "EV_L2_MDL_NOTEIPROC", + "EV_L2_T200", + "EV_L2_ACK_PULL", + "EV_L2_T203", + "EV_L2_RNR", +}; + +int errcount = 0; + +static int l2addrsize(struct Layer2 *tsp); + +static void +InitWin(struct Layer2 *l2) +{ + int i; + + for (i = 0; i < MAX_WINDOW; i++) + l2->windowar[i] = NULL; +} + +static void +ReleaseWin(struct Layer2 *l2) +{ + int i, cnt = 0; + + + for (i = 0; i < MAX_WINDOW; i++) { + if (l2->windowar[i]) { + cnt++; + dev_kfree_skb(l2->windowar[i], FREE_WRITE); + l2->windowar[i] = NULL; + } + } + if (cnt) + printk(KERN_WARNING "isdl2 freed %d skbuffs in release\n", cnt); +} + +static int +cansend(struct PStack *st) +{ + int p1; + + p1 = (st->l2.va + st->l2.window) % (st->l2.extended ? 128 : 8); + return (st->l2.vs != p1); +} + +static void +discard_i_queue(struct PStack *st) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&st->l2.i_queue))) { + SET_SKB_FREE(skb); + dev_kfree_skb(skb, FREE_READ); + } +} + +int +l2headersize(struct Layer2 *tsp, int ui) +{ + return ((tsp->extended && (!ui) ? 2 : 1) + (tsp->laptype == LAPD ? 2 : 1)); +} + +int +l2addrsize(struct Layer2 *tsp) +{ + return (tsp->laptype == LAPD ? 2 : 1); +} + +static int +sethdraddr(struct Layer2 *tsp, + u_char * header, int rsp) +{ + u_char *ptr = header; + int crbit; + + if (tsp->laptype == LAPD) { + crbit = rsp; + if (!tsp->orig) + crbit = !crbit; + *ptr++ = (tsp->sap << 2) | (crbit ? 2 : 0); + *ptr++ = (tsp->tei << 1) | 1; + return (2); + } else { + crbit = rsp; + if (tsp->orig) + crbit = !crbit; + if (crbit) + *ptr++ = 1; + else + *ptr++ = 3; + return (1); + } +} + +static void +enqueue_ui(struct PStack *st, + struct sk_buff *skb) +{ + st->l2.l2l1(st, PH_DATA, skb); +} + +static void +enqueue_super(struct PStack *st, + struct sk_buff *skb) +{ + st->l2.l2l1(st, PH_DATA, skb); +} + +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 + l2->extended ? 128 : 8); + lnr = (nr >= l2->va) ? nr : (nr + l2->extended ? 128 : 8); + return (lnr <= lvs); +} + +static void +setva(struct PStack *st, int nr) +{ + struct Layer2 *l2 = &st->l2; + + 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); + } + } +} + +static void +l2s1(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + st->l2.l2tei(st, MDL_ASSIGN, (void *) st->l2.ces); + FsmChangeState(fi, ST_L2_3); +} + +static void +l2_send_ui(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + u_char header[MAX_HEADER_LEN]; + int i; + + i = sethdraddr(&(st->l2), header, CMD); + header[i++] = UI; + memcpy(skb_push(skb, i), header, i); + enqueue_ui(st, skb); +} + +static void +l2_receive_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)); + st->l2.l2l3(st, DL_UNIT_DATA, skb); +} + +inline void +send_uframe(struct PStack *st, u_char cmd, u_char cr) +{ + struct sk_buff *skb; + u_char tmp[MAX_HEADER_LEN]; + int i; + + i = sethdraddr(&st->l2, tmp, cr); + tmp[i++] = cmd; + if (!(skb = alloc_skb(i, GFP_ATOMIC))) { + printk(KERN_WARNING "isdl2 can't alloc sbbuff for send_uframe\n"); + return; + } + SET_SKB_FREE(skb); + memcpy(skb_put(skb, i), tmp, i); + enqueue_super(st, skb); +} + +static void +establishlink(struct FsmInst *fi) +{ + struct PStack *st = fi->userdata; + u_char cmd; + + FsmChangeState(fi, ST_L2_5); + st->l2.rc = 0; + + 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"); + + + cmd = (st->l2.extended ? SABME : SABM) | 0x10; + send_uframe(st, cmd, CMD); +} + +static void +l2_establish(struct FsmInst *fi, int event, void *arg) +{ + establishlink(fi); +} + +static void +l2_send_disconn(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct Channel *chanp = st->l4.userdata; + + FsmChangeState(fi, ST_L2_6); + + FsmDelTimer(&st->l2.t203_timer, 1); + if (st->l2.t200_running) { + FsmDelTimer(&st->l2.t200_timer, 2); + st->l2.t200_running = 0; + } + 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 (!((chanp->impair == 2) && (st->l2.laptype == LAPB))) + send_uframe(st, DISC | 0x10, CMD); + + discard_i_queue(st); +} + +static void +l2_got_SABMX(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + int est = 1, state; + 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) + if (st->l2.vs != st->l2.va) { + discard_i_queue(st); + est = 1; + } else + est = 0; + + 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"); + + if (est) + st->l2.l2man(st, DL_ESTABLISH, NULL); + + if (ST_L2_8 == state) + if (skb_queue_len(&st->l2.i_queue) && cansend(st)) + st->l2.l2l1(st, PH_REQUEST_PULL, NULL); +} + +static void +l2_got_disconn(struct FsmInst *fi, int event, void *arg) +{ + 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); + + FsmChangeState(fi, ST_L2_4); + + FsmDelTimer(&st->l2.t203_timer, 3); + if (st->l2.t200_running) { + FsmDelTimer(&st->l2.t200_timer, 4); + st->l2.t200_running = 0; + } + 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) +{ + 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); + + if (!((chanp->impair == 1) && (st->l2.laptype == LAPB))) + send_uframe(st, DM | (PollFlag ? 0x10 : 0x0), RSP); + +} + +static void +l2_got_ua_establish(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); + + 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"); + + 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); + } +} + +inline void +enquiry_cr(struct PStack *st, u_char typ, u_char cr, u_char pf) +{ + struct sk_buff *skb; + struct Layer2 *l2; + u_char tmp[MAX_HEADER_LEN]; + int i; + + l2 = &st->l2; + i = sethdraddr(l2, tmp, cr); + if (l2->extended) { + tmp[i++] = typ; + tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0); + } else + tmp[i++] = (l2->vr << 5) | typ | (pf ? 0x10 : 0); + if (!(skb = alloc_skb(i, GFP_ATOMIC))) { + printk(KERN_WARNING "isdl2 can't alloc sbbuff for enquiry_cr\n"); + return; + } + SET_SKB_FREE(skb); + memcpy(skb_put(skb, i), tmp, i); + enqueue_super(st, skb); +} + +inline void +enquiry_response(struct PStack *st, u_char typ, u_char final) +{ + enquiry_cr(st, typ, RSP, final); +} + +inline void +enquiry_command(struct PStack *st, u_char typ, u_char poll) +{ + enquiry_cr(st, typ, CMD, poll); +} + +static void +nrerrorrecovery(struct FsmInst *fi) +{ + /* should log error here */ + establishlink(fi); +} + +static void +l2_got_st7_RR(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; + + 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; + } else { + PollFlag = (skb->data[0] & 0x10); + seq = (skb->data[0] >> 5) & 0x7; + } + SET_SKB_FREE(skb); + dev_kfree_skb(skb, FREE_READ); + + 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); + } + } 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); + } +} + +static void +l2_feed_iqueue(struct FsmInst *fi, int event, void *arg) +{ + 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); +} + +static int +icommandreceived(struct FsmInst *fi, int event, void *arg, int *nr) +{ + 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]; + + 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; + } 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); + skb_pull(skb, l2headersize(l2, 0)); + } 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); + } else { + st->l2.rejexp = !0; + enquiry_command(st, REJ, 1); + } + } + 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); + } + } else + nrerrorrecovery(fi); + + if (wasok) + st->l2.l2l3(st, DL_DATA, skb); +} + +static void +l2_got_st8_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)) { + setva(st, nr); + if (skb_queue_len(&st->l2.i_queue)) + st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + } else + nrerrorrecovery(fi); + + if (wasok) + st->l2.l2l3(st, DL_DATA, skb); +} + +static void +l2_got_tei(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); + } +} + +static void +l2_got_st7_rej(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; + } else { + PollFlag = (skb->data[0] & 0x10); + seq = (skb->data[0] >> 5) & 0x7; + } + 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) +{ + 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); + } else { + 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) +{ + 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); + + } +} + +static void +l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb; + struct Layer2 *l2 = &st->l2; + u_char header[MAX_HEADER_LEN]; + int p1, i; + + if (!cansend(st)) + return; + + skb = skb_dequeue(&l2->i_queue); + if (!skb) + return; + + p1 = l2->vs - l2->va; + if (p1 < 0) + p1 += l2->extended ? 128 : 8; + p1 = (p1 + l2->sow) % l2->window; + if (l2->windowar[p1]) { + printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n", + p1); + dev_kfree_skb(l2->windowar[p1], FREE_WRITE); + } + l2->windowar[p1] = skb_clone(skb, GFP_ATOMIC); + + i = sethdraddr(&st->l2, header, CMD); + + if (l2->extended) { + header[i++] = l2->vs << 1; + header[i++] = l2->vr << 1; + l2->vs = (l2->vs + 1) % 128; + } else { + 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; + } + 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; +} + +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) +{ + 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; + } else { + PollFlag = (skb->data[0] & 0x10); + seq = (skb->data[0] >> 5) & 0x7; + } + SET_SKB_FREE(skb); + dev_kfree_skb(skb, FREE_READ); + + if (rsp && PollFlag) { + if (legalnr(st, seq)) { + 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); + else if (fi->userint & LC_FLUSH_WAIT) { + fi->userint &= ~LC_FLUSH_WAIT; + st->l2.l2man(st, DL_FLUSH, NULL); + } + } + } else { + if (!rsp && PollFlag) + enquiry_response(st, RR, PollFlag); + if (legalnr(st, seq)) { + setva(st, seq); + } + } +} + +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->data[0], skb->data[1], skb->data[2], + skb->data[3], skb->data[4]); + else + sprintf(tmp, "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); +} + +static void +l2_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + 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 */ + 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) +{ + u_char d = data[0] & ~0x10; + + 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); +} + +static struct FsmNode L2FnList[] = +{ + {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_4, EV_L2_DL_ESTABLISH, l2_establish}, + {ST_L2_7, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {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_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_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_8, EV_L2_DISC, l2_got_disconn}, + {ST_L2_8, EV_L2_FRMR, l2_got_FRMR}, + {ST_L2_8, EV_L2_I, l2_got_st8_data}, + + {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_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}, +}; + +#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode)) + +static void +isdnl2_l1l2(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + u_char *datap; + int ret = !0; + + switch (pr) { + case (PH_DATA): + datap = skb->data; + datap += l2addrsize(&st->l2); + + if (IsI(datap, st->l2.extended)) + 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)) + ret = FsmEvent(&st->l2.l2m, EV_L2_UI, skb); + else if (IsSABMX(datap, st->l2.extended)) + ret = FsmEvent(&st->l2.l2m, EV_L2_SABMX, skb); + else if (IsUA(datap, st->l2.extended)) + ret = FsmEvent(&st->l2.l2m, EV_L2_UA, skb); + else if (IsDISC(datap, st->l2.extended)) + 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)) + 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); + + if (ret) { + SET_SKB_FREE(skb); + dev_kfree_skb(skb, FREE_READ); + } + break; + case (PH_PULL_ACK): + FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg); + break; + } +} + +static void +isdnl2_l3l2(struct PStack *st, int pr, void *arg) +{ + switch (pr) { + case (DL_DATA): + if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg)) { + dev_kfree_skb((struct sk_buff *) arg, FREE_READ); + } + break; + case (DL_UNIT_DATA): + 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); + break; + case (DL_RELEASE): + 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): + (&st->l2.l2m)->userint |= LC_FLUSH_WAIT; + break; + } +} + +static void +isdnl2_teil2(struct PStack *st, int pr, void *arg) +{ + switch (pr) { + case (MDL_ASSIGN): + FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg); + break; + case (MDL_REMOVE): + FsmEvent(&st->l2.l2m, EV_L2_MDL_REMOVE, arg); + break; + } +} + +void +releasestack_isdnl2(struct PStack *st) +{ + FsmDelTimer(&st->l2.t200_timer, 15); + FsmDelTimer(&st->l2.t203_timer, 16); + discard_i_queue(st); + ReleaseWin(&st->l2); +} + +static void +l2m_debug(struct FsmInst *fi, char *s) +{ + 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); +} + +void +setstack_isdnl2(struct PStack *st, char *debug_id) +{ + 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); + InitWin(&st->l2); + st->l2.rejexp = 0; + st->l2.debug = 0; + + st->l2.l2m.fsm = &l2fsm; + st->l2.l2m.state = ST_L2_1; + st->l2.l2m.debug = 0; + st->l2.l2m.userdata = st; + st->l2.l2m.userint = 0; + 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; +} + +void +setstack_transl2(struct PStack *st) +{ +} + +void +releasestack_transl2(struct PStack *st) +{ +} + +void +Isdnl2New(void) +{ + l2fsm.state_count = L2_STATE_COUNT; + l2fsm.event_count = L2_EVENT_COUNT; + l2fsm.strEvent = strL2Event; + l2fsm.strState = strL2State; + FsmNew(&l2fsm, L2FnList, L2_FN_COUNT); +} + +void +Isdnl2Free(void) +{ + FsmFree(&l2fsm); +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/isdnl2.h linux/drivers/isdn/hisax/isdnl2.h --- v2.0.30/linux/drivers/isdn/hisax/isdnl2.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/isdnl2.h Mon Aug 4 17:34:00 1997 @@ -0,0 +1,18 @@ +/* isdnl2.h */ + +#define RR 0x01 +#define RNR 0x05 +#define REJ 0x09 +#define SABME 0x6f +#define SABM 0x2f +#define DM 0x0f +#define UI 0x03 +#define DISC 0x43 +#define UA 0x63 +#define FRMR 0x87 +#define XID 0xaf + +#define CMD 0 +#define RSP 1 + +#define LC_FLUSH_WAIT 1 diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/isdnl3.c linux/drivers/isdn/hisax/isdnl3.c --- v2.0.30/linux/drivers/isdn/hisax/isdnl3.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/isdnl3.c Mon Aug 4 17:34:00 1997 @@ -0,0 +1,201 @@ +/* $Id: isdnl3.c,v 1.10 1997/04/06 22:54:16 keil Exp $ + + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * based on the teles driver from Jan den Ouden + * + * 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.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 + * + * Revision 1.7 1997/03/17 18:34:38 keil + * fixed oops if no protocol selected during config + * + * Revision 1.6 1997/02/16 01:04:08 fritz + * Bugfix: Changed timer handling caused hang with 2.1.X + * + * Revision 1.5 1997/02/09 00:26:27 keil + * new interface handling, one interface per card + * leased line changes + * + * Revision 1.4 1997/01/27 23:17:44 keil + * delete timers while unloading + * + * Revision 1.3 1997/01/21 22:31:12 keil + * new statemachine; L3 timers + * + * Revision 1.2 1996/11/05 19:42:04 keil + * using config.h + * + * Revision 1.1 1996/10/13 20:04:54 keil + * Initial revision + * + * + * + */ +#define __NO_VERSION__ +#include "hisax.h" +#include "isdnl3.h" +#include + +const char *l3_revision = "$Revision: 1.10 $"; + +void +l3_debug(struct PStack *st, char *s) +{ + char str[256], tm[32]; + + jiftime(tm, jiffies); + sprintf(str, "%s Channel %d l3 %s\n", tm, st->l3.channr, s); + HiSax_putstatus(st->l1.hardware, str); +} + + + +void +newl3state(struct PStack *st, int state) +{ + char tmp[80]; + + if (st->l3.debug & L3_DEB_STATE) { + sprintf(tmp, "newstate %d --> %d", st->l3.state, state); + l3_debug(st, tmp); + } + st->l3.state = state; +} + +static void +L3ExpireTimer(struct L3Timer *t) +{ + t->st->l4.l4l3(t->st, t->event, NULL); +} + +void +L3InitTimer(struct PStack *st, struct L3Timer *t) +{ + t->st = st; + t->tl.function = (void *) L3ExpireTimer; + t->tl.data = (long) t; + init_timer(&t->tl); +} + +void +L3DelTimer(struct L3Timer *t) +{ + del_timer(&t->tl); +} + +int +L3AddTimer(struct L3Timer *t, + int millisec, int event) +{ + if (t->tl.next || t->tl.prev) { + printk(KERN_WARNING "L3AddTimer: timer already active!\n"); + return -1; + } + init_timer(&t->tl); + t->event = event; + t->tl.expires = jiffies + (millisec * HZ) / 1000; + add_timer(&t->tl); + return 0; +} + +void +StopAllL3Timer(struct PStack *st) +{ + L3DelTimer(&st->l3.timer); +} + +struct sk_buff * +l3_alloc_skb(int len) +{ + struct sk_buff *skb; + + if (!(skb = alloc_skb(len + MAX_HEADER_LEN, GFP_ATOMIC))) { + printk(KERN_WARNING "HiSax: No skb for D-channel\n"); + return (NULL); + } + SET_SKB_FREE(skb); + skb_reserve(skb, MAX_HEADER_LEN); + return (skb); +} + +static void +no_l3_proto(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + + l3_debug(st, "no protocol"); + if (skb) + dev_kfree_skb(skb, FREE_READ); +} + +#ifdef CONFIG_HISAX_EURO +extern void setstack_dss1(struct PStack *st); +#endif + +#ifdef CONFIG_HISAX_NI1 +extern void setstack_ni1(struct PStack *st); +#endif + +#ifdef CONFIG_HISAX_1TR6 +extern void setstack_1tr6(struct PStack *st); +#endif + +void +setstack_isdnl3(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); + +#ifdef CONFIG_HISAX_EURO + if (st->protocol == ISDN_PTYPE_EURO) { + setstack_dss1(st); + } else +#endif +#ifdef CONFIG_HISAX_NI1 + if (st->protocol == ISDN_PTYPE_NI1) { + setstack_ni1(st); + } else +#endif +#ifdef CONFIG_HISAX_1TR6 + if (st->protocol == ISDN_PTYPE_1TR6) { + setstack_1tr6(st); + } else +#endif + if (st->protocol == ISDN_PTYPE_LEASED) { + st->l4.l4l3 = no_l3_proto; + st->l2.l2l3 = no_l3_proto; + printk(KERN_NOTICE "HiSax: Leased line mode\n"); + } else { + st->l4.l4l3 = no_l3_proto; + st->l2.l2l3 = no_l3_proto; + sprintf(tmp, "protocol %s not supported", + (st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" : + (st->protocol == ISDN_PTYPE_EURO) ? "euro" : + (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 +releasestack_isdnl3(struct PStack *st) +{ + StopAllL3Timer(st); +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/isdnl3.h linux/drivers/isdn/hisax/isdnl3.h --- v2.0.30/linux/drivers/isdn/hisax/isdnl3.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/isdnl3.h Mon Aug 4 17:34:00 1997 @@ -0,0 +1,38 @@ +/* $Id: isdnl3.h,v 1.3 1997/04/06 22:54:17 keil Exp $ + * + * $Log: isdnl3.h,v $ + * Revision 1.3 1997/04/06 22:54:17 keil + * Using SKB's + * + * Revision 1.2 1997/01/21 22:31:28 keil + * new statemachine; L3 timers + * + * Revision 1.1 1996/10/13 20:03:47 keil + * Initial revision + * + * + */ + +#define SBIT(state) (1< + +extern const char *CardType[]; +const char *ix1_revision = "$Revision: 1.3 $"; + +#define byteout(addr,val) outb_p(val,addr) +#define bytein(addr) inb_p(addr) + +#define SPECIAL_PORT_OFFSET 3 + +#define ISAC_COMMAND_OFFSET 2 +#define ISAC_DATA_OFFSET 0 +#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) +{ + 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); +} + +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++; + } +} + +static inline void +waitforCEC(int adr, int WhichHscx) +{ + int to = TIMEOUT; + + while ((HscxReadReg(adr, WhichHscx, HSCX_STAR) & 0x04) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "ix1-Micro: waitforCEC timeout\n"); +} + + +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) +{ + long flags; + + save_flags(flags); + cli(); + waitforCEC(adr, WhichHscx); + HscxWriteReg(adr, WhichHscx, HSCX_CMDR, 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) +{ + 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); +} + +/* + * HSCX stuff goes here + */ + +static void +hscx_empty_fifo(struct HscxState *hsp, int count) +{ + 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); + } +} + +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], 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); + } +} + +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 = 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); + } +} + +/* + * 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)) + 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); + } +} + +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; + 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); + } +} + +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); + } + IsacWriteReg(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 = 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); + } + } +} + +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 = 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); + } +} + +static void +ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *sp; + u_char val, stat = 0; + + sp = (struct IsdnCardState *) irq2dev_map[intno]; + + if (!sp) { + printk(KERN_WARNING "Teles: Spurious interrupt!\n"); + return; + } + val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA); + Start_HSCX: + if (val) { + hscx_int_main(sp, val); + stat |= 1; + } + val = IsacReadReg(sp->isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(sp, val); + stat |= 2; + } + val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA); + if (val) { + if (sp->debug & L1_DEB_HSCX) + debugl1(sp, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = IsacReadReg(sp->isac, ISAC_ISTA); + if (val) { + if (sp->debug & L1_DEB_ISAC) + debugl1(sp, "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); + } + if (stat & 2) { + IsacWriteReg(sp->isac, ISAC_MASK, 0xFF); + IsacWriteReg(sp->isac, ISAC_MASK, 0x0); + } +} + + +static void +initisac(struct IsdnCardState *sp) +{ + 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); +} + +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; + 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; + } + 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); +} + +static void +clear_pending_ints(struct IsdnCardState *sp) +{ + 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); + } + } + return (ret); +} + +int +setup_ix1micro(struct IsdnCard *card) +{ + u_char val, verA, verB; + struct IsdnCardState *sp = card->sp; + long flags; + 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) + 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)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + sp->cfg_reg, + sp->cfg_reg + 4); + return (0); + } else + request_region(sp->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_WARNING + "ix1-Micro: wrong HSCX versions check IO address\n"); + release_io_ix1micro(card); + 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.30/linux/drivers/isdn/hisax/ix1_micro.h linux/drivers/isdn/hisax/ix1_micro.h --- v2.0.30/linux/drivers/isdn/hisax/ix1_micro.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/ix1_micro.h Mon Aug 4 17:34:00 1997 @@ -0,0 +1,50 @@ +/* $Id: ix1_micro.h,v 1.1 1997/01/27 15:42:48 keil Exp $ + + * ix1_micro.h low level stuff for ITK ix1-micro Rev.2 isdn cards + * + * derived from teles3.h from Karsten Keil + * + * Copyright (C) 1997 Klaus-Peter Nischke (ITK AG) (for the modifications + to the original file teles.c) + * + * $Log: ix1_micro.h,v $ + * Revision 1.1 1997/01/27 15:42:48 keil + * first version + * + * + */ + +/* + For the modification done by the author the following terms and conditions + apply (GNU PUBLIC LICENSE) + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + + You may contact Klaus-Peter Nischke by email: klaus@nischke.do.eunet.de + or by conventional mail: + + Klaus-Peter Nischke + Deusener Str. 287 + 44369 Dortmund + Germany + */ + + +extern void ix1micro_report(struct IsdnCardState *sp); +extern void release_io_ix1micro(struct IsdnCard *card); +extern int setup_ix1micro(struct IsdnCard *card); +extern int initix1micro(struct IsdnCardState *sp); diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/l3_1tr6.c linux/drivers/isdn/hisax/l3_1tr6.c --- v2.0.30/linux/drivers/isdn/hisax/l3_1tr6.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/l3_1tr6.c Mon Aug 4 17:34:00 1997 @@ -0,0 +1,796 @@ +/* $Id: l3_1tr6.c,v 1.11 1997/04/06 22:54:18 keil Exp $ + + * German 1TR6 D-channel protocol + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $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 + * + * Revision 1.9 1997/02/11 01:37:40 keil + * Changed setup-interface (incoming and outgoing) + * + * Revision 1.8 1997/01/27 23:20:21 keil + * report revision only ones + * + * Revision 1.7 1997/01/21 22:30:07 keil + * new statemachine; L3 timers + * + * Revision 1.6 1996/12/14 21:07:20 keil + * additional states for CC_REJECT + * + * Revision 1.5 1996/12/08 19:55:17 keil + * change CC_REJECT_REQ routine + * + * Revision 1.4 1996/10/30 10:18:01 keil + * bugfixes in debugging output + * + * Revision 1.3 1996/10/27 22:15:37 keil + * bugfix reject handling + * + * Revision 1.2 1996/10/13 23:08:56 keil + * added missing state for callback reject + * + * Revision 1.1 1996/10/13 20:04:55 keil + * Initial revision + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "l3_1tr6.h" +#include "isdnl3.h" +#include + +extern char *HiSax_getrev(const char *revision); +const char *l3_1tr6_revision = "$Revision: 1.11 $"; + +#define MsgHead(ptr, cref, mty, dis) \ + *ptr++ = dis; \ + *ptr++ = 0x1; \ + *ptr++ = cref; \ + *ptr++ = mty + +static void +l3_1TR6_message(struct PStack *st, u_char mt, u_char pd) +{ + struct sk_buff *skb; + u_char *p; + + 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); +} + +static void +l3_1tr6_setup_req(struct PStack *st, u_char pr, void *arg) +{ + struct sk_buff *skb; + u_char tmp[128]; + u_char *p = tmp; + u_char *teln; + u_char *eaz; + 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; + if (!isdigit(*teln)) { + switch (0x5f & *teln) { + case 'S': + st->pa->spv = 1; + break; + case 'C': + channel = 0x08; + case 'P': + channel |= 0x80; + teln++; + if (*teln == '1') + channel |= 0x01; + else + channel |= 0x02; + break; + default: + if (st->l3.debug & L3_DEB_WARN) + l3_debug(st, "Wrong MSN Code"); + break; + } + teln++; + } + if (channel) { + *p++ = 0x18; /* channel indicator */ + *p++ = 1; + *p++ = channel; + } + if (st->pa->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++ = 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 */ + } + eaz = st->pa->setup.eazmsn; + if (*eaz) { + *p++ = WE0_origAddr; + *p++ = strlen(eaz) + 1; + /* Classify as AnyPref. */ + *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */ + while (*eaz) + *p++ = *eaz++ & 0x7f; + } + *p++ = WE0_destAddr; + *p++ = strlen(teln) + 1; + /* Classify as AnyPref. */ + *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */ + while (*teln) + *p++ = *teln++ & 0x7f; + + *p++ = WE_Shift_F6; + /* Codesatz 6 fuer Service */ + *p++ = WE6_serviceInd; + *p++ = 2; /* len=2 info,info2 */ + *p++ = st->pa->setup.si1; + *p++ = st->pa->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); + +} + +static void +l3_1tr6_setup(struct PStack *st, u_char pr, void *arg) +{ + u_char *p; + int bcfound = 0; + char tmp[80]; + 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"); + + 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"); + + p = skb->data; + if ((p = findie(p, skb->len, WE0_destAddr, 0))) + iecpy(st->pa->setup.eazmsn, p, 1); + else + st->pa->setup.eazmsn[0] = 0; + + p = skb->data; + if ((p = findie(p, skb->len, WE0_origAddr, 0))) { + iecpy(st->pa->setup.phone, p, 1); + } else + st->pa->setup.phone[0] = 0; + + p = skb->data; + st->pa->spv = 0; + if ((p = findie(p, skb->len, WE0_netSpecFac, 0))) { + if ((FAC_SPV == p[3]) || (FAC_Activate == p[3])) + st->pa->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)) { + sprintf(tmp, "non-digital call: %s -> %s", + st->pa->setup.phone, + st->pa->setup.eazmsn); + l3_debug(st, tmp); + } + newl3state(st, 6); + st->l3.l3l4(st, CC_SETUP_IND, NULL); + } +} + +static void +l3_1tr6_setup_ack(struct PStack *st, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + + L3DelTimer(&st->l3.timer); + p = skb->data; + newl3state(st, 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); + dev_kfree_skb(skb, FREE_READ); + L3AddTimer(&st->l3.timer, st->l3.t304, CC_T304); + st->l3.l3l4(st, CC_MORE_INFO, NULL); +} + +static void +l3_1tr6_call_sent(struct PStack *st, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + + L3DelTimer(&st->l3.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); + 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); +} + +static void +l3_1tr6_alert(struct PStack *st, 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); +} + +static void +l3_1tr6_info(struct PStack *st, u_char pr, void *arg) +{ + u_char *p; + int i, tmpcharge = 0; + char a_charge[8], tmp[32]; + struct sk_buff *skb = arg; + + p = skb->data; + if ((p = findie(p, skb->len, WE6_chargingInfo, 6))) { + iecpy(a_charge, p, 1); + for (i = 0; i < strlen(a_charge); i++) { + 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); + } + } else if (st->l3.debug & L3_DEB_CHARGE) + l3_debug(st, "charging info not found"); + SET_SKB_FREE(skb); + dev_kfree_skb(skb, FREE_READ); + +} + +static void +l3_1tr6_info_s2(struct PStack *st, 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) +{ + struct sk_buff *skb = arg; + + L3DelTimer(&st->l3.timer); /* T310 */ + newl3state(st, 10); + SET_SKB_FREE(skb); + dev_kfree_skb(skb, FREE_READ); + st->pa->chargeinfo = 0; + st->l3.l3l4(st, CC_SETUP_CNF, NULL); +} + +static void +l3_1tr6_rel(struct PStack *st, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + u_char *p; + + p = skb->data; + if ((p = findie(p, skb->len, WE0_cause, 0))) { + if (p[1] > 0) { + st->pa->cause = p[2]; + if (p[1] > 1) + st->pa->loc = p[3]; + else + st->pa->loc = 0; + } else { + st->pa->cause = 0; + st->pa->loc = 0; + } + } else + st->pa->cause = -1; + SET_SKB_FREE(skb); + 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); +} + +static void +l3_1tr6_rel_ack(struct PStack *st, 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); +} + +static void +l3_1tr6_disc(struct PStack *st, 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); + p = skb->data; + if ((p = findie(p, skb->len, WE6_chargingInfo, 6))) { + iecpy(a_charge, p, 1); + for (i = 0; i < strlen(a_charge); i++) { + 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); + } + } else if (st->l3.debug & L3_DEB_CHARGE) + l3_debug(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]; + if (p[1] > 1) + st->pa->loc = p[3]; + else + st->pa->loc = 0; + } else { + st->pa->cause = 0; + st->pa->loc = 0; + } + } else { + if (st->l3.debug & L3_DEB_WARN) + l3_debug(st, "cause not found"); + st->pa->cause = -1; + } + SET_SKB_FREE(skb); + dev_kfree_skb(skb, FREE_READ); + newl3state(st, 12); + st->l3.l3l4(st, CC_DISCONNECT_IND, NULL); +} + + +static void +l3_1tr6_connect_ack(struct PStack *st, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + + SET_SKB_FREE(skb); + 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); +} + +static void +l3_1tr6_alert_req(struct PStack *st, u_char pr, void *arg) +{ + newl3state(st, 7); + l3_1TR6_message(st, MT_N1_ALERT, PROTO_DIS_N1); +} + +static void +l3_1tr6_setup_rsp(struct PStack *st, 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 ? */ + /* 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++ = WE0_netSpecFac; + *p++ = 4; /* Laenge */ + *p++ = 0; + *p++ = FAC_Activate; /* aktiviere SPV */ + *p++ = st->pa->setup.si1; + *p++ = st->pa->setup.si2; + } + newl3state(st, 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); +} + +static void +l3_1tr6_reset(struct PStack *st, u_char pr, void *arg) +{ + newl3state(st, 0); +} + +static void +l3_1tr6_disconnect_req(struct PStack *st, u_char pr, void *arg) +{ + struct sk_buff *skb; + u_char tmp[16]; + u_char *p = tmp; + int l; + u_char cause = 0x10; + u_char clen = 1; + + if (st->pa->cause > 0) + cause = st->pa->cause; + /* Map DSS1 causes */ + switch (cause & 0x7f) { + case 0x10: + clen = 0; + break; + case 0x15: + cause = CAUSE_CallRejected; + break; + } + StopAllL3Timer(st); + MsgHead(p, st->l3.callref, MT_N1_DISC, PROTO_DIS_N1); + *p++ = WE0_cause; + *p++ = clen; /* Laenge */ + if (clen) + *p++ = cause | 0x80; + newl3state(st, 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); +} + +static void +l3_1tr6_release_req(struct PStack *st, 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); + } else { + L3DelTimer(&st->l3.timer); + st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL); + st->l3.n_t303 = 1; + newl3state(st, 0); + } +} + +static void +l3_1tr6_t304(struct PStack *st, 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); + +} + +static void +l3_1tr6_t305(struct PStack *st, u_char pr, void *arg) +{ + struct sk_buff *skb; + u_char tmp[16]; + u_char *p = tmp; + int l; + u_char cause = 0x90; + u_char clen = 1; + + L3DelTimer(&st->l3.timer); + if (st->pa->cause > 0) + cause = st->pa->cause; + /* Map DSS1 causes */ + switch (cause & 0x7f) { + case 0x10: + clen = 0; + break; + case 0x15: + cause = CAUSE_CallRejected; + break; + } + MsgHead(p, st->l3.callref, MT_N1_REL, PROTO_DIS_N1); + *p++ = WE0_cause; + *p++ = clen; /* Laenge */ + if (clen) + *p++ = cause; + newl3state(st, 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); +} + +static void +l3_1tr6_t310(struct PStack *st, 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); +} + +static void +l3_1tr6_t313(struct PStack *st, 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); +} + +static void +l3_1tr6_t308_1(struct PStack *st, 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); +} + +static void +l3_1tr6_t308_2(struct PStack *st, u_char pr, void *arg) +{ + L3DelTimer(&st->l3.timer); + st->l3.l3l4(st, CC_RELEASE_ERR, NULL); + newl3state(st, 0); +} +/* *INDENT-OFF* */ +static struct stateentry downstl[] = +{ + {SBIT(0), + CC_SETUP_REQ, 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}, + {SBIT(12), + CC_RELEASE_REQ, l3_1tr6_release_req}, + {ALL_STATES, + CC_DLRL, l3_1tr6_reset}, + {SBIT(6), + CC_IGNORE, l3_1tr6_reset}, + {SBIT(6), + CC_REJECT_REQ, l3_1tr6_disconnect_req}, + {SBIT(6), + CC_ALERTING_REQ, l3_1tr6_alert_req}, + {SBIT(6) | SBIT(7), + CC_SETUP_RSP, l3_1tr6_setup_rsp}, + {SBIT(1), + CC_T303, l3_1tr6_t303}, + {SBIT(2), + CC_T304, l3_1tr6_t304}, + {SBIT(3), + CC_T310, l3_1tr6_t310}, + {SBIT(8), + CC_T313, l3_1tr6_t313}, + {SBIT(11), + CC_T305, l3_1tr6_t305}, + {SBIT(19), + CC_T308_1, l3_1tr6_t308_1}, + {SBIT(19), + CC_T308_2, l3_1tr6_t308_2}, +}; + +static int downstl_len = sizeof(downstl) / +sizeof(struct stateentry); + +static struct stateentry datastln1[] = +{ + {SBIT(0), + MT_N1_SETUP, l3_1tr6_setup}, + {SBIT(1), + MT_N1_SETUP_ACK, l3_1tr6_setup_ack}, + {SBIT(1) | SBIT(2), + MT_N1_CALL_SENT, l3_1tr6_call_sent}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10), + MT_N1_DISC, l3_1tr6_disc}, + {SBIT(2) | SBIT(3) | SBIT(4), + MT_N1_ALERT, l3_1tr6_alert}, + {SBIT(2) | SBIT(3) | SBIT(4), + MT_N1_CONN, l3_1tr6_connect}, + {SBIT(2), + MT_N1_INFO, l3_1tr6_info_s2}, + {SBIT(8), + MT_N1_CONN_ACK, l3_1tr6_connect_ack}, + {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), + MT_N1_REL, l3_1tr6_rel}, + {SBIT(19), + MT_N1_REL_ACK, l3_1tr6_rel_ack} +}; +/* *INDENT-ON* */ + + + +static int datastln1_len = sizeof(datastln1) / +sizeof(struct stateentry); + +static void +up1tr6(struct PStack *st, int pr, void *arg) +{ + int i, mt; + struct sk_buff *skb = arg; + char tmp[80]; + + 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); + l3_debug(st, tmp); + } + SET_SKB_FREE(skb); + dev_kfree_skb(skb, FREE_READ); + return; + } + mt = skb->data[skb->data[1] + 2]; + 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); + l3_debug(st, tmp); + } + } else if (skb->data[0] == PROTO_DIS_N1) { + for (i = 0; i < datastln1_len; i++) + if ((mt == datastln1[i].primitive) && + ((1 << st->l3.state) & datastln1[i].state)) + break; + if (i == datastln1_len) { + SET_SKB_FREE(skb); + 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); + 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); + l3_debug(st, tmp); + } + datastln1[i].rout(st, pr, skb); + } + } +} + +static void +down1tr6(struct PStack *st, int pr, void *arg) +{ + int i; + char tmp[80]; + + for (i = 0; i < downstl_len; i++) + if ((pr == downstl[i].primitive) && + ((1 << st->l3.state) & downstl[i].state)) + break; + if (i == downstl_len) { + if (st->l3.debug & L3_DEB_STATE) { + sprintf(tmp, "down1tr6 state %d prim %d unhandled", + st->l3.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); + l3_debug(st, tmp); + } + downstl[i].rout(st, pr, arg); + } +} + +void +setstack_1tr6(struct PStack *st) +{ + char tmp[64]; + + st->l4.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)); + } +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/l3_1tr6.h linux/drivers/isdn/hisax/l3_1tr6.h --- v2.0.30/linux/drivers/isdn/hisax/l3_1tr6.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/l3_1tr6.h Mon Aug 4 17:34:00 1997 @@ -0,0 +1,160 @@ +/* $Id: l3_1tr6.h,v 1.1 1996/10/13 20:03:48 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 + * + * + * + */ +#ifndef l3_1tr6 +#define l3_1tr6 + +#define PROTO_DIS_N0 0x40 +#define PROTO_DIS_N1 0x41 + +/* + * MsgType N0 + */ +#define MT_N0_REG_IND 0x61 +#define MT_N0_CANC_IND 0x62 +#define MT_N0_FAC_STA 0x63 +#define MT_N0_STA_ACK 0x64 +#define MT_N0_STA_REJ 0x65 +#define MT_N0_FAC_INF 0x66 +#define MT_N0_INF_ACK 0x67 +#define MT_N0_INF_REJ 0x68 +#define MT_N0_CLOSE 0x75 +#define MT_N0_CLO_ACK 0x77 + + +/* + * MsgType N1 + */ + +#define MT_N1_ESC 0x00 +#define MT_N1_ALERT 0x01 +#define MT_N1_CALL_SENT 0x02 +#define MT_N1_CONN 0x07 +#define MT_N1_CONN_ACK 0x0F +#define MT_N1_SETUP 0x05 +#define MT_N1_SETUP_ACK 0x0D +#define MT_N1_RES 0x26 +#define MT_N1_RES_ACK 0x2E +#define MT_N1_RES_REJ 0x22 +#define MT_N1_SUSP 0x25 +#define MT_N1_SUSP_ACK 0x2D +#define MT_N1_SUSP_REJ 0x21 +#define MT_N1_USER_INFO 0x20 +#define MT_N1_DET 0x40 +#define MT_N1_DISC 0x45 +#define MT_N1_REL 0x4D +#define MT_N1_REL_ACK 0x5A +#define MT_N1_CANC_ACK 0x6E +#define MT_N1_CANC_REJ 0x67 +#define MT_N1_CON_CON 0x69 +#define MT_N1_FAC 0x60 +#define MT_N1_FAC_ACK 0x68 +#define MT_N1_FAC_CAN 0x66 +#define MT_N1_FAC_REG 0x64 +#define MT_N1_FAC_REJ 0x65 +#define MT_N1_INFO 0x6D +#define MT_N1_REG_ACK 0x6C +#define MT_N1_REG_REJ 0x6F +#define MT_N1_STAT 0x63 + + + +/* + * W Elemente + */ + +#define WE_Shift_F0 0x90 +#define WE_Shift_F6 0x96 +#define WE_Shift_OF0 0x98 +#define WE_Shift_OF6 0x9E + +#define WE0_cause 0x08 +#define WE0_connAddr 0x0C +#define WE0_callID 0x10 +#define WE0_chanID 0x18 +#define WE0_netSpecFac 0x20 +#define WE0_display 0x28 +#define WE0_keypad 0x2C +#define WE0_origAddr 0x6C +#define WE0_destAddr 0x70 +#define WE0_userInfo 0x7E + +#define WE0_moreData 0xA0 +#define WE0_congestLevel 0xB0 + +#define WE6_serviceInd 0x01 +#define WE6_chargingInfo 0x02 +#define WE6_date 0x03 +#define WE6_facSelect 0x05 +#define WE6_facStatus 0x06 +#define WE6_statusCalled 0x07 +#define WE6_addTransAttr 0x08 + +/* + * FacCodes + */ +#define FAC_Sperre 0x01 +#define FAC_Sperre_All 0x02 +#define FAC_Sperre_Fern 0x03 +#define FAC_Sperre_Intl 0x04 +#define FAC_Sperre_Interk 0x05 + +#define FAC_Forward1 0x02 +#define FAC_Forward2 0x03 +#define FAC_Konferenz 0x06 +#define FAC_GrabBchan 0x0F +#define FAC_Reactivate 0x10 +#define FAC_Konferenz3 0x11 +#define FAC_Dienstwechsel1 0x12 +#define FAC_Dienstwechsel2 0x13 +#define FAC_NummernIdent 0x14 +#define FAC_GBG 0x15 +#define FAC_DisplayUebergeben 0x17 +#define FAC_DisplayUmgeleitet 0x1A +#define FAC_Unterdruecke 0x1B +#define FAC_Deactivate 0x1E +#define FAC_Activate 0x1D +#define FAC_SPV 0x1F +#define FAC_Rueckwechsel 0x23 +#define FAC_Umleitung 0x24 + +/* + * Cause codes + */ +#define CAUSE_InvCRef 0x01 +#define CAUSE_BearerNotImpl 0x03 +#define CAUSE_CIDunknown 0x07 +#define CAUSE_CIDinUse 0x08 +#define CAUSE_NoChans 0x0A +#define CAUSE_FacNotImpl 0x10 +#define CAUSE_FacNotSubscr 0x11 +#define CAUSE_OutgoingBarred 0x20 +#define CAUSE_UserAccessBusy 0x21 +#define CAUSE_NegativeGBG 0x22 +#define CAUSE_UnknownGBG 0x23 +#define CAUSE_NoSPVknown 0x25 +#define CAUSE_DestNotObtain 0x35 +#define CAUSE_NumberChanged 0x38 +#define CAUSE_OutOfOrder 0x39 +#define CAUSE_NoUserResponse 0x3A +#define CAUSE_UserBusy 0x3B +#define CAUSE_IncomingBarred 0x3D +#define CAUSE_CallRejected 0x3E +#define CAUSE_NetworkCongestion 0x59 +#define CAUSE_RemoteUser 0x5A +#define CAUSE_LocalProcErr 0x70 +#define CAUSE_RemoteProcErr 0x71 +#define CAUSE_RemoteUserSuspend 0x72 +#define CAUSE_RemoteUserResumed 0x73 +#define CAUSE_UserInfoDiscarded 0x7F + + +#endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/l3dss1.c linux/drivers/isdn/hisax/l3dss1.c --- v2.0.30/linux/drivers/isdn/hisax/l3dss1.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/l3dss1.c Mon Aug 4 17:34:00 1997 @@ -0,0 +1,809 @@ +/* $Id: l3dss1.c,v 1.16 1997/06/03 20:43:46 keil Exp $ + + * EURO/DSS1 D-channel protocol + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * based on the teles driver from Jan den Ouden + * + * 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.11 1997/02/16 12:12:47 fritz + * Bugfix: SI2 was nont initialized on incoming calls. + * + * Revision 1.10 1997/02/11 01:37:24 keil + * Changed setup-interface (incoming and outgoing) + * + * Revision 1.9 1997/01/27 23:20:52 keil + * report revision only ones + * + * Revision 1.8 1997/01/21 22:29:41 keil + * new statemachine; L3 timers + * + * Revision 1.7 1996/12/14 21:06:59 keil + * additional states for CC_REJECT + * + * Revision 1.6 1996/12/08 22:59:16 keil + * fixed calling party number without octet 3a + * + * Revision 1.5 1996/12/08 19:53:31 keil + * fixes from Pekka Sarnila + * + * Revision 1.4 1996/11/05 19:44:36 keil + * some fixes from Henner Eisen + * + * Revision 1.3 1996/10/30 10:18:01 keil + * bugfixes in debugging output + * + * Revision 1.2 1996/10/27 22:15:16 keil + * bugfix reject handling + * + * Revision 1.1 1996/10/13 20:04:55 keil + * Initial revision + * + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isdnl3.h" +#include + +extern char *HiSax_getrev(const char *revision); +const char *dss1_revision = "$Revision: 1.16 $"; + +#define MsgHead(ptr, cref, mty) \ + *ptr++ = 0x8; \ + *ptr++ = 0x1; \ + *ptr++ = cref; \ + *ptr++ = mty + +static void +l3dss1_message(struct PStack *st, u_char mt) +{ + struct sk_buff *skb; + u_char *p; + + 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); +} + +static void +l3dss1_release_req(struct PStack *st, 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); +} + +static void +l3dss1_release_cmpl(struct PStack *st, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + int cause = -1; + + p = skb->data; + st->pa->loc = 0; + if ((p = findie(p, skb->len, IE_CAUSE, 0))) { + p++; + if (*p++ == 2) + st->pa->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); +} + +static void +l3dss1_setup_req(struct PStack *st, u_char pr, + void *arg) +{ + struct sk_buff *skb; + u_char tmp[128]; + u_char *p = tmp; + u_char channel = 0; + u_char screen = 0x80; + u_char *teln; + u_char *msn; + int l; + + st->l3.callref = st->pa->callref; + MsgHead(p, st->l3.callref, MT_SETUP); + + /* + * Set Bearer Capability, Map info from 1TR6-convention to EDSS1 + */ + *p++ = 0xa1; /* complete indicator */ + switch (st->pa->setup.si1) { + case 1: /* Telephony */ + *p++ = 0x4; /* BC-IE-code */ + *p++ = 0x3; /* Length */ + *p++ = 0x90; /* Coding Std. CCITT, 3.1 kHz audio */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + *p++ = 0xa3; /* A-Law Audio */ + break; + case 5: /* Datatransmission 64k, BTX */ + case 7: /* Datatransmission 64k */ + default: + *p++ = 0x4; /* BC-IE-code */ + *p++ = 0x2; /* Length */ + *p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inform. */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + break; + } + /* + * What about info2? Mapping to High-Layer-Compatibility? + */ + teln = st->pa->setup.phone; + if (*teln) { + /* parse number for special things */ + if (!isdigit(*teln)) { + switch (0x5f & *teln) { + case 'C': + channel = 0x08; + case 'P': + channel |= 0x80; + teln++; + if (*teln == '1') + channel |= 0x01; + else + channel |= 0x02; + break; + case 'R': + screen = 0xA0; + break; + case 'D': + screen = 0x80; + break; + default: + if (st->l3.debug & L3_DEB_WARN) + l3_debug(st, "Wrong MSN Code"); + break; + } + teln++; + } + } + if (channel) { + *p++ = 0x18; /* channel indicator */ + *p++ = 1; + *p++ = channel; + } + msn = st->pa->setup.eazmsn; + if (*msn) { + *p++ = 0x6c; + *p++ = strlen(msn) + (screen ? 2 : 1); + /* Classify as AnyPref. */ + if (screen) { + *p++ = 0x01; /* Ext = '0'B, Type = '000'B, Plan = '0001'B. */ + *p++ = screen; + } else + *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */ + while (*msn) + *p++ = *msn++ & 0x7f; + } + *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; + + 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); + +} + +static void +l3dss1_call_proc(struct PStack *st, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + + L3DelTimer(&st->l3.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); + 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); +} + +static void +l3dss1_setup_ack(struct PStack *st, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + + L3DelTimer(&st->l3.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); + 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); +} + +static void +l3dss1_disconnect(struct PStack *st, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + int cause = -1; + + StopAllL3Timer(st); + p = skb->data; + st->pa->loc = 0; + if ((p = findie(p, skb->len, IE_CAUSE, 0))) { + p++; + if (*p++ == 2) + st->pa->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); +} + +static void +l3dss1_connect(struct PStack *st, 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); +} + +static void +l3dss1_alerting(struct PStack *st, 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); +} + +static void +l3dss1_setup(struct PStack *st, u_char pr, void *arg) +{ + u_char *p; + int bcfound = 0; + char tmp[80]; + 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, 0x18, 0))) { + st->pa->bchannel = p[2] & 0x3; + if (st->pa->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"); + + /* + * Bearer Capabilities + */ + p = skb->data; + if ((p = findie(p, skb->len, 0x04, 0))) { + st->pa->setup.si2 = 0; + switch (p[2] & 0x1f) { + case 0x00: + /* Speech */ + case 0x10: + /* 3.1 Khz audio */ + st->pa->setup.si1 = 1; + break; + case 0x08: + /* Unrestricted digital information */ + st->pa->setup.si1 = 7; + break; + case 0x09: + /* Restricted digital information */ + st->pa->setup.si1 = 2; + break; + case 0x11: + /* Unrestr. digital information with tones/announcements */ + st->pa->setup.si1 = 3; + break; + case 0x18: + /* Video */ + st->pa->setup.si1 = 4; + break; + default: + st->pa->setup.si1 = 0; + } + } else if (st->l3.debug & L3_DEB_WARN) + l3_debug(st, "setup without bearer capabilities"); + + p = skb->data; + if ((p = findie(p, skb->len, 0x70, 0))) + iecpy(st->pa->setup.eazmsn, p, 1); + else + st->pa->setup.eazmsn[0] = 0; + + p = skb->data; + if ((p = findie(p, skb->len, 0x6c, 0))) { + st->pa->setup.plan = p[2]; + if (p[2] & 0x80) { + iecpy(st->pa->setup.phone, p, 1); + st->pa->setup.screen = 0; + } else { + iecpy(st->pa->setup.phone, p, 2); + st->pa->setup.screen = p[3]; + } + } else { + st->pa->setup.phone[0] = 0; + st->pa->setup.plan = 0; + st->pa->setup.screen = 0; + } + 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); + } + newl3state(st, 6); + st->l3.l3l4(st, CC_SETUP_IND, NULL); + } +} + +static void +l3dss1_reset(struct PStack *st, u_char pr, void *arg) +{ + StopAllL3Timer(st); + newl3state(st, 0); +} + +static void +l3dss1_setup_rsp(struct PStack *st, 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); +} + +static void +l3dss1_connect_ack(struct PStack *st, 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); +} + +static void +l3dss1_disconnect_req(struct PStack *st, u_char pr, void *arg) +{ + struct sk_buff *skb; + u_char tmp[16]; + u_char *p = tmp; + int l; + u_char cause = 0x10; + + if (st->pa->cause > 0) + cause = st->pa->cause; + + StopAllL3Timer(st); + + MsgHead(p, st->l3.callref, MT_DISCONNECT); + + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = cause | 0x80; + + l = p - tmp; + 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); +} + +static void +l3dss1_reject_req(struct PStack *st, u_char pr, void *arg) +{ + struct sk_buff *skb; + u_char tmp[16]; + u_char *p = tmp; + int l; + u_char cause = 0x95; + + if (st->pa->cause > 0) + cause = st->pa->cause; + + MsgHead(p, st->l3.callref, MT_RELEASE_COMPLETE); + + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = cause; + + l = p - tmp; + 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); +} + +static void +l3dss1_release(struct PStack *st, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + int cause = -1; + + p = skb->data; + if ((p = findie(p, skb->len, IE_CAUSE, 0))) { + p++; + if (*p++ == 2) + st->pa->loc = *p++; + cause = *p & 0x7f; + } + SET_SKB_FREE(skb); + 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); +} + +static void +l3dss1_alert_req(struct PStack *st, u_char pr, + void *arg) +{ + newl3state(st, 7); + l3dss1_message(st, MT_ALERTING); +} + +static void +l3dss1_status_enq(struct PStack *st, 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); + + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = 0x9E; /* answer status enquire */ + + *p++ = 0x14; /* CallState */ + *p++ = 0x1; + *p++ = st->l3.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); +} + +static void +l3dss1_t303(struct PStack *st, 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); + } else { + newl3state(st, 0); + L3DelTimer(&st->l3.timer); + st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL); + st->l3.n_t303 = 1; + } +} + +static void +l3dss1_t304(struct PStack *st, 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); + +} + +static void +l3dss1_t305(struct PStack *st, u_char pr, void *arg) +{ + u_char tmp[16]; + u_char *p = tmp; + int l; + struct sk_buff *skb; + u_char cause = 0x90; + + L3DelTimer(&st->l3.timer); + if (st->pa->cause > 0) + cause = st->pa->cause; + + MsgHead(p, st->l3.callref, MT_RELEASE); + + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = cause; + + l = p - tmp; + 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); +} + +static void +l3dss1_t310(struct PStack *st, 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); +} + +static void +l3dss1_t313(struct PStack *st, 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); +} + +static void +l3dss1_t308_1(struct PStack *st, 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); +} + +static void +l3dss1_t308_2(struct PStack *st, u_char pr, void *arg) +{ + newl3state(st, 0); + L3DelTimer(&st->l3.timer); + st->l3.l3l4(st, CC_RELEASE_ERR, NULL); +} +/* *INDENT-OFF* */ +static struct stateentry downstatelist[] = +{ + {SBIT(0), + CC_SETUP_REQ, l3dss1_setup_req}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(10), + CC_DISCONNECT_REQ, l3dss1_disconnect_req}, + {SBIT(12), + CC_RELEASE_REQ, l3dss1_release_req}, + {ALL_STATES, + CC_DLRL, l3dss1_reset}, + {SBIT(6), + CC_IGNORE, l3dss1_reset}, + {SBIT(6), + CC_REJECT_REQ, l3dss1_reject_req}, + {SBIT(6), + CC_ALERTING_REQ, l3dss1_alert_req}, + {SBIT(6) | SBIT(7), + CC_SETUP_RSP, l3dss1_setup_rsp}, + {SBIT(1), + CC_T303, l3dss1_t303}, + {SBIT(2), + CC_T304, l3dss1_t304}, + {SBIT(3), + CC_T310, l3dss1_t310}, + {SBIT(8), + CC_T313, l3dss1_t313}, + {SBIT(11), + CC_T305, l3dss1_t305}, + {SBIT(19), + CC_T308_1, l3dss1_t308_1}, + {SBIT(19), + CC_T308_2, l3dss1_t308_2}, +}; + +static int downsllen = sizeof(downstatelist) / +sizeof(struct stateentry); + +static struct stateentry datastatelist[] = +{ + {ALL_STATES, + MT_STATUS_ENQUIRY, l3dss1_status_enq}, + {SBIT(0) | SBIT(6), + MT_SETUP, l3dss1_setup}, + {SBIT(1) | SBIT(2), + MT_CALL_PROCEEDING, l3dss1_call_proc}, + {SBIT(1), + MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack}, + {SBIT(1) | SBIT(2) | SBIT(3), + MT_ALERTING, l3dss1_alerting}, + {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), + MT_RELEASE, l3dss1_release}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10), + MT_DISCONNECT, l3dss1_disconnect}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4), + MT_CONNECT, l3dss1_connect}, + {SBIT(8), + MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack}, +}; +/* *INDENT-ON* */ + + +static int datasllen = sizeof(datastatelist) / +sizeof(struct stateentry); + +static void +dss1up(struct PStack *st, int pr, void *arg) +{ + int i, mt; + struct sk_buff *skb = arg; + char tmp[80]; + + 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); + } + SET_SKB_FREE(skb); + dev_kfree_skb(skb, FREE_READ); + return; + } + mt = skb->data[skb->data[1] + 2]; + for (i = 0; i < datasllen; i++) + if ((mt == datastatelist[i].primitive) && + ((1 << st->l3.state) & datastatelist[i].state)) + break; + if (i == datasllen) { + SET_SKB_FREE(skb); + 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); + } + 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); + } + datastatelist[i].rout(st, pr, skb); + } +} + +static void +dss1down(struct PStack *st, int pr, void *arg) +{ + int i; + char tmp[80]; + + for (i = 0; i < downsllen; i++) + if ((pr == downstatelist[i].primitive) && + ((1 << st->l3.state) & downstatelist[i].state)) + break; + 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); + } + } else { + if (st->l3.debug & L3_DEB_STATE) { + sprintf(tmp, "dss1down state %d prim %d", + st->l3.state, pr); + l3_debug(st, tmp); + } + downstatelist[i].rout(st, pr, arg); + } +} + +void +setstack_dss1(struct PStack *st) +{ + char tmp[64]; + + st->l4.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)); + } +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/q931.c linux/drivers/isdn/hisax/q931.c --- v2.0.30/linux/drivers/isdn/hisax/q931.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/q931.c Mon Aug 4 17:34:00 1997 @@ -0,0 +1,1218 @@ +/* $Id: q931.c,v 1.5 1997/04/06 22:56:43 keil Exp $ + + * q931.c code to decode ITU Q.931 call control messages + * + * Author Jan den Ouden + * + * Changelog + * + * Pauline Middelink general improvements + * + * Beat Doebeli cause texts, display information element + * + * Karsten Keil cause texts, display information element for 1TR6 + * + * + * $Log: q931.c,v $ + * Revision 1.5 1997/04/06 22:56:43 keil + * Some cosmetic changes + * + * Revision 1.4 1997/02/09 00:29:11 keil + * new interface handling, one interface per card + * + * Revision 1.3 1997/01/21 22:24:59 keil + * cleanups + * + * Revision 1.2 1996/10/27 22:12:45 keil + * reporting unknown level 3 protocol ids + * + * Revision 1.1 1996/10/13 20:04:56 keil + * Initial revision + * + * + */ + + +#define __NO_VERSION__ +#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) +{ + u_char *p; + int l; + + p = iestart + ieoffset + 2; + l = iestart[1] - ieoffset; + while (l--) + *dest++ = *p++; + *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 + */ +static +struct MessageType { + u_char nr; + char *descr; +} mtlist[] = { + + { + 0x1, "ALERTING" + }, + { + 0x2, "CALL PROCEEDING" + }, + { + 0x7, "CONNECT" + }, + { + 0xf, "CONNECT ACKNOWLEDGE" + }, + { + 0x3, "PROGRESS" + }, + { + 0x5, "SETUP" + }, + { + 0xd, "SETUP ACKNOWLEDGE" + }, + { + 0x26, "RESUME" + }, + { + 0x2e, "RESUME ACKNOWLEDGE" + }, + { + 0x22, "RESUME REJECT" + }, + { + 0x25, "SUSPEND" + }, + { + 0x2d, "SUSPEND ACKNOWLEDGE" + }, + { + 0x21, "SUSPEND REJECT" + }, + { + 0x20, "USER INFORMATION" + }, + { + 0x45, "DISCONNECT" + }, + { + 0x4d, "RELEASE" + }, + { + 0x5a, "RELEASE COMPLETE" + }, + { + 0x46, "RESTART" + }, + { + 0x4e, "RESTART ACKNOWLEDGE" + }, + { + 0x60, "SEGMENT" + }, + { + 0x79, "CONGESTION CONTROL" + }, + { + 0x7b, "INFORMATION" + }, + { + 0x62, "FACILITY" + }, + { + 0x6e, "NOTIFY" + }, + { + 0x7d, "STATUS" + }, + { + 0x75, "STATUS ENQUIRY" + } +}; + +#define MTSIZE sizeof(mtlist)/sizeof(struct MessageType) + +static +struct MessageType mt_n0[] = +{ + {MT_N0_REG_IND, "REGister INDication"}, + {MT_N0_CANC_IND, "CANCel INDication"}, + {MT_N0_FAC_STA, "FACility STAtus"}, + {MT_N0_STA_ACK, "STAtus ACKnowledge"}, + {MT_N0_STA_REJ, "STAtus REJect"}, + {MT_N0_FAC_INF, "FACility INFormation"}, + {MT_N0_INF_ACK, "INFormation ACKnowledge"}, + {MT_N0_INF_REJ, "INFormation REJect"}, + {MT_N0_CLOSE, "CLOSE"}, + {MT_N0_CLO_ACK, "CLOse ACKnowledge"} +}; + +int mt_n0_len = (sizeof(mt_n0) / sizeof(struct MessageType)); + +static +struct MessageType mt_n1[] = +{ + {MT_N1_ESC, "ESCape"}, + {MT_N1_ALERT, "ALERT"}, + {MT_N1_CALL_SENT, "CALL SENT"}, + {MT_N1_CONN, "CONNect"}, + {MT_N1_CONN_ACK, "CONNect ACKnowledge"}, + {MT_N1_SETUP, "SETUP"}, + {MT_N1_SETUP_ACK, "SETUP ACKnowledge"}, + {MT_N1_RES, "RESume"}, + {MT_N1_RES_ACK, "RESume ACKnowledge"}, + {MT_N1_RES_REJ, "RESume REJect"}, + {MT_N1_SUSP, "SUSPend"}, + {MT_N1_SUSP_ACK, "SUSPend ACKnowledge"}, + {MT_N1_SUSP_REJ, "SUSPend REJect"}, + {MT_N1_USER_INFO, "USER INFO"}, + {MT_N1_DET, "DETach"}, + {MT_N1_DISC, "DISConnect"}, + {MT_N1_REL, "RELease"}, + {MT_N1_REL_ACK, "RELease ACKnowledge"}, + {MT_N1_CANC_ACK, "CANCel ACKnowledge"}, + {MT_N1_CANC_REJ, "CANCel REJect"}, + {MT_N1_CON_CON, "CONgestion CONtrol"}, + {MT_N1_FAC, "FACility"}, + {MT_N1_FAC_ACK, "FACility ACKnowledge"}, + {MT_N1_FAC_CAN, "FACility CANcel"}, + {MT_N1_FAC_REG, "FACility REGister"}, + {MT_N1_FAC_REJ, "FACility REJect"}, + {MT_N1_INFO, "INFOrmation"}, + {MT_N1_REG_ACK, "REGister ACKnowledge"}, + {MT_N1_REG_REJ, "REGister REJect"}, + {MT_N1_STAT, "STATus"} +}; + +int mt_n1_len = (sizeof(mt_n1) / sizeof(struct MessageType)); + +static struct MessageType fac_1tr6[] = +{ + {FAC_Sperre, "Sperre"}, + {FAC_Forward1, "Forward 1"}, + {FAC_Forward2, "Forward 2"}, + {FAC_Konferenz, "Konferenz"}, + {FAC_GrabBchan, "Grab Bchannel"}, + {FAC_Reactivate, "Reactivate"}, + {FAC_Konferenz3, "Dreier Konferenz"}, + {FAC_Dienstwechsel1, "Einseitiger Dienstwechsel"}, + {FAC_Dienstwechsel2, "Zweiseitiger Dienstwechsel"}, + {FAC_NummernIdent, "Rufnummer-Identifizierung"}, + {FAC_GBG, "GBG"}, + {FAC_DisplayUebergeben, "Display Uebergeben"}, + {FAC_DisplayUmgeleitet, "Display Umgeleitet"}, + {FAC_Unterdruecke, "Unterdruecke Rufnummer"}, + {FAC_Deactivate, "Deactivate"}, + {FAC_Activate, "Activate"}, + {FAC_SPV, "SPV"}, + {FAC_Rueckwechsel, "Rueckwechsel"}, + {FAC_Umleitung, "Umleitung"} +}; +int fac_1tr6_len = (sizeof(fac_1tr6) / sizeof(struct MessageType)); + + + +static int +prbits(char *dest, u_char b, int start, int len) +{ + char *dp = dest; + + b = b << (8 - start); + while (len--) { + if (b & 0x80) + *dp++ = '1'; + else + *dp++ = '0'; + b = b << 1; + } + return (dp - dest); +} + +static +u_char * +skipext(u_char * p) +{ + while (!(*p++ & 0x80)); + return (p); +} + +/* + * Cause Values According to Q.850 + * edescr: English description + * ddescr: German description used by Swissnet II (Swiss Telecom + * not yet written... + */ + +static +struct CauseValue { + u_char nr; + char *edescr; + char *ddescr; +} cvlist[] = { + + { + 0x01, "Unallocated (unassigned) number", "Nummer nicht zugeteilt" + }, + { + 0x02, "No route to specified transit network", "" + }, + { + 0x03, "No route to destination", "" + }, + { + 0x04, "Send special information tone", "" + }, + { + 0x05, "Misdialled trunk prefix", "" + }, + { + 0x06, "Channel unacceptable", "Kanal nicht akzeptierbar" + }, + { + 0x07, "Channel awarded and being delivered in an established channel", "" + }, + { + 0x08, "Preemption", "" + }, + { + 0x09, "Preemption - circuit reserved for reuse", "" + }, + { + 0x10, "Normal call clearing", "Normale Ausloesung" + }, + { + 0x11, "User busy", "TNB besetzt" + }, + { + 0x12, "No user responding", "" + }, + { + 0x13, "No answer from user (user alerted)", "" + }, + { + 0x14, "Subscriber absent", "" + }, + { + 0x15, "Call rejected", "" + }, + { + 0x16, "Number changed", "" + }, + { + 0x1a, "non-selected user clearing", "" + }, + { + 0x1b, "Destination out of order", "" + }, + { + 0x1c, "Invalid number format (address incomplete)", "" + }, + { + 0x1d, "Facility rejected", "" + }, + { + 0x1e, "Response to Status enquiry", "" + }, + { + 0x1f, "Normal, unspecified", "" + }, + { + 0x22, "No circuit/channel available", "" + }, + { + 0x26, "Network out of order", "" + }, + { + 0x27, "Permanent frame mode connection out-of-service", "" + }, + { + 0x28, "Permanent frame mode connection operational", "" + }, + { + 0x29, "Temporary failure", "" + }, + { + 0x2a, "Switching equipment congestion", "" + }, + { + 0x2b, "Access information discarded", "" + }, + { + 0x2c, "Requested circuit/channel not available", "" + }, + { + 0x2e, "Precedence call blocked", "" + }, + { + 0x2f, "Resource unavailable, unspecified", "" + }, + { + 0x31, "Quality of service unavailable", "" + }, + { + 0x32, "Requested facility not subscribed", "" + }, + { + 0x35, "Outgoing calls barred within CUG", "" + }, + { + 0x37, "Incoming calls barred within CUG", "" + }, + { + 0x39, "Bearer capability not authorized", "" + }, + { + 0x3a, "Bearer capability not presently available", "" + }, + { + 0x3e, "Inconsistency in designated outgoing access information and subscriber class ", " " + }, + { + 0x3f, "Service or option not available, unspecified", "" + }, + { + 0x41, "Bearer capability not implemented", "" + }, + { + 0x42, "Channel type not implemented", "" + }, + { + 0x43, "Requested facility not implemented", "" + }, + { + 0x44, "Only restricted digital information bearer capability is available", "" + }, + { + 0x4f, "Service or option not implemented", "" + }, + { + 0x51, "Invalid call reference value", "" + }, + { + 0x52, "Identified channel does not exist", "" + }, + { + 0x53, "A suspended call exists, but this call identity does not", "" + }, + { + 0x54, "Call identity in use", "" + }, + { + 0x55, "No call suspended", "" + }, + { + 0x56, "Call having the requested call identity has been cleared", "" + }, + { + 0x57, "User not member of CUG", "" + }, + { + 0x58, "Incompatible destination", "" + }, + { + 0x5a, "Non-existent CUG", "" + }, + { + 0x5b, "Invalid transit network selection", "" + }, + { + 0x5f, "Invalid message, unspecified", "" + }, + { + 0x60, "Mandatory information element is missing", "" + }, + { + 0x61, "Message type non-existent or not implemented", "" + }, + { + 0x62, "Message not compatible with call state or message type non-existent or not implemented ", " " + }, + { + 0x63, "Information element/parameter non-existent or not implemented", "" + }, + { + 0x64, "Invalid information element contents", "" + }, + { + 0x65, "Message not compatible with call state", "" + }, + { + 0x66, "Recovery on timer expiry", "" + }, + { + 0x67, "Parameter non-existent or not implemented - passed on", "" + }, + { + 0x6e, "Message with unrecognized parameter discarded", "" + }, + { + 0x6f, "Protocol error, unspecified", "" + }, + { + 0x7f, "Interworking, unspecified", "" + }, +}; + +#define CVSIZE sizeof(cvlist)/sizeof(struct CauseValue) + +static +int +prcause(char *dest, u_char * p) +{ + u_char *end; + char *dp = dest; + int i, cause; + + end = p + p[1] + 1; + p += 2; + dp += sprintf(dp, " coding "); + dp += prbits(dp, *p, 7, 2); + dp += sprintf(dp, " location "); + dp += prbits(dp, *p, 4, 4); + *dp++ = '\n'; + p = skipext(p); + + cause = 0x7f & *p++; + + /* locate cause value */ + for (i = 0; i < CVSIZE; i++) + if (cvlist[i].nr == cause) + break; + + /* display cause value if it exists */ + if (i == CVSIZE) + dp += sprintf(dp, "Unknown cause type %x!\n", cause); + else + dp += sprintf(dp, " cause value %x : %s \n", cause, cvlist[i].edescr); + + while (!0) { + if (p > end) + break; + dp += sprintf(dp, " diag attribute %d ", *p++ & 0x7f); + dp += sprintf(dp, " rej %d ", *p & 0x7f); + if (*p & 0x80) { + *dp++ = '\n'; + break; + } else + dp += sprintf(dp, " av %d\n", (*++p) & 0x7f); + } + return (dp - dest); + +} + +static +struct MessageType cause_1tr6[] = +{ + {CAUSE_InvCRef, "Invalid Call Reference"}, + {CAUSE_BearerNotImpl, "Bearer Service Not Implemented"}, + {CAUSE_CIDunknown, "Caller Identity unknown"}, + {CAUSE_CIDinUse, "Caller Identity in Use"}, + {CAUSE_NoChans, "No Channels available"}, + {CAUSE_FacNotImpl, "Facility Not Implemented"}, + {CAUSE_FacNotSubscr, "Facility Not Subscribed"}, + {CAUSE_OutgoingBarred, "Outgoing calls barred"}, + {CAUSE_UserAccessBusy, "User Access Busy"}, + {CAUSE_NegativeGBG, "Negative GBG"}, + {CAUSE_UnknownGBG, "Unknown GBG"}, + {CAUSE_NoSPVknown, "No SPV known"}, + {CAUSE_DestNotObtain, "Destination not obtainable"}, + {CAUSE_NumberChanged, "Number changed"}, + {CAUSE_OutOfOrder, "Out Of Order"}, + {CAUSE_NoUserResponse, "No User Response"}, + {CAUSE_UserBusy, "User Busy"}, + {CAUSE_IncomingBarred, "Incoming Barred"}, + {CAUSE_CallRejected, "Call Rejected"}, + {CAUSE_NetworkCongestion, "Network Congestion"}, + {CAUSE_RemoteUser, "Remote User initiated"}, + {CAUSE_LocalProcErr, "Local Procedure Error"}, + {CAUSE_RemoteProcErr, "Remote Procedure Error"}, + {CAUSE_RemoteUserSuspend, "Remote User Suspend"}, + {CAUSE_RemoteUserResumed, "Remote User Resumed"}, + {CAUSE_UserInfoDiscarded, "User Info Discarded"} +}; + +int cause_1tr6_len = (sizeof(cause_1tr6) / sizeof(struct MessageType)); + +static int +prcause_1tr6(char *dest, u_char * p) +{ + char *dp = dest; + int i, cause; + + p++; + if (0 == *p) { + dp += sprintf(dp, " OK (cause length=0)\n"); + return (dp - dest); + } else if (*p > 1) { + dp += sprintf(dp, " coding "); + dp += prbits(dp, p[2], 7, 2); + dp += sprintf(dp, " location "); + dp += prbits(dp, p[2], 4, 4); + *dp++ = '\n'; + } + p++; + cause = 0x7f & *p; + + /* locate cause value */ + for (i = 0; i < cause_1tr6_len; i++) + if (cause_1tr6[i].nr == cause) + break; + + /* display cause value if it exists */ + if (i == cause_1tr6_len) + dp += sprintf(dp, "Unknown cause type %x!\n", cause); + else + dp += sprintf(dp, " cause value %x : %s \n", cause, cause_1tr6[i].descr); + + return (dp - dest); + +} + +static int +prchident(char *dest, u_char * p) +{ + char *dp = dest; + + p += 2; + dp += sprintf(dp, " octet 3 "); + dp += prbits(dp, *p, 8, 8); + *dp++ = '\n'; + return (dp - dest); +} + +static int +prcalled(char *dest, u_char * p) +{ + int l; + char *dp = dest; + + p++; + l = *p++ - 1; + dp += sprintf(dp, " octet 3 "); + dp += prbits(dp, *p++, 8, 8); + *dp++ = '\n'; + dp += sprintf(dp, " number digits "); + while (l--) + *dp++ = *p++; + *dp++ = '\n'; + return (dp - dest); +} +static int +prcalling(char *dest, u_char * p) +{ + int l; + char *dp = dest; + + p++; + l = *p++ - 1; + dp += sprintf(dp, " octet 3 "); + dp += prbits(dp, *p, 8, 8); + *dp++ = '\n'; + if (!(*p & 0x80)) { + dp += sprintf(dp, " octet 3a "); + dp += prbits(dp, *++p, 8, 8); + *dp++ = '\n'; + l--; + }; + p++; + + dp += sprintf(dp, " number digits "); + while (l--) + *dp++ = *p++; + *dp++ = '\n'; + return (dp - dest); +} + +static +int +prbearer(char *dest, u_char * p) +{ + char *dp = dest, ch; + + p += 2; + dp += sprintf(dp, " octet 3 "); + dp += prbits(dp, *p++, 8, 8); + *dp++ = '\n'; + dp += sprintf(dp, " octet 4 "); + dp += prbits(dp, *p, 8, 8); + *dp++ = '\n'; + if ((*p++ & 0x1f) == 0x18) { + dp += sprintf(dp, " octet 4.1 "); + dp += prbits(dp, *p++, 8, 8); + *dp++ = '\n'; + } + /* check for user information layer 1 */ + if ((*p & 0x60) == 0x20) { + ch = ' '; + do { + dp += sprintf(dp, " octet 5%c ", ch); + dp += prbits(dp, *p, 8, 8); + *dp++ = '\n'; + if (ch == ' ') + ch = 'a'; + else + ch++; + } + while (!(*p++ & 0x80)); + } + /* check for user information layer 2 */ + if ((*p & 0x60) == 0x40) { + dp += sprintf(dp, " octet 6 "); + dp += prbits(dp, *p++, 8, 8); + *dp++ = '\n'; + } + /* check for user information layer 3 */ + if ((*p & 0x60) == 0x60) { + dp += sprintf(dp, " octet 7 "); + dp += prbits(dp, *p++, 8, 8); + *dp++ = '\n'; + } + return (dp - dest); +} + +static int +general(char *dest, u_char * p) +{ + char *dp = dest; + char ch = ' '; + int l, octet = 3; + + p++; + l = *p++; + /* Iterate over all octets in the information element */ + while (l--) { + dp += sprintf(dp, " octet %d%c ", octet, ch); + dp += prbits(dp, *p++, 8, 8); + *dp++ = '\n'; + + /* last octet in group? */ + if (*p & 0x80) { + octet++; + ch = ' '; + } else if (ch == ' ') + ch = 'a'; + else + ch++; + } + return (dp - dest); +} + +static int +prcharge(char *dest, u_char * p) +{ + char *dp = dest; + int l; + + p++; + l = *p++ - 1; + dp += sprintf(dp, " GEA "); + dp += prbits(dp, *p++, 8, 8); + dp += sprintf(dp, " Anzahl: "); + /* Iterate over all octets in the * information element */ + while (l--) + *dp++ = *p++; + *dp++ = '\n'; + return (dp - dest); +} +static int +prtext(char *dest, u_char * p) +{ + char *dp = dest; + int l; + + p++; + l = *p++; + dp += sprintf(dp, " "); + /* Iterate over all octets in the * information element */ + while (l--) + *dp++ = *p++; + *dp++ = '\n'; + return (dp - dest); +} +static int +display(char *dest, u_char * p) +{ + char *dp = dest; + char ch = ' '; + int l, octet = 3; + + p++; + l = *p++; + /* Iterate over all octets in the * display-information element */ + dp += sprintf(dp, " \""); + while (l--) { + dp += sprintf(dp, "%c", *p++); + + /* last octet in group? */ + if (*p & 0x80) { + octet++; + ch = ' '; + } else if (ch == ' ') + ch = 'a'; + + else + ch++; + } + *dp++ = '\"'; + *dp++ = '\n'; + return (dp - dest); +} + +int +prfacility(char *dest, u_char * p) +{ + char *dp = dest; + int l, l2; + + p++; + l = *p++; + dp += sprintf(dp, " octet 3 "); + dp += prbits(dp, *p++, 8, 8); + dp += sprintf(dp, "\n"); + l -= 1; + + while (l > 0) { + dp += sprintf(dp, " octet 4 "); + dp += prbits(dp, *p++, 8, 8); + dp += sprintf(dp, "\n"); + dp += sprintf(dp, " octet 5 %d\n", l2 = *p++ & 0x7f); + l -= 2; + dp += sprintf(dp, " contents "); + while (l2--) { + dp += sprintf(dp, "%2x ", *p++); + l--; + } + dp += sprintf(dp, "\n"); + } + + return (dp - dest); +} + +static +struct InformationElement { + u_char nr; + char *descr; + int (*f) (char *, u_char *); +} ielist[] = { + + { + 0x00, "Segmented message", general + }, + { + 0x04, "Bearer capability", prbearer + }, + { + 0x08, "Cause", prcause + }, + { + 0x10, "Call identity", general + }, + { + 0x14, "Call state", general + }, + { + 0x18, "Channel identification", prchident + }, + { + 0x1c, "Facility", prfacility + }, + { + 0x1e, "Progress indicator", general + }, + { + 0x20, "Network-specific facilities", general + }, + { + 0x27, "Notification indicator", general + }, + { + 0x28, "Display", display + }, + { + 0x29, "Date/Time", general + }, + { + 0x2c, "Keypad facility", general + }, + { + 0x34, "Signal", general + }, + { + 0x40, "Information rate", general + }, + { + 0x42, "End-to-end delay", general + }, + { + 0x43, "Transit delay selection and indication", general + }, + { + 0x44, "Packet layer binary parameters", general + }, + { + 0x45, "Packet layer window size", general + }, + { + 0x46, "Packet size", general + }, + { + 0x47, "Closed user group", general + }, + { + 0x4a, "Reverse charge indication", general + }, + { + 0x6c, "Calling party number", prcalling + }, + { + 0x6d, "Calling party subaddress", general + }, + { + 0x70, "Called party number", prcalled + }, + { + 0x71, "Called party subaddress", general + }, + { + 0x74, "Redirecting number", general + }, + { + 0x78, "Transit network selection", general + }, + { + 0x79, "Restart indicator", general + }, + { + 0x7c, "Low layer compatibility", general + }, + { + 0x7d, "High layer compatibility", general + }, + { + 0x7e, "User-user", general + }, + { + 0x7f, "Escape for extension", general + }, +}; + + +#define IESIZE sizeof(ielist)/sizeof(struct InformationElement) + +static struct InformationElement we_0[] = +{ + {WE0_cause, "Cause", prcause_1tr6}, + {WE0_connAddr, "Connecting Address", prcalled}, + {WE0_callID, "Call IDentity", general}, + {WE0_chanID, "Channel IDentity", general}, + {WE0_netSpecFac, "Network Specific Facility", general}, + {WE0_display, "Display", general}, + {WE0_keypad, "Keypad", general}, + {WE0_origAddr, "Origination Address", prcalled}, + {WE0_destAddr, "Destination Address", prcalled}, + {WE0_userInfo, "User Info", general} +}; + +static int we_0_len = (sizeof(we_0) / sizeof(struct InformationElement)); + +static struct InformationElement we_6[] = +{ + {WE6_serviceInd, "Service Indicator", general}, + {WE6_chargingInfo, "Charging Information", prcharge}, + {WE6_date, "Date", prtext}, + {WE6_facSelect, "Facility Select", general}, + {WE6_facStatus, "Facility Status", general}, + {WE6_statusCalled, "Status Called", general}, + {WE6_addTransAttr, "Additional Transmission Attributes", general} +}; +static int we_6_len = (sizeof(we_6) / sizeof(struct InformationElement)); + +int +QuickHex(char *txt, u_char * p, int cnt) +{ + register int i; + register char *t = txt; + register u_char w; + + for (i = 0; i < cnt; i++) { + *t++ = ' '; + w = (p[i] >> 4) & 0x0f; + if (w < 10) + *t++ = '0' + w; + else + *t++ = 'A' - 10 + w; + w = p[i] & 0x0f; + if (w < 10) + *t++ = '0' + w; + else + *t++ = 'A' - 10 + w; + } + *t++ = 0; + return (t - txt); +} + +void +LogFrame(struct IsdnCardState *sp, u_char * buf, int size) +{ + char *dp; + + if (size < 1) + return; + dp = sp->dlogspace; + if (size < 4096 / 3 - 10) { + dp += sprintf(dp, "HEX:"); + dp += QuickHex(dp, buf, size); + dp--; + *dp++ = '\n'; + *dp = 0; + } else + sprintf(dp, "LogFrame: warning Frame too big (%d)\n", + size); + HiSax_putstatus(sp, sp->dlogspace); +} + +void +dlogframe(struct IsdnCardState *sp, u_char * buf, int size, char *comment) +{ + u_char *bend = buf + size; + char *dp; + unsigned char pd, cr_l, cr, mt; + int i, cs = 0, cs_old = 0, cs_fest = 0; + + if (size < 1) + return; + /* display header */ + dp = sp->dlogspace; + dp += sprintf(dp, "%s\n", comment); + + if ((0xfe & buf[0]) == PROTO_DIS_N0) { /* 1TR6 */ + /* locate message type */ + pd = *buf++; + cr_l = *buf++; + if (cr_l) + cr = *buf++; + else + cr = 0; + mt = *buf++; + if (pd == PROTO_DIS_N0) { /* N0 */ + 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) + dp += sprintf(dp, "callref %d %s size %d unknown message type N0 %x!\n", + cr & 0x7f, (cr & 0x80) ? "called" : "caller", + size, mt); + else + dp += sprintf(dp, "callref %d %s size %d message type %s\n", + cr & 0x7f, (cr & 0x80) ? "called" : "caller", + size, mt_n0[i].descr); + } else { /* N1 */ + 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) + dp += sprintf(dp, "callref %d %s size %d unknown message type N1 %x!\n", + cr & 0x7f, (cr & 0x80) ? "called" : "caller", + size, mt); + else + dp += sprintf(dp, "callref %d %s size %d message type %s\n", + cr & 0x7f, (cr & 0x80) ? "called" : "caller", + size, mt_n1[i].descr); + } + + /* display each information element */ + while (buf < bend) { + /* Is it a single octet information element? */ + if (*buf & 0x80) { + switch ((*buf >> 4) & 7) { + case 1: + dp += sprintf(dp, " Shift %x\n", *buf & 0xf); + cs_old = cs; + cs = *buf & 7; + cs_fest = *buf & 8; + break; + case 3: + dp += sprintf(dp, " Congestion level %x\n", *buf & 0xf); + break; + case 2: + if (*buf == 0xa0) { + dp += sprintf(dp, " More data\n"); + break; + } + if (*buf == 0xa1) { + dp += sprintf(dp, " Sending complete\n"); + } + break; + /* fall through */ + default: + dp += sprintf(dp, " Reserved %x\n", *buf); + break; + } + buf++; + continue; + } + /* No, locate it in the table */ + if (cs == 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) { + 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++) + if (*buf == we_6[i].nr) + break; + + /* When found, give appropriate msg */ + 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]); + } else + dp += sprintf(dp, " Unknown Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]); + /* Skip to next element */ + if (cs_fest == 8) { + cs = cs_old; + cs_old = 0; + cs_fest = 0; + } + buf += buf[1] + 2; + } + } else if (buf[0] == 8) { /* EURO */ + /* locate message type */ + buf++; + cr_l = *buf++; + if (cr_l) + cr = *buf++; + else + cr = 0; + mt = *buf++; + for (i = 0; i < MTSIZE; i++) + if (mtlist[i].nr == mt) + break; + + /* display message type if it exists */ + if (i == MTSIZE) + dp += sprintf(dp, "callref %d %s size %d unknown message type %x!\n", + cr & 0x7f, (cr & 0x80) ? "called" : "caller", + size, mt); + else + dp += sprintf(dp, "callref %d %s size %d message type %s\n", + cr & 0x7f, (cr & 0x80) ? "called" : "caller", + size, mtlist[i].descr); + + /* display each information element */ + while (buf < bend) { + /* Is it a single octet information element? */ + if (*buf & 0x80) { + switch ((*buf >> 4) & 7) { + case 1: + dp += sprintf(dp, " Shift %x\n", *buf & 0xf); + break; + case 3: + dp += sprintf(dp, " Congestion level %x\n", *buf & 0xf); + break; + case 5: + dp += sprintf(dp, " Repeat indicator %x\n", *buf & 0xf); + break; + case 2: + if (*buf == 0xa0) { + dp += sprintf(dp, " More data\n"); + break; + } + if (*buf == 0xa1) { + dp += sprintf(dp, " Sending complete\n"); + } + break; + /* fall through */ + default: + dp += sprintf(dp, " Reserved %x\n", *buf); + break; + } + buf++; + continue; + } + /* No, locate it in the table */ + for (i = 0; i < IESIZE; i++) + if (*buf == ielist[i].nr) + break; + + /* When not found, give appropriate msg */ + if (i != IESIZE) { + dp += sprintf(dp, " %s\n", ielist[i].descr); + dp += ielist[i].f(dp, buf); + } else + dp += sprintf(dp, " attribute %x attribute size %d\n", *buf, buf[1]); + + /* Skip to next element */ + buf += buf[1] + 2; + } + } else { + dp += sprintf(dp, "Unknown protocol %x!", buf[0]); + } + dp += sprintf(dp, "\n"); + HiSax_putstatus(sp, sp->dlogspace); +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/siemens.h linux/drivers/isdn/hisax/siemens.h --- v2.0.30/linux/drivers/isdn/hisax/siemens.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/siemens.h Mon Aug 4 17:34:00 1997 @@ -0,0 +1,71 @@ +/* $Id: siemens.h,v 1.4 1997/01/21 22:24:33 keil Exp $ + * + * siemens.h ISAC and HSCX spezific Macros + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: siemens.h,v $ + * Revision 1.4 1997/01/21 22:24:33 keil + * cleanups + * + * Revision 1.3 1996/12/08 19:48:34 keil + * adding Monitor channel registers + * + * Revision 1.2 1996/10/27 22:24:00 keil + * HSCX version code removed + * + * Revision 1.1 1996/10/12 21:39:39 keil + * Initial revision + * + * +*/ + + +/* All Registers without FIFOs (original Siemens Spec - 20 hex) */ + +#define ISAC_MASK 0x0 +#define ISAC_ISTA 0x0 +#define ISAC_STAR 0x1 +#define ISAC_CMDR 0x1 +#define ISAC_EXIR 0x4 +#define ISAC_RBCH 0xa +#define ISAC_ADF2 0x19 +#define ISAC_SPCR 0x10 +#define ISAC_ADF1 0x18 +#define ISAC_CIR0 0x11 +#define ISAC_CIX0 0x11 +#define ISAC_STCR 0x17 +#define ISAC_MODE 0x2 +#define ISAC_RSTA 0x7 +#define ISAC_RBCL 0x5 +#define ISAC_TIMR 0x3 +#define ISAC_SQXR 0x1b +#define ISAC_MOSR 0x1a +#define ISAC_MOCR 0x1a +#define ISAC_MOR0 0x12 +#define ISAC_MOX0 0x12 +#define ISAC_MOR1 0x14 +#define ISAC_MOX1 0x14 + +#define HSCX_ISTA 0x0 +#define HSCX_CCR1 0xf +#define HSCX_CCR2 0xc +#define HSCX_TSAR 0x11 +#define HSCX_TSAX 0x10 +#define HSCX_XCCR 0x12 +#define HSCX_RCCR 0x13 +#define HSCX_MODE 0x2 +#define HSCX_CMDR 0x1 +#define HSCX_EXIR 0x4 +#define HSCX_XAD1 0x4 +#define HSCX_XAD2 0x5 +#define HSCX_RAH2 0x7 +#define HSCX_RSTA 0x7 +#define HSCX_TIMR 0x3 +#define HSCX_STAR 0x1 +#define HSCX_RBCL 0x5 +#define HSCX_XBCH 0xd +#define HSCX_VSTR 0xe +#define HSCX_RLCR 0xe +#define HSCX_MASK 0x0 diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/tei.c linux/drivers/isdn/hisax/tei.c --- v2.0.30/linux/drivers/isdn/hisax/tei.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/tei.c Mon Aug 4 17:34:00 1997 @@ -0,0 +1,317 @@ +/* $Id: tei.c,v 1.8 1997/04/07 22:59:08 keil Exp $ + + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * based on the teles driver from Jan den Ouden + * + * 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.7 1997/04/06 22:54:03 keil + * Using SKB's + * + * Revision 1.6 1997/02/09 00:25:12 keil + * new interface handling, one interface per card + * + * Revision 1.5 1997/01/27 15:57:51 keil + * cosmetics + * + * Revision 1.4 1997/01/21 22:32:44 keil + * Tei verify request + * + * Revision 1.3 1997/01/04 13:45:02 keil + * cleanup,adding remove tei request (thanks to Sim Yskes) + * + * Revision 1.2 1996/12/08 19:52:39 keil + * minor debug fix + * + * Revision 1.1 1996/10/13 20:04:57 keil + * Initial revision + * + * + * + */ +#define __NO_VERSION__ +#include "hisax.h" + +extern struct IsdnCard cards[]; +extern int nrcards; + +const char *tei_revision = "$Revision: 1.8 $"; + +static struct PStack * +findces(struct PStack *st, int ces) +{ + struct PStack *ptr = *(st->l1.stlistp); + + while (ptr) + if (ptr->l2.ces == ces) + return (ptr); + else + ptr = ptr->next; + return (NULL); +} + +static struct PStack * +findtei(struct PStack *st, int tei) +{ + struct PStack *ptr = *(st->l1.stlistp); + + if (tei == 127) + return (NULL); + + while (ptr) + if (ptr->l2.tei == tei) + return (ptr); + else + ptr = ptr->next; + return (NULL); +} + +static void +mdl_unit_data_res(struct PStack *st, unsigned int ri, u_char mt, u_char ai) +{ + struct sk_buff *skb; + u_char *bp; + + if (!(skb = alloc_skb(6 + MAX_HEADER_LEN, 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, 5); + bp[0] = 0xf; + bp[1] = ri >> 8; + bp[2] = ri & 0xff; + bp[3] = mt; + bp[4] = (ai << 1) | 1; + st->l3.l3l2(st, DL_UNIT_DATA, skb); +} + +static void +mdl_unit_data_ind(struct PStack *st, unsigned int ri, u_char mt, u_char ai) +{ + unsigned int tces; + struct PStack *otsp, *ptr; + char tmp[64]; + + 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); + } + } +} + +void +tei_handler(struct PStack *st, + u_char pr, struct sk_buff *skb) +{ + u_char *bp; + unsigned int data; + char tmp[32]; + + 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; + } +} + +unsigned int +randomces(void) +{ + int x = jiffies & 0xffff; + + return (x); +} + +static void +tei_man(struct PStack *sp, int i, void *v) +{ + + printk(KERN_DEBUG "tei_man\n"); +} + +static void +tei_l2tei(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *sp = st->l1.hardware; + + tei_handler(sp->teistack, pr, arg); +} + +void +setstack_tei(struct PStack *st) +{ + st->l2.l2tei = tei_l2tei; +} + +void +init_tei(struct IsdnCardState *sp, 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; + + 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; + + HiSax_addlist(sp, st); + sp->teistack = st; +} + +void +release_tei(struct IsdnCardState *sp) +{ + struct PStack *st = sp->teistack; + + HiSax_rmlist(sp, st); + kfree((void *) st); +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/teles0.c linux/drivers/isdn/hisax/teles0.c --- v2.0.30/linux/drivers/isdn/hisax/teles0.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/teles0.c Tue Aug 5 09:00:12 1997 @@ -0,0 +1,968 @@ +/* $Id: teles0.c,v 1.8 1997/04/13 19:54:04 keil Exp $ + + * teles0.c low level stuff for Teles Memory IO isdn cards + * based on the teles driver from Jan den Ouden + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * Thanks to Jan den Ouden + * Fritz Elfert + * 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.7 1997/04/06 22:54:04 keil + * Using SKB's + * + * Revision 1.6 1997/01/27 15:52:18 keil + * SMP proof,cosmetics + * + * Revision 1.5 1997/01/21 22:25:59 keil + * cleanups + * + * Revision 1.4 1996/11/05 19:41:27 keil + * more changes for 2.1 + * + * Revision 1.3 1996/10/30 10:22:58 keil + * Changes for 2.1 kernels + * + * Revision 1.2 1996/10/27 22:08:34 keil + * cosmetic changes + * + * Revision 1.1 1996/10/13 20:04:58 keil + * Initial revision + * + * + * + */ +#define __NO_VERSION__ +#include "siemens.h" +#include "hisax.h" +#include "teles0.h" +#include "isdnl1.h" +#include + +extern const char *CardType[]; + +const char *teles0_revision = "$Revision: 1.8 $"; + +#define byteout(addr,val) outb_p(val,addr) +#define bytein(addr) inb_p(addr) + +static inline u_char +readisac(unsigned int adr, u_char off) +{ + return readb(adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off); +} + +static inline void +writeisac(unsigned int adr, u_char off, u_char data) +{ + writeb(data, adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off); +} + + +static inline u_char +readhscx(unsigned int adr, int hscx, u_char off) +{ + return readb(adr + (hscx ? 0x1e0 : 0x1a0) + + ((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); +} + +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); + for (i = 0; i < size; i++) + data[i] = readb(ad); +} + +static 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); +} + +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)); + for (i = 0; i < size; i++) + data[i] = readb(ad); +} + +static inline void +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--; + } + if (!to) + printk(KERN_WARNING "Teles0: waitforCEC timeout\n"); +} + + +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) +{ + long flags; + + save_flags(flags); + cli(); + waitforCEC(adr, hscx); + writehscx(adr, hscx, HSCX_CMDR, 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", 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)); +} + +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) +{ + 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); + } +} + +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) +{ + 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); + } +} + +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_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); + } +} + +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->membase, 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 = 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); + } + } +} + +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->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); + } +} + +static void +telesS0_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *sp; + u_char val, stat = 0; + + sp = (struct IsdnCardState *) irq2dev_map[intno]; + + if (!sp) { + printk(KERN_WARNING "Teles0: Spurious interrupt!\n"); + return; + } + val = readhscx(sp->membase, 1, HSCX_ISTA); + Start_HSCX: + if (val) { + hscx_int_main(sp, val); + stat |= 1; + } + val = readisac(sp->membase, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(sp, val); + stat |= 2; + } + val = readhscx(sp->membase, 1, HSCX_ISTA); + if (val) { + if (sp->debug & L1_DEB_HSCX) + debugl1(sp, "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"); + 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); + } + if (stat & 2) { + writeisac(sp->membase, ISAC_MASK, 0xFF); + writeisac(sp->membase, ISAC_MASK, 0x0); + } +} + + +static void +initisac(struct IsdnCardState *sp) +{ + 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); +} + +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->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; + } + 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); +} + +static void +clear_pending_ints(struct IsdnCardState *sp) +{ + 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); + } + } + return (ret); +} + +int +setup_teles0(struct IsdnCard *card) +{ + u_char cfval, val, verA, verB; + struct IsdnCardState *sp = card->sp; + long flags; + 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)) + return (0); + + if (sp->typ == ISDN_CTYPE_16_0) + sp->cfg_reg = card->para[2]; + else /* 8.0 */ + sp->cfg_reg = 0; + + if (card->para[1] < 0x10000) { + card->para[1] <<= 4; + printk(KERN_INFO + "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)) { + 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); + } else { + request_region(sp->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) { + printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n", + sp->cfg_reg + 0, val); + release_region(sp->cfg_reg, 8); + return (0); + } + if ((val = bytein(sp->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); + return (0); + } + val = bytein(sp->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); + 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)) { + printk(KERN_WARNING + "Teles0: wrong HSCX versions check IO/MEM addresses\n"); + release_io_teles0(card); + 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.30/linux/drivers/isdn/hisax/teles0.h linux/drivers/isdn/hisax/teles0.h --- v2.0.30/linux/drivers/isdn/hisax/teles0.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/teles0.h Mon Aug 4 17:34:00 1997 @@ -0,0 +1,21 @@ +/* $Id: teles0.h,v 1.2 1997/01/21 22:26:52 keil Exp $ + * + * teles0.h Header for Teles 16.0 8.0 & compatible + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: teles0.h,v $ + * Revision 1.2 1997/01/21 22:26:52 keil + * cleanups + * + * Revision 1.1 1996/10/13 20:03:48 keil + * Initial revision + * + * +*/ + +extern void teles0_report(struct IsdnCardState *sp); +extern void release_io_teles0(struct IsdnCard *card); +extern int setup_teles0(struct IsdnCard *card); +extern int initteles0(struct IsdnCardState *sp); diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/hisax/teles3.c linux/drivers/isdn/hisax/teles3.c --- v2.0.30/linux/drivers/isdn/hisax/teles3.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/teles3.c Tue Aug 5 09:00:12 1997 @@ -0,0 +1,1034 @@ +/* $Id: teles3.c,v 1.11 1997/04/13 19:54:05 keil Exp $ + + * teles3.c low level stuff for Teles 16.3 & PNP isdn cards + * + * based on the teles driver from Jan den Ouden + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * Thanks to Jan den Ouden + * Fritz Elfert + * Beat Doebeli + * + * $Log: teles3.c,v $ + * Revision 1.11 1997/04/13 19:54:05 keil + * Change in IRQ check delay for SMP + * + * Revision 1.10 1997/04/06 22:54:05 keil + * Using SKB's + * + * Revision 1.9 1997/03/22 02:01:07 fritz + * -Reworked toplevel Makefile. From now on, no different Makefiles + * for standalone- and in-kernel-compilation are needed any more. + * -Added local Rules.make for above reason. + * -Experimental changes in teles3.c for enhanced IRQ-checking with + * 2.1.X and SMP kernels. + * -Removed diffstd-script, same functionality is in stddiff -r. + * -Enhanced scripts std2kern and stddiff. + * + * Revision 1.8 1997/02/23 18:43:55 fritz + * Added support for Teles-Vision. + * + * Revision 1.7 1997/01/28 22:48:33 keil + * fixes for Teles PCMCIA (Christof Petig) + * + * 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 + * + * + * + */ +#define __NO_VERSION__ +#include "siemens.h" +#include "hisax.h" +#include "teles3.h" +#include "isdnl1.h" +#include + +extern const char *CardType[]; +const char *teles3_revision = "$Revision: 1.11 $"; + +#define byteout(addr,val) outb_p(val,addr) +#define bytein(addr) inb_p(addr) + +static inline u_char +readreg(unsigned int adr, u_char off) +{ + return (bytein(adr + off)); +} + +static inline void +writereg(unsigned int adr, u_char off, u_char data) +{ + byteout(adr + off, data); +} + + +static inline void +read_fifo(unsigned int adr, u_char * data, int size) +{ + insb(adr + 0x1e, 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"); +} +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); +} + +/* + * fast interrupt here + */ + +static void +hscxreport(struct IsdnCardState *sp, int hscx) +{ + 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 +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) +{ + 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); + } +} + +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) +{ + 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); + } +} + +/* + * 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)) + 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); + } +} + +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); + } + } +} + +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 = 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); + } +} + +static void +teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ +#define MAXCOUNT 20 + struct IsdnCardState *sp; + u_char val, stat = 0; + int count = 0; + + sp = (struct IsdnCardState *) irq2dev_map[intno]; + + if (!sp) { + printk(KERN_WARNING "Teles: Spurious interrupt!\n"); + return; + } + val = readreg(sp->hscx[1], HSCX_ISTA); + Start_HSCX: + if (val) { + hscx_int_main(sp, val); + stat |= 1; + } + val = readreg(sp->isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(sp, val); + stat |= 2; + } + count++; + val = readreg(sp->hscx[1], HSCX_ISTA); + if (val && count < MAXCOUNT) { + if (sp->debug & L1_DEB_HSCX) + debugl1(sp, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(sp->isac, ISAC_ISTA); + if (val && count < MAXCOUNT) { + if (sp->debug & L1_DEB_ISAC) + debugl1(sp, "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); + } + 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(sp->hscx[hscx], HSCX_ISTA, 0x00); +} + +inline static void +release_ioregs(struct IsdnCard *card, int mask) +{ + if (mask & 1) + release_region(card->sp->isac, 32); + if (mask & 2) + release_region(card->sp->hscx[0], 32); + if (mask & 4) + release_region(card->sp->hscx[1], 32); +} + +void +release_io_teles3(struct IsdnCard *card) +{ + if (card->sp->typ == ISDN_CTYPE_TELESPCMCIA) + release_region(card->sp->hscx[0], 97); + else { + if (card->sp->cfg_reg) + release_region(card->sp->cfg_reg, 8); + release_ioregs(card, 0x7); + } +} + +static void +clear_pending_ints(struct IsdnCardState *sp) +{ + 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 +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); + } + } + return (ret); +} + +int +setup_teles3(struct IsdnCard *card) +{ + u_char cfval = 0, val, verA, verB; + struct IsdnCardState *sp = card->sp; + long flags; + 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)) + return (0); + + if (sp->typ == ISDN_CTYPE_16_3) { + sp->cfg_reg = card->para[1]; + switch (sp->cfg_reg) { + case 0x180: + case 0x280: + case 0x380: + sp->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)) { + printk(KERN_WARNING + "HiSax: %s ports %x-%x already in use\n", + CardType[sp->typ], + sp->hscx[0], + sp->hscx[0] + 96); + return (0); + } else + request_region(sp->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); + } else { + request_region(sp->cfg_reg, 8, "teles3 cfg"); + } + } + if (check_region((sp->isac), 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); + } + return (0); + } else { + request_region(sp->isac, 32, "HiSax isac"); + } + if (check_region((sp->hscx[0]), 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); + return (0); + } else { + request_region(sp->hscx[0], 32, "HiSax hscx A"); + } + if (check_region((sp->hscx[1]), 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); + 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; + } + } + if (sp->cfg_reg) { + if ((val = bytein(sp->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); + return (0); + } + if ((val = bytein(sp->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); + 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) { + printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n", + sp->cfg_reg + 2, val); + release_io_teles3(card); + 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_WARNING + "Teles3: wrong HSCX versions check IO address\n"); + release_io_teles3(card); + 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.30/linux/drivers/isdn/hisax/teles3.h linux/drivers/isdn/hisax/teles3.h --- v2.0.30/linux/drivers/isdn/hisax/teles3.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/teles3.h Mon Aug 4 17:34:00 1997 @@ -0,0 +1,21 @@ +/* $Id: teles3.h,v 1.2 1997/01/21 22:27:14 keil Exp $ + * + * teles3.h Header for Teles 16.3 PNP & compatible + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: teles3.h,v $ + * Revision 1.2 1997/01/21 22:27:14 keil + * cleanups + * + * Revision 1.1 1996/10/13 20:03:49 keil + * Initial revision + * + * +*/ + +extern void teles3_report(struct IsdnCardState *sp); +extern void release_io_teles3(struct IsdnCard *card); +extern int setup_teles3(struct IsdnCard *card); +extern int initteles3(struct IsdnCardState *sp); diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/icn/icn.c linux/drivers/isdn/icn/icn.c --- v2.0.30/linux/drivers/isdn/icn/icn.c Tue Nov 12 22:36:19 1996 +++ linux/drivers/isdn/icn/icn.c Tue Aug 5 17:10:02 1997 @@ -1,5 +1,5 @@ -/* $Id: icn.c,v 1.31 1996/11/13 02:36:25 fritz Exp $ - * +/* $Id: icn.c,v 1.45 1997/06/21 10:42:06 fritz Exp $ + * ISDN low-level module for the ICN active ISDN-Card. * * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) @@ -16,9 +16,54 @@ * * 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. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: icn.c,v $ + * Revision 1.45 1997/06/21 10:42:06 fritz + * Added availability to select leased mode on only one channel. + * + * Revision 1.44 1997/03/30 16:51:26 calle + * changed calls to copy_from_user/copy_to_user and removed verify_area + * were possible. + * + * Revision 1.43 1997/03/21 18:27:04 fritz + * Corrected parsing of incoming setup. + * + * Revision 1.42 1997/03/05 21:13:18 fritz + * Bugfix: sndcount was not reset on hangup. + * + * Revision 1.41 1997/02/24 23:34:29 fritz + * Bugfix in Layer1 error-recovery. + * + * Revision 1.40 1997/02/23 23:34:45 fritz + * Minor bugfixes in debugging code. + * + * Revision 1.39 1997/02/23 16:21:56 fritz + * Bugfix: Check for NULL pointer in icn_parse_status(). + * + * Revision 1.38 1997/02/11 18:29:31 fritz + * Bugfix in D64S initialization. + * + * Revision 1.37 1997/02/10 22:43:20 fritz + * Added plan and screen elements on ISDN_STAT_ICALL + * + * Revision 1.36 1997/02/10 21:31:20 fritz + * Changed setup-interface (incoming and outgoing). + * + * Revision 1.35 1997/02/10 10:10:28 fritz + * Changes for Kernel 2.1.X compatibility. + * Enhanced initialization, can recover from + * misconfiguration by other autoprobing drivers. + * + * Revision 1.34 1997/01/29 22:34:44 fritz + * Cleanup, Corrected D64S setup of 2nd channel. + * + * Revision 1.33 1996/12/05 20:31:48 tsbogend + * added handling of L2: DATA LINK LOST messages + * + * Revision 1.32 1996/11/14 23:49:18 fritz + * Bugfix: copy_to_user/copy_from_user mismatch in debugging-ioctl. + * * Revision 1.31 1996/11/13 02:36:25 fritz * Fixed a race condition in writecmd. * Some optimizations and cleanup. @@ -148,21 +193,25 @@ #undef MAP_DEBUG static char -*revision = "$Revision: 1.31 $"; +*revision = "$Revision: 1.45 $"; static int icn_addcard(int, char *, char *); /* - * Free queue completely. + * Free send-queue completely. * Parameter: - * queue = pointer to queue-head + * card = pointer to card struct + * channel = channel number */ -static void icn_free_queue(struct sk_buff_head *queue) +static void +icn_free_queue(icn_card * card, int channel) { - struct sk_buff *skb; + struct sk_buff_head *queue = &card->spqueue[channel]; + struct sk_buff *skb; - while ((skb = skb_dequeue(queue))) - dev_kfree_skb(skb, FREE_WRITE); + while ((skb = skb_dequeue(queue))) + dev_kfree_skb(skb, FREE_WRITE); + card->sndcount[channel] = 0; } /* Put a value into a shift-register, highest bit first. @@ -172,53 +221,57 @@ * firstbit = Bit-Number of highest bit * bitcount = Number of bits to output */ -static inline void icn_shiftout(unsigned short port, - unsigned long val, - int firstbit, - int bitcount) +static inline void +icn_shiftout(unsigned short port, + unsigned long val, + int firstbit, + int bitcount) { - register u_char s; - register u_char c; + register u_char s; + register u_char c; - for (s = firstbit, c = bitcount; c > 0; s--, c--) - OUTB_P((u_char) ((val >> s) & 1) ? 0xff : 0, port); + for (s = firstbit, c = bitcount; c > 0; s--, c--) + OUTB_P((u_char) ((val >> s) & 1) ? 0xff : 0, port); } /* * disable a cards shared memory */ -static inline void icn_disable_ram(icn_card *card) +static inline void +icn_disable_ram(icn_card * card) { - OUTB_P(0, ICN_MAPRAM); + OUTB_P(0, ICN_MAPRAM); } /* * enable a cards shared memory */ -static inline void icn_enable_ram(icn_card *card) +static inline void +icn_enable_ram(icn_card * card) { - OUTB_P(0xff, ICN_MAPRAM); + OUTB_P(0xff, ICN_MAPRAM); } /* * Map a cards channel0 (Bank0/Bank8) or channel1 (Bank4/Bank12) */ -static inline void icn_map_channel(icn_card *card, int channel) +static inline void +icn_map_channel(icn_card * card, int channel) { #ifdef MAP_DEBUG - printk(KERN_DEBUG "icn_map_channel %d %d\n", dev->channel, channel); + printk(KERN_DEBUG "icn_map_channel %d %d\n", dev.channel, channel); #endif - if ((channel == dev.channel) && (card == dev.mcard)) - return; - if (dev.mcard) - icn_disable_ram(dev.mcard); - icn_shiftout(ICN_BANK, chan2bank[channel], 3, 4); /* Select Bank */ - icn_enable_ram(card); - dev.mcard = card; - dev.channel = channel; + if ((channel == dev.channel) && (card == dev.mcard)) + return; + if (dev.mcard) + icn_disable_ram(dev.mcard); + icn_shiftout(ICN_BANK, chan2bank[channel], 3, 4); /* Select Bank */ + icn_enable_ram(card); + dev.mcard = card; + dev.channel = channel; #ifdef MAP_DEBUG - printk(KERN_DEBUG "icn_map_channel done\n"); + printk(KERN_DEBUG "icn_map_channel done\n"); #endif } @@ -227,98 +280,102 @@ * Return 0 if requested card/channel is unmapped (failure). * Return 1 on success. */ -static inline int icn_lock_channel(icn_card *card, int channel) +static inline int +icn_lock_channel(icn_card * card, int channel) { - register int retval; - ulong flags; + register int retval; + ulong flags; #ifdef MAP_DEBUG - printk(KERN_DEBUG "icn_lock_channel %d\n", channel); + printk(KERN_DEBUG "icn_lock_channel %d\n", channel); #endif - save_flags(flags); - cli(); - if ((dev.channel == channel) && (card == dev.mcard)) { - dev.chanlock++; - retval = 1; + save_flags(flags); + cli(); + if ((dev.channel == channel) && (card == dev.mcard)) { + dev.chanlock++; + retval = 1; #ifdef MAP_DEBUG - printk(KERN_DEBUG "icn_lock_channel %d OK\n", channel); + printk(KERN_DEBUG "icn_lock_channel %d OK\n", channel); #endif - } else { - retval = 0; + } else { + retval = 0; #ifdef MAP_DEBUG - printk(KERN_DEBUG "icn_lock_channel %d FAILED, dc=%d\n", channel, device->channel); + printk(KERN_DEBUG "icn_lock_channel %d FAILED, dc=%d\n", channel, dev.channel); #endif - } - restore_flags(flags); - return retval; + } + restore_flags(flags); + return retval; } /* * Release current card/channel lock */ -static inline void icn_release_channel(void) +static inline void +icn_release_channel(void) { - ulong flags; + ulong flags; #ifdef MAP_DEBUG - printk(KERN_DEBUG "icn_release_channel l=%d\n", device->chanlock); + printk(KERN_DEBUG "icn_release_channel l=%d\n", dev.chanlock); #endif - save_flags(flags); - cli(); - if (dev.chanlock > 0) - dev.chanlock--; - restore_flags(flags); + save_flags(flags); + cli(); + if (dev.chanlock > 0) + dev.chanlock--; + restore_flags(flags); } /* * Try to map and lock a cards channel. * Return 1 on success, 0 on failure. */ -static inline int icn_trymaplock_channel(icn_card *card, int channel) +static inline int +icn_trymaplock_channel(icn_card * card, int channel) { - ulong flags; + ulong flags; #ifdef MAP_DEBUG - printk(KERN_DEBUG "trymaplock c=%d dc=%d l=%d\n", channel, dev.channel, - dev.chanlock); + printk(KERN_DEBUG "trymaplock c=%d dc=%d l=%d\n", channel, dev.channel, + dev.chanlock); #endif - save_flags(flags); - cli(); - if ((!dev.chanlock) || - ((dev.channel == channel) && (dev.mcard == card))) { - dev.chanlock++; - icn_map_channel(card,channel); - restore_flags(flags); + save_flags(flags); + cli(); + if ((!dev.chanlock) || + ((dev.channel == channel) && (dev.mcard == card))) { + dev.chanlock++; + icn_map_channel(card, channel); + restore_flags(flags); #ifdef MAP_DEBUG - printk(KERN_DEBUG "trymaplock %d OK\n", channel); + printk(KERN_DEBUG "trymaplock %d OK\n", channel); #endif - return 1; - } - restore_flags(flags); + return 1; + } + restore_flags(flags); #ifdef MAP_DEBUG - printk(KERN_DEBUG "trymaplock %d FAILED\n", channel); + printk(KERN_DEBUG "trymaplock %d FAILED\n", channel); #endif - return 0; + return 0; } /* * Release current card/channel lock, * then map same or other channel without locking. */ -static inline void icn_maprelease_channel(icn_card *card, int channel) +static inline void +icn_maprelease_channel(icn_card * card, int channel) { - ulong flags; + ulong flags; #ifdef MAP_DEBUG - printk(KERN_DEBUG "map_release c=%d l=%d\n", channel, dev.chanlock); + printk(KERN_DEBUG "map_release c=%d l=%d\n", channel, dev.chanlock); #endif - save_flags(flags); - cli(); - if (dev.chanlock > 0) - dev.chanlock--; - if (!dev.chanlock) - icn_map_channel(card,channel); - restore_flags(flags); + save_flags(flags); + cli(); + if (dev.chanlock > 0) + dev.chanlock--; + if (!dev.chanlock) + icn_map_channel(card, channel); + restore_flags(flags); } /* Get Data from the B-Channel, assemble fragmented packets and put them @@ -326,47 +383,48 @@ * This routine is called via timer-callback from icn_pollbchan(). */ -static void icn_pollbchan_receive(int channel, icn_card *card) +static void +icn_pollbchan_receive(int channel, icn_card * card) { - int mch = channel + ((card->secondhalf) ? 2 : 0); - int eflag; - int cnt; + int mch = channel + ((card->secondhalf) ? 2 : 0); + int eflag; + int cnt; struct sk_buff *skb; - if (icn_trymaplock_channel(card,mch)) { - while (rbavl) { - cnt = readb(&rbuf_l); - if ((card->rcvidx[channel] + cnt) > 4000) { - printk(KERN_WARNING - "icn: (%s) bogus packet on ch%d, dropping.\n", - CID, - channel + 1); - card->rcvidx[channel] = 0; - eflag = 0; - } else { - memcpy_fromio(&card->rcvbuf[channel][card->rcvidx[channel]], - &rbuf_d, cnt); - card->rcvidx[channel] += cnt; - eflag = readb(&rbuf_f); - } - rbnext; - icn_maprelease_channel(card, mch & 2); - if (!eflag) { - if ((cnt = card->rcvidx[channel])) { - if (!(skb = dev_alloc_skb(cnt))) { - printk(KERN_WARNING "ïcn: receive out of memory\n"); - break; - } - memcpy(skb_put(skb, cnt), card->rcvbuf[channel], cnt); - card->rcvidx[channel] = 0; - card->interface.rcvcallb_skb(card->myid, channel, skb); - } - } - if (!icn_trymaplock_channel(card, mch)) - break; - } - icn_maprelease_channel(card, mch & 2); - } + if (icn_trymaplock_channel(card, mch)) { + while (rbavl) { + cnt = readb(&rbuf_l); + if ((card->rcvidx[channel] + cnt) > 4000) { + printk(KERN_WARNING + "icn: (%s) bogus packet on ch%d, dropping.\n", + CID, + channel + 1); + card->rcvidx[channel] = 0; + eflag = 0; + } else { + memcpy_fromio(&card->rcvbuf[channel][card->rcvidx[channel]], + &rbuf_d, cnt); + card->rcvidx[channel] += cnt; + eflag = readb(&rbuf_f); + } + rbnext; + icn_maprelease_channel(card, mch & 2); + if (!eflag) { + if ((cnt = card->rcvidx[channel])) { + if (!(skb = dev_alloc_skb(cnt))) { + printk(KERN_WARNING "ïcn: receive out of memory\n"); + break; + } + memcpy(skb_put(skb, cnt), card->rcvbuf[channel], cnt); + card->rcvidx[channel] = 0; + card->interface.rcvcallb_skb(card->myid, channel, skb); + } + } + if (!icn_trymaplock_channel(card, mch)) + break; + } + icn_maprelease_channel(card, mch & 2); + } } /* Send data-packet to B-Channel, split it up into fragments of @@ -376,58 +434,59 @@ * directly from icn_sendbuf(). */ -static void icn_pollbchan_send(int channel, icn_card *card) +static void +icn_pollbchan_send(int channel, icn_card * card) { - int mch = channel + ((card->secondhalf) ? 2 : 0); - int cnt; + int mch = channel + ((card->secondhalf) ? 2 : 0); + int cnt; unsigned long flags; - struct sk_buff *skb; - isdn_ctrl cmd; + struct sk_buff *skb; + isdn_ctrl cmd; - if (!(card->sndcount[channel] || - skb_queue_len(&card->spqueue[channel]))) - return; - if (icn_trymaplock_channel(card,mch)) { - while (sbfree && (card->sndcount[channel] || - skb_queue_len(&card->spqueue[channel]))) { - save_flags(flags); - cli(); - if (card->xmit_lock[channel]) { - restore_flags(flags); - break; - } - card->xmit_lock[channel]++; - restore_flags(flags); - skb = skb_dequeue(&card->spqueue[channel]); - if (!skb) - break; - if (skb->len > ICN_FRAGSIZE) { - writeb (0xff, &sbuf_f); - cnt = ICN_FRAGSIZE; + if (!(card->sndcount[channel] || + skb_queue_len(&card->spqueue[channel]))) + return; + if (icn_trymaplock_channel(card, mch)) { + while (sbfree && (card->sndcount[channel] || + skb_queue_len(&card->spqueue[channel]))) { + save_flags(flags); + cli(); + if (card->xmit_lock[channel]) { + restore_flags(flags); + break; + } + card->xmit_lock[channel]++; + restore_flags(flags); + skb = skb_dequeue(&card->spqueue[channel]); + if (!skb) + break; + if (skb->len > ICN_FRAGSIZE) { + writeb(0xff, &sbuf_f); + cnt = ICN_FRAGSIZE; } else { - writeb (0x0, &sbuf_f); - cnt = skb->len; + writeb(0x0, &sbuf_f); + cnt = skb->len; } - writeb (cnt, &sbuf_l); - memcpy_toio(&sbuf_d, skb->data, cnt); - skb_pull(skb, cnt); - card->sndcount[channel] -= cnt; - sbnext; /* switch to next buffer */ - icn_maprelease_channel(card, mch & 2); - if (!skb->len) { - dev_kfree_skb(skb, FREE_WRITE); - cmd.command = ISDN_STAT_BSENT; - cmd.driver = card->myid; - cmd.arg = channel; - card->interface.statcallb(&cmd); - } else - skb_queue_head(&card->spqueue[channel], skb); - card->xmit_lock[channel] = 0; - if (!icn_trymaplock_channel(card, mch)) - break; - } - icn_maprelease_channel(card, mch & 2); - } + writeb(cnt, &sbuf_l); + memcpy_toio(&sbuf_d, skb->data, cnt); + skb_pull(skb, cnt); + card->sndcount[channel] -= cnt; + sbnext; /* switch to next buffer */ + icn_maprelease_channel(card, mch & 2); + if (!skb->len) { + dev_kfree_skb(skb, FREE_WRITE); + cmd.command = ISDN_STAT_BSENT; + cmd.driver = card->myid; + cmd.arg = channel; + card->interface.statcallb(&cmd); + } else + skb_queue_head(&card->spqueue[channel], skb); + card->xmit_lock[channel] = 0; + if (!icn_trymaplock_channel(card, mch)) + break; + } + icn_maprelease_channel(card, mch & 2); + } } /* Send/Receive Data to/from the B-Channel. @@ -435,54 +494,61 @@ * It schedules itself while any B-Channel is open. */ -static void icn_pollbchan(unsigned long data) +static void +icn_pollbchan(unsigned long data) { - icn_card *card = (icn_card *)data; - unsigned long flags; + icn_card *card = (icn_card *) data; + unsigned long flags; - if (card->flags & ICN_FLAGS_B1ACTIVE) { - icn_pollbchan_receive(0, card); - icn_pollbchan_send(0, card); - } - if (card->flags & ICN_FLAGS_B2ACTIVE) { - icn_pollbchan_receive(1, card); - icn_pollbchan_send(1, card); - } - if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) { - /* schedule b-channel polling again */ - save_flags(flags); - cli(); - del_timer(&card->rb_timer); - card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD; - add_timer(&card->rb_timer); - card->flags |= ICN_FLAGS_RBTIMER; - restore_flags(flags); - } else - card->flags &= ~ICN_FLAGS_RBTIMER; + if (card->flags & ICN_FLAGS_B1ACTIVE) { + icn_pollbchan_receive(0, card); + icn_pollbchan_send(0, card); + } + if (card->flags & ICN_FLAGS_B2ACTIVE) { + icn_pollbchan_receive(1, card); + icn_pollbchan_send(1, card); + } + if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) { + /* schedule b-channel polling again */ + save_flags(flags); + cli(); + del_timer(&card->rb_timer); + card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD; + add_timer(&card->rb_timer); + card->flags |= ICN_FLAGS_RBTIMER; + restore_flags(flags); + } else + card->flags &= ~ICN_FLAGS_RBTIMER; } typedef struct icn_stat { - char *statstr; - int command; - int action; + char *statstr; + int command; + int action; } icn_stat; - -static icn_stat icn_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 */ - {NULL, 0 , -1} +/* *INDENT-OFF* */ +static icn_stat icn_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* */ + /* * Check Statusqueue-Pointer from isdn-cards. @@ -494,199 +560,244 @@ * This routine is called periodically via timer. */ -static int icn_parse_status(u_char *status, int channel, icn_card *card) +static int +icn_parse_status(u_char * status, int channel, icn_card * card) +{ + icn_stat *s = icn_stat_table; + int action = -1; + int dflag = 0; + unsigned long flags; + 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 0; + cmd.driver = card->myid; + cmd.arg = channel; + switch (action) { + case 1: + card->flags |= (channel) ? + ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE; + break; + case 2: + card->flags &= ~((channel) ? + ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE); + icn_free_queue(card, channel); + save_flags(flags); + cli(); + card->rcvidx[channel] = 0; + restore_flags(flags); + dflag |= (channel + 1); + break; + case 3: + { + char *t = status + 6; + 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; + break; + case 4: + 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: + strncpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num) - 1); + break; + case 6: + sprintf(cmd.parm.num, "%d", + (int) simple_strtoul(status + 7, NULL, 16)); + break; + case 7: + 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: + dflag = 3; + card->flags &= ~ICN_FLAGS_B1ACTIVE; + icn_free_queue(card, 0); + save_flags(flags); + cli(); + card->rcvidx[0] = 0; + restore_flags(flags); + 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 &= ~ICN_FLAGS_B2ACTIVE; + icn_free_queue(card, 1); + save_flags(flags); + cli(); + card->rcvidx[1] = 0; + restore_flags(flags); + 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); + return dflag; +} + +static void +icn_putmsg(icn_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); +} + +static void +icn_polldchan(unsigned long data) { - icn_stat *s = icn_stat_table; - int action = -1; - int dflag = 0; - unsigned long flags; - 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 0; - cmd.driver = card->myid; - cmd.arg = channel; - switch (action) { - case 1: - card->flags |= (channel)? - ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE; - break; - case 2: - card->flags &= ~((channel)? - ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE); - icn_free_queue(&card->spqueue[channel]); - save_flags(flags); - cli(); - card->rcvidx[channel] = 0; - restore_flags(flags); - dflag |= (channel+1); - break; - case 3: - strncpy(cmd.num, status + 6, sizeof(cmd.num) - 1); - break; - case 4: - sprintf(cmd.num,"LEASED%d,07,00,%d", - card->myid,channel+1); - break; - case 5: - strncpy(cmd.num, status + 3, sizeof(cmd.num) - 1); - break; - case 6: - sprintf(cmd.num,"%d", - (int)simple_strtoul(status + 7,NULL,16)); - break; - case 7: - status += 3; - if (strlen(status)==4) - sprintf(cmd.num,"%s%c%c", - status+2,*status,*(status+1)); - else - strncpy(cmd.num, status+1, sizeof(cmd.num) - 1); - break; - case 8: - 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; - 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); - return dflag; -} - -static void icn_putmsg(icn_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); -} - -static void icn_polldchan(unsigned long data) -{ - icn_card *card = (icn_card *)data; - int mch = card->secondhalf ? 2 : 0; - int avail = 0; - int dflag = 0; - int left; - u_char c; - int ch; - int flags; - int i; - u_char *p; - isdn_ctrl cmd; - - if (icn_trymaplock_channel(card,mch)) { - avail = msg_avail; - for (left = avail, i = readb(&msg_o); left > 0; i++, left--) { - c = readb(&dev.shmem->comm_buffers.iopc_buf[i & 0xff]); - icn_putmsg(card, c); - if (c == 0xff) { - 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]; - dflag |= icn_parse_status(p, ch, card); - } else { - p = card->imsg; - if (!strncmp(p, "DRV1.", 5)) { - u_char vstr[10]; - u_char *q = vstr; - - printk(KERN_INFO "icn: (%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 - "icn: (%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 - "icn: (%s) Euro-Protocol loaded and running\n",CID); - } - p = strstr(card->imsg,"BRV") + 3; - while (*p) { - if (*p>='0' && *p<='9') - *q++ = *p; - p++; - } - *q = '\0'; - strcat(vstr,"000"); - vstr[3] = '\0'; - card->fw_rev = (int)simple_strtoul(vstr,NULL,10); - continue; - - } - } - } else { - card->imsg[card->iptr] = c; - if (card->iptr < 59) - card->iptr++; - } - } - writeb((readb(&msg_o) + avail) & 0xff, &msg_o); - icn_release_channel(); - } - if (avail) { - cmd.command = ISDN_STAT_STAVAIL; - cmd.driver = card->myid; - cmd.arg = avail; - card->interface.statcallb(&cmd); - } - if (dflag & 1) - card->interface.rcvcallb(card->myid, 0, card->rcvbuf[0], 0); - if (dflag & 2) - card->interface.rcvcallb(card->myid, 1, card->rcvbuf[1], 0); - if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) - if (!(card->flags & ICN_FLAGS_RBTIMER)) { - /* schedule b-channel polling */ - card->flags |= ICN_FLAGS_RBTIMER; - save_flags(flags); - cli(); - del_timer(&card->rb_timer); - card->rb_timer.function = icn_pollbchan; - card->rb_timer.data = (unsigned long)card; - card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD; - add_timer(&card->rb_timer); - restore_flags(flags); - } - /* schedule again */ - save_flags(flags); - cli(); - del_timer(&card->st_timer); - card->st_timer.expires = jiffies + ICN_TIMER_DCREAD; - add_timer(&card->st_timer); - restore_flags(flags); + icn_card *card = (icn_card *) data; + int mch = card->secondhalf ? 2 : 0; + int avail = 0; + int dflag = 0; + int left; + u_char c; + int ch; + int flags; + int i; + u_char *p; + isdn_ctrl cmd; + + if (icn_trymaplock_channel(card, mch)) { + avail = msg_avail; + for (left = avail, i = readb(&msg_o); left > 0; i++, left--) { + c = readb(&dev.shmem->comm_buffers.iopc_buf[i & 0xff]); + icn_putmsg(card, c); + if (c == 0xff) { + 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]; + dflag |= icn_parse_status(p, ch, card); + } else { + p = card->imsg; + if (!strncmp(p, "DRV1.", 5)) { + u_char vstr[10]; + u_char *q = vstr; + + printk(KERN_INFO "icn: (%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 + "icn: (%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 + "icn: (%s) Euro-Protocol loaded and running\n", CID); + } + p = strstr(card->imsg, "BRV") + 3; + while (*p) { + if (*p >= '0' && *p <= '9') + *q++ = *p; + p++; + } + *q = '\0'; + strcat(vstr, "000"); + vstr[3] = '\0'; + card->fw_rev = (int) simple_strtoul(vstr, NULL, 10); + continue; + + } + } + } else { + card->imsg[card->iptr] = c; + if (card->iptr < 59) + card->iptr++; + } + } + writeb((readb(&msg_o) + avail) & 0xff, &msg_o); + icn_release_channel(); + } + if (avail) { + cmd.command = ISDN_STAT_STAVAIL; + cmd.driver = card->myid; + cmd.arg = avail; + card->interface.statcallb(&cmd); + } + if (dflag & 1) + card->interface.rcvcallb(card->myid, 0, card->rcvbuf[0], 0); + if (dflag & 2) + card->interface.rcvcallb(card->myid, 1, card->rcvbuf[1], 0); + if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) + if (!(card->flags & ICN_FLAGS_RBTIMER)) { + /* schedule b-channel polling */ + card->flags |= ICN_FLAGS_RBTIMER; + save_flags(flags); + cli(); + del_timer(&card->rb_timer); + card->rb_timer.function = icn_pollbchan; + card->rb_timer.data = (unsigned long) card; + card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD; + add_timer(&card->rb_timer); + restore_flags(flags); + } + /* schedule again */ + save_flags(flags); + cli(); + del_timer(&card->st_timer); + card->st_timer.expires = jiffies + ICN_TIMER_DCREAD; + add_timer(&card->st_timer); + restore_flags(flags); } /* Append a packet to the transmit buffer-queue. @@ -698,34 +809,35 @@ * Number of bytes transferred, -E??? on error */ -static int icn_sendbuf(int channel, struct sk_buff *skb, icn_card * card) +static int +icn_sendbuf(int channel, struct sk_buff *skb, icn_card * card) { - int len = skb->len; - unsigned long flags; - struct sk_buff *nskb; - - if (len > 4000) { - printk(KERN_WARNING - "icn: Send packet too large\n"); - return -EINVAL; - } - if (len) { - if (!(card->flags & (channel)?ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE)) - return 0; - if (card->sndcount[channel] > ICN_MAX_SQUEUE) - return 0; - save_flags(flags); - cli(); - nskb = skb_clone(skb, GFP_ATOMIC); - if (nskb) { - skb_queue_tail(&card->spqueue[channel], nskb); - dev_kfree_skb(skb, FREE_WRITE); - } else - len = 0; - card->sndcount[channel] += len; - restore_flags(flags); - } - return len; + int len = skb->len; + unsigned long flags; + struct sk_buff *nskb; + + if (len > 4000) { + printk(KERN_WARNING + "icn: Send packet too large\n"); + return -EINVAL; + } + if (len) { + if (!(card->flags & (channel) ? ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE)) + return 0; + if (card->sndcount[channel] > ICN_MAX_SQUEUE) + return 0; + save_flags(flags); + cli(); + nskb = skb_clone(skb, GFP_ATOMIC); + if (nskb) { + skb_queue_tail(&card->spqueue[channel], nskb); + dev_kfree_skb(skb, FREE_WRITE); + } else + len = 0; + card->sndcount[channel] += len; + restore_flags(flags); + } + return len; } /* @@ -735,42 +847,43 @@ * 0 on success (Boot loader ready) * -EIO on failure (timeout) */ -static int icn_check_loader(int cardnumber) +static int +icn_check_loader(int cardnumber) { - int timer = 0; + int timer = 0; - while (1) { + while (1) { #ifdef BOOT_DEBUG - printk(KERN_DEBUG "Loader %d ?\n", cardnumber); + printk(KERN_DEBUG "Loader %d ?\n", cardnumber); #endif - if (readb(&dev.shmem->data_control.scns) || - readb(&dev.shmem->data_control.scnr)) { - if (timer++ > 5) { - printk(KERN_WARNING - "icn: Boot-Loader %d timed out.\n", - cardnumber); - icn_release_channel(); - return -EIO; - } + if (readb(&dev.shmem->data_control.scns) || + readb(&dev.shmem->data_control.scnr)) { + if (timer++ > 5) { + printk(KERN_WARNING + "icn: Boot-Loader %d timed out.\n", + cardnumber); + icn_release_channel(); + return -EIO; + } #ifdef BOOT_DEBUG - printk(KERN_DEBUG "Loader %d TO?\n", cardnumber); + printk(KERN_DEBUG "Loader %d TO?\n", cardnumber); #endif - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + ICN_BOOT_TIMEOUT1; - schedule(); - } else { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + ICN_BOOT_TIMEOUT1; + schedule(); + } else { #ifdef BOOT_DEBUG - printk(KERN_DEBUG "Loader %d OK\n", cardnumber); + printk(KERN_DEBUG "Loader %d OK\n", cardnumber); #endif - icn_release_channel(); - return 0; - } - } + icn_release_channel(); + return 0; + } + } } /* Load the boot-code into the interface-card's memory and start it. * Always called from user-process. - * + * * Parameters: * buffer = pointer to packet * Return: @@ -792,891 +905,937 @@ #define SLEEP(sec) #endif -static int icn_loadboot(u_char * buffer, icn_card * card) +static int +icn_loadboot(u_char * buffer, icn_card * card) { - int ret; - ulong flags; + int ret; + ulong flags; u_char *codebuf; #ifdef BOOT_DEBUG - printk(KERN_DEBUG "icn_loadboot called, buffaddr=%08lx\n", (ulong) buffer); + printk(KERN_DEBUG "icn_loadboot called, buffaddr=%08lx\n", (ulong) buffer); #endif - if ((ret = verify_area(VERIFY_READ, (void *) buffer, ICN_CODE_STAGE1))) - return ret; - if (!(codebuf = kmalloc(ICN_CODE_STAGE1,GFP_KERNEL))) { - printk(KERN_WARNING "icn: Could not allocate code buffer\n"); - return -ENOMEM; - } - save_flags(flags); - cli(); - if (!card->rvalid) { - if (check_region(card->port, ICN_PORTLEN)) { - printk(KERN_WARNING - "icn: (%s) ports 0x%03x-0x%03x in use.\n", - CID, - card->port, - card->port + ICN_PORTLEN); - restore_flags(flags); - kfree(codebuf); - return -EBUSY; - } - request_region(card->port, ICN_PORTLEN, card->regname); - card->rvalid = 1; - if (card->doubleS0) - card->other->rvalid = 1; - } - if (!dev.mvalid) { - if (check_shmem((ulong) dev.shmem, 0x4000)) { - printk(KERN_WARNING - "icn: memory at 0x%08lx in use.\n", - (ulong) dev.shmem); - restore_flags(flags); - return -EBUSY; - } - request_shmem((ulong) dev.shmem, 0x4000, "icn"); - dev.mvalid = 1; - } - restore_flags(flags); - OUTB_P(0, ICN_RUN); /* Reset Controller */ - OUTB_P(0, ICN_MAPRAM); /* Disable RAM */ - icn_shiftout(ICN_CFG, 0x0f, 3, 4); /* Windowsize= 16k */ - icn_shiftout(ICN_CFG, (unsigned long) dev.shmem, 23, 10); /* Set RAM-Addr. */ + if (!(codebuf = kmalloc(ICN_CODE_STAGE1, GFP_KERNEL))) { + printk(KERN_WARNING "icn: Could not allocate code buffer\n"); + return -ENOMEM; + } + if ((ret = copy_from_user(codebuf, buffer, ICN_CODE_STAGE1))) { + kfree(codebuf); + return ret; + } + save_flags(flags); + cli(); + if (!card->rvalid) { + if (check_region(card->port, ICN_PORTLEN)) { + printk(KERN_WARNING + "icn: (%s) ports 0x%03x-0x%03x in use.\n", + CID, + card->port, + card->port + ICN_PORTLEN); + restore_flags(flags); + kfree(codebuf); + return -EBUSY; + } + request_region(card->port, ICN_PORTLEN, card->regname); + card->rvalid = 1; + if (card->doubleS0) + card->other->rvalid = 1; + } + if (!dev.mvalid) { + if (check_shmem((ulong) dev.shmem, 0x4000)) { + printk(KERN_WARNING + "icn: memory at 0x%08lx in use.\n", + (ulong) dev.shmem); + restore_flags(flags); + return -EBUSY; + } + request_shmem((ulong) dev.shmem, 0x4000, "icn"); + dev.mvalid = 1; + } + restore_flags(flags); + OUTB_P(0, ICN_RUN); /* Reset Controller */ + OUTB_P(0, ICN_MAPRAM); /* Disable RAM */ + icn_shiftout(ICN_CFG, 0x0f, 3, 4); /* Windowsize= 16k */ + icn_shiftout(ICN_CFG, (unsigned long) dev.shmem, 23, 10); /* Set RAM-Addr. */ #ifdef BOOT_DEBUG - printk(KERN_DEBUG "shmem=%08lx\n", (ulong) dev.shmem); + printk(KERN_DEBUG "shmem=%08lx\n", (ulong) dev.shmem); #endif - SLEEP(1); + SLEEP(1); #ifdef BOOT_DEBUG - printk(KERN_DEBUG "Map Bank 0\n"); + printk(KERN_DEBUG "Map Bank 0\n"); #endif - save_flags(flags); - cli(); - icn_map_channel(card,0); /* Select Bank 0 */ - icn_lock_channel(card,0); /* Lock Bank 0 */ - restore_flags(flags); - SLEEP(1); - copy_from_user(codebuf, buffer, ICN_CODE_STAGE1); - memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1); /* Copy code */ + save_flags(flags); + cli(); + icn_map_channel(card, 0); /* Select Bank 0 */ + icn_lock_channel(card, 0); /* Lock Bank 0 */ + restore_flags(flags); + SLEEP(1); + memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1); /* Copy code */ #ifdef BOOT_DEBUG - printk(KERN_DEBUG "Bootloader transfered\n"); + printk(KERN_DEBUG "Bootloader transfered\n"); #endif - if (card->doubleS0) { - SLEEP(1); + if (card->doubleS0) { + SLEEP(1); #ifdef BOOT_DEBUG - printk(KERN_DEBUG "Map Bank 8\n"); + printk(KERN_DEBUG "Map Bank 8\n"); #endif - save_flags(flags); - cli(); - icn_release_channel(); - icn_map_channel(card,2); /* Select Bank 8 */ - icn_lock_channel(card,2); /* Lock Bank 8 */ - restore_flags(flags); - SLEEP(1); - memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1); /* Copy code */ + save_flags(flags); + cli(); + icn_release_channel(); + icn_map_channel(card, 2); /* Select Bank 8 */ + icn_lock_channel(card, 2); /* Lock Bank 8 */ + restore_flags(flags); + SLEEP(1); + memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1); /* Copy code */ #ifdef BOOT_DEBUG - printk(KERN_DEBUG "Bootloader transfered\n"); + printk(KERN_DEBUG "Bootloader transfered\n"); #endif - } - kfree(codebuf); - SLEEP(1); - OUTB_P(0xff, ICN_RUN); /* Start Boot-Code */ - if ((ret = icn_check_loader(card->doubleS0 ? 2 : 1))) - return ret; - if (!card->doubleS0) - return 0; - /* reached only, if we have a Double-S0-Card */ + } + kfree(codebuf); + SLEEP(1); + OUTB_P(0xff, ICN_RUN); /* Start Boot-Code */ + if ((ret = icn_check_loader(card->doubleS0 ? 2 : 1))) + return ret; + if (!card->doubleS0) + return 0; + /* reached only, if we have a Double-S0-Card */ #ifdef BOOT_DEBUG - printk(KERN_DEBUG "Map Bank 0\n"); + printk(KERN_DEBUG "Map Bank 0\n"); #endif - save_flags(flags); - cli(); - icn_map_channel(card,0); /* Select Bank 0 */ - icn_lock_channel(card,0); /* Lock Bank 0 */ - restore_flags(flags); - SLEEP(1); - return (icn_check_loader(1)); -} - -static int icn_loadproto(u_char * buffer, icn_card * card) -{ - register u_char *p = buffer; - u_char codebuf[256]; - uint left = ICN_CODE_STAGE2; - uint cnt; - int timer; - int ret; - unsigned long flags; + save_flags(flags); + cli(); + icn_map_channel(card, 0); /* Select Bank 0 */ + icn_lock_channel(card, 0); /* Lock Bank 0 */ + restore_flags(flags); + SLEEP(1); + return (icn_check_loader(1)); +} + +static int +icn_loadproto(u_char * buffer, icn_card * card) +{ + register u_char *p = buffer; + u_char codebuf[256]; + uint left = ICN_CODE_STAGE2; + uint cnt; + int timer; + int ret; + unsigned long flags; #ifdef BOOT_DEBUG - printk(KERN_DEBUG "icn_loadproto called\n"); + printk(KERN_DEBUG "icn_loadproto called\n"); #endif - if ((ret = verify_area(VERIFY_READ, (void *) buffer, ICN_CODE_STAGE2))) - return ret; - timer = 0; - save_flags(flags); - cli(); - if (card->secondhalf) { - icn_map_channel(card, 2); - icn_lock_channel(card, 2); - } else { - icn_map_channel(card, 0); - icn_lock_channel(card, 0); - } - restore_flags(flags); - while (left) { - if (sbfree) { /* If there is a free buffer... */ - cnt = MIN(256, left); - copy_from_user(codebuf, p, cnt); - memcpy_toio(&sbuf_l, codebuf, cnt); /* copy data */ - sbnext; /* switch to next buffer */ - p += cnt; - left -= cnt; - timer = 0; - } else { + if ((ret = verify_area(VERIFY_READ, (void *) buffer, ICN_CODE_STAGE2))) + return ret; + timer = 0; + save_flags(flags); + cli(); + if (card->secondhalf) { + icn_map_channel(card, 2); + icn_lock_channel(card, 2); + } else { + icn_map_channel(card, 0); + icn_lock_channel(card, 0); + } + restore_flags(flags); + while (left) { + if (sbfree) { /* If there is a free buffer... */ + cnt = MIN(256, left); + if (copy_from_user(codebuf, p, cnt)) { + icn_maprelease_channel(card, 0); + return -EFAULT; + } + memcpy_toio(&sbuf_l, codebuf, cnt); /* copy data */ + sbnext; /* switch to next buffer */ + p += cnt; + left -= cnt; + timer = 0; + } else { #ifdef BOOT_DEBUG - printk(KERN_DEBUG "boot 2 !sbfree\n"); + printk(KERN_DEBUG "boot 2 !sbfree\n"); #endif - if (timer++ > 5) { - icn_maprelease_channel(card, 0); - return -EIO; - } - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 10; - schedule(); - } - } - writeb (0x20, &sbuf_n); - timer = 0; - while (1) { - if (readb(&cmd_o) || readb(&cmd_i)) { + if (timer++ > 5) { + icn_maprelease_channel(card, 0); + return -EIO; + } + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 10; + schedule(); + } + } + writeb(0x20, &sbuf_n); + timer = 0; + while (1) { + if (readb(&cmd_o) || readb(&cmd_i)) { #ifdef BOOT_DEBUG - printk(KERN_DEBUG "Proto?\n"); + printk(KERN_DEBUG "Proto?\n"); #endif - if (timer++ > 5) { - printk(KERN_WARNING - "icn: (%s) Protocol timed out.\n", - CID); + if (timer++ > 5) { + printk(KERN_WARNING + "icn: (%s) Protocol timed out.\n", + CID); #ifdef BOOT_DEBUG - printk(KERN_DEBUG "Proto TO!\n"); + printk(KERN_DEBUG "Proto TO!\n"); #endif - icn_maprelease_channel(card, 0); - return -EIO; - } + icn_maprelease_channel(card, 0); + return -EIO; + } #ifdef BOOT_DEBUG - printk(KERN_DEBUG "Proto TO?\n"); + printk(KERN_DEBUG "Proto TO?\n"); #endif - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + ICN_BOOT_TIMEOUT1; - schedule(); - } else { - if ((card->secondhalf) || (!card->doubleS0)) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + ICN_BOOT_TIMEOUT1; + schedule(); + } else { + if ((card->secondhalf) || (!card->doubleS0)) { #ifdef BOOT_DEBUG - printk(KERN_DEBUG "Proto loaded, install poll-timer %d\n", - card->secondhalf); + printk(KERN_DEBUG "Proto loaded, install poll-timer %d\n", + card->secondhalf); #endif - save_flags(flags); - cli(); - init_timer(&card->st_timer); - card->st_timer.expires = jiffies + ICN_TIMER_DCREAD; - card->st_timer.function = icn_polldchan; - card->st_timer.data = (unsigned long)card; - add_timer(&card->st_timer); - card->flags |= ICN_FLAGS_RUNNING; - if (card->doubleS0) { - init_timer(&card->other->st_timer); - card->other->st_timer.expires = jiffies + ICN_TIMER_DCREAD; - card->other->st_timer.function = icn_polldchan; - card->other->st_timer.data = (unsigned long)card->other; - add_timer(&card->other->st_timer); - card->other->flags |= ICN_FLAGS_RUNNING; - } - restore_flags(flags); - } - icn_maprelease_channel(card, 0); - return 0; - } - } + save_flags(flags); + cli(); + init_timer(&card->st_timer); + card->st_timer.expires = jiffies + ICN_TIMER_DCREAD; + card->st_timer.function = icn_polldchan; + card->st_timer.data = (unsigned long) card; + add_timer(&card->st_timer); + card->flags |= ICN_FLAGS_RUNNING; + if (card->doubleS0) { + init_timer(&card->other->st_timer); + card->other->st_timer.expires = jiffies + ICN_TIMER_DCREAD; + card->other->st_timer.function = icn_polldchan; + card->other->st_timer.data = (unsigned long) card->other; + add_timer(&card->other->st_timer); + card->other->flags |= ICN_FLAGS_RUNNING; + } + restore_flags(flags); + } + icn_maprelease_channel(card, 0); + return 0; + } + } } /* Read the Status-replies from the Interface */ -static int icn_readstatus(u_char * buf, int len, int user, icn_card * card) +static int +icn_readstatus(u_char * buf, int len, int user, icn_card * card) { - int count; - u_char *p; + 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; + 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; } /* Put command-strings into the command-queue of the Interface */ -static int icn_writecmd(const u_char * buf, int len, int user, icn_card * card) +static int +icn_writecmd(const u_char * buf, int len, int user, icn_card * card) { int mch = card->secondhalf ? 2 : 0; - int avail; - int pp; - int i; - int count; - int xcount; - int ocount; - int loop; - unsigned long flags; - int lastmap_channel; - struct icn_card *lastmap_card; - u_char *p; - isdn_ctrl cmd; - u_char msg[0x100]; - - ocount = 1; - xcount = loop = 0; - while (len) { - save_flags(flags); - cli(); - lastmap_card = dev.mcard; - lastmap_channel = dev.channel; - icn_map_channel(card, mch); - - avail = cmd_free; - count = MIN(avail, len); - if (user) - copy_from_user(msg, buf, count); - else - memcpy(msg, buf, count); - icn_putmsg(card, '>'); - for (p = msg, pp = readb(&cmd_i), i = count; i > 0; i--, p++, pp - ++) { - writeb((*p == '\n') ? 0xff : *p, - &dev.shmem->comm_buffers.pcio_buf[pp & 0xff]); - len--; - xcount++; - icn_putmsg(card, *p); - if ((*p == '\n') && (i > 1)) { - icn_putmsg(card, '>'); - ocount++; - } - ocount++; - } - writeb((readb(&cmd_i) + count) & 0xff, &cmd_i); - if (lastmap_card) - icn_map_channel(lastmap_card, lastmap_channel); - restore_flags(flags); - if (len) { - udelay(1000); - if (loop++ > 20) - break; - } else - break; - } - if (len && (!user)) - printk(KERN_WARNING "icn: writemsg incomplete!\n"); - cmd.command = ISDN_STAT_STAVAIL; - cmd.driver = card->myid; - cmd.arg = ocount; - card->interface.statcallb(&cmd); - return xcount; + int avail; + int pp; + int i; + int count; + int xcount; + int ocount; + int loop; + unsigned long flags; + int lastmap_channel; + struct icn_card *lastmap_card; + u_char *p; + isdn_ctrl cmd; + u_char msg[0x100]; + + ocount = 1; + xcount = loop = 0; + while (len) { + save_flags(flags); + cli(); + lastmap_card = dev.mcard; + lastmap_channel = dev.channel; + icn_map_channel(card, mch); + + avail = cmd_free; + count = MIN(avail, len); + if (user) + copy_from_user(msg, buf, count); + else + memcpy(msg, buf, count); + icn_putmsg(card, '>'); + for (p = msg, pp = readb(&cmd_i), i = count; i > 0; i--, p++, pp + ++) { + writeb((*p == '\n') ? 0xff : *p, + &dev.shmem->comm_buffers.pcio_buf[pp & 0xff]); + len--; + xcount++; + icn_putmsg(card, *p); + if ((*p == '\n') && (i > 1)) { + icn_putmsg(card, '>'); + ocount++; + } + ocount++; + } + writeb((readb(&cmd_i) + count) & 0xff, &cmd_i); + if (lastmap_card) + icn_map_channel(lastmap_card, lastmap_channel); + restore_flags(flags); + if (len) { + udelay(1000); + if (loop++ > 20) + break; + } else + break; + } + if (len && (!user)) + printk(KERN_WARNING "icn: writemsg incomplete!\n"); + 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 icn_stopcard(icn_card * card) +static void +icn_stopcard(icn_card * card) { - unsigned long flags; - isdn_ctrl cmd; + unsigned long flags; + isdn_ctrl cmd; - save_flags(flags); - cli(); - if (card->flags & ICN_FLAGS_RUNNING) { - card->flags &= ~ICN_FLAGS_RUNNING; - del_timer(&card->st_timer); - del_timer(&card->rb_timer); - cmd.command = ISDN_STAT_STOP; - cmd.driver = card->myid; - card->interface.statcallb(&cmd); - if (card->doubleS0) - icn_stopcard(card->other); - } - restore_flags(flags); -} - -static void icn_stopallcards(void) -{ - icn_card *p = cards; - - while (p) { - icn_stopcard(p); - p = p->next; - } -} - -static int icn_command(isdn_ctrl * c, icn_card * card) -{ - ulong a; - ulong flags; - int i; - char cbuf[60]; - isdn_ctrl cmd; - icn_cdef cdef; - - switch (c->command) { - case ISDN_CMD_IOCTL: - memcpy(&a, c->num, sizeof(ulong)); - switch (c->arg) { - case ICN_IOCTL_SETMMIO: - if ((unsigned long) dev.shmem != (a & 0x0ffc000)) { - if (check_shmem((ulong) (a & 0x0ffc000), 0x4000)) { - printk(KERN_WARNING - "icn: memory at 0x%08lx in use.\n", - (ulong) (a & 0x0ffc000)); - return -EINVAL; - } - icn_stopallcards(); - save_flags(flags); - cli(); - if (dev.mvalid) - release_shmem((ulong) dev.shmem, 0x4000); - dev.mvalid = 0; - dev.shmem = (icn_shmem *) (a & 0x0ffc000); - restore_flags(flags); - printk(KERN_INFO - "icn: (%s) mmio set to 0x%08lx\n", - CID, - (unsigned long) dev.shmem); - } - break; - case ICN_IOCTL_GETMMIO: - return (long) dev.shmem; - case ICN_IOCTL_SETPORT: - if (a == 0x300 || a == 0x310 || a == 0x320 || a == 0x330 - || a == 0x340 || a == 0x350 || a == 0x360 || - a == 0x308 || a == 0x318 || a == 0x328 || a == 0x338 - || a == 0x348 || a == 0x358 || a == 0x368) { - if (card->port != (unsigned short) a) { - if (check_region((unsigned short) a, ICN_PORTLEN)) { - printk(KERN_WARNING - "icn: (%s) ports 0x%03x-0x%03x in use.\n", - CID, (int) a, (int) a + ICN_PORTLEN); - return -EINVAL; - } - icn_stopcard(card); - save_flags(flags); - cli(); - if (card->rvalid) - release_region(card->port, ICN_PORTLEN); - card->port = (unsigned short) a; - card->rvalid = 0; - if (card->doubleS0) { - card->other->port = (unsigned short) a; - card->other->rvalid = 0; - } - restore_flags(flags); - printk(KERN_INFO - "icn: (%s) port set to 0x%03x\n", - CID, card->port); - } - } else - return -EINVAL; - break; - case ICN_IOCTL_GETPORT: - return (int) card->port; - case ICN_IOCTL_GETDOUBLE: - return (int) card->doubleS0; - case ICN_IOCTL_DEBUGVAR: - if ((i = verify_area(VERIFY_WRITE, - (void *) a, - sizeof(ulong) * 2))) - return i; - copy_from_user((char *)a, - (char *)&card, sizeof(ulong)); - a += sizeof(ulong); - { - ulong l = (ulong)&dev; - copy_from_user((char *)a, - (char *)&l, sizeof(ulong)); - } - return 0; - case ICN_IOCTL_LOADBOOT: - icn_stopcard(card); - return (icn_loadboot((u_char *) a, card)); - case ICN_IOCTL_LOADPROTO: - icn_stopcard(card); - if ((i = (icn_loadproto((u_char *) a, card)))) - return i; - if (card->doubleS0) - i = icn_loadproto((u_char *) (a + ICN_CODE_STAGE2), card->other); - return i; - break; - case ICN_IOCTL_ADDCARD: - if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(icn_cdef)))) - return i; - copy_from_user((char *)&cdef, (char *)a, sizeof(cdef)); - return (icn_addcard(cdef.port, cdef.id1, cdef.id2)); - break; - case ICN_IOCTL_LEASEDCFG: - if (a) { - if (!card->leased) { - card->leased = 1; - while (card->ptype == ISDN_PTYPE_UNKNOWN) { - current->timeout = jiffies + ICN_BOOT_TIMEOUT1; - schedule(); - } - current->timeout = jiffies + ICN_BOOT_TIMEOUT1; - schedule(); - sprintf(cbuf, "00;FV2ON\n01;EAZ1\n"); - i = icn_writecmd(cbuf, strlen(cbuf), 0, card); - printk(KERN_INFO - "icn: (%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 = icn_writecmd(cbuf, strlen(cbuf), 0, card); - printk(KERN_INFO - "icn: (%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 & ICN_FLAGS_RUNNING) - return -ENODEV; - if (card->leased) - break; - if ((c->arg & 255) < ICN_BCH) { - char *p; - char *p2; - char dial[50]; - char sis[50]; - char dcode[4]; - int si1, si2; - - a = c->arg; - strcpy(sis, c->num); - p = strrchr(sis, ','); - *p++ = '\0'; - si2 = simple_strtoul(p,NULL,10); - p = strrchr(sis, ',') + 1; - si1 = simple_strtoul(p,NULL,10); - p = c->num; - if (*p == 's' || *p == 'S') { - /* Dial for SPV */ - p++; - strcpy(dcode, "SCA"); - } else - /* Normal Dial */ - strcpy(dcode, "CAL"); - strcpy(dial, p); - p = strchr(dial, ','); - *p++ = '\0'; - p2 = strchr(p, ','); - *p2 = '\0'; - sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1), dcode, dial, si1, - si2, p); - i = icn_writecmd(cbuf, strlen(cbuf), 0, card); - } - break; - case ISDN_CMD_ACCEPTD: - if (!card->flags & ICN_FLAGS_RUNNING) - return -ENODEV; - if (c->arg < ICN_BCH) { - a = c->arg + 1; - if (card->fw_rev >= 300) { - 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; - } - i = icn_writecmd(cbuf, strlen(cbuf), 0, card); - } - sprintf(cbuf, "%02d;DCON_R\n", (int) a); - i = icn_writecmd(cbuf, strlen(cbuf), 0, card); - } - break; - case ISDN_CMD_ACCEPTB: - if (!card->flags & ICN_FLAGS_RUNNING) - return -ENODEV; - if (c->arg < ICN_BCH) { - a = c->arg + 1; - if (card->fw_rev >= 300) - 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; - } - else - sprintf(cbuf, "%02d;BCON_R\n", (int) a); - i = icn_writecmd(cbuf, strlen(cbuf), 0, card); - } - break; - case ISDN_CMD_HANGUP: - if (!card->flags & ICN_FLAGS_RUNNING) - return -ENODEV; - if (c->arg < ICN_BCH) { - a = c->arg + 1; - sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a); - i = icn_writecmd(cbuf, strlen(cbuf), 0, card); - } - break; - case ISDN_CMD_SETEAZ: - if (!card->flags & ICN_FLAGS_RUNNING) - return -ENODEV; - if (card->leased) - break; - if (c->arg < ICN_BCH) { - a = c->arg + 1; - if (card->ptype == ISDN_PTYPE_EURO) { - sprintf(cbuf, "%02d;MS%s%s\n", (int) a, - c->num[0] ? "N" : "ALL", c->num); - } else - sprintf(cbuf, "%02d;EAZ%s\n", (int) a, - c->num[0] ? c->num : "0123456789"); - i = icn_writecmd(cbuf, strlen(cbuf), 0, card); - } - break; - case ISDN_CMD_CLREAZ: - if (!card->flags & ICN_FLAGS_RUNNING) - return -ENODEV; - if (card->leased) - break; - if (c->arg < ICN_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 = icn_writecmd(cbuf, strlen(cbuf), 0, card); - } - break; - case ISDN_CMD_SETL2: - if (!card->flags & ICN_FLAGS_RUNNING) - return -ENODEV; - if ((c->arg & 255) < ICN_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 = icn_writecmd(cbuf, strlen(cbuf), 0, card); - card->l2_proto[a & 255] = (a >> 8); - } - break; - case ISDN_CMD_GETL2: - if (!card->flags & ICN_FLAGS_RUNNING) - return -ENODEV; - if ((c->arg & 255) < ICN_BCH) - return card->l2_proto[c->arg & 255]; - else - return -ENODEV; - case ISDN_CMD_SETL3: - if (!card->flags & ICN_FLAGS_RUNNING) - return -ENODEV; - return 0; - case ISDN_CMD_GETL3: - if (!card->flags & ICN_FLAGS_RUNNING) - return -ENODEV; - if ((c->arg & 255) < ICN_BCH) - return ISDN_PROTO_L3_TRANS; - else - return -ENODEV; - case ISDN_CMD_GETEAZ: - if (!card->flags & ICN_FLAGS_RUNNING) - return -ENODEV; - break; - case ISDN_CMD_SETSIL: - if (!card->flags & ICN_FLAGS_RUNNING) - return -ENODEV; - break; - case ISDN_CMD_GETSIL: - if (!card->flags & ICN_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; + save_flags(flags); + cli(); + if (card->flags & ICN_FLAGS_RUNNING) { + card->flags &= ~ICN_FLAGS_RUNNING; + del_timer(&card->st_timer); + del_timer(&card->rb_timer); + cmd.command = ISDN_STAT_STOP; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + if (card->doubleS0) + icn_stopcard(card->other); + } + restore_flags(flags); +} + +static void +icn_stopallcards(void) +{ + icn_card *p = cards; + + while (p) { + icn_stopcard(p); + p = p->next; + } } /* - * Find card with given driverId + * Unmap all cards, because some of them may be mapped accidetly during + * autoprobing of some network drivers (SMC-driver?) */ -static inline icn_card * - icn_findcard(int driverid) +static void +icn_disable_cards(void) { - icn_card *p = cards; + icn_card *card = cards; + unsigned long flags; - while (p) { - if (p->myid == driverid) - return p; - p = p->next; - } - return (icn_card *)0; + save_flags(flags); + cli(); + while (card) { + if (check_region(card->port, ICN_PORTLEN)) { + printk(KERN_WARNING + "icn: (%s) ports 0x%03x-0x%03x in use.\n", + CID, + card->port, + card->port + ICN_PORTLEN); + cli(); + } else { + OUTB_P(0, ICN_RUN); /* Reset Controller */ + OUTB_P(0, ICN_MAPRAM); /* Disable RAM */ + } + card = card->next; + } + restore_flags(flags); +} + +static int +icn_command(isdn_ctrl * c, icn_card * card) +{ + ulong a; + ulong flags; + int i; + char cbuf[60]; + isdn_ctrl cmd; + icn_cdef cdef; + + switch (c->command) { + case ISDN_CMD_IOCTL: + memcpy(&a, c->parm.num, sizeof(ulong)); + switch (c->arg) { + case ICN_IOCTL_SETMMIO: + if ((unsigned long) dev.shmem != (a & 0x0ffc000)) { + if (check_shmem((ulong) (a & 0x0ffc000), 0x4000)) { + printk(KERN_WARNING + "icn: memory at 0x%08lx in use.\n", + (ulong) (a & 0x0ffc000)); + return -EINVAL; + } + icn_stopallcards(); + save_flags(flags); + cli(); + if (dev.mvalid) + release_shmem((ulong) dev.shmem, 0x4000); + dev.mvalid = 0; + dev.shmem = (icn_shmem *) (a & 0x0ffc000); + restore_flags(flags); + printk(KERN_INFO + "icn: (%s) mmio set to 0x%08lx\n", + CID, + (unsigned long) dev.shmem); + } + break; + case ICN_IOCTL_GETMMIO: + return (long) dev.shmem; + case ICN_IOCTL_SETPORT: + if (a == 0x300 || a == 0x310 || a == 0x320 || a == 0x330 + || a == 0x340 || a == 0x350 || a == 0x360 || + a == 0x308 || a == 0x318 || a == 0x328 || a == 0x338 + || a == 0x348 || a == 0x358 || a == 0x368) { + if (card->port != (unsigned short) a) { + if (check_region((unsigned short) a, ICN_PORTLEN)) { + printk(KERN_WARNING + "icn: (%s) ports 0x%03x-0x%03x in use.\n", + CID, (int) a, (int) a + ICN_PORTLEN); + return -EINVAL; + } + icn_stopcard(card); + save_flags(flags); + cli(); + if (card->rvalid) + release_region(card->port, ICN_PORTLEN); + card->port = (unsigned short) a; + card->rvalid = 0; + if (card->doubleS0) { + card->other->port = (unsigned short) a; + card->other->rvalid = 0; + } + restore_flags(flags); + printk(KERN_INFO + "icn: (%s) port set to 0x%03x\n", + CID, card->port); + } + } else + return -EINVAL; + break; + case ICN_IOCTL_GETPORT: + return (int) card->port; + case ICN_IOCTL_GETDOUBLE: + return (int) card->doubleS0; + case ICN_IOCTL_DEBUGVAR: + if ((i = copy_to_user((char *) a, + (char *) &card, sizeof(ulong)))) + return i; + a += sizeof(ulong); + { + ulong l = (ulong) & dev; + if ((i = copy_to_user((char *) a, + (char *) &l, sizeof(ulong)))) + return i; + } + return 0; + case ICN_IOCTL_LOADBOOT: + if (dev.firstload) { + icn_disable_cards(); + dev.firstload = 0; + } + icn_stopcard(card); + return (icn_loadboot((u_char *) a, card)); + case ICN_IOCTL_LOADPROTO: + icn_stopcard(card); + if ((i = (icn_loadproto((u_char *) a, card)))) + return i; + if (card->doubleS0) + i = icn_loadproto((u_char *) (a + ICN_CODE_STAGE2), card->other); + return i; + break; + case ICN_IOCTL_ADDCARD: + if (!dev.firstload) + return -EBUSY; + if ((i = copy_from_user((char *) &cdef, (char *) a, sizeof(cdef)))) + return i; + return (icn_addcard(cdef.port, cdef.id1, cdef.id2)); + break; + case ICN_IOCTL_LEASEDCFG: + if (a) { + if (!card->leased) { + card->leased = 1; + while (card->ptype == ISDN_PTYPE_UNKNOWN) { + current->timeout = jiffies + ICN_BOOT_TIMEOUT1; + schedule(); + } + current->timeout = jiffies + ICN_BOOT_TIMEOUT1; + schedule(); + sprintf(cbuf, "00;FV2ON\n01;EAZ%c\n02;EAZ%c\n", + (a & 1)?'1':'C', (a & 2)?'2':'C'); + i = icn_writecmd(cbuf, strlen(cbuf), 0, card); + printk(KERN_INFO + "icn: (%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 = icn_writecmd(cbuf, strlen(cbuf), 0, card); + printk(KERN_INFO + "icn: (%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 & ICN_FLAGS_RUNNING) + return -ENODEV; + if (card->leased) + break; + if ((c->arg & 255) < ICN_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 = icn_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_ACCEPTD: + if (!card->flags & ICN_FLAGS_RUNNING) + return -ENODEV; + if (c->arg < ICN_BCH) { + a = c->arg + 1; + if (card->fw_rev >= 300) { + 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; + } + i = icn_writecmd(cbuf, strlen(cbuf), 0, card); + } + sprintf(cbuf, "%02d;DCON_R\n", (int) a); + i = icn_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_ACCEPTB: + if (!card->flags & ICN_FLAGS_RUNNING) + return -ENODEV; + if (c->arg < ICN_BCH) { + a = c->arg + 1; + if (card->fw_rev >= 300) + 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; + } else + sprintf(cbuf, "%02d;BCON_R\n", (int) a); + i = icn_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_HANGUP: + if (!card->flags & ICN_FLAGS_RUNNING) + return -ENODEV; + if (c->arg < ICN_BCH) { + a = c->arg + 1; + sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a); + i = icn_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_SETEAZ: + if (!card->flags & ICN_FLAGS_RUNNING) + return -ENODEV; + if (card->leased) + break; + if (c->arg < ICN_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 = icn_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_CLREAZ: + if (!card->flags & ICN_FLAGS_RUNNING) + return -ENODEV; + if (card->leased) + break; + if (c->arg < ICN_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 = icn_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_SETL2: + if (!card->flags & ICN_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg & 255) < ICN_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 = icn_writecmd(cbuf, strlen(cbuf), 0, card); + card->l2_proto[a & 255] = (a >> 8); + } + break; + case ISDN_CMD_GETL2: + if (!card->flags & ICN_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg & 255) < ICN_BCH) + return card->l2_proto[c->arg & 255]; + else + return -ENODEV; + case ISDN_CMD_SETL3: + if (!card->flags & ICN_FLAGS_RUNNING) + return -ENODEV; + return 0; + case ISDN_CMD_GETL3: + if (!card->flags & ICN_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg & 255) < ICN_BCH) + return ISDN_PROTO_L3_TRANS; + else + return -ENODEV; + case ISDN_CMD_GETEAZ: + if (!card->flags & ICN_FLAGS_RUNNING) + return -ENODEV; + break; + case ISDN_CMD_SETSIL: + if (!card->flags & ICN_FLAGS_RUNNING) + return -ENODEV; + break; + case ISDN_CMD_GETSIL: + if (!card->flags & ICN_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; } /* - * Wrapper functions for interface to linklevel + * Find card with given driverId */ -static int if_command(isdn_ctrl * c) -{ - icn_card *card = icn_findcard(c->driver); - - if (card) - return (icn_command(c, card)); - printk(KERN_ERR - "icn: if_command %d called with invalid driverId %d!\n", - c->command, c->driver); - return -ENODEV; -} - -static int if_writecmd(const u_char * buf, int len, int user, int id, int channel) -{ - icn_card *card = icn_findcard(id); - - if (card) { - if (!card->flags & ICN_FLAGS_RUNNING) - return -ENODEV; - return (icn_writecmd(buf, len, user, card)); - } - printk(KERN_ERR - "icn: if_writecmd called with invalid driverId!\n"); - return -ENODEV; -} - -static int if_readstatus(u_char * buf, int len, int user, int id, int channel) +static inline icn_card * +icn_findcard(int driverid) { - icn_card *card = icn_findcard(id); + icn_card *p = cards; - if (card) { - if (!card->flags & ICN_FLAGS_RUNNING) - return -ENODEV; - return (icn_readstatus(buf, len, user, card)); - } - printk(KERN_ERR - "icn: if_readstatus called with invalid driverId!\n"); - return -ENODEV; + while (p) { + if (p->myid == driverid) + return p; + p = p->next; + } + return (icn_card *) 0; } -static int if_sendbuf(int id, int channel, struct sk_buff *skb) +/* + * Wrapper functions for interface to linklevel + */ +static int +if_command(isdn_ctrl * c) { - icn_card *card = icn_findcard(id); + icn_card *card = icn_findcard(c->driver); - if (card) { - if (!card->flags & ICN_FLAGS_RUNNING) - return -ENODEV; - return (icn_sendbuf(channel, skb, card)); - } - printk(KERN_ERR - "icn: if_sendbuf called with invalid driverId!\n"); - return -ENODEV; + if (card) + return (icn_command(c, card)); + printk(KERN_ERR + "icn: if_command %d called with invalid driverId %d!\n", + c->command, c->driver); + return -ENODEV; +} + +static int +if_writecmd(const u_char * buf, int len, int user, int id, int channel) +{ + icn_card *card = icn_findcard(id); + + if (card) { + if (!card->flags & ICN_FLAGS_RUNNING) + return -ENODEV; + return (icn_writecmd(buf, len, user, card)); + } + printk(KERN_ERR + "icn: if_writecmd called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_readstatus(u_char * buf, int len, int user, int id, int channel) +{ + icn_card *card = icn_findcard(id); + + if (card) { + if (!card->flags & ICN_FLAGS_RUNNING) + return -ENODEV; + return (icn_readstatus(buf, len, user, card)); + } + printk(KERN_ERR + "icn: if_readstatus called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_sendbuf(int id, int channel, struct sk_buff *skb) +{ + icn_card *card = icn_findcard(id); + + if (card) { + if (!card->flags & ICN_FLAGS_RUNNING) + return -ENODEV; + return (icn_sendbuf(channel, skb, card)); + } + printk(KERN_ERR + "icn: if_sendbuf 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 icn_card *icn_initcard(int port, char *id) { - icn_card *card; - int i; - - if (!(card = (icn_card *) kmalloc(sizeof(icn_card), GFP_KERNEL))) { - printk(KERN_WARNING - "icn: (%s) Could not allocate card-struct.\n", id); - return (icn_card *)0; - } - memset((char *) card, 0, sizeof(icn_card)); - card->port = port; - card->interface.channels = ICN_BCH; - card->interface.maxbufsize = 4000; - card->interface.command = if_command; +static icn_card * +icn_initcard(int port, char *id) +{ + icn_card *card; + int i; + + if (!(card = (icn_card *) kmalloc(sizeof(icn_card), GFP_KERNEL))) { + printk(KERN_WARNING + "icn: (%s) Could not allocate card-struct.\n", id); + return (icn_card *) 0; + } + memset((char *) card, 0, sizeof(icn_card)); + card->port = port; + card->interface.channels = ICN_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;il2_proto[i] = ISDN_PROTO_L2_X75I; - skb_queue_head_init(&card->spqueue[i]); - } - card->next = cards; - cards = card; - if (!register_isdn(&card->interface)) { - cards = cards->next; - printk(KERN_WARNING - "icn: Unable to register %s\n", id); - kfree(card); - return (icn_card*)0; - } - card->myid = card->interface.channels; - sprintf(card->regname, "icn-isdn (%s)", card->interface.id); - return card; -} - -static int icn_addcard(int port, char *id1, char *id2) -{ - ulong flags; - icn_card *card; - icn_card *card2; - - save_flags(flags); - cli(); - if (!(card = icn_initcard(port,id1))) { - restore_flags(flags); - return -EIO; - } - if (!strlen(id2)) { - restore_flags(flags); - printk(KERN_INFO - "icn: (%s) ICN-2B, port 0x%x added\n", - card->interface.id, port); - return 0; - } - if (!(card2 = icn_initcard(port,id2))) { - restore_flags(flags); - printk(KERN_INFO - "icn: (%s) half ICN-4B, port 0x%x added\n", - card2->interface.id, port); - return 0; - } - card->doubleS0 = 1; - card->secondhalf = 0; - card->other = card2; - card2->doubleS0 = 1; - card2->secondhalf = 1; - card2->other = card; - restore_flags(flags); - printk(KERN_INFO - "icn: (%s and %s) ICN-4B, port 0x%x added\n", - card->interface.id, card2->interface.id, port); - return 0; + 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 < ICN_BCH; i++) { + card->l2_proto[i] = ISDN_PROTO_L2_X75I; + skb_queue_head_init(&card->spqueue[i]); + } + card->next = cards; + cards = card; + if (!register_isdn(&card->interface)) { + cards = cards->next; + printk(KERN_WARNING + "icn: Unable to register %s\n", id); + kfree(card); + return (icn_card *) 0; + } + card->myid = card->interface.channels; + sprintf(card->regname, "icn-isdn (%s)", card->interface.id); + return card; +} + +static int +icn_addcard(int port, char *id1, char *id2) +{ + ulong flags; + icn_card *card; + icn_card *card2; + + save_flags(flags); + cli(); + if (!(card = icn_initcard(port, id1))) { + restore_flags(flags); + return -EIO; + } + if (!strlen(id2)) { + restore_flags(flags); + printk(KERN_INFO + "icn: (%s) ICN-2B, port 0x%x added\n", + card->interface.id, port); + return 0; + } + if (!(card2 = icn_initcard(port, id2))) { + restore_flags(flags); + printk(KERN_INFO + "icn: (%s) half ICN-4B, port 0x%x added\n", + card2->interface.id, port); + return 0; + } + card->doubleS0 = 1; + card->secondhalf = 0; + card->other = card2; + card2->doubleS0 = 1; + card2->secondhalf = 1; + card2->other = card; + restore_flags(flags); + printk(KERN_INFO + "icn: (%s and %s) ICN-4B, port 0x%x added\n", + card->interface.id, card2->interface.id, port); + return 0; } #ifdef MODULE #define icn_init init_module #else -void icn_setup(char *str, int *ints) +void +icn_setup(char *str, int *ints) { - char *p; - static char sid[20]; - static char sid2[20]; - - if (ints[0]) - portbase = ints[1]; - if (ints[0]>1) - membase = ints[2]; - if (strlen(str)) { - strcpy(sid,str); - icn_id = sid; - if ((p = strchr(sid,','))) { - *p++ = 0; - strcpy(sid2,p); - icn_id2 = sid2; - } - } + char *p; + static char sid[20]; + static char sid2[20]; + + if (ints[0]) + portbase = ints[1]; + if (ints[0] > 1) + membase = ints[2]; + if (strlen(str)) { + strcpy(sid, str); + icn_id = sid; + if ((p = strchr(sid, ','))) { + *p++ = 0; + strcpy(sid2, p); + icn_id2 = sid2; + } + } } #endif -int icn_init(void) -{ - char *p; - char rev[10]; - - memset(&dev, 0, sizeof(icn_dev)); - dev.shmem = (icn_shmem *) ((unsigned long)membase & 0x0ffc000); - dev.channel = -1; - dev.mcard = NULL; - - /* No symbols to export, hide all symbols */ - register_symtab(NULL); - - if ((p = strchr(revision, ':'))) { - strcpy(rev, p + 1); - p = strchr(rev, '$'); - *p = 0; - } else - strcpy(rev, " ??? "); - printk(KERN_NOTICE "ICN-ISDN-driver Rev%smem=0x%08lx\n", rev, - (ulong) dev.shmem); - return (icn_addcard(portbase,icn_id,icn_id2)); +int +icn_init(void) +{ + char *p; + char rev[10]; + + memset(&dev, 0, sizeof(icn_dev)); + dev.shmem = (icn_shmem *) ((unsigned long) membase & 0x0ffc000); + dev.channel = -1; + dev.mcard = NULL; + dev.firstload = 1; + + /* 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 "ICN-ISDN-driver Rev%smem=0x%08lx\n", rev, + (ulong) dev.shmem); + return (icn_addcard(portbase, icn_id, icn_id2)); } #ifdef MODULE -void cleanup_module(void) +void +cleanup_module(void) { - isdn_ctrl cmd; - icn_card *card = cards; - icn_card *last; - int i; - - icn_stopallcards(); - while (card) { - cmd.command = ISDN_STAT_UNLOAD; - cmd.driver = card->myid; - card->interface.statcallb(&cmd); - if (card->rvalid) { - OUTB_P(0, ICN_RUN); /* Reset Controller */ - OUTB_P(0, ICN_MAPRAM); /* Disable RAM */ - if (card->secondhalf || (!card->doubleS0)) { - release_region(card->port, ICN_PORTLEN); - card->rvalid = 0; - } - for (i = 0; i < ICN_BCH; i++) - icn_free_queue(&card->spqueue[i]); - } - card = card->next; - } - card = cards; - while (card) { - last = card; - card = card->next; - kfree(last); - } - if (dev.mvalid) - release_shmem((ulong) dev.shmem, 0x4000); - printk(KERN_NOTICE "ICN-ISDN-driver unloaded\n"); + isdn_ctrl cmd; + icn_card *card = cards; + icn_card *last; + int i; + + icn_stopallcards(); + while (card) { + cmd.command = ISDN_STAT_UNLOAD; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + if (card->rvalid) { + OUTB_P(0, ICN_RUN); /* Reset Controller */ + OUTB_P(0, ICN_MAPRAM); /* Disable RAM */ + if (card->secondhalf || (!card->doubleS0)) { + release_region(card->port, ICN_PORTLEN); + card->rvalid = 0; + } + for (i = 0; i < ICN_BCH; i++) + icn_free_queue(card, i); + } + card = card->next; + } + card = cards; + while (card) { + last = card; + card = card->next; + kfree(last); + } + if (dev.mvalid) + release_shmem((ulong) dev.shmem, 0x4000); + printk(KERN_NOTICE "ICN-ISDN-driver unloaded\n"); } #endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/icn/icn.h linux/drivers/isdn/icn/icn.h --- v2.0.30/linux/drivers/isdn/icn/icn.h Tue Nov 12 22:36:19 1996 +++ linux/drivers/isdn/icn/icn.h Mon Aug 4 17:34:00 1997 @@ -1,5 +1,5 @@ -/* $Id: icn.h,v 1.22 1996/11/13 02:37:33 fritz Exp $ - * +/* $Id: icn.h,v 1.26 1997/02/14 12:23:16 fritz Exp $ + * ISDN lowlevel-module for the ICN active ISDN-Card. * * Copyright 1994 by Fritz Elfert (fritz@wuemaus.franken.de) @@ -16,9 +16,23 @@ * * 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. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: icn.h,v $ + * Revision 1.26 1997/02/14 12:23:16 fritz + * Added support for new insmod parameter handling. + * + * Revision 1.25 1997/02/10 10:10:31 fritz + * Changes for Kernel 2.1.X compatibility. + * Enhanced initialization, can recover from + * misconfiguration by other autoprobing drivers. + * + * Revision 1.24 1997/01/29 22:34:46 fritz + * Cleanup, Corrected D64S setup of 2nd channel. + * + * Revision 1.23 1996/12/17 18:47:55 tsbogend + * changed timeouts from absolute numbers to HZ based values + * * Revision 1.22 1996/11/13 02:37:33 fritz * Added delay include. * @@ -113,9 +127,9 @@ /* Struct for adding new cards */ typedef struct icn_cdef { - int port; - char id1[10]; - char id2[10]; + int port; + char id1[10]; + char id2[10]; } icn_cdef; #if defined(__KERNEL__) || defined(__DEBUGVAR__) @@ -141,7 +155,7 @@ #include #include -#endif /* __KERNEL__ */ +#endif /* __KERNEL__ */ /* some useful macros for debugging */ #ifdef ICN_DEBUG_PORT @@ -155,138 +169,154 @@ #define ICN_PORTLEN (0x04) #define ICN_MEMADDR 0x0d0000 -#define ICN_FLAGS_B1ACTIVE 1 /* B-Channel-1 is open */ -#define ICN_FLAGS_B2ACTIVE 2 /* B-Channel-2 is open */ -#define ICN_FLAGS_RUNNING 4 /* Cards driver activated */ -#define ICN_FLAGS_RBTIMER 8 /* cyclic scheduling of B-Channel-poll */ - -#define ICN_BOOT_TIMEOUT1 100 /* Delay for Boot-download (jiffies) */ -#define ICN_CHANLOCK_DELAY 10 /* Delay for Channel-mapping (jiffies) */ - -#define ICN_TIMER_BCREAD 1 /* B-Channel poll-cycle */ -#define ICN_TIMER_DCREAD 50 /* D-Channel poll-cycle */ - -#define ICN_CODE_STAGE1 4096 /* Size of bootcode */ -#define ICN_CODE_STAGE2 65536 /* Size of protocol-code */ - -#define ICN_MAX_SQUEUE 8000 /* Max. outstanding send-data (2* hw-buf.) */ -#define ICN_FRAGSIZE (250) /* Max. size of send-fragments */ -#define ICN_BCH 2 /* Number of supported channels per card */ +#define ICN_FLAGS_B1ACTIVE 1 /* B-Channel-1 is open */ +#define ICN_FLAGS_B2ACTIVE 2 /* B-Channel-2 is open */ +#define ICN_FLAGS_RUNNING 4 /* Cards driver activated */ +#define ICN_FLAGS_RBTIMER 8 /* cyclic scheduling of B-Channel-poll */ + +#define ICN_BOOT_TIMEOUT1 (HZ) /* Delay for Boot-download (jiffies) */ +#define ICN_CHANLOCK_DELAY (HZ/10) /* Delay for Channel-mapping (jiffies) */ + +#define ICN_TIMER_BCREAD (HZ/100) /* B-Channel poll-cycle */ +#define ICN_TIMER_DCREAD (HZ/2) /* D-Channel poll-cycle */ + +#define ICN_CODE_STAGE1 4096 /* Size of bootcode */ +#define ICN_CODE_STAGE2 65536 /* Size of protocol-code */ + +#define ICN_MAX_SQUEUE 8000 /* Max. outstanding send-data (2* hw-buf.) */ +#define ICN_FRAGSIZE (250) /* Max. size of send-fragments */ +#define ICN_BCH 2 /* Number of supported channels per card */ /* type-definitions for accessing the mmap-io-areas */ -#define SHM_DCTL_OFFSET (0) /* Offset to data-controlstructures in shm */ -#define SHM_CCTL_OFFSET (0x1d2) /* Offset to comm-controlstructures in shm */ -#define SHM_CBUF_OFFSET (0x200) /* Offset to comm-buffers in shm */ -#define SHM_DBUF_OFFSET (0x2000) /* Offset to data-buffers in shm */ +#define SHM_DCTL_OFFSET (0) /* Offset to data-controlstructures in shm */ +#define SHM_CCTL_OFFSET (0x1d2) /* Offset to comm-controlstructures in shm */ +#define SHM_CBUF_OFFSET (0x200) /* Offset to comm-buffers in shm */ +#define SHM_DBUF_OFFSET (0x2000) /* Offset to data-buffers in shm */ /* * Layout of card's data buffers */ typedef struct { - unsigned char length; /* Bytecount of fragment (max 250) */ - unsigned char endflag; /* 0=last frag., 0xff=frag. continued */ - unsigned char data[ICN_FRAGSIZE]; /* The data */ - /* Fill to 256 bytes */ - char unused[0x100 - ICN_FRAGSIZE - 2]; + unsigned char length; /* Bytecount of fragment (max 250) */ + unsigned char endflag; /* 0=last frag., 0xff=frag. continued */ + unsigned char data[ICN_FRAGSIZE]; /* The data */ + /* Fill to 256 bytes */ + char unused[0x100 - ICN_FRAGSIZE - 2]; } frag_buf; /* * Layout of card's shared memory */ typedef union { - struct { - unsigned char scns; /* Index to free SendFrag. */ - unsigned char scnr; /* Index to active SendFrag READONLY */ - unsigned char ecns; /* Index to free RcvFrag. READONLY */ - unsigned char ecnr; /* Index to valid RcvFrag */ - char unused[6]; - unsigned short fuell1; /* Internal Buf Bytecount */ - } data_control; - struct { - char unused[SHM_CCTL_OFFSET]; - unsigned char iopc_i; /* Read-Ptr Status-Queue READONLY */ - unsigned char iopc_o; /* Write-Ptr Status-Queue */ - unsigned char pcio_i; /* Write-Ptr Command-Queue */ - unsigned char pcio_o; /* Read-Ptr Command Queue READONLY */ - } comm_control; - struct { - char unused[SHM_CBUF_OFFSET]; - unsigned char pcio_buf[0x100]; /* Ring-Buffer Command-Queue */ - unsigned char iopc_buf[0x100]; /* Ring-Buffer Status-Queue */ - } comm_buffers; - struct { - char unused[SHM_DBUF_OFFSET]; - frag_buf receive_buf[0x10]; - frag_buf send_buf[0x10]; - } data_buffers; + struct { + unsigned char scns; /* Index to free SendFrag. */ + unsigned char scnr; /* Index to active SendFrag READONLY */ + unsigned char ecns; /* Index to free RcvFrag. READONLY */ + unsigned char ecnr; /* Index to valid RcvFrag */ + char unused[6]; + unsigned short fuell1; /* Internal Buf Bytecount */ + } data_control; + struct { + char unused[SHM_CCTL_OFFSET]; + unsigned char iopc_i; /* Read-Ptr Status-Queue READONLY */ + unsigned char iopc_o; /* Write-Ptr Status-Queue */ + unsigned char pcio_i; /* Write-Ptr Command-Queue */ + unsigned char pcio_o; /* Read-Ptr Command Queue READONLY */ + } comm_control; + struct { + char unused[SHM_CBUF_OFFSET]; + unsigned char pcio_buf[0x100]; /* Ring-Buffer Command-Queue */ + unsigned char iopc_buf[0x100]; /* Ring-Buffer Status-Queue */ + } comm_buffers; + struct { + char unused[SHM_DBUF_OFFSET]; + frag_buf receive_buf[0x10]; + frag_buf send_buf[0x10]; + } data_buffers; } icn_shmem; /* * Per card driver data */ typedef struct icn_card { - struct icn_card *next; /* Pointer to next device struct */ - struct icn_card *other; /* Pointer to other card for ICN4B */ - unsigned short port; /* Base-port-address */ - int myid; /* Driver-Nr. assigned by linklevel */ - int rvalid; /* IO-portregion has been requested */ - int leased; /* Flag: This Adapter is connected */ - /* to a leased line */ - unsigned short flags; /* Statusflags */ - int doubleS0; /* Flag: ICN4B */ - int secondhalf; /* Flag: Second half of a doubleS0 */ - int fw_rev; /* Firmware revision loaded */ - 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 */ - u_char rcvbuf[ICN_BCH][4096]; /* B-Channel-Receive-Buffers */ - int rcvidx[ICN_BCH]; /* Index for above buffers */ - int l2_proto[ICN_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 */ - 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[ICN_BCH]; /* Byte-counters for B-Ch.-send */ - struct sk_buff_head - spqueue[ICN_BCH]; /* Sendqueue */ - char regname[35]; /* Name used for request_region */ - u_char xmit_lock[ICN_BCH]; /* Semaphore for pollbchan_send() */ + struct icn_card *next; /* Pointer to next device struct */ + struct icn_card *other; /* Pointer to other card for ICN4B */ + unsigned short port; /* Base-port-address */ + int myid; /* Driver-Nr. assigned by linklevel */ + int rvalid; /* IO-portregion has been requested */ + int leased; /* Flag: This Adapter is connected */ + /* to a leased line */ + unsigned short flags; /* Statusflags */ + int doubleS0; /* Flag: ICN4B */ + int secondhalf; /* Flag: Second half of a doubleS0 */ + int fw_rev; /* Firmware revision loaded */ + 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 */ + u_char rcvbuf[ICN_BCH][4096]; /* B-Channel-Receive-Buffers */ + int rcvidx[ICN_BCH]; /* Index for above buffers */ + int l2_proto[ICN_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 */ + 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[ICN_BCH]; /* Byte-counters for B-Ch.-send */ + struct sk_buff_head + spqueue[ICN_BCH]; /* Sendqueue */ + char regname[35]; /* Name used for request_region */ + u_char xmit_lock[ICN_BCH]; /* Semaphore for pollbchan_send() */ } icn_card; /* * Main driver data */ typedef struct icn_dev { - icn_shmem *shmem; /* Pointer to memory-mapped-buffers */ - int mvalid; /* IO-shmem has been requested */ - int channel; /* Currently mapped channel */ - struct icn_card *mcard; /* Currently mapped card */ - int chanlock; /* Semaphore for channel-mapping */ + icn_shmem *shmem; /* Pointer to memory-mapped-buffers */ + int mvalid; /* IO-shmem has been requested */ + int channel; /* Currently mapped channel */ + struct icn_card *mcard; /* Currently mapped card */ + int chanlock; /* Semaphore for channel-mapping */ + int firstload; /* Flag: firmware never loaded */ } icn_dev; typedef icn_dev *icn_devptr; #ifdef __KERNEL__ -static icn_card *cards = (icn_card *) 0; -static u_char chan2bank[] = { 0, 4, 8, 12 }; /* for icn_map_channel() */ +static icn_card *cards = (icn_card *) 0; +static u_char chan2bank[] = +{0, 4, 8, 12}; /* for icn_map_channel() */ -static icn_dev dev; +static icn_dev dev; /* With modutils >= 1.1.67 Integers can be changed while loading a * module. For this reason define the Port-Base an Shmem-Base as * integers. */ -int portbase = ICN_BASEADDR; -int membase = ICN_MEMADDR; -char *icn_id = "\0"; -char *icn_id2 = "\0"; +static int portbase = ICN_BASEADDR; +static int membase = ICN_MEMADDR; +static char *icn_id = "\0"; +static char *icn_id2 = "\0"; + +#ifdef MODULE +#if (LINUX_VERSION_CODE > 0x020111) +MODULE_AUTHOR("Fritz Elfert"); +MODULE_PARM(portbase, "i"); +MODULE_PARM_DESC(portbase, "Port adress of first card"); +MODULE_PARM(membase, "i"); +MODULE_PARM_DESC(membase, "Shared memory adress of all cards"); +MODULE_PARM(icn_id, "s"); +MODULE_PARM_DESC(icn_id, "ID-String of first card"); +MODULE_PARM(icn_id2, "s"); +MODULE_PARM_DESC(icn_id2, "ID-String of first card, second S0 (4B only)"); +#endif +#endif -#endif /* __KERNEL__ */ +#endif /* __KERNEL__ */ /* Utility-Macros */ @@ -298,11 +328,11 @@ /* Return true, if there is a free transmit-buffer */ #define sbfree (((readb(&dev.shmem->data_control.scns)+1) & 0xf) != \ - readb(&dev.shmem->data_control.scnr)) + readb(&dev.shmem->data_control.scnr)) /* Switch to next transmit-buffer */ #define sbnext (writeb((readb(&dev.shmem->data_control.scns)+1) & 0xf, \ - &dev.shmem->data_control.scns)) + &dev.shmem->data_control.scns)) /* Shortcuts for transmit-buffer-access */ #define sbuf_n dev.shmem->data_control.scns @@ -312,11 +342,11 @@ /* Return true, if there is receive-data is available */ #define rbavl (readb(&dev.shmem->data_control.ecnr) != \ - readb(&dev.shmem->data_control.ecns)) + readb(&dev.shmem->data_control.ecns)) /* Switch to next receive-buffer */ #define rbnext (writeb((readb(&dev.shmem->data_control.ecnr)+1) & 0xf, \ - &dev.shmem->data_control.ecnr)) + &dev.shmem->data_control.ecnr)) /* Shortcuts for receive-buffer-access */ #define rbuf_n dev.shmem->data_control.ecnr @@ -330,8 +360,8 @@ /* Return free space in command-buffer */ #define cmd_free ((readb(&cmd_i)>=readb(&cmd_o))? \ - 0x100-readb(&cmd_i)+readb(&cmd_o): \ - readb(&cmd_o)-readb(&cmd_i)) + 0x100-readb(&cmd_i)+readb(&cmd_o): \ + readb(&cmd_o)-readb(&cmd_i)) /* Shortcuts for message-buffer-access */ #define msg_o (dev.shmem->comm_control.iopc_o) @@ -339,8 +369,8 @@ /* Return length of Message, if avail. */ #define msg_avail ((readb(&msg_o)>readb(&msg_i))? \ - 0x100-readb(&msg_o)+readb(&msg_i): \ - readb(&msg_i)-readb(&msg_o)) + 0x100-readb(&msg_o)+readb(&msg_i): \ + readb(&msg_i)-readb(&msg_o)) #define CID (card->interface.id) @@ -355,5 +385,5 @@ #define release_shmem release_region #define request_shmem request_region -#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */ -#endif /* icn_h */ +#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */ +#endif /* icn_h */ diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_audio.c linux/drivers/isdn/isdn_audio.c --- v2.0.30/linux/drivers/isdn/isdn_audio.c Fri Jun 7 06:02:40 1996 +++ linux/drivers/isdn/isdn_audio.c Mon Aug 4 17:34:00 1997 @@ -1,10 +1,10 @@ -/* $Id: isdn_audio.c,v 1.6 1996/06/06 14:43:31 fritz Exp $ - * +/* $Id: isdn_audio.c,v 1.8 1997/03/02 14:29:16 fritz Exp $ + * Linux ISDN subsystem, audio conversion and compression (linklevel). * * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) * DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at) - * + * * 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) @@ -17,9 +17,15 @@ * * 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. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_audio.c,v $ + * Revision 1.8 1997/03/02 14:29:16 fritz + * More ttyI related cleanup. + * + * Revision 1.7 1997/02/03 22:44:11 fritz + * Reformatted according CodingStyle + * * Revision 1.6 1996/06/06 14:43:31 fritz * Changed to support DTMF decoding on audio playback also. * @@ -47,154 +53,158 @@ #include "isdn_audio.h" #include "isdn_common.h" -char *isdn_audio_revision = "$Revision: 1.6 $"; +char *isdn_audio_revision = "$Revision: 1.8 $"; /* * Misc. lookup-tables. */ /* ulaw -> signed 16-bit */ -static short isdn_audio_ulaw_to_s16[] = { - 0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84, - 0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84, - 0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84, - 0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84, - 0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804, - 0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004, - 0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444, - 0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844, - 0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64, - 0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64, - 0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74, - 0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74, - 0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc, - 0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c, - 0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0, - 0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0x0000, - 0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c, - 0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c, - 0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c, - 0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c, - 0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc, - 0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc, - 0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc, - 0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc, - 0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c, - 0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c, - 0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c, - 0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c, - 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, - 0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084, - 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, - 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 +static short isdn_audio_ulaw_to_s16[] = +{ + 0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84, + 0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84, + 0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84, + 0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84, + 0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804, + 0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004, + 0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444, + 0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844, + 0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64, + 0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64, + 0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74, + 0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74, + 0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc, + 0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c, + 0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0, + 0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0x0000, + 0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c, + 0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c, + 0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c, + 0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c, + 0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc, + 0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc, + 0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc, + 0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc, + 0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c, + 0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c, + 0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c, + 0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c, + 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, + 0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084, + 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, + 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 }; /* alaw -> signed 16-bit */ -static short isdn_audio_alaw_to_s16[] = { - 0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4, - 0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74, - 0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4, - 0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64, - 0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4, - 0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4, - 0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4, - 0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4, - 0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64, - 0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34, - 0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844, - 0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24, - 0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64, - 0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4, - 0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964, - 0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4, - 0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24, - 0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94, - 0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924, - 0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94, - 0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24, - 0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14, - 0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24, - 0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14, - 0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4, - 0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54, - 0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4, - 0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64, - 0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4, - 0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4, - 0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4, - 0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4 +static short isdn_audio_alaw_to_s16[] = +{ + 0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4, + 0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74, + 0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4, + 0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64, + 0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4, + 0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4, + 0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4, + 0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4, + 0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64, + 0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34, + 0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844, + 0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24, + 0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64, + 0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4, + 0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964, + 0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4, + 0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24, + 0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94, + 0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924, + 0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94, + 0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24, + 0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14, + 0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24, + 0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14, + 0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4, + 0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54, + 0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4, + 0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64, + 0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4, + 0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4, + 0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4, + 0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4 }; /* alaw -> ulaw */ -static char isdn_audio_alaw_to_ulaw[] = { - 0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49, - 0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57, - 0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41, - 0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f, - 0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d, - 0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b, - 0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45, - 0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53, - 0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47, - 0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55, - 0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f, - 0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e, - 0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b, - 0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59, - 0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43, - 0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51, - 0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a, - 0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58, - 0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42, - 0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50, - 0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e, - 0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c, - 0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46, - 0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54, - 0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48, - 0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56, - 0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40, - 0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f, - 0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c, - 0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a, - 0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44, - 0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52 +static char isdn_audio_alaw_to_ulaw[] = +{ + 0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49, + 0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57, + 0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41, + 0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f, + 0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d, + 0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b, + 0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45, + 0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53, + 0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47, + 0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55, + 0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f, + 0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e, + 0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b, + 0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59, + 0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43, + 0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51, + 0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a, + 0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58, + 0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42, + 0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50, + 0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e, + 0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c, + 0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46, + 0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54, + 0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48, + 0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56, + 0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40, + 0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f, + 0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c, + 0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a, + 0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44, + 0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52 }; /* ulaw -> alaw */ -static char isdn_audio_ulaw_to_alaw[] = { - 0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35, - 0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25, - 0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d, - 0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d, - 0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31, - 0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21, - 0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9, - 0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9, - 0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47, - 0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf, - 0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f, - 0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33, - 0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23, - 0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b, - 0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b, - 0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b, - 0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34, - 0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24, - 0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c, - 0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c, - 0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30, - 0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20, - 0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8, - 0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8, - 0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46, - 0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde, - 0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e, - 0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32, - 0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22, - 0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a, - 0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a, - 0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a +static char isdn_audio_ulaw_to_alaw[] = +{ + 0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35, + 0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25, + 0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d, + 0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d, + 0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31, + 0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21, + 0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9, + 0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9, + 0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47, + 0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf, + 0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f, + 0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33, + 0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23, + 0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b, + 0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b, + 0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b, + 0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34, + 0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24, + 0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c, + 0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c, + 0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30, + 0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20, + 0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8, + 0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8, + 0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46, + 0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde, + 0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e, + 0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32, + 0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22, + 0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a, + 0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a, + 0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a }; #define NCOEFF 16 /* number of frequencies to be analyzed */ @@ -206,68 +216,72 @@ #define HIGRP 1 typedef struct { - int grp; /* low/high group */ - int k; /* k */ - int k2; /* k fuer 2. harmonic */ + int grp; /* low/high group */ + int k; /* k */ + int k2; /* k fuer 2. harmonic */ } dtmf_t; /* For DTMF recognition: * 2 * cos(2 * PI * k / N) precalculated for all k */ -static int cos2pik[NCOEFF] = { - 55812, 29528, 53603, 24032, 51193, 14443, 48590, 6517, - 38113, -21204, 33057, -32186, 25889, -45081, 18332, -55279 +static int cos2pik[NCOEFF] = +{ + 55812, 29528, 53603, 24032, 51193, 14443, 48590, 6517, + 38113, -21204, 33057, -32186, 25889, -45081, 18332, -55279 }; -static dtmf_t dtmf_tones[8] = { - { LOGRP, 0, 1 }, /* 697 Hz */ - { LOGRP, 2, 3 }, /* 770 Hz */ - { LOGRP, 4, 5 }, /* 852 Hz */ - { LOGRP, 6, 7 }, /* 941 Hz */ - { HIGRP, 8, 9 }, /* 1209 Hz */ - { HIGRP, 10, 11 }, /* 1336 Hz */ - { HIGRP, 12, 13 }, /* 1477 Hz */ - { HIGRP, 14, 15 } /* 1633 Hz */ +static dtmf_t dtmf_tones[8] = +{ + {LOGRP, 0, 1}, /* 697 Hz */ + {LOGRP, 2, 3}, /* 770 Hz */ + {LOGRP, 4, 5}, /* 852 Hz */ + {LOGRP, 6, 7}, /* 941 Hz */ + {HIGRP, 8, 9}, /* 1209 Hz */ + {HIGRP, 10, 11}, /* 1336 Hz */ + {HIGRP, 12, 13}, /* 1477 Hz */ + {HIGRP, 14, 15} /* 1633 Hz */ }; -static char dtmf_matrix[4][4] = { - {'1', '2', '3', 'A'}, - {'4', '5', '6', 'B'}, - {'7', '8', '9', 'C'}, - {'*', '0', '#', 'D'} +static char dtmf_matrix[4][4] = +{ + {'1', '2', '3', 'A'}, + {'4', '5', '6', 'B'}, + {'7', '8', '9', 'C'}, + {'*', '0', '#', 'D'} }; #if ((CPU == 386) || (CPU == 486) || (CPU == 586)) static inline void isdn_audio_tlookup(const void *table, void *buff, unsigned long n) { - __asm__("cld\n" - "1:\tlodsb\n\t" - "xlatb\n\t" - "stosb\n\t" - "loop 1b\n\t" - ::"b" ((long)table), "c" (n), "D" ((long)buff), "S" ((long)buff) - :"bx","cx","di","si","ax"); + __asm__("cld\n" + "1:\tlodsb\n\t" + "xlatb\n\t" + "stosb\n\t" + "loop 1b\n\t" + : : "b"((long) table), "c"(n), "D"((long) buff), "S"((long) buff) + : "bx", "cx", "di", "si", "ax"); } + #else static inline void isdn_audio_tlookup(const char *table, char *buff, unsigned long n) { - while (n--) - *buff++ = table[*buff]; + while (n--) + *buff++ = table[*buff]; } #endif void isdn_audio_ulaw2alaw(unsigned char *buff, unsigned long len) { - isdn_audio_tlookup(isdn_audio_ulaw_to_alaw, buff, len); + isdn_audio_tlookup(isdn_audio_ulaw_to_alaw, buff, len); } void isdn_audio_alaw2ulaw(unsigned char *buff, unsigned long len) { - isdn_audio_tlookup(isdn_audio_alaw_to_ulaw, buff, len); + isdn_audio_tlookup(isdn_audio_alaw_to_ulaw, buff, len); } /* @@ -278,207 +292,218 @@ */ -#define ZEROTRAP /* turn on the trap as per the MIL-STD */ +#define ZEROTRAP /* turn on the trap as per the MIL-STD */ #undef ZEROTRAP -#define BIAS 0x84 /* define the add-in bias for 16 bit samples */ +#define BIAS 0x84 /* define the add-in bias for 16 bit samples */ #define CLIP 32635 static unsigned char -isdn_audio_linear2ulaw(int sample) { - static int exp_lut[256] = { - 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 - }; - int sign, exponent, mantissa; - unsigned char ulawbyte; - - /* Get the sample into sign-magnitude. */ - sign = (sample >> 8) & 0x80; /* set aside the sign */ - if(sign != 0) sample = -sample; /* get magnitude */ - if(sample > CLIP) sample = CLIP; /* clip the magnitude */ - - /* Convert from 16 bit linear to ulaw. */ - sample = sample + BIAS; - exponent = exp_lut[( sample >> 7 ) & 0xFF]; - mantissa = (sample >> (exponent + 3)) & 0x0F; - ulawbyte = ~(sign | (exponent << 4) | mantissa); +isdn_audio_linear2ulaw(int sample) +{ + static int exp_lut[256] = + { + 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 + }; + int sign, + exponent, + mantissa; + unsigned char ulawbyte; + + /* Get the sample into sign-magnitude. */ + sign = (sample >> 8) & 0x80; /* set aside the sign */ + if (sign != 0) + sample = -sample; /* get magnitude */ + if (sample > CLIP) + sample = CLIP; /* clip the magnitude */ + + /* Convert from 16 bit linear to ulaw. */ + sample = sample + BIAS; + exponent = exp_lut[(sample >> 7) & 0xFF]; + mantissa = (sample >> (exponent + 3)) & 0x0F; + ulawbyte = ~(sign | (exponent << 4) | mantissa); #ifdef ZEROTRAP - /* optional CCITT trap */ - if (ulawbyte == 0) ulawbyte = 0x02; + /* optional CCITT trap */ + if (ulawbyte == 0) + ulawbyte = 0x02; #endif - return(ulawbyte); + return (ulawbyte); } -static int Mx[3][8] = { - { 0x3800, 0x5600, 0,0,0,0,0,0 }, - { 0x399a, 0x3a9f, 0x4d14, 0x6607, 0,0,0,0 }, - { 0x3556, 0x3556, 0x399A, 0x3A9F, 0x4200, 0x4D14, 0x6607, 0x6607 }, +static int Mx[3][8] = +{ + {0x3800, 0x5600, 0, 0, 0, 0, 0, 0}, + {0x399a, 0x3a9f, 0x4d14, 0x6607, 0, 0, 0, 0}, + {0x3556, 0x3556, 0x399A, 0x3A9F, 0x4200, 0x4D14, 0x6607, 0x6607}, }; -static int bitmask[9] = { - 0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff -}; +static int bitmask[9] = +{ + 0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff +}; static int -isdn_audio_get_bits (adpcm_state *s, unsigned char **in, int *len) +isdn_audio_get_bits(adpcm_state * s, unsigned char **in, int *len) { - while( s->nleft < s->nbits) { - int d = *((*in)++); - (*len)--; - s->word = (s->word << 8) | d; - s->nleft += 8; - } - s->nleft -= s->nbits; - return (s->word >> s->nleft) & bitmask[s->nbits]; + while (s->nleft < s->nbits) { + int d = *((*in)++); + (*len)--; + s->word = (s->word << 8) | d; + s->nleft += 8; + } + s->nleft -= s->nbits; + return (s->word >> s->nleft) & bitmask[s->nbits]; } static void -isdn_audio_put_bits (int data, int nbits, adpcm_state *s, - unsigned char **out, int *len) +isdn_audio_put_bits(int data, int nbits, adpcm_state * s, + unsigned char **out, int *len) { - s->word = (s->word << nbits) | (data & bitmask[nbits]); - s->nleft += nbits; - while(s->nleft >= 8) { - int d = (s->word >> (s->nleft-8)); - *(out[0]++) = d & 255; - (*len)++; - s->nleft -= 8; - } + s->word = (s->word << nbits) | (data & bitmask[nbits]); + s->nleft += nbits; + while (s->nleft >= 8) { + int d = (s->word >> (s->nleft - 8)); + *(out[0]++) = d & 255; + (*len)++; + s->nleft -= 8; + } } adpcm_state * -isdn_audio_adpcm_init(adpcm_state *s, int nbits) +isdn_audio_adpcm_init(adpcm_state * s, int nbits) { - if (!s) - s = (adpcm_state *) kmalloc(sizeof(adpcm_state), GFP_ATOMIC); - if (s) { - s->a = 0; - s->d = 5; - s->word = 0; - s->nleft = 0; - s->nbits = nbits; - } - return s; + if (!s) + s = (adpcm_state *) kmalloc(sizeof(adpcm_state), GFP_ATOMIC); + if (s) { + s->a = 0; + s->d = 5; + s->word = 0; + s->nleft = 0; + s->nbits = nbits; + } + return s; } dtmf_state * -isdn_audio_dtmf_init(dtmf_state *s) +isdn_audio_dtmf_init(dtmf_state * s) { - if (!s) - s = (dtmf_state *) kmalloc(sizeof(dtmf_state), GFP_ATOMIC); - if (s) { - s->idx = 0; - s->last = ' '; - } - return s; + if (!s) + s = (dtmf_state *) kmalloc(sizeof(dtmf_state), GFP_ATOMIC); + if (s) { + s->idx = 0; + s->last = ' '; + } + return s; } /* * Decompression of adpcm data to a/u-law * */ - + int -isdn_audio_adpcm2xlaw (adpcm_state *s, int fmt, unsigned char *in, - unsigned char *out, int len) +isdn_audio_adpcm2xlaw(adpcm_state * s, int fmt, unsigned char *in, + unsigned char *out, int len) { - int a = s->a; - int d = s->d; - int nbits = s->nbits; - int olen = 0; - - while (len) { - int e = isdn_audio_get_bits(s, &in, &len); - int sign; - - if (nbits == 4 && e == 0) - d = 4; - sign = (e >> (nbits-1))?-1:1; - e &= bitmask[nbits-1]; - a += sign * ((e << 1) + 1) * d >> 1; - if (d & 1) - a++; - if (fmt) - *out++ = isdn_audio_ulaw_to_alaw[ - isdn_audio_linear2ulaw(a << 2)]; - else - *out++ = isdn_audio_linear2ulaw(a << 2); - olen++; - d = (d * Mx[nbits-2][ e ] + 0x2000) >> 14; - if ( d < 5 ) - d = 5; - } - s->a = a; - s->d = d; - return olen; + int a = s->a; + int d = s->d; + int nbits = s->nbits; + int olen = 0; + + while (len) { + int e = isdn_audio_get_bits(s, &in, &len); + int sign; + + if (nbits == 4 && e == 0) + d = 4; + sign = (e >> (nbits - 1)) ? -1 : 1; + e &= bitmask[nbits - 1]; + a += sign * ((e << 1) + 1) * d >> 1; + if (d & 1) + a++; + if (fmt) + *out++ = isdn_audio_ulaw_to_alaw[ + isdn_audio_linear2ulaw(a << 2)]; + else + *out++ = isdn_audio_linear2ulaw(a << 2); + olen++; + d = (d * Mx[nbits - 2][e] + 0x2000) >> 14; + if (d < 5) + d = 5; + } + s->a = a; + s->d = d; + return olen; } int -isdn_audio_2adpcm_flush (adpcm_state *s, unsigned char *out) +isdn_audio_2adpcm_flush(adpcm_state * s, unsigned char *out) { int olen = 0; - if (s->nleft) - isdn_audio_put_bits(0, 8-s->nleft, s, &out, &olen); - return olen; + if (s->nleft) + isdn_audio_put_bits(0, 8 - s->nleft, s, &out, &olen); + return olen; } int -isdn_audio_xlaw2adpcm (adpcm_state *s, int fmt, unsigned char *in, - unsigned char *out, int len) +isdn_audio_xlaw2adpcm(adpcm_state * s, int fmt, unsigned char *in, + unsigned char *out, int len) { - int a = s->a; - int d = s->d; - int nbits = s->nbits; - int olen = 0; - - while (len--) { - int e = 0, nmax = 1 << (nbits - 1); - int sign, delta; - - if (fmt) - delta = (isdn_audio_alaw_to_s16[*in++] >> 2) - a; - else - delta = (isdn_audio_ulaw_to_s16[*in++] >> 2) - a; - if (delta < 0) { - e = nmax; - delta = -delta; - } - while( --nmax && delta > d ) { - delta -= d; - e++; - } - if (nbits == 4 && ((e & 0x0f) == 0)) - e = 8; - isdn_audio_put_bits(e, nbits, s, &out, &olen); - sign = (e >> (nbits-1))?-1:1 ; - e &= bitmask[nbits-1]; - - a += sign * ((e << 1) + 1) * d >> 1; - if (d & 1) - a++; - d = (d * Mx[nbits-2][ e ] + 0x2000) >> 14; - if (d < 5) - d=5; - } + int a = s->a; + int d = s->d; + int nbits = s->nbits; + int olen = 0; + + while (len--) { + int e = 0, + nmax = 1 << (nbits - 1); + int sign, + delta; + + if (fmt) + delta = (isdn_audio_alaw_to_s16[*in++] >> 2) - a; + else + delta = (isdn_audio_ulaw_to_s16[*in++] >> 2) - a; + if (delta < 0) { + e = nmax; + delta = -delta; + } + while (--nmax && delta > d) { + delta -= d; + e++; + } + if (nbits == 4 && ((e & 0x0f) == 0)) + e = 8; + isdn_audio_put_bits(e, nbits, s, &out, &olen); + sign = (e >> (nbits - 1)) ? -1 : 1; + e &= bitmask[nbits - 1]; + + a += sign * ((e << 1) + 1) * d >> 1; + if (d & 1) + a++; + d = (d * Mx[nbits - 2][e] + 0x2000) >> 14; + if (d < 5) + d = 5; + } s->a = a; s->d = d; - return olen; + return olen; } /* @@ -488,99 +513,109 @@ * Result is stored into an sk_buff and queued up for later * evaluation. */ -void -isdn_audio_goertzel(int *sample, modem_info *info) { - int sk, sk1, sk2; - int k, n; - struct sk_buff *skb; - int *result; - - skb = dev_alloc_skb(sizeof(int) * NCOEFF); - if (!skb) { - printk(KERN_WARNING - "isdn_audio: Could not alloc DTMF result for ttyI%d\n", - info->line); - return; - } - result = (int *)skb_put(skb, sizeof(int) * NCOEFF); - skb->free = 1; - skb->users = 0; - for (k = 0; k < NCOEFF; k++) { - sk = sk1 = sk2 = 0; - for (n = 0; n < DTMF_NPOINTS; n++) { - sk = sample[n] + ((cos2pik[k] * sk1) >> 15) - sk2; - sk2 = sk1; - sk1 = sk; - } - result[k] = - ((sk * sk) >> AMP_BITS) - - ((((cos2pik[k] * sk) >> 15) * sk2) >> AMP_BITS) + - ((sk2 * sk2) >> AMP_BITS); - } - skb_queue_tail(&info->dtmf_queue, skb); - isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); +static void +isdn_audio_goertzel(int *sample, modem_info * info) +{ + int sk, + sk1, + sk2; + int k, + n; + struct sk_buff *skb; + int *result; + + skb = dev_alloc_skb(sizeof(int) * NCOEFF); + if (!skb) { + printk(KERN_WARNING + "isdn_audio: Could not alloc DTMF result for ttyI%d\n", + info->line); + return; + } + SET_SKB_FREE(skb); + result = (int *) skb_put(skb, sizeof(int) * NCOEFF); + for (k = 0; k < NCOEFF; k++) { + sk = sk1 = sk2 = 0; + for (n = 0; n < DTMF_NPOINTS; n++) { + sk = sample[n] + ((cos2pik[k] * sk1) >> 15) - sk2; + sk2 = sk1; + sk1 = sk; + } + result[k] = + ((sk * sk) >> AMP_BITS) - + ((((cos2pik[k] * sk) >> 15) * sk2) >> AMP_BITS) + + ((sk2 * sk2) >> AMP_BITS); + } + skb_queue_tail(&info->dtmf_queue, skb); + isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); } void -isdn_audio_eval_dtmf(modem_info *info) +isdn_audio_eval_dtmf(modem_info * info) { - struct sk_buff *skb; - int *result; - dtmf_state *s; - int silence; - int i; - int di; - int ch; - unsigned long flags; - int grp[2]; - char what; - char *p; - - while ((skb = skb_dequeue(&info->dtmf_queue))) { - result = (int *)skb->data; - s = info->dtmf_state; - grp[LOGRP] = grp[HIGRP] = -2; - silence = 0; - for(i = 0; i < 8; i++) { - if ((result[dtmf_tones[i].k] > DTMF_TRESH) && - (result[dtmf_tones[i].k2] < H2_TRESH) ) - grp[dtmf_tones[i].grp] = (grp[dtmf_tones[i].grp] == -2)?i:-1; - else - if ((result[dtmf_tones[i].k] < SILENCE_TRESH) && - (result[dtmf_tones[i].k2] < SILENCE_TRESH) ) - silence++; - } - if(silence == 8) - what = ' '; - else { - if((grp[LOGRP] >= 0) && (grp[HIGRP] >= 0)) { - what = dtmf_matrix[grp[LOGRP]][grp[HIGRP] - 4]; - if(s->last != ' ' && s->last != '.') - s->last = what; /* min. 1 non-DTMF between DTMF */ - } else - what = '.'; - } - if ((what != s->last) && (what != ' ') && (what != '.')) { - printk(KERN_DEBUG "dtmf: tt='%c'\n", what); - p = skb->data; - *p++ = 0x10; - *p = what; - skb_trim(skb, 2); - 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]); - } else - kfree_skb(skb, FREE_READ); - s->last = what; - } + struct sk_buff *skb; + int *result; + dtmf_state *s; + int silence; + int i; + int di; + int ch; + unsigned long flags; + int grp[2]; + char what; + char *p; + + while ((skb = skb_dequeue(&info->dtmf_queue))) { + result = (int *) skb->data; + s = info->dtmf_state; + grp[LOGRP] = grp[HIGRP] = -2; + silence = 0; + for (i = 0; i < 8; i++) { + if ((result[dtmf_tones[i].k] > DTMF_TRESH) && + (result[dtmf_tones[i].k2] < H2_TRESH)) + grp[dtmf_tones[i].grp] = (grp[dtmf_tones[i].grp] == -2) ? i : -1; + else if ((result[dtmf_tones[i].k] < SILENCE_TRESH) && + (result[dtmf_tones[i].k2] < SILENCE_TRESH)) + silence++; + } + if (silence == 8) + what = ' '; + else { + if ((grp[LOGRP] >= 0) && (grp[HIGRP] >= 0)) { + what = dtmf_matrix[grp[LOGRP]][grp[HIGRP] - 4]; + if (s->last != ' ' && s->last != '.') + s->last = what; /* min. 1 non-DTMF between DTMF */ + } else + what = '.'; + } + if ((what != s->last) && (what != ' ') && (what != '.')) { + printk(KERN_DEBUG "dtmf: tt='%c'\n", what); + p = skb->data; + *p++ = 0x10; + *p = what; + skb_trim(skb, 2); + 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]); + } else + kfree_skb(skb, FREE_READ); + s->last = what; + } } /* @@ -593,30 +628,28 @@ * fmt = audio data format (0 = ulaw, 1 = alaw) */ void -isdn_audio_calc_dtmf(modem_info *info, unsigned char *buf, int len, int fmt) +isdn_audio_calc_dtmf(modem_info * info, unsigned char *buf, int len, int fmt) { - dtmf_state *s = info->dtmf_state; - int i; - int c; - - while (len) { - c = MIN(len, (DTMF_NPOINTS - s->idx)); - if (c <= 0) - break; - for (i = 0; i < c; i++) { - if (fmt) - s->buf[s->idx++] = - isdn_audio_alaw_to_s16[*buf++] >> (15 - AMP_BITS); - else - s->buf[s->idx++] = - isdn_audio_ulaw_to_s16[*buf++] >> (15 - AMP_BITS); - } - if (s->idx == DTMF_NPOINTS) { - isdn_audio_goertzel(s->buf, info); - s->idx = 0; - } - len -= c; - } + dtmf_state *s = info->dtmf_state; + int i; + int c; + + while (len) { + c = MIN(len, (DTMF_NPOINTS - s->idx)); + if (c <= 0) + break; + for (i = 0; i < c; i++) { + if (fmt) + s->buf[s->idx++] = + isdn_audio_alaw_to_s16[*buf++] >> (15 - AMP_BITS); + else + s->buf[s->idx++] = + isdn_audio_ulaw_to_s16[*buf++] >> (15 - AMP_BITS); + } + if (s->idx == DTMF_NPOINTS) { + isdn_audio_goertzel(s->buf, info); + s->idx = 0; + } + len -= c; + } } - - diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_audio.h linux/drivers/isdn/isdn_audio.h --- v2.0.30/linux/drivers/isdn/isdn_audio.h Fri Jun 7 06:02:40 1996 +++ linux/drivers/isdn/isdn_audio.h Mon Aug 4 17:34:00 1997 @@ -1,9 +1,9 @@ -/* $Id: isdn_audio.h,v 1.4 1996/06/06 14:43:32 fritz Exp $ - * +/* $Id: isdn_audio.h,v 1.5 1997/02/03 22:45:21 fritz Exp $ + * Linux ISDN subsystem, audio conversion and compression (linklevel). * * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.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) @@ -16,9 +16,12 @@ * * 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. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_audio.h,v $ + * Revision 1.5 1997/02/03 22:45:21 fritz + * Reformatted according CodingStyle + * * Revision 1.4 1996/06/06 14:43:32 fritz * Changed to support DTMF decoding on audio playback also. * @@ -33,27 +36,27 @@ * */ -#define DTMF_NPOINTS 205 /* Number of samples for DTMF recognition */ +#define DTMF_NPOINTS 205 /* Number of samples for DTMF recognition */ typedef struct adpcm_state { - int a; - int d; - int word; - int nleft; - int nbits; + int a; + int d; + int word; + int nleft; + int nbits; } adpcm_state; typedef struct dtmf_state { - char last; - int idx; - int buf[DTMF_NPOINTS]; + char last; + int idx; + int buf[DTMF_NPOINTS]; } dtmf_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); -extern int isdn_audio_adpcm2xlaw(adpcm_state *, int, unsigned char *, unsigned char *, int); -extern int isdn_audio_xlaw2adpcm(adpcm_state *, int, unsigned char *, unsigned char *, int); -extern int isdn_audio_2adpcm_flush(adpcm_state *s, unsigned char *out); +extern int isdn_audio_adpcm2xlaw(adpcm_state *, int, unsigned char *, unsigned char *, int); +extern int isdn_audio_xlaw2adpcm(adpcm_state *, int, unsigned char *, unsigned char *, int); +extern int isdn_audio_2adpcm_flush(adpcm_state * s, unsigned char *out); 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 *); diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_cards.c linux/drivers/isdn/isdn_cards.c --- v2.0.30/linux/drivers/isdn/isdn_cards.c Tue Nov 12 22:36:19 1996 +++ linux/drivers/isdn/isdn_cards.c Mon Aug 4 17:34:00 1997 @@ -1,9 +1,9 @@ -/* $Id: isdn_cards.c,v 1.2 1996/10/13 19:52:17 keil Exp $ - * +/* $Id: isdn_cards.c,v 1.6 1997/04/23 18:56:03 fritz Exp $ + * Linux ISDN subsystem, initialization for non-modularized drivers. * * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.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) @@ -16,9 +16,21 @@ * * 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. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_cards.c,v $ + * Revision 1.6 1997/04/23 18:56:03 fritz + * Old Teles driver removed, Changed doc and scripts accordingly. + * + * Revision 1.5 1997/03/30 17:10:36 calle + * added support for AVM-B1-PCI card. + * + * Revision 1.4 1997/03/04 21:59:44 calle + * Added AVM-B1-CAPI2.0 driver + * + * Revision 1.3 1997/02/03 23:31:14 fritz + * Reformatted according CodingStyle + * * Revision 1.2 1996/10/13 19:52:17 keil * HiSax support * @@ -33,10 +45,6 @@ extern void icn_init(void); #endif -#ifdef CONFIG_ISDN_DRV_TELES -extern void teles_init(void); -#endif - #ifdef CONFIG_ISDN_DRV_HISAX extern void HiSax_init(void); #endif @@ -45,19 +53,33 @@ extern void pcbit_init(void); #endif -void isdn_cards_init(void) +#ifdef CONFIG_ISDN_DRV_AVMB1 +extern void avmb1_init(void); +extern void capi_init(void); +extern void capidrv_init(void); +#ifdef CONFIG_PCI +extern int b1pci_init(void); +#endif +#endif + +void +isdn_cards_init(void) { #if CONFIG_ISDN_DRV_ICN - icn_init(); -#endif -#if CONFIG_ISDN_DRV_TELES - teles_init(); + icn_init(); #endif #ifdef CONFIG_ISDN_DRV_HISAX HiSax_init(); #endif #if CONFIG_ISDN_DRV_PCBIT - pcbit_init(); + pcbit_init(); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1 + avmb1_init(); +#ifdef CONFIG_PCI + b1pci_init(); +#endif + capi_init(); + capidrv_init(); #endif } - diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_cards.h linux/drivers/isdn/isdn_cards.h --- v2.0.30/linux/drivers/isdn/isdn_cards.h Sun Apr 21 01:56:14 1996 +++ linux/drivers/isdn/isdn_cards.h Mon Aug 4 17:34:00 1997 @@ -1,9 +1,9 @@ -/* $Id: isdn_cards.h,v 1.1 1996/04/20 16:04:03 fritz Exp $ - * +/* $Id: isdn_cards.h,v 1.2 1997/02/03 23:31:55 fritz Exp $ + * Linux ISDN subsystem, initialization for non-modularized drivers. * * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.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) @@ -16,13 +16,15 @@ * * 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. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_cards.h,v $ + * Revision 1.2 1997/02/03 23:31:55 fritz + * Reformatted according CodingStyle + * * Revision 1.1 1996/04/20 16:04:03 fritz * Initial revision * */ extern void isdn_cards_init(void); - diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c --- v2.0.30/linux/drivers/isdn/isdn_common.c Tue Nov 12 22:36:19 1996 +++ linux/drivers/isdn/isdn_common.c Tue Aug 5 11:05:16 1997 @@ -1,11 +1,11 @@ -/* $Id: isdn_common.c,v 1.28 1996/11/13 02:33:19 fritz Exp $ - * +/* $Id: isdn_common.c,v 1.44 1997/05/27 15:17:23 fritz Exp $ + * Linux ISDN subsystem, common used functions (linklevel). * * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) * Copyright 1995,96 Thinking Objects Software GmbH Wuerzburg * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.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) @@ -18,9 +18,72 @@ * * 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. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.c,v $ + * Revision 1.44 1997/05/27 15:17:23 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.43 1997/03/31 14:09:43 fritz + * Fixed memory leak in isdn_close(). + * + * Revision 1.42 1997/03/30 16:51:08 calle + * changed calls to copy_from_user/copy_to_user and removed verify_area + * were possible. + * + * Revision 1.41 1997/03/24 22:54:41 fritz + * Some small fixes in debug code. + * + * Revision 1.40 1997/03/08 08:13:51 fritz + * Bugfix: IIOCSETMAP (Set mapping) was broken. + * + * Revision 1.39 1997/03/07 01:32:54 fritz + * Added proper ifdef's for CONFIG_ISDN_AUDIO + * + * Revision 1.38 1997/03/05 21:15:02 fritz + * Fix: reduced stack usage of isdn_ioctl() and isdn_set_allcfg() + * + * Revision 1.37 1997/03/02 14:29:18 fritz + * More ttyI related cleanup. + * + * Revision 1.36 1997/02/28 02:32:40 fritz + * Cleanup: Moved some tty related stuff from isdn_common.c + * to isdn_tty.c + * Bugfix: Bisync protocol did not behave like documented. + * + * Revision 1.35 1997/02/21 13:01:19 fritz + * Changes CAUSE message output in kernel log. + * + * Revision 1.34 1997/02/10 20:12:43 fritz + * Changed interface for reporting incoming calls. + * + * Revision 1.33 1997/02/10 10:05:42 fritz + * More changes for Kernel 2.1.X + * Symbol information moved to isdn_syms.c + * + * Revision 1.32 1997/02/03 22:55:26 fritz + * Reformatted according CodingStyle. + * Changed isdn_writebuf_stub static. + * Slow down tty-RING counter. + * skb->free stuff replaced by macro. + * Bugfix in audio-skb locking. + * Bugfix in HL-driver locking. + * + * Revision 1.31 1997/01/17 01:19:18 fritz + * Applied chargeint patch. + * + * Revision 1.30 1997/01/14 01:27:47 fritz + * Changed audio receive not to rely on skb->users and skb->lock. + * Added ATI2 and related variables. + * Started adding full-duplex audio capability. + * + * Revision 1.29 1997/01/12 23:33:03 fritz + * Made isdn_all_eaz foolproof. + * * Revision 1.28 1996/11/13 02:33:19 fritz * Fixed a race condition. * @@ -134,11 +197,13 @@ */ #include +#define __NO_VERSION__ #include #include -#ifndef __GENKSYMS__ /* Don't want genksyms report unneeded structs */ -#include +#if (LINUX_VERSION_CODE >= 0x020117) +#include #endif +#include #include "isdn_common.h" #include "isdn_tty.h" #include "isdn_net.h" @@ -149,13 +214,11 @@ #include "isdn_cards.h" /* Debugflags */ -#undef ISDN_DEBUG_STATCALLB -#define NEW_ISDN_TIMER_CTRL +#undef ISDN_DEBUG_STATCALLB isdn_dev *dev = (isdn_dev *) 0; -static int has_exported = 0; -static char *isdn_revision = "$Revision: 1.28 $"; +static char *isdn_revision = "$Revision: 1.44 $"; extern char *isdn_net_revision; extern char *isdn_tty_revision; @@ -170,18 +233,23 @@ static char *isdn_audio_revision = ": none $"; #endif -void isdn_MOD_INC_USE_COUNT(void) +static int isdn_writebuf_stub(int, int, const u_char *, int, int); + +void +isdn_MOD_INC_USE_COUNT(void) { MOD_INC_USE_COUNT; } -void isdn_MOD_DEC_USE_COUNT(void) +void +isdn_MOD_DEC_USE_COUNT(void) { MOD_DEC_USE_COUNT; } #if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) -void isdn_dumppkt(char *s, u_char * p, int len, int dumplen) +void +isdn_dumppkt(char *s, u_char * p, int len, int dumplen) { int dumpc; @@ -192,26 +260,29 @@ } #endif -static __inline void isdn_trash_skb(struct sk_buff *skb, int rw) +static __inline void +isdn_trash_skb(struct sk_buff *skb, int rw) { - skb->free = 1; - kfree_skb(skb, rw); + SET_SKB_FREE(skb); + kfree_skb(skb, rw); } -static void isdn_free_queue(struct sk_buff_head *queue) +static void +isdn_free_queue(struct sk_buff_head *queue) { - struct sk_buff *skb; - unsigned long flags; + struct sk_buff *skb; + unsigned long flags; - save_flags(flags); - cli(); - if (skb_queue_len(queue)) - while ((skb = skb_dequeue(queue))) - isdn_trash_skb(skb, FREE_READ); - restore_flags(flags); + save_flags(flags); + cli(); + if (skb_queue_len(queue)) + while ((skb = skb_dequeue(queue))) + isdn_trash_skb(skb, FREE_READ); + restore_flags(flags); } -int isdn_dc2minor(int di, int ch) +int +isdn_dc2minor(int di, int ch) { int i; for (i = 0; i < ISDN_MAX_CHANNELS; i++) @@ -222,8 +293,10 @@ static int isdn_timer_cnt1 = 0; static int isdn_timer_cnt2 = 0; +static int isdn_timer_cnt3 = 0; -static void isdn_timer_funct(ulong dummy) +static void +isdn_timer_funct(ulong dummy) { int tf = dev->tflags; @@ -241,34 +314,35 @@ if (tf & ISDN_TIMER_NETDIAL) isdn_net_dial(); } - if (++isdn_timer_cnt2 >= ISDN_TIMER_1SEC) { - isdn_timer_cnt2 = 0; - if (tf & ISDN_TIMER_NETHANGUP) - isdn_net_autohup(); - if (tf & ISDN_TIMER_MODEMRING) - isdn_tty_modem_ring(); + if (++isdn_timer_cnt2 >= ISDN_TIMER_1SEC) { + isdn_timer_cnt2 = 0; + if (tf & ISDN_TIMER_NETHANGUP) + isdn_net_autohup(); + if (++isdn_timer_cnt3 > ISDN_TIMER_RINGING) { + isdn_timer_cnt3 = 0; + if (tf & ISDN_TIMER_MODEMRING) + isdn_tty_modem_ring(); + } #if (defined CONFIG_ISDN_PPP) && (defined CONFIG_ISDN_MPP) - if (tf & ISDN_TIMER_IPPP) - isdn_ppp_timer_timeout(); + if (tf & ISDN_TIMER_IPPP) + isdn_ppp_timer_timeout(); #endif - } + } } if (tf) { - int flags; + int flags; save_flags(flags); cli(); - del_timer(&dev->timer); -#ifndef NEW_ISDN_TIMER_CTRL - dev->timer.function = isdn_timer_funct; -#endif + del_timer(&dev->timer); dev->timer.expires = jiffies + ISDN_TIMER_RES; add_timer(&dev->timer); restore_flags(flags); } } -void isdn_timer_ctrl(int tf, int onoff) +void +isdn_timer_ctrl(int tf, int onoff) { int flags; @@ -283,447 +357,287 @@ dev->tflags |= tf; else dev->tflags &= ~tf; -#ifdef NEW_ISDN_TIMER_CTRL if (dev->tflags) { - if (!del_timer(&dev->timer)) /* del_timer is 1, when active */ - dev->timer.expires = jiffies + ISDN_TIMER_RES; + if (!del_timer(&dev->timer)) /* del_timer is 1, when active */ + dev->timer.expires = jiffies + ISDN_TIMER_RES; add_timer(&dev->timer); } -#else - if (dev->tflags) { - del_timer(&dev->timer); - dev->timer.function = isdn_timer_funct; - dev->timer.expires = jiffies + ISDN_TIMER_RES; - add_timer(&dev->timer); - } -#endif restore_flags(flags); } /* * Receive a packet from B-Channel. (Called from low-level-module) */ -static void isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb) +static void +isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb) { - ulong flags; int i; - int midx; -#ifdef CONFIG_ISDN_AUDIO - int ifmt; -#endif - modem_info *info; - - if ((i = isdn_dc2minor(di,channel))==-1) { - isdn_trash_skb(skb, FREE_READ); - return; - } + + if ((i = isdn_dc2minor(di, channel)) == -1) { + isdn_trash_skb(skb, FREE_READ); + return; + } /* Update statistics */ - dev->ibytes[i] += skb->len; + dev->ibytes[i] += skb->len; /* First, try to deliver data to network-device */ if (isdn_net_rcv_skb(i, skb)) return; /* No network-device found, deliver to tty or raw-channel */ - skb->free = 1; + SET_SKB_FREE(skb); if (skb->len) { - if ((midx = dev->m_idx[i])<0) { - /* if midx is invalid, drop packet */ - isdn_trash_skb(skb, FREE_READ); - return; - } - info = &dev->mdm.info[midx]; -#ifdef CONFIG_ISDN_AUDIO - ifmt = 1; - - if (info->vonline) - isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt); -#endif - if ((info->online < 2) && - (!(info->vonline & 1))) { - /* If Modem not listening, drop data */ - isdn_trash_skb(skb, FREE_READ); - return; - } - if (info->emu.mdmreg[13] & 2) - /* T.70 decoding: Simply throw away the T.70 header (4 bytes) */ - if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1))) - skb_pull(skb,4); - /* The users field of an sk_buff is used in a special way - * with tty's incoming data: - * users is set to the number of DLE codes when in audio mode. - */ - skb->users = 0; -#ifdef CONFIG_ISDN_AUDIO - if (info->vonline & 1) { - /* voice conversion/compression */ - switch (info->emu.vpar[3]) { - case 2: - case 3: - case 4: - /* adpcm - * Since compressed data takes less - * space, we can overwrite the buffer. - */ - skb_trim(skb,isdn_audio_xlaw2adpcm(info->adpcmr, - ifmt, - skb->data, - skb->data, - skb->len)); - break; - case 5: - /* a-law */ - if (!ifmt) - isdn_audio_ulaw2alaw(skb->data,skb->len); - break; - case 6: - /* u-law */ - if (ifmt) - isdn_audio_alaw2ulaw(skb->data,skb->len); - break; - } - skb->users = isdn_tty_countDLE(skb->data,skb->len); - } -#endif - /* Try to deliver directly via tty-flip-buf if queue is empty */ - save_flags(flags); - cli(); - if (skb_queue_empty(&dev->drv[di]->rpqueue[channel])) - if (isdn_tty_try_read(info, skb)) { - restore_flags(flags); - return; - } - /* Direct deliver failed or queue wasn't empty. - * Queue up for later dequeueing via timer-irq. - */ - __skb_queue_tail(&dev->drv[di]->rpqueue[channel], skb); - dev->drv[di]->rcvcount[channel] += (skb->len + skb->users); - 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[channel]); + if (isdn_tty_rcv_skb(i, di, channel, skb)) + return; + wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]); } else - isdn_trash_skb(skb, FREE_READ); + isdn_trash_skb(skb, FREE_READ); } -void isdn_all_eaz(int di, int ch) +void +isdn_all_eaz(int di, int ch) { isdn_ctrl cmd; + if (di < 0) + return; cmd.driver = di; cmd.arg = ch; cmd.command = ISDN_CMD_SETEAZ; - cmd.num[0] = '\0'; + cmd.parm.num[0] = '\0'; (void) dev->drv[di]->interface->command(&cmd); } -static int isdn_status_callback(isdn_ctrl * c) +static int +isdn_status_callback(isdn_ctrl * c) { int di; - int mi; ulong flags; int i; int r; - int retval=0; - modem_info *info; + int retval = 0; isdn_ctrl cmd; di = c->driver; - i = isdn_dc2minor(di, c->arg); + i = isdn_dc2minor(di, c->arg); switch (c->command) { - case ISDN_STAT_BSENT: - if (i<0) + case ISDN_STAT_BSENT: + if (i < 0) return -1; - if (dev->global_flags & ISDN_GLOBAL_STOPPED) - return 0; - if (isdn_net_stat_callback(i, c->command)) - return 0; - isdn_tty_bsent(di, c->arg); - wake_up_interruptible(&dev->drv[di]->snd_waitq[c->arg]); - break; - case ISDN_STAT_STAVAIL: - save_flags(flags); - cli(); - dev->drv[di]->stavail += c->arg; - restore_flags(flags); - wake_up_interruptible(&dev->drv[di]->st_waitq); - break; - case ISDN_STAT_RUN: - dev->drv[di]->running = 1; - 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; - break; - case ISDN_STAT_ICALL: - if (i<0) + if (dev->global_flags & ISDN_GLOBAL_STOPPED) + return 0; + if (isdn_net_stat_callback(i, c->command)) + return 0; + if (isdn_tty_stat_callback(i, c)) + return 0; + wake_up_interruptible(&dev->drv[di]->snd_waitq[c->arg]); + break; + case ISDN_STAT_STAVAIL: + save_flags(flags); + cli(); + dev->drv[di]->stavail += c->arg; + restore_flags(flags); + wake_up_interruptible(&dev->drv[di]->st_waitq); + break; + case ISDN_STAT_RUN: + dev->drv[di]->running = 1; + 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; + break; + case ISDN_STAT_ICALL: + if (i < 0) return -1; #ifdef ISDN_DEBUG_STATCALLB - printk(KERN_DEBUG "ICALL (net): %d %ld %s\n", di, c->arg, c->num); + printk(KERN_DEBUG "ICALL (net): %d %ld %s\n", di, c->arg, c->parm.num); #endif - if (dev->global_flags & ISDN_GLOBAL_STOPPED) { - cmd.driver = di; - cmd.arg = c->arg; - cmd.command = ISDN_CMD_HANGUP; - dev->drv[di]->interface->command(&cmd); - return 0; - } - + if (dev->global_flags & ISDN_GLOBAL_STOPPED) { + cmd.driver = di; + cmd.arg = c->arg; + cmd.command = ISDN_CMD_HANGUP; + dev->drv[di]->interface->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->num); + 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); switch (r) { - case 0: - /* No network-device replies. Schedule RING-message to - * tty and set RI-bit of modem-status. - */ - if ((mi = isdn_tty_find_icall(di, c->arg, c->num)) >= 0) { - info = &dev->mdm.info[mi]; - info->msr |= UART_MSR_RI; - isdn_tty_modem_result(2, info); - isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1); - return 1; - } else if (dev->drv[di]->reject_bus) { - cmd.driver = di; - cmd.arg = c->arg; - cmd.command = ISDN_CMD_HANGUP; - dev->drv[di]->interface->command(&cmd); - retval=2; - } - break; - case 1: - /* Schedule connection-setup */ - isdn_net_dial(); - cmd.driver = di; - cmd.arg = c->arg; - cmd.command = ISDN_CMD_ACCEPTD; - dev->drv[di]->interface->command(&cmd); - return 1; - break; - case 2: /* For calling back, first reject incoming call ... */ - case 3: /* Interface found, but down, reject call actively */ - retval=2; - printk(KERN_INFO "isdn: Rejecting Call\n"); - cmd.driver = di; - cmd.arg = c->arg; - cmd.command = ISDN_CMD_HANGUP; - dev->drv[di]->interface->command(&cmd); - if (r == 3) - break; - /* Fall through */ - case 4: - /* ... then start callback. */ - isdn_net_dial(); - return 2; + case 0: + /* No network-device replies. + * Try ttyI's + */ + if (isdn_tty_find_icall(di, c->arg, c->parm.setup) >= 0) + retval = 1; + else if (dev->drv[di]->reject_bus) { + cmd.driver = di; + cmd.arg = c->arg; + cmd.command = ISDN_CMD_HANGUP; + dev->drv[di]->interface->command(&cmd); + retval = 2; + } + break; + case 1: + /* Schedule connection-setup */ + isdn_net_dial(); + cmd.driver = di; + cmd.arg = c->arg; + cmd.command = ISDN_CMD_ACCEPTD; + dev->drv[di]->interface->command(&cmd); + retval = 1; + break; + case 2: /* For calling back, first reject incoming call ... */ + case 3: /* Interface found, but down, reject call actively */ + retval = 2; + printk(KERN_INFO "isdn: Rejecting Call\n"); + cmd.driver = di; + cmd.arg = c->arg; + cmd.command = ISDN_CMD_HANGUP; + dev->drv[di]->interface->command(&cmd); + if (r == 3) + break; + /* Fall through */ + case 4: + /* ... then start callback. */ + isdn_net_dial(); + break; } - 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: - if (i<0) + 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: + if (i < 0) return -1; #ifdef ISDN_DEBUG_STATCALLB - printk(KERN_DEBUG "CINF: %ld %s\n", c->arg, c->num); + printk(KERN_DEBUG "CINF: %ld %s\n", c->arg, c->parm.num); #endif - if (dev->global_flags & ISDN_GLOBAL_STOPPED) - return 0; - if (strcmp(c->num, "0")) - isdn_net_stat_callback(i, c->command); - break; - case ISDN_STAT_CAUSE: + if (dev->global_flags & ISDN_GLOBAL_STOPPED) + return 0; + if (strcmp(c->parm.num, "0")) + isdn_net_stat_callback(i, c->command); + break; + case ISDN_STAT_CAUSE: #ifdef ISDN_DEBUG_STATCALLB - printk(KERN_DEBUG "CAUSE: %ld %s\n", c->arg, c->num); + printk(KERN_DEBUG "CAUSE: %ld %s\n", c->arg, c->parm.num); #endif - printk(KERN_INFO "isdn: cause: %s\n", c->num); - break; - case ISDN_STAT_DCONN: - if (i<0) + printk(KERN_INFO "isdn: %s,ch%ld cause: %s\n", + dev->drvid[di], c->arg, c->parm.num); + isdn_tty_stat_callback(i, c); + break; + case ISDN_STAT_DCONN: + if (i < 0) return -1; #ifdef ISDN_DEBUG_STATCALLB - printk(KERN_DEBUG "DCONN: %ld\n", c->arg); + printk(KERN_DEBUG "DCONN: %ld\n", c->arg); #endif - if (dev->global_flags & ISDN_GLOBAL_STOPPED) - return 0; - /* Find any network-device, waiting for D-channel setup */ - if (isdn_net_stat_callback(i, c->command)) - break; - - if ((mi = dev->m_idx[i]) >= 0) { - /* If any tty has just dialed-out, setup B-Channel */ - info = &dev->mdm.info[mi]; - if (info->flags & - (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) { - if (info->dialing == 1) { - info->dialing = 2; - cmd.driver = di; - cmd.arg = c->arg; - cmd.command = ISDN_CMD_ACCEPTB; - dev->drv[di]->interface->command(&cmd); - return 0; - } - } - } - break; - case ISDN_STAT_DHUP: - if (i<0) + 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)) + 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); + break; + } + break; + case ISDN_STAT_DHUP: + if (i < 0) return -1; #ifdef ISDN_DEBUG_STATCALLB - printk(KERN_DEBUG "DHUP: %ld\n", c->arg); -#endif - if (dev->global_flags & ISDN_GLOBAL_STOPPED) - return 0; - dev->drv[di]->flags &= ~(1 << (c->arg)); - isdn_info_update(); - /* Signal hangup to network-devices */ - if (isdn_net_stat_callback(i, c->command)) - break; - if ((mi = dev->m_idx[i]) >= 0) { - /* Signal hangup to tty-device */ - info = &dev->mdm.info[mi]; - if (info->flags & - (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) { - if (info->dialing == 1) { - info->dialing = 0; - isdn_tty_modem_result(7, info); - } - if (info->online) - isdn_tty_modem_result(3, info); -#ifdef ISDN_DEBUG_MODEM_HUP - printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n"); + printk(KERN_DEBUG "DHUP: %ld\n", c->arg); #endif - isdn_tty_modem_hup(info); - return 0; - } - } - break; - case ISDN_STAT_BCONN: - if (i<0) + if (dev->global_flags & ISDN_GLOBAL_STOPPED) + return 0; + dev->drv[di]->flags &= ~(1 << (c->arg)); + isdn_info_update(); + /* Signal hangup to network-devices */ + if (isdn_net_stat_callback(i, c->command)) + break; + if (isdn_tty_stat_callback(i, c)) + break; + break; + case ISDN_STAT_BCONN: + if (i < 0) return -1; #ifdef ISDN_DEBUG_STATCALLB - printk(KERN_DEBUG "BCONN: %ld\n", c->arg); + printk(KERN_DEBUG "BCONN: %ld\n", c->arg); #endif - /* Signal B-channel-connect to network-devices */ - if (dev->global_flags & ISDN_GLOBAL_STOPPED) - return 0; - dev->drv[di]->flags |= (1 << (c->arg)); - isdn_info_update(); - if (isdn_net_stat_callback(i, c->command)) - break; - if ((mi = dev->m_idx[i]) >= 0) { - /* Schedule CONNECT-Message to any tty, waiting for it and - * set DCD-bit of its modem-status. - */ - info = &dev->mdm.info[mi]; - if (info->flags & - (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) { - info->msr |= UART_MSR_DCD; - if (info->dialing) - info->dialing = 0; - info->rcvsched = 1; - if (USG_MODEM(dev->usage[i])) - isdn_tty_modem_result(5, info); - if (USG_VOICE(dev->usage[i])) - isdn_tty_modem_result(11, info); - } - } - break; - case ISDN_STAT_BHUP: - if (i<0) + /* Signal B-channel-connect to network-devices */ + if (dev->global_flags & ISDN_GLOBAL_STOPPED) + return 0; + dev->drv[di]->flags |= (1 << (c->arg)); + isdn_info_update(); + if (isdn_net_stat_callback(i, c->command)) + break; + if (isdn_tty_stat_callback(i, c)) + break; + break; + case ISDN_STAT_BHUP: + if (i < 0) return -1; #ifdef ISDN_DEBUG_STATCALLB - printk(KERN_DEBUG "BHUP: %ld\n", c->arg); -#endif - if (dev->global_flags & ISDN_GLOBAL_STOPPED) - return 0; - dev->drv[di]->flags &= ~(1 << (c->arg)); - isdn_info_update(); - if ((mi = dev->m_idx[i]) >= 0) { - /* Signal hangup to tty-device, schedule NO CARRIER-message */ - info = &dev->mdm.info[mi]; - if (info->flags & - (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) { - if (info->msr & UART_MSR_DCD) - isdn_tty_modem_result(3, info); - info->msr &= ~(UART_MSR_DCD | UART_MSR_RI); -#ifdef ISDN_DEBUG_MODEM_HUP - printk(KERN_DEBUG "Mhup in ISDN_STAT_BHUP\n"); + printk(KERN_DEBUG "BHUP: %ld\n", c->arg); #endif - isdn_tty_modem_hup(info); - } - } - break; - case ISDN_STAT_NODCH: - if (i<0) + if (dev->global_flags & ISDN_GLOBAL_STOPPED) + return 0; + dev->drv[di]->flags &= ~(1 << (c->arg)); + isdn_info_update(); + if (isdn_tty_stat_callback(i, c)) + break; + break; + case ISDN_STAT_NODCH: + if (i < 0) return -1; #ifdef ISDN_DEBUG_STATCALLB - printk(KERN_DEBUG "NODCH: %ld\n", c->arg); + printk(KERN_DEBUG "NODCH: %ld\n", c->arg); #endif - if (dev->global_flags & ISDN_GLOBAL_STOPPED) - return 0; - if (isdn_net_stat_callback(i, c->command)) - break; - if ((mi = dev->m_idx[i]) >= 0) { - info = &dev->mdm.info[mi]; - if (info->flags & - (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) { - if (info->dialing) { - info->dialing = 0; - isdn_tty_modem_result(6, info); - } - info->msr &= ~UART_MSR_DCD; - if (info->online) { - isdn_tty_modem_result(3, info); - info->online = 0; - } + if (dev->global_flags & ISDN_GLOBAL_STOPPED) + return 0; + if (isdn_net_stat_callback(i, c->command)) + break; + if (isdn_tty_stat_callback(i, c)) + break; + break; + case ISDN_STAT_ADDCH: + break; + case ISDN_STAT_UNLOAD: + save_flags(flags); + cli(); + isdn_tty_stat_callback(i, c); + for (i = 0; i < ISDN_MAX_CHANNELS; i++) + if (dev->drvmap[i] == di) { + dev->drvmap[i] = -1; + dev->chanmap[i] = -1; } - } - break; - case ISDN_STAT_ADDCH: - break; - case ISDN_STAT_UNLOAD: - save_flags(flags); - cli(); - for (i = 0; i < ISDN_MAX_CHANNELS; i++) - if (dev->drvmap[i] == di) { - dev->drvmap[i] = -1; - dev->chanmap[i] = -1; - } - for (i = 0; i < ISDN_MAX_CHANNELS; i++) { - modem_info *info = &dev->mdm.info[i]; - - if (info->isdn_driver == di) { - info->isdn_driver = -1; - info->isdn_channel = -1; - if (info->online) { - isdn_tty_modem_result(3, info); - isdn_tty_modem_hup(info); - } - } - } - dev->drivers--; - dev->channels -= dev->drv[di]->channels; - kfree(dev->drv[di]->rcverr); - kfree(dev->drv[di]->rcvcount); - for (i = 0; i < dev->drv[di]->channels; i++) - isdn_free_queue(&dev->drv[di]->rpqueue[i]); - kfree(dev->drv[di]->rpqueue); - kfree(dev->drv[di]->rcv_waitq); - kfree(dev->drv[di]->snd_waitq); - kfree(dev->drv[di]); - dev->drv[di] = NULL; - dev->drvid[di][0] = '\0'; - isdn_info_update(); - restore_flags(flags); - return 0; - default: - return -1; + dev->drivers--; + dev->channels -= dev->drv[di]->channels; + kfree(dev->drv[di]->rcverr); + kfree(dev->drv[di]->rcvcount); + for (i = 0; i < dev->drv[di]->channels; i++) + isdn_free_queue(&dev->drv[di]->rpqueue[i]); + kfree(dev->drv[di]->rpqueue); + kfree(dev->drv[di]->rcv_waitq); + kfree(dev->drv[di]->snd_waitq); + kfree(dev->drv[di]); + dev->drv[di] = NULL; + dev->drvid[di][0] = '\0'; + isdn_info_update(); + restore_flags(flags); + return 0; + default: + return -1; } return 0; } @@ -731,7 +645,8 @@ /* * Get integer from char-pointer, set pointer to end of number */ -int isdn_getnum(char **p) +int +isdn_getnum(char **p) { int v = -1; @@ -746,14 +661,15 @@ * isdn_readbchan() tries to get data from the read-queue. * It MUST be called with interrupts off. */ -int isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, int user) +int +isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, int user) { int left; int count; int count_pull; - int count_put; + int count_put; int dflag; - struct sk_buff *skb; + struct sk_buff *skb; u_char *cp; if (!dev->drv[di]) @@ -768,99 +684,106 @@ cp = buf; count = 0; while (left) { - if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel]))) - break; - if (skb->lock) - break; - skb->lock = 1; - if (skb->users) { - /* users is the count of DLE's in - * this buff when in voice mode. - */ - char *p = skb->data; - unsigned long DLEmask = (1 << channel); - - dflag = 0; - count_pull = count_put = 0; - while ((count_pull < skb->len) && (left-- > 0)) { - if (dev->drv[di]->DLEflag & DLEmask) { - if (user) - put_user(DLE,cp++); - else - *cp++ = DLE; - dev->drv[di]->DLEflag &= ~DLEmask; - } else { - if (user) - put_user(*p,cp++); - else - *cp++ = *p; - if (*p == DLE) { - dev->drv[di]->DLEflag |= DLEmask; - skb->users--; - } - p++; - count_pull++; - } - count_put++; - } - if (count_pull >= skb->len) - dflag = 1; - } else { - /* No DLE's in buff, so simply copy it */ - dflag = 1; - if ((count_pull = skb->len) > left) { - count_pull = left; - dflag = 0; - } - count_put = count_pull; - if (user) - copy_to_user(cp, skb->data, count_put); - else - memcpy(cp, skb->data, count_put); - cp += count_put; - left -= count_put; - } - count += count_put; - if (fp) { - memset(fp, 0, count_put); - fp += count_put; - } - if (dflag) { - /* We got all the data in this buff. - * Now we can dequeue it. - */ + if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel]))) + break; +#ifdef CONFIG_ISDN_AUDIO + if (ISDN_AUDIO_SKB_LOCK(skb)) + break; + ISDN_AUDIO_SKB_LOCK(skb) = 1; + if (ISDN_AUDIO_SKB_DLECOUNT(skb)) { + char *p = skb->data; + unsigned long DLEmask = (1 << channel); + + dflag = 0; + count_pull = count_put = 0; + while ((count_pull < skb->len) && (left-- > 0)) { + if (dev->drv[di]->DLEflag & DLEmask) { + if (user) + put_user(DLE, cp++); + else + *cp++ = DLE; + dev->drv[di]->DLEflag &= ~DLEmask; + } else { + if (user) + put_user(*p, cp++); + else + *cp++ = *p; + if (*p == DLE) { + dev->drv[di]->DLEflag |= DLEmask; + (ISDN_AUDIO_SKB_DLECOUNT(skb))--; + } + p++; + count_pull++; + } + count_put++; + } + if (count_pull >= skb->len) + dflag = 1; + } else { +#endif + /* No DLE's in buff, so simply copy it */ + dflag = 1; + if ((count_pull = skb->len) > left) { + count_pull = left; + dflag = 0; + } + count_put = count_pull; + if (user) + copy_to_user(cp, skb->data, count_put); + else + memcpy(cp, skb->data, count_put); + cp += count_put; + left -= count_put; +#ifdef CONFIG_ISDN_AUDIO + } +#endif + count += count_put; + if (fp) { + memset(fp, 0, count_put); + fp += count_put; + } + if (dflag) { + /* We got all the data in this buff. + * Now we can dequeue it. + */ if (fp) *(fp - 1) = 0xff; - skb->lock = 0; - skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]); - isdn_trash_skb(skb, FREE_READ); +#ifdef CONFIG_ISDN_AUDIO + ISDN_AUDIO_SKB_LOCK(skb) = 0; +#endif + skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]); + isdn_trash_skb(skb, FREE_READ); } else { - /* Not yet emptied this buff, so it - * must stay in the queue, for further calls - * but we pull off the data we got until now. - */ - skb_pull(skb,count_pull); - skb->lock = 0; - } + /* Not yet emptied this buff, so it + * must stay in the queue, for further calls + * but we pull off the data we got until now. + */ + skb_pull(skb, count_pull); +#ifdef CONFIG_ISDN_AUDIO + ISDN_AUDIO_SKB_LOCK(skb) = 0; +#endif + } dev->drv[di]->rcvcount[channel] -= count_put; } return count; } -static __inline int isdn_minor2drv(int minor) +static __inline int +isdn_minor2drv(int minor) { return (dev->drvmap[minor]); } -static __inline int isdn_minor2chan(int minor) +static __inline int +isdn_minor2chan(int minor) { return (dev->chanmap[minor]); } -#define INF_DV 0x01 /* Data version for /dev/isdninfo */ +#define INF_DV 0x01 /* Data version for /dev/isdninfo */ static char * - isdn_statstr(void) +isdn_statstr(void) { static char istatbuf[2048]; char *p; @@ -913,7 +836,8 @@ /* Module interface-code */ -void isdn_info_update(void) +void +isdn_info_update(void) { infostruct *p = dev->infochain; @@ -924,7 +848,8 @@ wake_up_interruptible(&(dev->info_waitq)); } -static RWTYPE isdn_read(struct inode *inode, struct file *file, char *buf, RWARG count) +static RWTYPE +isdn_read(struct inode *inode, struct file *file, char *buf, RWARG count) { uint minor = MINOR(inode->i_rdev); int len = 0; @@ -935,14 +860,15 @@ if (minor == ISDN_MINOR_STATUS) { char *p; if (!file->private_data) { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; interruptible_sleep_on(&(dev->info_waitq)); - } + } p = isdn_statstr(); file->private_data = 0; if ((len = strlen(p)) <= count) { - copy_to_user(buf, p, len); + if (copy_to_user(buf, p, len)) + return -EFAULT; file->f_pos += len; return len; } @@ -957,11 +883,11 @@ if (!dev->drv[drvidx]->running) return -ENODEV; chidx = isdn_minor2chan(minor); - save_flags(flags); - cli(); + save_flags(flags); + cli(); len = isdn_readbchan(drvidx, chidx, buf, 0, count, 1); file->f_pos += len; - restore_flags(flags); + restore_flags(flags); return len; } if (minor <= ISDN_MINOR_CTRLMAX) { @@ -969,10 +895,10 @@ if (drvidx < 0) return -ENODEV; if (!dev->drv[drvidx]->stavail) { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq)); - } + } if (dev->drv[drvidx]->interface->readstat) len = dev->drv[drvidx]->interface-> readstat(buf, MIN(count, dev->drv[drvidx]->stavail), @@ -981,10 +907,10 @@ len = 0; save_flags(flags); cli(); - if (len) - dev->drv[drvidx]->stavail -= len; - else - dev->drv[drvidx]->stavail = 0; + if (len) + dev->drv[drvidx]->stavail -= len; + else + dev->drv[drvidx]->stavail = 0; restore_flags(flags); file->f_pos += len; return len; @@ -996,12 +922,14 @@ return -ENODEV; } -static LSTYPE isdn_lseek(struct inode *inode, struct file *file, LSARG offset, int orig) +static LSTYPE +isdn_lseek(struct inode *inode, struct file *file, LSARG offset, int orig) { return -ESPIPE; } -static RWTYPE isdn_write(struct inode *inode, struct file *file, const char *buf, RWARG count) +static RWTYPE +isdn_write(struct inode *inode, struct file *file, const char *buf, RWARG count) { uint minor = MINOR(inode->i_rdev); int drvidx; @@ -1045,10 +973,12 @@ return -ENODEV; } -static int isdn_select(struct inode *inode, struct file *file, int type, select_table * st) +#if (LINUX_VERSION_CODE < 0x020117) +static int +isdn_select(struct inode *inode, struct file *file, int type, select_table * st) { uint minor = MINOR(inode->i_rdev); - int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); + int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); if (minor == ISDN_MINOR_STATUS) { if (file->private_data) @@ -1063,49 +993,81 @@ if (drvidx < 0) return -ENODEV; if (dev->drv[drvidx]->stavail) - return 1; - else { - if (st) - select_wait(&(dev->drv[drvidx]->st_waitq), st); - return 0; - } + return 1; + else { + if (st) + select_wait(&(dev->drv[drvidx]->st_waitq), st); + return 0; + } return 1; - } + } #ifdef CONFIG_ISDN_PPP if (minor <= ISDN_MINOR_PPPMAX) return (isdn_ppp_select(minor - ISDN_MINOR_PPP, file, type, st)); #endif return -ENODEV; } +#else +static unsigned int +isdn_poll(struct file *file, poll_table * wait) +{ + unsigned int mask = 0; + unsigned int minor = MINOR(file->f_inode->i_rdev); + int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); + + if (minor == ISDN_MINOR_STATUS) { + poll_wait(&(dev->info_waitq), wait); + /* mask = POLLOUT | POLLWRNORM; */ + if (file->private_data) { + mask |= POLLIN | POLLRDNORM; + } + return mask; + } + if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) { + poll_wait(&(dev->drv[drvidx]->st_waitq), wait); + if (drvidx < 0) { + printk(KERN_ERR "isdn_common: isdn_poll 1 -> what the hell\n"); + return POLLERR; + } + mask = POLLOUT | POLLWRNORM; + if (dev->drv[drvidx]->stavail) { + mask |= POLLIN | POLLRDNORM; + } + return mask; + } +#ifdef CONFIG_ISDN_PPP + if (minor <= ISDN_MINOR_PPPMAX) + return (isdn_ppp_poll(file, wait)); +#endif + printk(KERN_ERR "isdn_common: isdn_poll 2 -> what the hell\n"); + return POLLERR; +} +#endif -static int isdn_set_allcfg(char *src) +static int +isdn_set_allcfg(char *src) { int ret; int i; ulong flags; - char buf[1024]; isdn_net_ioctl_cfg cfg; 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(); - if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(int)))) { - restore_flags(flags); - return ret; - } - copy_from_user((char *) &i, src, sizeof(int)); - src += sizeof(int); + src += sizeof(int); while (i) { - char *c; - char *c2; + int phone_len; + int out_flag; - if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(cfg)))) { + if ((ret = copy_from_user((char *) &cfg, src, sizeof(cfg)))) { restore_flags(flags); return ret; } - copy_from_user((char *) &cfg, src, sizeof(cfg)); src += sizeof(cfg); if (!isdn_net_new(cfg.name, NULL)) { restore_flags(flags); @@ -1115,49 +1077,31 @@ restore_flags(flags); return ret; } - if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(buf)))) { - restore_flags(flags); - return ret; - } - copy_from_user(buf, src, sizeof(buf)); - src += sizeof(buf); - c = buf; - while (*c) { - if ((c2 = strchr(c, ' '))) - *c2++ = '\0'; - strcpy(phone.phone, c); - strcpy(phone.name, cfg.name); - phone.outgoing = 0; - if ((ret = isdn_net_addphone(&phone))) { + phone_len = out_flag = 0; + while (out_flag < 2) { + if ((ret = verify_area(VERIFY_READ, src, 1))) { restore_flags(flags); return ret; } - if (c2) - c = c2; - else - c += strlen(c); - } - if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(buf)))) { - restore_flags(flags); - return ret; - } - copy_from_user(buf, src, sizeof(buf)); - src += sizeof(buf); - c = buf; - while (*c) { - if ((c2 = strchr(c, ' '))) - *c2++ = '\0'; - strcpy(phone.phone, c); - strcpy(phone.name, cfg.name); - phone.outgoing = 1; - if ((ret = isdn_net_addphone(&phone))) { - 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 (c2) - c = c2; - else - c += strlen(c); + if (++phone_len >= sizeof(phone.phone)) + printk(KERN_WARNING + "%s: IIOCSETSET phone number too long, ignored\n", + cfg.name); } i--; } @@ -1165,7 +1109,8 @@ return 0; } -static int isdn_get_allcfg(char *dest) +static int +isdn_get_allcfg(char *dest) { isdn_net_ioctl_cfg cfg; isdn_net_ioctl_phone phone; @@ -1178,7 +1123,7 @@ cli(); p = dev->netdev; while (p) { - if ((ret = verify_area(VERIFY_WRITE, (void *) dest, sizeof(cfg) + 10))) { + if ((ret = verify_area(VERIFY_WRITE, (void *) dest, sizeof(cfg) + 200))) { restore_flags(flags); return ret; } @@ -1196,11 +1141,18 @@ 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 & 4) ? 1 : 0; - cfg.ihup = (p->local.hupflags & 8) ? 1 : 0; - copy_to_user(dest, p->local.name, 10); + 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; - copy_to_user(dest, (char *) &cfg, sizeof(cfg)); + 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; @@ -1216,51 +1168,63 @@ return ret; } else dest += ret; + put_user(0, dest); p = p->next; } restore_flags(flags); return 0; } -static int isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +static int +isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) { uint minor = MINOR(inode->i_rdev); isdn_ctrl c; int drvidx; int chidx; int ret; + int i; + char *p; char *s; - char name[10]; - char bname[21]; - isdn_ioctl_struct iocts; - isdn_net_ioctl_phone phone; - isdn_net_ioctl_cfg cfg; + 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 if (minor == ISDN_MINOR_STATUS) { - switch (cmd) { - case IIOCGETDVR: - return(TTY_DV + - (NET_DV << 8) + - (INF_DV << 16)); - case IIOCGETCPS: - if (arg) { - ulong *p = (ulong *)arg; - int i; - if ((ret = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(ulong)*ISDN_MAX_CHANNELS*2))) - return ret; - for (i = 0;iibytes[i],p++); - put_user(dev->obytes[i],p++); - } - return 0; - } else - return -EINVAL; - break; - default: - return -EINVAL; - } - } + switch (cmd) { + case IIOCGETDVR: + return (TTY_DV + + (NET_DV << 8) + + (INF_DV << 16)); + case IIOCGETCPS: + if (arg) { + ulong *p = (ulong *) arg; + int i; + if ((ret = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(ulong) * ISDN_MAX_CHANNELS * 2))) + return ret; + for (i = 0; i < ISDN_MAX_CHANNELS; i++) { + put_user(dev->ibytes[i], p++); + put_user(dev->obytes[i], p++); + } + return 0; + } else + return -EINVAL; + break; + default: + return -EINVAL; + } + } if (!dev->drivers) return -ENODEV; if (minor < ISDN_MINOR_CTRL) { @@ -1275,336 +1239,329 @@ if (minor <= ISDN_MINOR_CTRLMAX) { switch (cmd) { #ifdef CONFIG_NETDEVICES - case IIOCNETAIF: - /* Add a network-interface */ - if (arg) { - if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name)))) - return ret; - copy_from_user(name, (char *) arg, sizeof(name)); - s = name; - } else - s = NULL; - if ((s = isdn_net_new(s, NULL))) { - if ((ret = verify_area(VERIFY_WRITE, (void *) arg, strlen(s) + 1))) - return ret; - copy_to_user((char *) arg, s, strlen(s) + 1); - return 0; - } else - return -ENODEV; - case IIOCNETASL: - /* Add a slave to a network-interface */ - if (arg) { - if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(bname)))) - return ret; - copy_from_user(bname, (char *) arg, sizeof(bname)); - } else - return -EINVAL; - if ((s = isdn_net_newslave(bname))) { - if ((ret = verify_area(VERIFY_WRITE, (void *) arg, strlen(s) + 1))) - return ret; - copy_to_user((char *) arg, s, strlen(s) + 1); - return 0; - } else - return -ENODEV; - case IIOCNETDIF: - /* Delete a network-interface */ - if (arg) { - if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name)))) - return ret; - copy_from_user(name, (char *) arg, sizeof(name)); - return isdn_net_rm(name); - } else - return -EINVAL; - case IIOCNETSCF: - /* Set configurable parameters of a network-interface */ - if (arg) { - if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(cfg)))) - return ret; - copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)); - return isdn_net_setcfg(&cfg); - } else - return -EINVAL; - case IIOCNETGCF: - /* Get configurable parameters of a network-interface */ - if (arg) { - if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(cfg)))) - return ret; - copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)); - if (!(ret = isdn_net_getcfg(&cfg))) { - if ((ret = verify_area(VERIFY_WRITE, (void *) arg, sizeof(cfg)))) - return ret; - copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg)); - } - return ret; - } else - return -EINVAL; - case IIOCNETANM: - /* Add a phone-number to a network-interface */ - if (arg) { - if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(phone)))) - return ret; - copy_from_user((char *) &phone, (char *) arg, sizeof(phone)); - return isdn_net_addphone(&phone); - } else - return -EINVAL; - case IIOCNETGNM: - /* Get list of phone-numbers of a network-interface */ - if (arg) { - if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(phone)))) - return ret; - copy_from_user((char *) &phone, (char *) arg, sizeof(phone)); - return isdn_net_getphones(&phone, (char *) arg); - } else - return -EINVAL; - case IIOCNETDNM: - /* Delete a phone-number of a network-interface */ - if (arg) { - if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(phone)))) - return ret; - copy_from_user((char *) &phone, (char *) arg, sizeof(phone)); - return isdn_net_delphone(&phone); - } else - return -EINVAL; - case IIOCNETDIL: - /* Force dialing of a network-interface */ - if (arg) { - if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name)))) - return ret; - copy_from_user(name, (char *) arg, sizeof(name)); - return isdn_net_force_dial(name); - } else - return -EINVAL; + case IIOCNETAIF: + /* Add a network-interface */ + if (arg) { + if ((ret = copy_from_user(name, (char *) arg, sizeof(name)))) + 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; + } else + 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; + } else + return -EINVAL; + if ((s = isdn_net_newslave(bname))) { + if ((ret = copy_to_user((char *) arg, s, strlen(s) + 1))) + return ret; + return 0; + } else + 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); + } else + 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); + } else + 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; + if (!(ret = isdn_net_getcfg(&cfg))) { + if ((ret = copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg)))) + return ret; + } + return ret; + } else + 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); + } else + 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); + } else + 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); + } else + 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); + } else + return -EINVAL; #ifdef CONFIG_ISDN_PPP - case IIOCNETALN: - if(arg) { - if ((ret = verify_area(VERIFY_READ, - (void*)arg, - sizeof(name)))) - return ret; - } else - return -EINVAL; - copy_from_user(name,(char*)arg,sizeof(name)); - return isdn_ppp_dial_slave(name); - case IIOCNETDLN: - if(arg) { - if ((ret = verify_area(VERIFY_READ, - (void*)arg, - sizeof(name)))) - return ret; - } else - return -EINVAL; - copy_from_user(name,(char*)arg,sizeof(name)); - return isdn_ppp_hangup_slave(name); -#endif - case IIOCNETHUP: - /* Force hangup of a network-interface */ - if (arg) { - if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name)))) - return ret; - copy_from_user(name, (char *) arg, sizeof(name)); - return isdn_net_force_hangup(name); - } else - return -EINVAL; - break; -#endif /* CONFIG_NETDEVICES */ - case IIOCSETVER: - dev->net_verbose = arg; - printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose); - return 0; - case IIOCSETGST: - if (arg) - dev->global_flags |= ISDN_GLOBAL_STOPPED; - else - dev->global_flags &= ~ISDN_GLOBAL_STOPPED; - printk(KERN_INFO "isdn: Global Mode %s\n", - (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running"); - return 0; - case IIOCSETBRJ: - drvidx = -1; - if (arg) { - int i; - char *p; - if ((ret = verify_area(VERIFY_READ, (void *) arg, - sizeof(isdn_ioctl_struct)))) - return ret; - copy_from_user((char *) &iocts, (char *) arg, - sizeof(isdn_ioctl_struct)); - if (strlen(iocts.drvid)) { - if ((p = strchr(iocts.drvid, ','))) - *p = 0; - drvidx = -1; - for (i = 0; i < ISDN_MAX_DRIVERS; i++) - if (!(strcmp(dev->drvid[i], iocts.drvid))) { - drvidx = i; - break; - } - } - } - 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)); - else - return -EINVAL; - break; - case IIOCSIGPRF: - dev->profd = current; - return 0; - break; - case IIOCGETPRF: - /* Get all Modem-Profiles */ - if (arg) { - char *p = (char *) arg; - int i; - - if ((ret = verify_area(VERIFY_WRITE, (void *) arg, - (ISDN_MODEM_ANZREG + ISDN_MSNLEN) - * ISDN_MAX_CHANNELS))) - return ret; - - for (i = 0; i < ISDN_MAX_CHANNELS; i++) { - copy_to_user(p, dev->mdm.info[i].emu.profile, - ISDN_MODEM_ANZREG); - p += ISDN_MODEM_ANZREG; - copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN); - p += ISDN_MSNLEN; - } - return (ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS; - } else - return -EINVAL; - break; - case IIOCSETPRF: - /* Set all Modem-Profiles */ - if (arg) { - char *p = (char *) arg; - int i; - - if ((ret = verify_area(VERIFY_READ, (void *) arg, - (ISDN_MODEM_ANZREG + ISDN_MSNLEN) - * ISDN_MAX_CHANNELS))) - return ret; - - for (i = 0; i < ISDN_MAX_CHANNELS; i++) { - copy_from_user(dev->mdm.info[i].emu.profile, p, - ISDN_MODEM_ANZREG); - p += ISDN_MODEM_ANZREG; - copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN); - p += ISDN_MSNLEN; - } - return 0; - } else - return -EINVAL; - break; - case IIOCSETMAP: - case IIOCGETMAP: - /* Set/Get MSN->EAZ-Mapping for a driver */ - if (arg) { - int i; - char *p; - char nstring[255]; - - if ((ret = verify_area(VERIFY_READ, (void *) arg, - sizeof(isdn_ioctl_struct)))) - return ret; - copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct)); - if (strlen(iocts.drvid)) { - drvidx = -1; - for (i = 0; i < ISDN_MAX_DRIVERS; i++) - if (!(strcmp(dev->drvid[i], iocts.drvid))) { - drvidx = i; - break; - } - } else - drvidx = 0; - if (drvidx == -1) - return -ENODEV; - if (cmd == IIOCSETMAP) { - if ((ret = verify_area(VERIFY_READ, (void *) iocts.arg, 255))) - return ret; - copy_from_user(nstring, (char *) iocts.arg, 255); - memset(dev->drv[drvidx]->msn2eaz, 0, - sizeof(dev->drv[drvidx]->msn2eaz)); - p = strtok(nstring, ","); - i = 0; - while ((p) && (i < 10)) { - strcpy(dev->drv[drvidx]->msn2eaz[i++], p); - p = strtok(NULL, ","); - } - } else { - p = nstring; - for (i = 0; i < 10; i++) - p += sprintf(p, "%s%s", - strlen(dev->drv[drvidx]->msn2eaz[i]) ? - dev->drv[drvidx]->msn2eaz[i] : "-", - (i < 9) ? "," : "\0"); - if ((ret = verify_area(VERIFY_WRITE, (void *) iocts.arg, - strlen(nstring) + 1))) - return ret; - copy_to_user((char *) iocts.arg, nstring, strlen(nstring) + 1); - } - return 0; - } else - return -EINVAL; - case IIOCDBGVAR: - if (arg) { - if ((ret = verify_area(VERIFY_WRITE, (void *) arg, sizeof(ulong)))) - return ret; - copy_to_user((char *) arg, (char *) &dev, sizeof(ulong)); - return 0; - } else - return -EINVAL; - break; - default: - if ((cmd&IIOCDRVCTL)==IIOCDRVCTL) - cmd = ((cmd>>_IOC_NRSHIFT)&_IOC_NRMASK)& ISDN_DRVIOCTL_MASK; + case IIOCNETALN: + if (!arg) + return -EINVAL; + if ((ret = copy_from_user(name, (char *) arg, sizeof(name)))) + return ret; + return isdn_ppp_dial_slave(name); + case IIOCNETDLN: + if (!arg) + return -EINVAL; + if ((ret = copy_from_user(name, (char *) arg, sizeof(name)))) + return ret; + return isdn_ppp_hangup_slave(name); +#endif + case IIOCNETHUP: + /* Force hangup of a network-interface */ + if (!arg) + return -EINVAL; + if ((ret = copy_from_user(name, (char *) arg, sizeof(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; + case IIOCSETGST: + if (arg) + dev->global_flags |= ISDN_GLOBAL_STOPPED; + else + dev->global_flags &= ~ISDN_GLOBAL_STOPPED; + printk(KERN_INFO "isdn: Global Mode %s\n", + (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running"); + return 0; + case IIOCSETBRJ: + drvidx = -1; + if (arg) { + int i; + char *p; + if ((ret = copy_from_user((char *) &iocts, (char *) arg, + sizeof(isdn_ioctl_struct)))) + return ret; + if (strlen(iocts.drvid)) { + if ((p = strchr(iocts.drvid, ','))) + *p = 0; + drvidx = -1; + for (i = 0; i < ISDN_MAX_DRIVERS; i++) + if (!(strcmp(dev->drvid[i], iocts.drvid))) { + drvidx = i; + break; + } + } + } + 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; - if (arg) { - int i; - char *p; - if ((ret = verify_area(VERIFY_READ, (void *) arg, - sizeof(isdn_ioctl_struct)))) - return ret; - copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct)); - if (strlen(iocts.drvid)) { - if ((p = strchr(iocts.drvid, ','))) - *p = 0; - drvidx = -1; - for (i = 0; i < ISDN_MAX_DRIVERS; i++) - if (!(strcmp(dev->drvid[i], iocts.drvid))) { - drvidx = i; - break; - } - } else - drvidx = 0; - if (drvidx == -1) - return -ENODEV; - if ((ret = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(isdn_ioctl_struct)))) - return ret; - c.driver = drvidx; - c.command = ISDN_CMD_IOCTL; - c.arg = cmd; - memcpy(c.num, (char *) &iocts.arg, sizeof(ulong)); - ret = dev->drv[drvidx]->interface->command(&c); - memcpy((char *) &iocts.arg, c.num, sizeof(ulong)); - copy_to_user((char *) arg, &iocts, sizeof(isdn_ioctl_struct)); - return ret; - } 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)); + else + return -EINVAL; + break; + case IIOCSIGPRF: + dev->profd = current; + return 0; + break; + case IIOCGETPRF: + /* Get all Modem-Profiles */ + if (arg) { + char *p = (char *) arg; + int i; + + if ((ret = verify_area(VERIFY_WRITE, (void *) arg, + (ISDN_MODEM_ANZREG + ISDN_MSNLEN) + * ISDN_MAX_CHANNELS))) + 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; + p += ISDN_MODEM_ANZREG; + if (copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN)) + return -EFAULT; + p += ISDN_MSNLEN; + } + return (ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS; + } else + return -EINVAL; + break; + case IIOCSETPRF: + /* Set all Modem-Profiles */ + if (arg) { + char *p = (char *) arg; + int i; + + if ((ret = verify_area(VERIFY_READ, (void *) arg, + (ISDN_MODEM_ANZREG + ISDN_MSNLEN) + * ISDN_MAX_CHANNELS))) + 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; + p += ISDN_MODEM_ANZREG; + if ((ret = copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN))) + return ret; + p += ISDN_MSNLEN; + } + return 0; + } else + return -EINVAL; + break; + case IIOCSETMAP: + case IIOCGETMAP: + /* Set/Get MSN->EAZ-Mapping for a driver */ + if (arg) { + + if ((ret = copy_from_user((char *) &iocts, + (char *) arg, + sizeof(isdn_ioctl_struct)))) + return ret; + if (strlen(iocts.drvid)) { + drvidx = -1; + for (i = 0; i < ISDN_MAX_DRIVERS; i++) + if (!(strcmp(dev->drvid[i], iocts.drvid))) { + drvidx = i; + break; + } + } else + drvidx = 0; + if (drvidx == -1) + return -ENODEV; + if (cmd == IIOCSETMAP) { + int loop = 1; + + p = (char *) iocts.arg; + i = 0; + while (loop) { + int j = 0; + + while (1) { + if ((ret = verify_area(VERIFY_READ, p, 1))) + return ret; + GET_USER(bname[j], p++); + switch (bname[j]) { + case '\0': + loop = 0; + /* Fall through */ + case ',': + bname[j] = '\0'; + strcpy(dev->drv[drvidx]->msn2eaz[i], bname); + j = ISDN_MSNLEN; + break; + default: + j++; + } + if (j >= ISDN_MSNLEN) + break; + } + if (++i > 9) + break; + } + } else { + p = (char *) iocts.arg; + for (i = 0; i < 10; i++) { + sprintf(bname, "%s%s", + strlen(dev->drv[drvidx]->msn2eaz[i]) ? + dev->drv[drvidx]->msn2eaz[i] : "-", + (i < 9) ? "," : "\0"); + if ((ret = copy_to_user(p, bname, strlen(bname) + 1))) + return ret; + p += strlen(bname); + } + } + return 0; + } else + return -EINVAL; + case IIOCDBGVAR: + if (arg) { + if ((ret = copy_to_user((char *) arg, (char *) &dev, sizeof(ulong)))) + return ret; + return 0; + } else + return -EINVAL; + break; + default: + if ((cmd & IIOCDRVCTL) == IIOCDRVCTL) + cmd = ((cmd >> _IOC_NRSHIFT) & _IOC_NRMASK) & ISDN_DRVIOCTL_MASK; + else + return -EINVAL; + if (arg) { + int i; + char *p; + if ((ret = copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct)))) + return ret; + if (strlen(iocts.drvid)) { + if ((p = strchr(iocts.drvid, ','))) + *p = 0; + drvidx = -1; + for (i = 0; i < ISDN_MAX_DRIVERS; i++) + if (!(strcmp(dev->drvid[i], iocts.drvid))) { + drvidx = i; + break; + } + } else + drvidx = 0; + if (drvidx == -1) + return -ENODEV; + if ((ret = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(isdn_ioctl_struct)))) + 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); + memcpy((char *) &iocts.arg, c.parm.num, sizeof(ulong)); + if ((copy_to_user((char *) arg, &iocts, sizeof(isdn_ioctl_struct)))) + return -EFAULT; + return ret; + } else + return -EINVAL; } } #ifdef CONFIG_ISDN_PPP @@ -1612,6 +1569,12 @@ return (isdn_ppp_ioctl(minor - ISDN_MINOR_PPP, file, cmd, arg)); #endif return -ENODEV; + +#undef name +#undef bname +#undef iocts +#undef phone +#undef cfg } /* @@ -1619,7 +1582,8 @@ * MOD_INC_USE_COUNT make sure that the driver memory is not freed * while the device is in use. */ -static int isdn_open(struct inode *ino, struct file *filep) +static int +isdn_open(struct inode *ino, struct file *filep) { uint minor = MINOR(ino->i_rdev); int drvidx; @@ -1671,20 +1635,21 @@ if (minor <= ISDN_MINOR_PPPMAX) { int ret; if (!(ret = isdn_ppp_open(minor - ISDN_MINOR_PPP, filep))) - MOD_INC_USE_COUNT; + MOD_INC_USE_COUNT; return ret; } #endif return -ENODEV; } -static void isdn_close(struct inode *ino, struct file *filep) +static CLOSETYPE +isdn_close(struct inode *ino, struct file *filep) { uint minor = MINOR(ino->i_rdev); int drvidx; isdn_ctrl c; - MOD_DEC_USE_COUNT; + MOD_DEC_USE_COUNT; if (minor == ISDN_MINOR_STATUS) { infostruct *p = dev->infochain; infostruct *q = NULL; @@ -1694,38 +1659,40 @@ q->next = p->next; else dev->infochain = (infostruct *) (p->next); - return; + kfree(p); + return CLOSEVAL; } q = p; p = (infostruct *) (p->next); } printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n"); - return; + return CLOSEVAL; } if (minor < ISDN_MINOR_CTRL) { drvidx = isdn_minor2drv(minor); if (drvidx < 0) - return; + return CLOSEVAL; c.command = ISDN_CMD_UNLOCK; c.driver = drvidx; (void) dev->drv[drvidx]->interface->command(&c); - return; + return CLOSEVAL; } if (minor <= ISDN_MINOR_CTRLMAX) { drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); if (drvidx < 0) - return; + 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; + return CLOSEVAL; } #ifdef CONFIG_ISDN_PPP if (minor <= ISDN_MINOR_PPPMAX) isdn_ppp_release(minor - ISDN_MINOR_PPP, filep); #endif + return CLOSEVAL; } static struct file_operations isdn_fops = @@ -1733,17 +1700,21 @@ isdn_lseek, isdn_read, isdn_write, - NULL, /* isdn_readdir */ - isdn_select, /* isdn_select */ - isdn_ioctl, /* isdn_ioctl */ - NULL, /* isdn_mmap */ + NULL, /* isdn_readdir */ +#if (LINUX_VERSION_CODE < 0x020117) + isdn_select, /* isdn_select */ +#else + isdn_poll, /* isdn_poll */ +#endif + isdn_ioctl, /* isdn_ioctl */ + NULL, /* isdn_mmap */ isdn_open, isdn_close, - NULL /* fsync */ + NULL /* fsync */ }; char * - isdn_map_eaz2msn(char *msn, int di) +isdn_map_eaz2msn(char *msn, int di) { driver *this = dev->drv[di]; int i; @@ -1761,13 +1732,14 @@ * Find an unused ISDN-channel, whose feature-flags match the * given L2- and L3-protocols. */ -int isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev - ,int pre_chan) +int +isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev + ,int pre_chan) { int i; ulong flags; ulong features; - isdn_ctrl cmd; + isdn_ctrl cmd; save_flags(flags); cli(); @@ -1785,10 +1757,10 @@ 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); + cmd.driver = d; + cmd.arg = 0; + cmd.command = ISDN_CMD_LOCK; + (void) dev->drv[d]->interface->command(&cmd); restore_flags(flags); return i; } else { @@ -1796,10 +1768,10 @@ 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); + cmd.driver = d; + cmd.arg = 0; + cmd.command = ISDN_CMD_LOCK; + (void) dev->drv[d]->interface->command(&cmd); restore_flags(flags); return i; } @@ -1814,7 +1786,8 @@ /* * Set state of ISDN-channel to 'unused' */ -void isdn_free_channel(int di, int ch, int usage) +void +isdn_free_channel(int di, int ch, int usage) { int i; ulong flags; @@ -1828,15 +1801,15 @@ (dev->chanmap[i] == ch)) { dev->usage[i] &= (ISDN_USAGE_NONE | ISDN_USAGE_EXCLUSIVE); strcpy(dev->num[i], "???"); - dev->ibytes[i] = 0; - dev->obytes[i] = 0; + dev->ibytes[i] = 0; + dev->obytes[i] = 0; isdn_info_update(); - isdn_free_queue(&dev->drv[di]->rpqueue[ch]); - cmd.driver = di; - cmd.arg = ch; - cmd.command = ISDN_CMD_UNLOCK; - restore_flags(flags); - (void) dev->drv[di]->interface->command(&cmd); + isdn_free_queue(&dev->drv[di]->rpqueue[ch]); + cmd.driver = di; + cmd.arg = ch; + cmd.command = ISDN_CMD_UNLOCK; + restore_flags(flags); + (void) dev->drv[di]->interface->command(&cmd); return; } restore_flags(flags); @@ -1845,7 +1818,8 @@ /* * Cancel Exclusive-Flag for ISDN-channel */ -void isdn_unexclusive_channel(int di, int ch) +void +isdn_unexclusive_channel(int di, int ch) { int i; ulong flags; @@ -1873,55 +1847,57 @@ * len = Length of packet-data * */ -void isdn_receive_callback(int drvidx, int chan, u_char *buf, int len) +static void +isdn_receive_callback(int drvidx, int chan, u_char * buf, int len) { - struct sk_buff *skb; + struct sk_buff *skb; if (dev->global_flags & ISDN_GLOBAL_STOPPED) return; - skb = dev_alloc_skb(len); - if (skb) { - memcpy(skb_put(skb, len), buf, len); - isdn_receive_skb_callback(drvidx, chan, skb); - } else - printk(KERN_WARNING "isdn: rcv alloc_skb failed, packet dropped.\n"); + skb = dev_alloc_skb(len); + if (skb) { + memcpy(skb_put(skb, len), buf, len); + isdn_receive_skb_callback(drvidx, chan, skb); + } else + printk(KERN_WARNING "isdn: rcv alloc_skb failed, packet dropped.\n"); } /* * writebuf replacement for SKB_ABLE drivers */ -int isdn_writebuf_stub(int drvidx, int chan, const u_char *buf, int len, - int user) +static int +isdn_writebuf_stub(int drvidx, int chan, const u_char * buf, int len, + int user) { int ret; - if (dev->drv[drvidx]->interface->writebuf) - ret = dev->drv[drvidx]->interface->writebuf(drvidx, chan, buf, - len, user); - else { - struct sk_buff * skb; - - skb = alloc_skb(dev->drv[drvidx]->interface->hl_hdrlen + len, - GFP_ATOMIC); - if (skb == NULL) - return 0; - - skb_reserve(skb, dev->drv[drvidx]->interface->hl_hdrlen); - skb->free = 1; - - if (user) - copy_from_user(skb_put(skb, len), buf, len); - else - memcpy(skb_put(skb, len), buf, len); - - ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, - chan, skb); - if (ret <= 0) - kfree_skb(skb, FREE_WRITE); - } - if (ret > 0) - dev->obytes[isdn_dc2minor(drvidx,chan)] += ret; - return ret; + if (dev->drv[drvidx]->interface->writebuf) + ret = dev->drv[drvidx]->interface->writebuf(drvidx, chan, buf, + len, user); + else { + struct sk_buff *skb; + + skb = alloc_skb(dev->drv[drvidx]->interface->hl_hdrlen + len, + GFP_ATOMIC); + if (skb == NULL) + return 0; + + skb_reserve(skb, dev->drv[drvidx]->interface->hl_hdrlen); + SET_SKB_FREE(skb); + + if (user) + copy_from_user(skb_put(skb, len), buf, len); + else + memcpy(skb_put(skb, len), buf, len); + + ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, + chan, skb); + if (ret <= 0) + kfree_skb(skb, FREE_WRITE); + } + if (ret > 0) + dev->obytes[isdn_dc2minor(drvidx, chan)] += ret; + return ret; } /* @@ -1932,32 +1908,36 @@ * Return: length of data on success, -ERRcode on failure. */ -int isdn_writebuf_skb_stub(int drvidx, int chan, struct sk_buff * skb) +int +isdn_writebuf_skb_stub(int drvidx, int chan, struct sk_buff *skb) { - int ret; - int len = skb->len; /* skb pointer no longer valid after free */ + int ret; + int len = skb->len; /* skb pointer no longer valid after free */ - if (dev->drv[drvidx]->interface->writebuf_skb) + if (dev->drv[drvidx]->interface->writebuf_skb) ret = dev->drv[drvidx]->interface-> - writebuf_skb(drvidx, chan, skb); + writebuf_skb(drvidx, chan, skb); else { if ((ret = dev->drv[drvidx]->interface-> - writebuf(drvidx,chan,skb->data,skb->len,0)) == len) - dev_kfree_skb(skb, FREE_WRITE); - } - if (ret > 0) - dev->obytes[isdn_dc2minor(drvidx,chan)] += len; - return ret; + writebuf(drvidx, chan, skb->data, skb->len, 0)) == len) + dev_kfree_skb(skb, FREE_WRITE); + } + if (ret > 0) + dev->obytes[isdn_dc2minor(drvidx, chan)] += len; + return ret; } /* * Low-level-driver registration */ -int register_isdn(isdn_if * i) +int +register_isdn(isdn_if * i) { driver *d; - int n, j, k; + int n, + j, + k; ulong flags; int drvidx; @@ -1973,9 +1953,9 @@ return 0; } if ((!i->writebuf_skb) && (!i->writebuf)) { - printk(KERN_WARNING "register_isdn: No write routine given.\n"); - return 0; - } + printk(KERN_WARNING "register_isdn: No write routine given.\n"); + return 0; + } if (!(d = (driver *) kmalloc(sizeof(driver), GFP_KERNEL))) { printk(KERN_WARNING "register_isdn: Could not alloc driver-struct\n"); return 0; @@ -1994,17 +1974,17 @@ return 0; } memset((char *) d->rcvcount, 0, sizeof(int) * n); - if (!(d->rpqueue = - (struct sk_buff_head *) kmalloc(sizeof(struct sk_buff_head) * n, GFP_KERNEL))) { - printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n"); - kfree(d->rcvcount); - kfree(d->rcverr); - kfree(d); - return 0; - } - for (j = 0; j < n; j++) { - skb_queue_head_init(&d->rpqueue[j]); - } + if (!(d->rpqueue = + (struct sk_buff_head *) kmalloc(sizeof(struct sk_buff_head) * n, GFP_KERNEL))) { + printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n"); + kfree(d->rcvcount); + kfree(d->rcverr); + kfree(d); + return 0; + } + for (j = 0; j < n; j++) { + skb_queue_head_init(&d->rpqueue[j]); + } if (!(d->rcv_waitq = (struct wait_queue **) kmalloc(sizeof(struct wait_queue *) * n, GFP_KERNEL))) { printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n"); @@ -2038,17 +2018,17 @@ if (!dev->drv[drvidx]) break; i->channels = drvidx; - + i->rcvcallb_skb = isdn_receive_skb_callback; - i->rcvcallb = isdn_receive_callback; - i->statcallb = isdn_status_callback; + i->rcvcallb = isdn_receive_callback; + i->statcallb = isdn_status_callback; if (!strlen(i->id)) sprintf(i->id, "line%d", drvidx); save_flags(flags); cli(); - for (j = 0; j < drvidx; j++) - if (!strcmp(i->id,dev->drvid[j])) - sprintf(i->id, "line%d", drvidx); + for (j = 0; j < drvidx; j++) + if (!strcmp(i->id, dev->drvid[j])) + sprintf(i->id, "line%d", drvidx); for (j = 0; j < n; j++) for (k = 0; k < ISDN_MAX_CHANNELS; k++) if (dev->chanmap[k] < 0) { @@ -2077,7 +2057,8 @@ #define isdn_init init_module #endif -static char *isdn_getrev(const char *revision) +static char * +isdn_getrev(const char *revision) { char *rev; char *p; @@ -2091,29 +2072,18 @@ return rev; } -static struct symbol_table isdn_syms = { -#include - X(register_isdn), -#include -}; - -static void isdn_export_syms(void) -{ - register_symtab(&isdn_syms); - has_exported = 1; -} - /* * Allocate and initialize all data, register modem-devices */ -int isdn_init(void) +int +isdn_init(void) { int i; - char irev[50]; - char trev[50]; - char nrev[50]; - char prev[50]; - char arev[50]; + char irev[50]; + char trev[50]; + char nrev[50]; + char prev[50]; + char arev[50]; sti(); if (!(dev = (isdn_dev *) kmalloc(sizeof(isdn_dev), GFP_KERNEL))) { @@ -2121,10 +2091,8 @@ return -EIO; } memset((char *) dev, 0, sizeof(isdn_dev)); -#ifdef NEW_ISDN_TIMER_CTRL - init_timer(&dev->timer); - dev->timer.function = isdn_timer_funct; -#endif + init_timer(&dev->timer); + dev->timer.function = isdn_timer_funct; for (i = 0; i < ISDN_MAX_CHANNELS; i++) { dev->drvmap[i] = -1; dev->chanmap[i] = -1; @@ -2157,21 +2125,20 @@ kfree(dev); return -EIO; } -#endif /* CONFIG_ISDN_PPP */ +#endif /* CONFIG_ISDN_PPP */ - if (!has_exported) - isdn_export_syms(); + isdn_export_syms(); - strcpy(irev,isdn_revision); - strcpy(trev,isdn_tty_revision); - strcpy(nrev,isdn_net_revision); - strcpy(prev,isdn_ppp_revision); - strcpy(arev,isdn_audio_revision); + strcpy(irev, isdn_revision); + strcpy(trev, isdn_tty_revision); + strcpy(nrev, isdn_net_revision); + strcpy(prev, isdn_ppp_revision); + strcpy(arev, isdn_audio_revision); printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(irev)); - printk("%s/", isdn_getrev(trev)); - printk("%s/", isdn_getrev(nrev)); - printk("%s/", isdn_getrev(prev)); - printk("%s", isdn_getrev(arev)); + printk("%s/", isdn_getrev(trev)); + printk("%s/", isdn_getrev(nrev)); + printk("%s/", isdn_getrev(prev)); + printk("%s", isdn_getrev(arev)); #ifdef MODULE printk(" loaded\n"); @@ -2187,7 +2154,8 @@ /* * Unload module */ -void cleanup_module(void) +void +cleanup_module(void) { int flags; int i; @@ -2213,9 +2181,9 @@ return; } for (i = 0; i < ISDN_MAX_CHANNELS; i++) { - isdn_tty_cleanup_xmit(&dev->mdm.info[i]); - kfree(dev->mdm.info[i].xmit_buf - 4); - } + isdn_tty_cleanup_xmit(&dev->mdm.info[i]); + kfree(dev->mdm.info[i].xmit_buf - 4); + } if (unregister_chrdev(ISDN_MAJOR, "isdn") != 0) { printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n"); } else { diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_common.h linux/drivers/isdn/isdn_common.h --- v2.0.30/linux/drivers/isdn/isdn_common.h Sat Jun 1 01:56:51 1996 +++ linux/drivers/isdn/isdn_common.h Mon Aug 4 17:34:00 1997 @@ -1,11 +1,11 @@ -/* $Id: isdn_common.h,v 1.3 1996/05/19 00:13:05 fritz Exp $ - * +/* $Id: isdn_common.h,v 1.6 1997/02/28 02:32:44 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 1995,96 by Thinking Objects Software GmbH Wuerzburg * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.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) @@ -18,9 +18,21 @@ * * 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. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.h,v $ + * Revision 1.6 1997/02/28 02:32:44 fritz + * Cleanup: Moved some tty related stuff from isdn_common.c + * to isdn_tty.c + * Bugfix: Bisync protocol did not behave like documented. + * + * Revision 1.5 1997/02/10 10:05:45 fritz + * More changes for Kernel 2.1.X + * Symbol information moved to isdn_syms.c + * + * Revision 1.4 1997/02/03 22:56:50 fritz + * Removed isdn_writebuf_stub prototype. + * * Revision 1.3 1996/05/19 00:13:05 fritz * Removed debug flag. * @@ -38,26 +50,32 @@ #undef ISDN_DEBUG_MODEM_HUP #undef ISDN_DEBUG_MODEM_ICALL #undef ISDN_DEBUG_MODEM_DUMP +#undef ISDN_DEBUG_MODEM_VOICE #undef ISDN_DEBUG_AT #undef ISDN_DEBUG_NET_DUMP #undef ISDN_DEBUG_NET_DIAL #undef ISDN_DEBUG_NET_ICALL /* Prototypes */ -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 void isdn_all_eaz(int di, int ch); -extern int isdn_dc2minor(int di, int ch); -extern void isdn_info_update(void); -extern char* isdn_map_eaz2msn(char *msn, int di); -extern void isdn_timer_ctrl(int tf, int onoff); -extern void isdn_unexclusive_channel(int di, int ch); -extern int isdn_getnum(char **); -extern int isdn_readbchan (int, int, u_char *, u_char *, int, int); -extern int isdn_get_free_channel(int, int, int, int, int); -extern int isdn_writebuf_stub(int, int, const u_char *, int, int); -extern int isdn_writebuf_skb_stub(int, int, struct sk_buff *); +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 void isdn_all_eaz(int di, int ch); +extern int isdn_dc2minor(int di, int ch); +extern void isdn_info_update(void); +extern char *isdn_map_eaz2msn(char *msn, int di); +extern void isdn_timer_ctrl(int tf, int onoff); +extern void isdn_unexclusive_channel(int di, int ch); +extern int isdn_getnum(char **); +extern int isdn_readbchan(int, int, u_char *, u_char *, int, int); +extern int isdn_get_free_channel(int, int, int, int, int); +extern int isdn_writebuf_skb_stub(int, int, struct sk_buff *); +extern int register_isdn(isdn_if * i); +#if (LINUX_VERSION_CODE < 0x020111) +extern void isdn_export_syms(void); +#else +#define isdn_export_syms() +#endif #if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) -extern void isdn_dumppkt(char *, u_char *, int, int); +extern void isdn_dumppkt(char *, u_char *, int, int); #endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c --- v2.0.30/linux/drivers/isdn/isdn_net.c Tue Nov 12 22:36:19 1996 +++ linux/drivers/isdn/isdn_net.c Mon Aug 4 17:34:00 1997 @@ -1,11 +1,11 @@ -/* $Id: isdn_net.c,v 1.29 1996/11/13 02:31:38 fritz Exp $ - * +/* $Id: isdn_net.c,v 1.47 1997/06/21 10:52:05 fritz Exp $ + * Linux ISDN subsystem, network interfaces and related functions (linklevel). * * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.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) @@ -18,9 +18,73 @@ * * 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. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_net.c,v $ + * Revision 1.47 1997/06/21 10:52:05 fritz + * Removed wrong SET_SKB_FREE in isdn_net_send_skb() + * + * Revision 1.46 1997/06/17 13:05:24 hipp + * Applied Eric's underflow-patches (slightly modified) + * + * 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 + * queue_task_* -> queue_task + * clear/set_bit -> test_and_... where apropriate. + * changed type of hard_header_cache parameter. + * + * Revision 1.43 1997/03/30 16:51:13 calle + * changed calls to copy_from_user/copy_to_user and removed verify_area + * were possible. + * + * Revision 1.42 1997/03/11 08:43:51 fritz + * Perform a hangup if number is deleted while dialing. + * + * Revision 1.41 1997/03/08 08:16:31 fritz + * Bugfix: Deleting a phone number during dial gave unpredictable results. + * + * Revision 1.40 1997/03/05 21:16:08 fritz + * Fix: did not compile with 2.1.27 + * + * Revision 1.39 1997/03/04 21:36:52 fritz + * Added sending ICMP messages when no connetion is possible. + * + * Revision 1.38 1997/02/23 23:41:14 fritz + * Bugfix: Slave interfaces have to be hung up before master. + * + * Revision 1.37 1997/02/11 18:32:51 fritz + * Bugfix in isdn_ppp_free_mpqueue(). + * + * Revision 1.36 1997/02/10 21:31:11 fritz + * Changed setup-interface (incoming and outgoing). + * + * Revision 1.35 1997/02/10 20:12:45 fritz + * Changed interface for reporting incoming calls. + * + * Revision 1.34 1997/02/03 23:15:07 fritz + * Reformatted according CodingStyle. + * replaced arp_find prototype by proper include. + * made dev_purge_queues static. + * Bugfix in bogocps calculation. + * removed isdn_net_receive_callback - was never used ;-) + * Misc. fixes for Kernel 2.1.X comaptibility. + * + * Revision 1.33 1997/01/17 01:19:25 fritz + * Applied chargeint patch. + * + * Revision 1.32 1997/01/14 01:29:31 fritz + * Bugfix: isdn_net_hangup() did not reset ISDN_NET_CONNECTED. + * + * Revision 1.31 1997/01/11 23:30:42 fritz + * Speed up dial statemachine. + * + * Revision 1.30 1996/11/25 17:20:50 hipp + * fixed pppbind bug in isdn_net_find_icall() + * * Revision 1.29 1996/11/13 02:31:38 fritz * Minor cleanup. * @@ -140,38 +204,47 @@ #include #include #include +#include +#include #include "isdn_common.h" #include "isdn_net.h" #ifdef CONFIG_ISDN_PPP #include "isdn_ppp.h" #endif -/* In ksyms.c, but why not in some .h ??? */ -extern int arp_find(unsigned char *, u32, struct device *, u32, - struct sk_buff *); - /* Prototypes */ int isdn_net_force_dial_lp(isdn_net_local *); static int isdn_net_wildmat(char *s, char *p); static int isdn_net_start_xmit(struct sk_buff *, struct device *); static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *); - -extern void dev_purge_queues(struct device *dev); /* move this to net/core/dev.c */ +static void dev_purge_queues(struct device *dev); /* move this to net/core/dev.c */ -char *isdn_net_revision = "$Revision: 1.29 $"; +char *isdn_net_revision = "$Revision: 1.47 $"; /* * Code for raw-networking over ISDN */ 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); + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0 +#if (LINUX_VERSION_CODE < 0x02010f) /* 2.1.15 */ + ,dev +#endif + ); +} + +static void isdn_net_reset(struct device *dev) { ulong flags; save_flags(flags); - cli(); /* Avoid glitch on writes to CMD regs */ + cli(); /* Avoid glitch on writes to CMD regs */ dev->interrupt = 0; dev->tbusy = 0; restore_flags(flags); @@ -191,7 +264,7 @@ dev->dev_addr[i] = 0xfc; memcpy(&(dev->dev_addr[i]), &dev->pa_addr, sizeof(u32)); - /* If this interface has slaves, start them also */ + /* If this interface has slaves, start them also */ if ((p = (((isdn_net_local *) dev->priv)->slave))) { while (p) { @@ -200,7 +273,6 @@ p = (((isdn_net_local *) p->priv)->slave); } } - isdn_MOD_INC_USE_COUNT(); return 0; } @@ -211,15 +283,15 @@ static void isdn_net_bind_channel(isdn_net_local * lp, int idx) { - ulong flags; + ulong flags; - save_flags(flags); - cli(); + save_flags(flags); + cli(); lp->isdn_device = dev->drvmap[idx]; lp->isdn_channel = dev->chanmap[idx]; - dev->rx_netdev[idx] = lp->netdev; - dev->st_netdev[idx] = lp->netdev; - restore_flags(flags); + dev->rx_netdev[idx] = lp->netdev; + dev->st_netdev[idx] = lp->netdev; + restore_flags(flags); } /* @@ -232,25 +304,25 @@ save_flags(flags); cli(); - 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); + 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; } - if(!lp->master) /* purge only for master device */ + 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; + 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); lp->flags &= ~ISDN_NET_CONNECTED; lp->isdn_device = -1; lp->isdn_channel = -1; - restore_flags(flags); + restore_flags(flags); } /* @@ -272,37 +344,50 @@ isdn_net_autohup() { isdn_net_dev *p = dev->netdev; - int anymore; + int anymore; - anymore = 0; + anymore = 0; while (p) { isdn_net_local *l = (isdn_net_local *) & (p->local); if ((jiffies - last_jiffies) == 0) - l->cps = 0; + l->cps = l->transcount; else - l->cps = l->transcount / (jiffies - last_jiffies); + l->cps = (l->transcount * HZ) / (jiffies - last_jiffies); l->transcount = 0; if (dev->net_verbose > 3) printk(KERN_DEBUG "%s: %d bogocps\n", l->name, l->cps); if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) { - anymore = 1; + anymore = 1; l->huptimer++; if ((l->onhtime) && (l->huptimer > l->onhtime)) - if (l->outgoing) { - if (l->hupflags & 4) { - if (l->hupflags & 1) + if (l->hupflags & ISDN_MANCHARGE && + l->hupflags & ISDN_CHARGEHUP) { + while (jiffies - l->chargetime > l->chargeint) + l->chargetime += l->chargeint; + if (jiffies - l->chargetime >= l->chargeint - 2 * HZ) + if (l->outgoing || l->hupflags & ISDN_INHUP) isdn_net_hangup(&p->dev); - else if (jiffies - l->chargetime > l->chargeint) + } else if (l->outgoing) { + if (l->hupflags & ISDN_CHARGEHUP) { + if (l->hupflags & ISDN_WAITCHARGE) { + printk(KERN_DEBUG "isdn_net: Hupflags of %s are %X\n", + l->name, l->hupflags); isdn_net_hangup(&p->dev); + } else if (jiffies - l->chargetime > l->chargeint) { + printk(KERN_DEBUG + "isdn_net: %s: chtime = %d, chint = %d\n", + l->name, l->chargetime, l->chargeint); + isdn_net_hangup(&p->dev); + } } else isdn_net_hangup(&p->dev); - } else if (l->hupflags & 8) + } else if (l->hupflags & ISDN_INHUP) isdn_net_hangup(&p->dev); } p = (isdn_net_dev *) p->next; } last_jiffies = jiffies; - isdn_timer_ctrl(ISDN_TIMER_NETHANGUP,anymore); + isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, anymore); } /* @@ -318,57 +403,56 @@ if (p) { isdn_net_local *lp = &(p->local); - switch (cmd) { + switch (cmd) { case ISDN_STAT_BSENT: /* A packet has successfully been sent out */ if ((lp->flags & ISDN_NET_CONNECTED) && (!lp->dialstate)) { lp->stats.tx_packets++; - if(lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && lp->sav_skb) { + if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && lp->sav_skb) { struct device *mdev; - if(lp->master) + if (lp->master) mdev = lp->master; else mdev = &lp->netdev->dev; - if(!isdn_net_send_skb(mdev,lp,lp->sav_skb)) { + if (!isdn_net_send_skb(mdev, lp, lp->sav_skb)) { lp->sav_skb = NULL; mark_bh(NET_BH); - } - else { + } else { return 1; } } - if (clear_bit(0,(void*)&(p->dev.tbusy))) - mark_bh(NET_BH); + if (test_and_clear_bit(0, (void *) &(p->dev.tbusy))) + mark_bh(NET_BH); } return 1; case ISDN_STAT_DCONN: /* D-Channel is up */ - switch (lp->dialstate) { - case 4: - case 7: - case 8: - lp->dialstate++; - return 1; - case 12: - lp->dialstate = 5; - return 1; - } + switch (lp->dialstate) { + case 4: + case 7: + case 8: + lp->dialstate++; + return 1; + case 12: + 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); + 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); + 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); + ISDN_USAGE_NET); #ifdef CONFIG_ISDN_PPP isdn_ppp_free(lp); #endif @@ -380,49 +464,51 @@ lp->isdn_channel = -1; dev->st_netdev[idx] = NULL; dev->rx_netdev[idx] = NULL; - return 1; + return 1; } - break; + break; case ISDN_STAT_BCONN: /* B-Channel is up */ - switch (lp->dialstate) { - case 5: - case 6: - case 7: - case 8: - case 9: - case 10: - case 12: - if (lp->dialstate <= 6) { - dev->usage[idx] |= ISDN_USAGE_OUTGOING; - isdn_info_update(); - } else - dev->rx_netdev[idx] = p; - lp->dialstate = 0; - isdn_timer_ctrl(ISDN_TIMER_NETHANGUP,1); - printk(KERN_INFO "isdn_net: %s connected\n", lp->name); - /* If first Chargeinfo comes before B-Channel connect, - * we correct the timestamp here. - */ - lp->chargetime = jiffies; - /* Immediately send first skb to speed up arp */ + switch (lp->dialstate) { + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 12: + if (lp->dialstate <= 6) { + dev->usage[idx] |= ISDN_USAGE_OUTGOING; + isdn_info_update(); + } else + dev->rx_netdev[idx] = p; + lp->dialstate = 0; + isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 1); + printk(KERN_INFO "isdn_net: %s connected\n", lp->name); + /* If first Chargeinfo comes before B-Channel connect, + * we correct the timestamp here. + */ + lp->chargetime = jiffies; + printk(KERN_DEBUG "isdn_net: chargetime of %s now %d\n", + lp->name, lp->chargetime); + /* Immediately send first skb to speed up arp */ #ifdef CONFIG_ISDN_PPP - if(lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) + if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) isdn_ppp_wakeup_daemon(lp); #endif - if (lp->first_skb) { - if (!(isdn_net_xmit(&p->dev,lp,lp->first_skb))) - lp->first_skb = NULL; - } - return 1; + if (lp->first_skb) { + if (!(isdn_net_xmit(&p->dev, lp, lp->first_skb))) + lp->first_skb = NULL; + } + return 1; } break; case ISDN_STAT_NODCH: /* No D-Channel avail. */ if (lp->dialstate == 4) { lp->dialstate--; - return 1; - } + return 1; + } break; case ISDN_STAT_CINF: /* Charge-info from TelCo. Calculate interval between @@ -430,17 +516,19 @@ * usage by isdn_net_autohup() */ lp->charge++; - if (lp->hupflags & 2) { - lp->hupflags &= ~1; + if (lp->hupflags & ISDN_HAVECHARGE) { + lp->hupflags &= ~ISDN_WAITCHARGE; lp->chargeint = jiffies - lp->chargetime - (2 * HZ); } - if (lp->hupflags & 1) - lp->hupflags |= 2; + if (lp->hupflags & ISDN_WAITCHARGE) + lp->hupflags |= ISDN_HAVECHARGE; lp->chargetime = jiffies; + printk(KERN_DEBUG "isdn_net: Got CINF chargetime of %s now %d\n", + lp->name, lp->chargetime); return 1; - } + } } - return 0; + return 0; } /* @@ -473,191 +561,220 @@ isdn_net_dev *p = dev->netdev; int anymore = 0; int i; + int flags; isdn_ctrl cmd; while (p) { #ifdef ISDN_DEBUG_NET_DIAL - if (p->local.dialstate) - printk(KERN_DEBUG "%s: dialstate=%d\n", p->local.name,p->local.dialstate); + 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 */ - break; - case 1: - /* Initiate dialout. Set phone-number-pointer to first number - * of interface. - */ - p->local.dial = p->local.phone[1]; - anymore = 1; - p->local.dialstate++; - break; - /* Prepare dialing. Clear EAZ, then set EAZ. */ - case 2: - 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); - sprintf(cmd.num, "%s", isdn_map_eaz2msn(p->local.msn, cmd.driver)); - cmd.command = ISDN_CMD_SETEAZ; - dev->drv[p->local.isdn_device]->interface->command(&cmd); - p->local.dialretry = 0; - anymore = 1; - p->local.dialstate++; - break; - case 3: - /* Setup interface, dial current phone-number, switch to next number. - * If list of phone-numbers is exhausted, increment - * retry-counter. - */ - 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); - 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); - cmd.driver = p->local.isdn_device; - cmd.arg = p->local.isdn_channel; - p->local.huptimer = 0; - p->local.outgoing = 1; - p->local.hupflags |= 1; - p->local.hupflags &= ~2; - if (!strcmp(p->local.dial->num, "LEASED")) { - p->local.dialstate = 4; - printk(KERN_INFO "%s: Open leased line ...\n", p->local.name); - } else { - cmd.command = ISDN_CMD_DIAL; - sprintf(cmd.num, "%s,%s,7,0", p->local.dial->num, - isdn_map_eaz2msn(p->local.msn, cmd.driver)); - i = isdn_dc2minor(p->local.isdn_device, p->local.isdn_channel); - if (i >= 0) { - strcpy(dev->num[i], p->local.dial->num); - isdn_info_update(); + case 0: + /* Nothing to do for this interface */ + break; + case 1: + /* Initiate dialout. Set phone-number-pointer to first number + * of interface. + */ + save_flags(flags); + cli(); + p->local.dial = p->local.phone[1]; + restore_flags(flags); + if (!p->local.dial) { + printk(KERN_WARNING "%s: phone number deleted?\n", + p->local.name); + isdn_net_hangup(&p->dev); + break; } - printk(KERN_INFO "%s: dialing %d %s...\n", p->local.name, - p->local.dialretry, p->local.dial->num); - /* - * Switch to next number or back to start if at end of list. + anymore = 1; + p->local.dialstate++; + /* Fall through */ + case 2: + /* Prepare dialing. Clear EAZ, then set EAZ. */ + 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); + 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); + p->local.dialretry = 0; + anymore = 1; + p->local.dialstate++; + /* Falls through */ + case 3: + /* Setup interface, dial current phone-number, switch to next number. + * If list of phone-numbers is exhausted, increment + * retry-counter. */ - if (!(p->local.dial = (isdn_net_phone *) p->local.dial->next)) { - p->local.dial = p->local.phone[1]; - p->local.dialretry++; + 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); + 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); + cmd.driver = p->local.isdn_device; + cmd.arg = p->local.isdn_channel; + save_flags(flags); + cli(); + if (!p->local.dial) { + restore_flags(flags); + printk(KERN_WARNING "%s: phone number deleted?\n", + p->local.name); + isdn_net_hangup(&p->dev); + break; } - p->local.dtimer = 0; + if (!strcmp(p->local.dial->num, "LEASED")) { + restore_flags(flags); + p->local.dialstate = 4; + printk(KERN_INFO "%s: Open leased line ...\n", p->local.name); + } else { + sprintf(cmd.parm.setup.phone, "%s", p->local.dial->num); + /* + * Switch to next number or back to start if at end of list. + */ + if (!(p->local.dial = (isdn_net_phone *) p->local.dial->next)) { + p->local.dial = p->local.phone[1]; + p->local.dialretry++; + } + restore_flags(flags); + cmd.command = ISDN_CMD_DIAL; + cmd.parm.setup.si1 = 7; + cmd.parm.setup.si2 = 0; + sprintf(cmd.parm.setup.eazmsn, "%s", + isdn_map_eaz2msn(p->local.msn, cmd.driver)); + i = isdn_dc2minor(p->local.isdn_device, p->local.isdn_channel); + if (i >= 0) { + strcpy(dev->num[i], cmd.parm.setup.phone); + isdn_info_update(); + } + printk(KERN_INFO "%s: dialing %d %s...\n", p->local.name, + p->local.dialretry - 1, 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); + 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); + } + p->local.huptimer = 0; + p->local.outgoing = 1; + if (p->local.chargeint) { + p->local.hupflags |= ISDN_HAVECHARGE; + p->local.hupflags &= ~ISDN_WAITCHARGE; + } else { + p->local.hupflags |= ISDN_WAITCHARGE; + p->local.hupflags &= ~ISDN_HAVECHARGE; + } + anymore = 1; + p->local.dialstate = + (p->local.cbdelay && + (p->local.flags & ISDN_NET_CBOUT)) ? 12 : 4; + break; + case 4: + /* Wait for D-Channel-connect. + * If timeout and max retries not + * reached, switch back 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); + anymore = 1; + break; + case 5: + /* Got D-Channel-Connect, send B-Channel-request */ + cmd.driver = p->local.isdn_device; + cmd.arg = p->local.isdn_channel; + cmd.command = ISDN_CMD_ACCEPTB; + anymore = 1; + p->local.dtimer = 0; + p->local.dialstate++; dev->drv[p->local.isdn_device]->interface->command(&cmd); - } - anymore = 1; - p->local.dialstate = - (p->local.cbdelay && - (p->local.flags & ISDN_NET_CBOUT))?12:4; - break; - case 4: - /* Wait for D-Channel-connect. - * If timeout and max retries not - * reached, switch back 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); - anymore = 1; - break; - case 5: - /* Got D-Channel-Connect, send B-Channel-request */ - cmd.driver = p->local.isdn_device; - cmd.arg = p->local.isdn_channel; - cmd.command = ISDN_CMD_ACCEPTB; - anymore = 1; - p->local.dtimer = 0; - p->local.dialstate++; - dev->drv[p->local.isdn_device]->interface->command(&cmd); - break; - case 6: - /* Wait for B- or D-Channel-connect. If timeout, - * switch back to state 3. - */ + break; + case 6: + /* Wait for B- or D-Channel-connect. If timeout, + * switch back to state 3. + */ #ifdef ISDN_DEBUG_NET_DIAL - printk(KERN_DEBUG "dialtimer2: %d\n", p->local.dtimer); + printk(KERN_DEBUG "dialtimer2: %d\n", p->local.dtimer); #endif - if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10) - p->local.dialstate = 3; - anymore = 1; - break; - case 7: - /* Got incoming Call, setup L2 and L3 protocols, - * then wait for D-Channel-connect - */ + if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10) + p->local.dialstate = 3; + anymore = 1; + break; + case 7: + /* Got incoming Call, setup L2 and L3 protocols, + * then wait for D-Channel-connect + */ #ifdef ISDN_DEBUG_NET_DIAL - printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer); + printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer); #endif - 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); - 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); - if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT15) - isdn_net_hangup(&p->dev); - else { + 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); + 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); + if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT15) + isdn_net_hangup(&p->dev); + else { + anymore = 1; + p->local.dialstate++; + } + break; + case 9: + /* Got incoming D-Channel-Connect, send B-Channel-request */ + 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); anymore = 1; - p->local.dialstate++; - } - break; - case 9: - /* Got incoming D-Channel-Connect, send B-Channel-request */ - 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); - anymore = 1; - p->local.dtimer = 0; - p->local.dialstate++; - break; - case 8: - case 10: - /* Wait for B- or D-channel-connect */ + p->local.dtimer = 0; + p->local.dialstate++; + break; + case 8: + case 10: + /* Wait for B- or D-channel-connect */ #ifdef ISDN_DEBUG_NET_DIAL - printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer); + printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer); #endif - if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10) - isdn_net_hangup(&p->dev); - else + if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10) + isdn_net_hangup(&p->dev); + else + anymore = 1; + break; + case 11: + /* Callback Delay */ + if (p->local.dtimer++ > p->local.cbdelay) + p->local.dialstate = 1; anymore = 1; - break; - case 11: - /* Callback Delay */ - if (p->local.dtimer++ > p->local.cbdelay) - p->local.dialstate = 1; - anymore = 1; - break; - case 12: - /* Remote does callback. Hangup after cbdelay, then wait for incoming - * call (in state 4). - */ - 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; - 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_all_eaz(p->local.isdn_device, p->local.isdn_channel); - } - anymore = 1; - break; - default: - printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n", - p->local.dialstate, p->local.name); + break; + case 12: + /* Remote does callback. Hangup after cbdelay, then wait for incoming + * call (in state 4). + */ + 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; + 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_all_eaz(p->local.isdn_device, p->local.isdn_channel); + } + anymore = 1; + break; + default: + printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n", + p->local.dialstate, p->local.name); } p = (isdn_net_dev *) p->next; } @@ -674,6 +791,7 @@ isdn_ctrl cmd; if (lp->flags & ISDN_NET_CONNECTED) { + lp->flags &= ~ISDN_NET_CONNECTED; printk(KERN_INFO "isdn_net: local hangup %s\n", lp->name); #ifdef CONFIG_ISDN_PPP isdn_ppp_free(lp); @@ -685,7 +803,7 @@ 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); + isdn_net_unbind_channel(lp); } typedef struct { @@ -696,59 +814,59 @@ static void isdn_net_log_packet(u_char * buf, isdn_net_local * lp) { - u_char *p = buf; + u_char *p = buf; unsigned short proto = ETH_P_IP; - int data_ofs; + int data_ofs; ip_ports *ipp; char addinfo[100]; - addinfo[0] = '\0'; - switch (lp->p_encap) { - case ISDN_NET_ENCAP_IPTYP: - proto = ntohs(*(unsigned short *)&buf[0]); - p = &buf[2]; - break; - case ISDN_NET_ENCAP_ETHER: - proto = ntohs(*(unsigned short *)&buf[12]); - p = &buf[14]; - break; - case ISDN_NET_ENCAP_CISCOHDLC: - proto = ntohs(*(unsigned short *)&buf[2]); - p = &buf[4]; - break; - } + addinfo[0] = '\0'; + switch (lp->p_encap) { + case ISDN_NET_ENCAP_IPTYP: + proto = ntohs(*(unsigned short *) &buf[0]); + p = &buf[2]; + break; + case ISDN_NET_ENCAP_ETHER: + proto = ntohs(*(unsigned short *) &buf[12]); + p = &buf[14]; + break; + case ISDN_NET_ENCAP_CISCOHDLC: + proto = ntohs(*(unsigned short *) &buf[2]); + p = &buf[4]; + break; + } data_ofs = ((p[0] & 15) * 4); - switch (proto) { + switch (proto) { case ETH_P_IP: switch (p[9]) { - case 1: - strcpy(addinfo, " ICMP"); - break; - case 2: - strcpy(addinfo, " IGMP"); - break; - case 4: - strcpy(addinfo, " IPIP"); - break; - case 6: - ipp = (ip_ports *) (&p[data_ofs]); - sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source), - ntohs(ipp->dest)); - break; - case 8: - strcpy(addinfo, " EGP"); - break; - case 12: - strcpy(addinfo, " PUP"); - break; - case 17: - ipp = (ip_ports *) (&p[data_ofs]); - sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source), - ntohs(ipp->dest)); - break; - case 22: - strcpy(addinfo, " IDP"); - break; + case 1: + strcpy(addinfo, " ICMP"); + break; + case 2: + strcpy(addinfo, " IGMP"); + break; + case 4: + strcpy(addinfo, " IPIP"); + break; + case 6: + ipp = (ip_ports *) (&p[data_ofs]); + sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source), + ntohs(ipp->dest)); + break; + case 8: + strcpy(addinfo, " EGP"); + break; + case 12: + strcpy(addinfo, " PUP"); + break; + case 17: + ipp = (ip_ports *) (&p[data_ofs]); + sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source), + ntohs(ipp->dest)); + break; + case 22: + strcpy(addinfo, " IDP"); + break; } printk(KERN_INFO "OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n", p[12], p[13], p[14], p[15], @@ -760,7 +878,7 @@ p[14], p[15], p[16], p[17], p[24], p[25], p[26], p[27]); break; - } + } } /* @@ -772,28 +890,27 @@ * Side-effects: ndev->tbusy is cleared on success. */ int -isdn_net_send_skb(struct device *ndev, isdn_net_local *lp, - struct sk_buff *skb) +isdn_net_send_skb(struct device *ndev, isdn_net_local * lp, + struct sk_buff *skb) { int ret; - int len = skb->len; /* save len */ - - ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb); + int len = skb->len; /* save len */ + + ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb); if (ret == len) { lp->transcount += len; - clear_bit(0, (void *)&(ndev->tbusy)); + clear_bit(0, (void *) &(ndev->tbusy)); + return 0; + } + if (ret < 0) { + dev_kfree_skb(skb, FREE_WRITE); + lp->stats.tx_errors++; + clear_bit(0, (void *) &(ndev->tbusy)); return 0; } - if (ret < 0) { - skb->free = 1; - dev_kfree_skb(skb, FREE_WRITE); - lp->stats.tx_errors++; - clear_bit(0, (void *)&(ndev->tbusy)); - return 0; - } return 1; -} - +} + /* * Helper function for isdn_net_start_xmit. @@ -807,23 +924,23 @@ */ static int -isdn_net_xmit(struct device *ndev, isdn_net_local *lp, struct sk_buff *skb) +isdn_net_xmit(struct device *ndev, isdn_net_local * lp, struct sk_buff *skb) { - int ret; + int ret; /* For the other encaps the header has already been built */ #ifdef CONFIG_ISDN_PPP if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) { return isdn_ppp_xmit(skb, ndev); } -#endif +#endif /* Reset hangup-timeout */ lp->huptimer = 0; - if (lp->cps > 7000) { + if (lp->cps > lp->triggercps) { /* Device overloaded */ - /* - * Packet-delivery via round-robin over master + /* + * Packet-delivery via round-robin over master * and all connected slaves. */ if (lp->master) @@ -847,18 +964,16 @@ /* First time overload: set timestamp only */ lp->sqfull = 1; lp->sqfull_stamp = jiffies; - } - else { + } else { /* subsequent overload: if slavedelay exceeded, start dialing */ if ((jiffies - lp->sqfull_stamp) > lp->slavedelay) isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv); } } - } - else { + } else { /* Not overloaded, deliver locally */ ret = isdn_net_send_skb(ndev, lp, skb); - if (lp->sqfull && ((jiffies - lp->sqfull_stamp) > (lp->slavedelay + (10*HZ) ))) + if (lp->sqfull && ((jiffies - lp->sqfull_stamp) > (lp->slavedelay + (10 * HZ)))) lp->sqfull = 0; } return ret; @@ -877,8 +992,8 @@ if (ndev->tbusy) { if (jiffies - ndev->trans_start < (2 * HZ)) return 1; - if (!lp->dialstate) - lp->stats.tx_errors++; + if (!lp->dialstate) + lp->stats.tx_errors++; ndev->tbusy = 0; ndev->trans_start = jiffies; } @@ -887,10 +1002,10 @@ return 0; } /* Avoid timer-based retransmission conflicts. */ - if (set_bit(0, (void *) &ndev->tbusy) != 0) + if (test_and_set_bit(0, (void *) &ndev->tbusy) != 0) printk(KERN_WARNING - "%s: Transmitter access conflict.\n", - ndev->name); + "%s: Transmitter access conflict.\n", + ndev->name); else { u_char *buf = skb->data; #ifdef ISDN_DEBUG_NET_DUMP @@ -903,23 +1018,31 @@ save_flags(flags); cli(); /* Grab a free ISDN-Channel */ - if ((chi = - isdn_get_free_channel(ISDN_USAGE_NET, - lp->l2_proto, - lp->l3_proto, - lp->pre_device, - lp->pre_channel)) < 0) { - printk(KERN_WARNING - "isdn_net_start_xmit: No channel for %s\n", - ndev->name); + if ((chi = + isdn_get_free_channel(ISDN_USAGE_NET, + lp->l2_proto, + lp->l3_proto, + lp->pre_device, + lp->pre_channel)) < 0) { restore_flags(flags); - /* we probably should drop the skb here and return 0 to omit - 'socket destroy delayed' messages */ +#if 0 + printk(KERN_WARNING + "isdn_net_start_xmit: No channel for %s\n", + ndev->name); + /* we probably should drop the skb here and return 0 to omit + 'socket destroy delayed' messages */ return 1; +#else + isdn_net_unreachable(ndev, skb, + "No channel"); + dev_kfree_skb(skb, FREE_WRITE); + ndev->tbusy = 0; + return 0; +#endif } - /* Log packet, which triggered dialing */ + /* Log packet, which triggered dialing */ if (dev->net_verbose) - isdn_net_log_packet(buf, lp); + isdn_net_log_packet(buf, lp); lp->dialstate = 1; lp->flags |= ISDN_NET_CONNECTED; /* Connect interface with channel */ @@ -928,59 +1051,47 @@ if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) { /* no 'first_skb' handling for syncPPP */ if (isdn_ppp_bind(lp) < 0) { - dev_kfree_skb(skb,FREE_WRITE); + dev_kfree_skb(skb, FREE_WRITE); isdn_net_unbind_channel(lp); - restore_flags(flags); + restore_flags(flags); return 0; /* STN (skb to nirvana) ;) */ } restore_flags(flags); - isdn_net_dial(); /* Initiate dialing */ + isdn_net_dial(); /* Initiate dialing */ return 1; /* let upper layer requeue skb packet */ } #endif - /* remember first skb to speed up arp - * when using encap ETHER - */ - if (lp->first_skb) { - printk(KERN_WARNING "isdn_net_start_xmit: First skb already set!\n"); - dev_kfree_skb(lp->first_skb,FREE_WRITE); - lp->first_skb = NULL; - } - lp->first_skb = skb; + /* remember first skb to speed up arp + * when using encap ETHER + */ + if (lp->first_skb) { + printk(KERN_WARNING "isdn_net_start_xmit: First skb already set!\n"); + dev_kfree_skb(lp->first_skb, FREE_WRITE); + lp->first_skb = NULL; + } + lp->first_skb = skb; /* Initiate dialing */ - ndev->tbusy = 0; + ndev->tbusy = 0; restore_flags(flags); isdn_net_dial(); - return 0; + return 0; } else { - /* - * Having no phone-number is a permanent - * failure or misconfiguration. - * Instead of just dropping, we should also - * have the upper layers to respond - * with an ICMP No route to host in the - * future, however at the moment, i don't - * know a simple way to do that. - * The same applies, when the telecom replies - * "no destination" to our dialing-attempt. - */ - printk(KERN_WARNING - "isdn_net: No phone number for %s, packet dropped\n", - ndev->name); + isdn_net_unreachable(ndev, skb, + "No phone number"); dev_kfree_skb(skb, FREE_WRITE); ndev->tbusy = 0; - return 0; + return 0; } } else { - /* Connection is established, try sending */ + /* Connection is established, try sending */ ndev->trans_start = jiffies; if (!lp->dialstate) { - if (lp->first_skb) { - if (isdn_net_xmit(ndev,lp,lp->first_skb)) - return 1; - lp->first_skb = NULL; - } - return(isdn_net_xmit(ndev, lp, skb)); + if (lp->first_skb) { + if (isdn_net_xmit(ndev, lp, lp->first_skb)) + return 1; + lp->first_skb = NULL; + } + return (isdn_net_xmit(ndev, lp, skb)); } else ndev->tbusy = 1; } @@ -998,7 +1109,6 @@ dev->tbusy = 1; dev->start = 0; - isdn_net_hangup(dev); if ((p = (((isdn_net_local *) dev->priv)->slave))) { /* If this interface has slaves, stop them also */ while (p) { @@ -1008,6 +1118,7 @@ p = (((isdn_net_local *) p->priv)->slave); } } + isdn_net_hangup(dev); isdn_MOD_DEC_USE_COUNT(); return 0; } @@ -1016,7 +1127,7 @@ * Get statistics */ static struct enet_statistics * - isdn_net_get_stats(struct device *dev) +isdn_net_get_stats(struct device *dev) { isdn_net_local *lp = (isdn_net_local *) dev->priv; return &lp->stats; @@ -1031,51 +1142,50 @@ * This is normal practice and works for any 'now in use' protocol. */ -unsigned short isdn_net_type_trans(struct sk_buff *skb, struct device *dev) +static unsigned short +isdn_net_type_trans(struct sk_buff *skb, struct device *dev) { - struct ethhdr *eth; - unsigned char *rawp; - - skb_pull(skb,ETH_HLEN); - eth= skb->mac.ethernet; - - if(*eth->h_dest&1) { - if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0) - skb->pkt_type=PACKET_BROADCAST; - else - skb->pkt_type=PACKET_MULTICAST; - } - - /* - * This ALLMULTI check should be redundant by 1.4 - * so don't forget to remove it. - */ - - else if (dev->flags&(IFF_PROMISC|IFF_ALLMULTI)) { - if (memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN)) - skb->pkt_type=PACKET_OTHERHOST; - } - - if (ntohs(eth->h_proto) >= 1536) - return eth->h_proto; - - rawp = skb->data; - - /* - * This is a magic hack to spot IPX packets. Older Novell breaks - * the protocol design and runs IPX over 802.3 without an 802.2 LLC - * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This - * won't work for fault tolerant netware but does for the rest. - */ - if (*(unsigned short *)rawp == 0xFFFF) - return htons(ETH_P_802_3); - /* - * Real 802.2 LLC - */ - return htons(ETH_P_802_2); + struct ethhdr *eth; + unsigned char *rawp; + + skb_pull(skb, ETH_HLEN); + eth = skb->mac.ethernet; + + if (*eth->h_dest & 1) { + if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_MULTICAST; + } + /* + * This ALLMULTI check should be redundant by 1.4 + * so don't forget to remove it. + */ + + else if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { + if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN)) + skb->pkt_type = PACKET_OTHERHOST; + } + if (ntohs(eth->h_proto) >= 1536) + return eth->h_proto; + + rawp = skb->data; + + /* + * This is a magic hack to spot IPX packets. Older Novell breaks + * the protocol design and runs IPX over 802.3 without an 802.2 LLC + * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This + * won't work for fault tolerant netware but does for the rest. + */ + if (*(unsigned short *) rawp == 0xFFFF) + return htons(ETH_P_802_3); + /* + * Real 802.2 LLC + */ + return htons(ETH_P_802_2); } -/* +/* * Got a packet from ISDN-Channel. */ static void @@ -1083,21 +1193,21 @@ { isdn_net_local *lp = (isdn_net_local *) ndev->priv; #ifdef CONFIG_ISDN_PPP - isdn_net_local *olp = lp; /* original 'lp' */ - int proto = PPP_PROTOCOL(skb->data); + isdn_net_local *olp = lp; /* original 'lp' */ + int proto = PPP_PROTOCOL(skb->data); #endif lp->transcount += skb->len; lp->stats.rx_packets++; #ifdef CONFIG_ISDN_PPP - /* - * If encapsulation is syncppp, don't reset - * huptimer on LCP packets. - */ - if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP || - (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && proto != PPP_LCP)) + /* + * If encapsulation is syncppp, don't reset + * huptimer on LCP packets. + */ + if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP || + (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && proto != PPP_LCP)) #endif - lp->huptimer = 0; + lp->huptimer = 0; if (lp->master) { /* Bundling: If device is a slave-device, deliver to master, also @@ -1107,56 +1217,55 @@ lp = (isdn_net_local *) ndev->priv; lp->stats.rx_packets++; #ifdef CONFIG_ISDN_PPP - /* - * If encapsulation is syncppp, don't reset - * huptimer on LCP packets. - */ - if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP || - (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && proto != PPP_LCP)) + /* + * If encapsulation is syncppp, don't reset + * huptimer on LCP packets. + */ + if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP || + (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && proto != PPP_LCP)) #endif - lp->huptimer = 0; + lp->huptimer = 0; } - skb->dev = ndev; skb->pkt_type = PACKET_HOST; - skb->mac.raw = skb->data; + skb->mac.raw = skb->data; #ifdef ISDN_DEBUG_NET_DUMP - isdn_dumppkt("R:", skb->data, skb->len, 40); + isdn_dumppkt("R:", skb->data, skb->len, 40); #endif switch (lp->p_encap) { - case ISDN_NET_ENCAP_ETHER: - /* Ethernet over ISDN */ - skb->protocol = isdn_net_type_trans(skb,ndev); - break; - case ISDN_NET_ENCAP_UIHDLC: - /* HDLC with UI-frame (for ispa with -h1 option) */ - skb_pull(skb,2); - /* Fall through */ - case ISDN_NET_ENCAP_RAWIP: - /* RAW-IP without MAC-Header */ - skb->protocol = htons(ETH_P_IP); - break; - case ISDN_NET_ENCAP_CISCOHDLC: - /* CISCO-HDLC IP with type field and fake I-frame-header */ - skb_pull(skb, 2); - /* Fall through */ - case ISDN_NET_ENCAP_IPTYP: - /* IP with type field */ - skb->protocol = *(unsigned short *)&(skb->data[0]); - skb_pull(skb, 2); - if (*(unsigned short *)skb->data == 0xFFFF) - skb->protocol = htons(ETH_P_802_3); - break; + case ISDN_NET_ENCAP_ETHER: + /* Ethernet over ISDN */ + skb->protocol = isdn_net_type_trans(skb, ndev); + break; + case ISDN_NET_ENCAP_UIHDLC: + /* HDLC with UI-frame (for ispa with -h1 option) */ + skb_pull(skb, 2); + /* Fall through */ + case ISDN_NET_ENCAP_RAWIP: + /* RAW-IP without MAC-Header */ + skb->protocol = htons(ETH_P_IP); + break; + case ISDN_NET_ENCAP_CISCOHDLC: + /* CISCO-HDLC IP with type field and fake I-frame-header */ + skb_pull(skb, 2); + /* Fall through */ + case ISDN_NET_ENCAP_IPTYP: + /* IP with type field */ + skb->protocol = *(unsigned short *) &(skb->data[0]); + skb_pull(skb, 2); + if (*(unsigned short *) skb->data == 0xFFFF) + skb->protocol = htons(ETH_P_802_3); + break; #ifdef CONFIG_ISDN_PPP - case ISDN_NET_ENCAP_SYNCPPP: - isdn_ppp_receive(lp->netdev, olp, skb); - return; -#endif - default: - printk(KERN_WARNING "%s: unknown encapsulation, dropping\n", - lp->name); - kfree_skb(skb,FREE_READ); - return; + case ISDN_NET_ENCAP_SYNCPPP: + isdn_ppp_receive(lp->netdev, olp, skb); + return; +#endif + default: + printk(KERN_WARNING "%s: unknown encapsulation, dropping\n", + lp->name); + kfree_skb(skb, FREE_READ); + return; } netif_rx(skb); return; @@ -1168,34 +1277,7 @@ * else return 0. */ int -isdn_net_receive_callback(int idx, u_char * buf, int len) -{ - isdn_net_dev *p = dev->rx_netdev[idx]; - struct sk_buff *skb; - - if (p) { - isdn_net_local *lp = &p->local; - if ((lp->flags & ISDN_NET_CONNECTED) && - (!lp->dialstate)) { - skb = dev_alloc_skb(len); - if (skb == NULL) { - printk(KERN_WARNING "out of memory\n"); - return 0; - } - memcpy(skb_put(skb, len), buf, len); - isdn_net_receive(&p->dev, skb); - return 1; - } - } - return 0; -} - -/* - * receive callback for lowlevel drivers, which support skb's - */ - -int -isdn_net_rcv_skb(int idx, struct sk_buff *skb) +isdn_net_rcv_skb(int idx, struct sk_buff *skb) { isdn_net_dev *p = dev->rx_netdev[idx]; @@ -1212,43 +1294,41 @@ static int my_eth_header(struct sk_buff *skb, struct device *dev, unsigned short type, - void *daddr, void *saddr, unsigned len) + void *daddr, void *saddr, unsigned len) { - struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN); + struct ethhdr *eth = (struct ethhdr *) skb_push(skb, ETH_HLEN); - /* + /* * Set the protocol type. For a packet of type ETH_P_802_3 we - * put the length here instead. It is up to the 802.2 layer to - * carry protocol information. + * put the length here instead. It is up to the 802.2 layer to + * carry protocol information. */ - - if(type!=ETH_P_802_3) + + if (type != ETH_P_802_3) eth->h_proto = htons(type); else eth->h_proto = htons(len); /* - * Set the source hardware address. + * Set the source hardware address. */ - if(saddr) - memcpy(eth->h_source,saddr,dev->addr_len); + if (saddr) + memcpy(eth->h_source, saddr, dev->addr_len); else - memcpy(eth->h_source,dev->dev_addr,dev->addr_len); + memcpy(eth->h_source, dev->dev_addr, dev->addr_len); /* - * Anyway, the loopback-device should never use this function... + * Anyway, the loopback-device should never use this function... */ if (dev->flags & IFF_LOOPBACK) { memset(eth->h_dest, 0, dev->addr_len); - return(dev->hard_header_len); + return (dev->hard_header_len); } - - if(daddr) { - memcpy(eth->h_dest,daddr,dev->addr_len); + if (daddr) { + memcpy(eth->h_dest, daddr, dev->addr_len); return dev->hard_header_len; } - return -dev->hard_header_len; } @@ -1256,76 +1336,114 @@ * build an header * depends on encaps that is being used. */ - + static int isdn_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, - void *daddr, void *saddr, unsigned plen) + void *daddr, void *saddr, unsigned plen) { isdn_net_local *lp = dev->priv; ushort len = 0; - + switch (lp->p_encap) { - case ISDN_NET_ENCAP_ETHER: - len = my_eth_header(skb, dev, type, daddr, saddr, plen); - break; - case ISDN_NET_ENCAP_RAWIP: - printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n"); + case ISDN_NET_ENCAP_ETHER: + len = my_eth_header(skb, dev, type, daddr, saddr, plen); + break; +#ifdef CONFIG_ISDN_PPP + case ISDN_NET_ENCAP_SYNCPPP: + /* stick on a fake header to keep fragmentation code happy. */ + len = IPPP_MAX_HEADER; + skb_push(skb,len); + break; +#endif + case ISDN_NET_ENCAP_RAWIP: + printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n"); len = 0; - break; - case ISDN_NET_ENCAP_IPTYP: - /* ethernet type field */ - *((ushort*) skb_push(skb, 2)) = htons(type); - len = 2; - break; - case ISDN_NET_ENCAP_UIHDLC: - /* HDLC with UI-Frames (for ispa with -h1 option) */ - *((ushort*) skb_push(skb, 2)) = htons(0x0103); - len = 2; - break; - case ISDN_NET_ENCAP_CISCOHDLC: + break; + case ISDN_NET_ENCAP_IPTYP: + /* ethernet type field */ + *((ushort *) skb_push(skb, 2)) = htons(type); + len = 2; + break; + case ISDN_NET_ENCAP_UIHDLC: + /* HDLC with UI-Frames (for ispa with -h1 option) */ + *((ushort *) skb_push(skb, 2)) = htons(0x0103); + len = 2; + break; + case ISDN_NET_ENCAP_CISCOHDLC: skb_push(skb, 4); - skb->data[0] = 0x0f; - skb->data[1] = 0x00; - *((ushort*)&skb->data[2]) = htons(type); - len = 4; - break; + skb->data[0] = 0x0f; + skb->data[1] = 0x00; + *((ushort *) & skb->data[2]) = htons(type); + len = 4; + break; } return len; } /* We don't need to send arp, because we have point-to-point connections. */ - +#if (LINUX_VERSION_CODE < 0x02010F) static int isdn_net_rebuild_header(void *buff, struct device *dev, unsigned long dst, - struct sk_buff *skb) + struct sk_buff *skb) { isdn_net_local *lp = dev->priv; - int ret = 0; + int ret = 0; + + if (lp->p_encap == ISDN_NET_ENCAP_ETHER) { + struct ethhdr *eth = (struct ethhdr *) buff; + + /* + * Only ARP/IP is currently supported + */ - if (lp->p_encap == ISDN_NET_ENCAP_ETHER) { - struct ethhdr *eth = (struct ethhdr *)buff; - - /* - * Only ARP/IP is currently supported - */ - - if(eth->h_proto != htons(ETH_P_IP)) { - printk(KERN_WARNING - "isdn_net: %s don't know how to resolve type %d addresses?\n", - dev->name, (int)eth->h_proto); - memcpy(eth->h_source, dev->dev_addr, dev->addr_len); - return 0; - } - /* - * Try to get ARP to resolve the header. - */ -#ifdef CONFIG_INET - ret = arp_find(eth->h_dest, dst, dev, dev->pa_addr, skb)? 1 : 0; -#endif - } + if (eth->h_proto != htons(ETH_P_IP)) { + printk(KERN_WARNING + "isdn_net: %s don't know how to resolve type %d addresses?\n", + dev->name, (int) eth->h_proto); + memcpy(eth->h_source, dev->dev_addr, dev->addr_len); + return 0; + } + /* + * Try to get ARP to resolve the header. + */ +#ifdef CONFIG_INET + ret = arp_find(eth->h_dest, dst, dev, dev->pa_addr, skb) ? 1 : 0; +#endif + } return ret; } +#else +static int +isdn_net_rebuild_header(struct sk_buff *skb) +{ + struct device *dev = skb->dev; + isdn_net_local *lp = dev->priv; + int ret = 0; + + if (lp->p_encap == ISDN_NET_ENCAP_ETHER) { + struct ethhdr *eth = (struct ethhdr *) skb->data; + /* + * Only ARP/IP is currently supported + */ + + if (eth->h_proto != htons(ETH_P_IP)) { + printk(KERN_WARNING + "isdn_net: %s don't know how to resolve type %d addresses?\n", + dev->name, (int) eth->h_proto); + memcpy(eth->h_source, dev->dev_addr, dev->addr_len); + return 0; + } + /* + * Try to get ARP to resolve the header. + */ +#ifdef CONFIG_INET + ret = arp_find(eth->h_dest, skb) ? 1 : 0; +#endif + } + return ret; +} +#endif /* * Interface-setup. (called just after registering a new interface) */ @@ -1333,8 +1451,9 @@ isdn_net_init(struct device *ndev) { ushort max_hlhdr_len = 0; - isdn_net_local *lp = (isdn_net_local *)ndev->priv; - int drvidx, i; + isdn_net_local *lp = (isdn_net_local *) ndev->priv; + int drvidx, + i; if (ndev == NULL) { printk(KERN_WARNING "isdn_net_init: dev = NULL!\n"); @@ -1344,54 +1463,61 @@ printk(KERN_WARNING "isdn_net_init: dev->priv = NULL!\n"); return -ENODEV; } - - ether_setup(ndev); - lp->org_hcb = ndev->header_cache_bind; - lp->org_hcu = ndev->header_cache_update; + ether_setup(ndev); +#if (LINUX_VERSION_CODE < 0x02010F) + lp->org_hcb = ndev->header_cache_bind; +#else + lp->org_hhc = ndev->hard_header_cache; +#endif + lp->org_hcu = ndev->header_cache_update; /* Setup the generic properties */ - ndev->hard_header = NULL; - ndev->header_cache_bind = NULL; - ndev->header_cache_update = NULL; - ndev->mtu = 1500; - ndev->flags = IFF_NOARP; - ndev->family = AF_INET; - ndev->type = ARPHRD_ETHER; - ndev->addr_len = ETH_ALEN; - ndev->pa_addr = 0; - ndev->pa_brdaddr = 0; - ndev->pa_mask = 0; - ndev->pa_alen = 4; + ndev->hard_header = NULL; +#if (LINUX_VERSION_CODE < 0x02010F) + ndev->header_cache_bind = NULL; +#else + ndev->hard_header_cache = NULL; +#endif + ndev->header_cache_update = NULL; + ndev->mtu = 1500; + ndev->flags = IFF_NOARP; + ndev->family = AF_INET; + ndev->type = ARPHRD_ETHER; + ndev->addr_len = ETH_ALEN; + ndev->pa_addr = 0; + ndev->pa_brdaddr = 0; + ndev->pa_mask = 0; + ndev->pa_alen = 4; - for (i = 0; i < ETH_ALEN; i++) - ndev->broadcast[i]=0xff; + for (i = 0; i < ETH_ALEN; i++) + ndev->broadcast[i] = 0xff; for (i = 0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&ndev->buffs[i]); - + skb_queue_head_init(&ndev->buffs[i]); + /* The ISDN-specific entries in the device structure. */ - ndev->open = &isdn_net_open; - ndev->hard_start_xmit = &isdn_net_start_xmit; + 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 - */ - + */ + for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) if (dev->drv[drvidx]) if (max_hlhdr_len < dev->drv[drvidx]->interface->hl_hdrlen) max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen; - ndev->hard_header_len = ETH_HLEN + max_hlhdr_len; + 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; + ndev->stop = &isdn_net_close; + ndev->get_stats = &isdn_net_get_stats; + ndev->rebuild_header = &isdn_net_rebuild_header; #ifdef CONFIG_ISDN_PPP - ndev->do_ioctl = isdn_ppp_dev_ioctl; + ndev->do_ioctl = isdn_ppp_dev_ioctl; #endif return 0; } @@ -1432,35 +1558,35 @@ for (; *p; s++, p++) switch (*p) { - case '\\': - /* - * Literal match with following character, - * fall through. - */ - p++; - default: - if (*s != *p) - return (0); - continue; - case '?': - /* Match anything. */ - if (*s == '\0') - return (0); - continue; - case '*': - /* Trailing star matches everything. */ - return (*++p ? isdn_net_Star(s, p) : 1); - case '[': - /* [^....] means inverse character class. */ - if ((reverse = (p[1] == '^'))) - p++; - for (last = 0, matched = 0; *++p && (*p != ']'); last = *p) - /* This next line requires a good C compiler. */ - if (*p == '-' ? *s <= *++p && *s >= last : *s == *p) - matched = 1; - if (matched == reverse) - return (0); - continue; + case '\\': + /* + * Literal match with following character, + * fall through. + */ + p++; + default: + if (*s != *p) + return (0); + continue; + case '?': + /* Match anything. */ + if (*s == '\0') + return (0); + continue; + case '*': + /* Trailing star matches everything. */ + return (*++p ? isdn_net_Star(s, p) : 1); + case '[': + /* [^....] means inverse character class. */ + if ((reverse = (p[1] == '^'))) + p++; + for (last = 0, matched = 0; *++p && (*p != ']'); last = *p) + /* This next line requires a good C compiler. */ + if (*p == '-' ? *s <= *++p && *s >= last : *s == *p) + matched = 1; + if (matched == reverse) + return (0); + continue; } return (*s == '\0'); } @@ -1477,12 +1603,12 @@ while (p) { if (p->local.pre_device == drvidx) switch (p->local.pre_channel) { - case 0: - p->local.pre_channel = 1; - break; - case 1: - p->local.pre_channel = 0; - break; + case 0: + p->local.pre_channel = 1; + break; + case 1: + p->local.pre_channel = 0; + break; } p = (isdn_net_dev *) p->next; } @@ -1519,7 +1645,7 @@ * 4 = Wait cbdelay, then call back */ int -isdn_net_find_icall(int di, int ch, int idx, char *num) +isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) { char *eaz; int si1; @@ -1530,40 +1656,24 @@ isdn_net_dev *p; isdn_net_phone *n; ulong flags; - char nr[31]; - char *s; + char nr[32]; /* Search name in netdev-chain */ save_flags(flags); cli(); - if (num[0] == ',') { + if (!setup.phone[0]) { nr[0] = '0'; - strncpy(&nr[1], num, 30); + nr[1] = '\0'; printk(KERN_INFO "isdn_net: Incoming call without OAD, assuming '0'\n"); } else - strncpy(nr, num, 30); - s = strtok(nr, ","); - s = strtok(NULL, ","); - if (!s) { - printk(KERN_WARNING "isdn_net: Incoming callinfo garbled, ignored: %s\n", - num); - restore_flags(flags); - return 0; - } - si1 = (int)simple_strtoul(s,NULL,10); - s = strtok(NULL, ","); - if (!s) { - printk(KERN_WARNING "isdn_net: Incoming callinfo garbled, ignored: %s\n", - num); - restore_flags(flags); - return 0; - } - si2 = (int)simple_strtoul(s,NULL,10); - eaz = strtok(NULL, ","); - if (!eaz) { + 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; 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) */ @@ -1583,12 +1693,12 @@ while (p) { /* If last check has triggered as binding-swap, revert it */ switch (swapped) { - case 2: - isdn_net_swap_usage(idx, sidx); - /* fall through */ - case 1: - isdn_net_swapbind(di); - break; + case 2: + isdn_net_swap_usage(idx, sidx); + /* fall through */ + case 1: + isdn_net_swapbind(di); + break; } swapped = 0; if (!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) @@ -1597,11 +1707,11 @@ 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 ((!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 */ ))) { #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n", @@ -1611,13 +1721,13 @@ if ((p->local.pre_channel != ch) || (p->local.pre_device != di)) { /* Here we got a problem: - If using an ICN-Card, an incoming call is always signaled on - on the first channel of the card, if both channels are - down. However this channel may be bound exclusive. If the - second channel is free, this call should be accepted. - The solution is horribly but it runs, so what: - We exchange the exclusive bindings of the two channels, the - corresponding variables in the interface-structs. + * If using an ICN-Card, an incoming call is always signaled on + * on the first channel of the card, if both channels are + * down. However this channel may be bound exclusive. If the + * second channel is free, this call should be accepted. + * The solution is horribly but it runs, so what: + * We exchange the exclusive bindings of the two channels, the + * corresponding variables in the interface-structs. */ if (ch == 0) { sidx = isdn_dc2minor(di, 1); @@ -1626,13 +1736,13 @@ #endif if (USG_NONE(dev->usage[sidx])) { /* Second Channel is free, now see if it is bound - exclusive too. */ + * exclusive too. */ if (dev->usage[sidx] & ISDN_USAGE_EXCLUSIVE) { #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: 2nd channel is down and bound\n"); #endif /* Yes, swap bindings only, if the original - binding is bound to channel 1 of this driver */ + * binding is bound to channel 1 of this driver */ if ((p->local.pre_device == di) && (p->local.pre_channel == 1)) { isdn_net_swapbind(di); @@ -1674,7 +1784,7 @@ continue; } } - } /* if (dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) */ + } #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: match2\n"); #endif @@ -1739,7 +1849,7 @@ return 0; } /* Setup dialstate. */ - lp->dtimer = 0; + lp->dtimer = 0; lp->dialstate = 11; lp->flags |= ISDN_NET_CONNECTED; /* Connect interface with channel */ @@ -1754,7 +1864,7 @@ #endif /* Initiate dialing by returning 2 or 4 */ restore_flags(flags); - return (lp->flags & ISDN_NET_CBHUP)?2:4; + return (lp->flags & ISDN_NET_CBHUP) ? 2 : 4; } else printk(KERN_WARNING "isdn_net: %s: No phone number\n", lp->name); restore_flags(flags); @@ -1766,28 +1876,27 @@ device, so free this device */ if ((p->local.dialstate == 4) || (p->local.dialstate == 12)) { #ifdef CONFIG_ISDN_PPP - if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) - isdn_ppp_free(lp); + if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) + isdn_ppp_free(lp); #endif isdn_free_channel(p->local.isdn_device, p->local.isdn_channel, ISDN_USAGE_NET); - } + } dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE; dev->usage[idx] |= ISDN_USAGE_NET; strcpy(dev->num[idx], nr); isdn_info_update(); - dev->st_netdev[idx] = lp->netdev; + dev->st_netdev[idx] = lp->netdev; p->local.isdn_device = di; p->local.isdn_channel = ch; p->local.ppp_slot = -1; - p->local.pppbind = -1; p->local.flags |= ISDN_NET_CONNECTED; p->local.dialstate = 7; p->local.dtimer = 0; p->local.outgoing = 0; p->local.huptimer = 0; - p->local.hupflags |= 1; - p->local.hupflags &= ~2; + p->local.hupflags |= ISDN_WAITCHARGE; + p->local.hupflags &= ~ISDN_HAVECHARGE; #ifdef CONFIG_ISDN_PPP if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) if (isdn_ppp_bind(lp) < 0) { @@ -1814,7 +1923,7 @@ * Search list of net-interfaces for an interface with given name. */ isdn_net_dev * - isdn_net_findif(char *name) +isdn_net_findif(char *name) { isdn_net_dev *p = dev->netdev; @@ -1831,7 +1940,8 @@ * This is called from the userlevel-routine below or * from isdn_net_start_xmit(). */ -int isdn_net_force_dial_lp(isdn_net_local * lp) +int +isdn_net_force_dial_lp(isdn_net_local * lp) { if ((!(lp->flags & ISDN_NET_CONNECTED)) && !lp->dialstate) { int chi; @@ -1841,8 +1951,8 @@ cli(); /* Grab a free ISDN-Channel */ if ((chi = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto, - lp->l3_proto, - lp->pre_device, + lp->l3_proto, + lp->pre_device, lp->pre_channel)) < 0) { printk(KERN_WARNING "isdn_net_force_dial: No channel for %s\n", lp->name); restore_flags(flags); @@ -1874,7 +1984,7 @@ * Force a net-interface to dial out. * This is always called from within userspace (ISDN_IOCTL_NET_DIAL). */ -int +int isdn_net_force_dial(char *name) { isdn_net_dev *p = isdn_net_findif(name); @@ -1906,9 +2016,9 @@ strcpy(netdev->local.name, " "); else strcpy(netdev->local.name, name); - netdev->dev.name = netdev->local.name; - netdev->dev.priv = &netdev->local; - netdev->dev.init = isdn_net_init; + netdev->dev.name = netdev->local.name; + netdev->dev.priv = &netdev->local; + netdev->dev.init = isdn_net_init; netdev->local.p_encap = ISDN_NET_ENCAP_RAWIP; if (master) { /* Device shall be a slave */ @@ -1936,7 +2046,7 @@ netdev->local.magic = ISDN_NET_MAGIC; #ifdef CONFIG_ISDN_PPP - netdev->mp_last = NULL; /* mpqueue is empty */ + netdev->mp_last = NULL; /* mpqueue is empty */ netdev->ib.next_num = 0; netdev->ib.last = NULL; #endif @@ -1952,18 +2062,19 @@ netdev->local.exclusive = -1; netdev->local.ppp_slot = -1; netdev->local.pppbind = -1; - netdev->local.sav_skb = NULL; - netdev->local.first_skb = NULL; + netdev->local.sav_skb = NULL; + netdev->local.first_skb = NULL; netdev->local.l2_proto = ISDN_PROTO_L2_X75I; netdev->local.l3_proto = ISDN_PROTO_L3_TRANS; + netdev->local.triggercps = 6000; netdev->local.slavedelay = 10 * HZ; netdev->local.srobin = &netdev->dev; - netdev->local.hupflags = 8; /* Do hangup even on incoming calls */ + 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.cbdelay = 25; /* Wait 5 secs before Callback */ + netdev->local.flags = ISDN_NET_CBHUP; /* Hangup before Callback */ + netdev->local.cbdelay = 25; /* Wait 5 secs before Callback */ /* Put into to netdev-chain */ netdev->next = (void *) dev->netdev; dev->netdev = netdev; @@ -1989,6 +2100,9 @@ /* Master must be a real interface, not a slave */ if (n->local.master) return NULL; + /* Master must not be started yet */ + if (n->dev.start) + return NULL; return (isdn_net_new(newname, &(n->dev))); } return NULL; @@ -2000,7 +2114,8 @@ * for not overwriting existing setups. It has to get the current * setup first, if only selected parameters are to be changed. */ -int isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) +int +isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) { isdn_net_dev *p = isdn_net_findif(cfg->name); ulong features; @@ -2020,33 +2135,34 @@ printk(KERN_WARNING "isdn_net: No driver with selected features\n"); return -ENODEV; } - if (p->local.p_encap != cfg->p_encap) - if (p->dev.start) { - printk(KERN_WARNING - "%s: cannot change encap when if is up\n", - p->local.name); - return -EBUSY; - } - if (cfg->p_encap == ISDN_NET_ENCAP_SYNCPPP) { + if (p->local.p_encap != cfg->p_encap) + if (p->dev.start) { + printk(KERN_WARNING + "%s: cannot change encap when if is up\n", + p->local.name); + return -EBUSY; + } + if (cfg->p_encap == ISDN_NET_ENCAP_SYNCPPP) { #ifndef CONFIG_ISDN_PPP - printk(KERN_WARNING "%s: SyncPPP support not configured\n", - p->local.name); - return -EINVAL; + printk(KERN_WARNING "%s: SyncPPP support not configured\n", + p->local.name); + return -EINVAL; #else - p->dev.type = ARPHRD_PPP; /* change ARP type */ - p->dev.addr_len = 0; + p->dev.type = ARPHRD_PPP; /* change ARP type */ + p->dev.addr_len = 0; #endif - } + } if (strlen(cfg->drvid)) { /* A bind has been requested ... */ - char *c,*e; + char *c, + *e; drvidx = -1; chidx = -1; strcpy(drvid, cfg->drvid); if ((c = strchr(drvid, ','))) { /* The channel-number is appended to the driver-Id with a comma */ - chidx = (int)simple_strtoul(c + 1,&e,10); + chidx = (int) simple_strtoul(c + 1, &e, 10); if (e == c) chidx = -1; *c = '\0'; @@ -2071,9 +2187,9 @@ /* If binding is exclusive, try to grab the channel */ save_flags(flags); if ((i = isdn_get_free_channel(ISDN_USAGE_NET, p->local.l2_proto, - p->local.l3_proto, - drvidx, - chidx)) < 0) { + p->local.l3_proto, + drvidx, + chidx)) < 0) { /* Grab failed, because desired channel is in use */ p->local.exclusive = -1; restore_flags(flags); @@ -2089,22 +2205,23 @@ p->local.exclusive = -1; if ((p->local.pre_device != -1) && (cfg->exclusive == -1)) { isdn_unexclusive_channel(p->local.pre_device, p->local.pre_channel); - isdn_free_channel(p->local.pre_device, p->local.pre_channel,ISDN_USAGE_NET); + isdn_free_channel(p->local.pre_device, p->local.pre_channel, ISDN_USAGE_NET); drvidx = -1; chidx = -1; } } strcpy(p->local.msn, cfg->eaz); - p->local.pre_device = drvidx; + p->local.pre_device = drvidx; p->local.pre_channel = chidx; - p->local.onhtime = cfg->onhtime; - p->local.charge = cfg->charge; - p->local.l2_proto = cfg->l2_proto; - p->local.l3_proto = cfg->l3_proto; - p->local.cbdelay = cfg->cbdelay; - p->local.dialmax = cfg->dialmax; - p->local.slavedelay = cfg->slavedelay * HZ; - p->local.pppbind = cfg->pppbind; + p->local.onhtime = cfg->onhtime; + p->local.charge = cfg->charge; + p->local.l2_proto = cfg->l2_proto; + p->local.l3_proto = cfg->l3_proto; + p->local.cbdelay = cfg->cbdelay; + p->local.dialmax = cfg->dialmax; + p->local.triggercps = cfg->triggercps; + p->local.slavedelay = cfg->slavedelay * HZ; + p->local.pppbind = cfg->pppbind; if (cfg->secure) p->local.flags |= ISDN_NET_SECURE; else @@ -2114,47 +2231,63 @@ else p->local.flags &= ~ISDN_NET_CBHUP; switch (cfg->callback) { - case 0: - p->local.flags &= ~(ISDN_NET_CALLBACK|ISDN_NET_CBOUT); - break; - case 1: - p->local.flags |= ISDN_NET_CALLBACK; - p->local.flags &= ~ISDN_NET_CBOUT; - break; - case 2: - p->local.flags |= ISDN_NET_CBOUT; - p->local.flags &= ~ISDN_NET_CALLBACK; - break; - } + case 0: + p->local.flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT); + break; + case 1: + p->local.flags |= ISDN_NET_CALLBACK; + p->local.flags &= ~ISDN_NET_CBOUT; + break; + case 2: + p->local.flags |= ISDN_NET_CBOUT; + p->local.flags &= ~ISDN_NET_CALLBACK; + break; + } if (cfg->chargehup) - p->local.hupflags |= 4; + p->local.hupflags |= ISDN_CHARGEHUP; else - p->local.hupflags &= ~4; + p->local.hupflags &= ~ISDN_CHARGEHUP; if (cfg->ihup) - p->local.hupflags |= 8; + p->local.hupflags |= ISDN_INHUP; else - p->local.hupflags &= ~8; + p->local.hupflags &= ~ISDN_INHUP; + if (cfg->chargeint > 10) { + p->local.hupflags |= ISDN_CHARGEHUP | ISDN_HAVECHARGE | ISDN_MANCHARGE; + p->local.chargeint = cfg->chargeint * HZ; + } if (cfg->p_encap != p->local.p_encap) { - if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) { - p->dev.hard_header = NULL; - p->dev.header_cache_bind = NULL; - p->dev.header_cache_update = NULL; - p->dev.flags = IFF_NOARP; - } else { - p->dev.hard_header = isdn_net_header; - if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) { - p->dev.header_cache_bind = p->local.org_hcb; - p->dev.header_cache_update = p->local.org_hcu; - p->dev.flags = IFF_BROADCAST | IFF_MULTICAST; - } else { - p->dev.header_cache_bind = NULL; - p->dev.header_cache_update = NULL; - p->dev.flags = IFF_NOARP; - } - } - } - p->local.p_encap = cfg->p_encap; - return 0; + if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) { + p->dev.hard_header = NULL; +#if (LINUX_VERSION_CODE < 0x02010F) + p->dev.header_cache_bind = NULL; +#else + p->dev.hard_header_cache = NULL; +#endif + p->dev.header_cache_update = NULL; + p->dev.flags = IFF_NOARP; + } else { + p->dev.hard_header = isdn_net_header; + if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) { +#if (LINUX_VERSION_CODE < 0x02010F) + p->dev.header_cache_bind = p->local.org_hcb; +#else + p->dev.hard_header_cache = p->local.org_hhc; +#endif + p->dev.header_cache_update = p->local.org_hcu; + p->dev.flags = IFF_BROADCAST | IFF_MULTICAST; + } else { +#if (LINUX_VERSION_CODE < 0x02010F) + p->dev.header_cache_bind = NULL; +#else + p->dev.hard_header_cache = NULL; +#endif + p->dev.header_cache_update = NULL; + p->dev.flags = IFF_NOARP; + } + } + } + p->local.p_encap = cfg->p_encap; + return 0; } return -ENODEV; } @@ -2162,7 +2295,8 @@ /* * Perform get-interface-parameters.ioctl */ -int isdn_net_getcfg(isdn_net_ioctl_cfg * cfg) +int +isdn_net_getcfg(isdn_net_ioctl_cfg * cfg) { isdn_net_dev *p = isdn_net_findif(cfg->name); @@ -2181,16 +2315,19 @@ cfg->p_encap = p->local.p_encap; cfg->secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0; cfg->callback = 0; - if (p->local.flags & ISDN_NET_CALLBACK) - cfg->callback = 1; - if (p->local.flags & ISDN_NET_CBOUT) - cfg->callback = 2; + if (p->local.flags & ISDN_NET_CALLBACK) + cfg->callback = 1; + if (p->local.flags & ISDN_NET_CBOUT) + cfg->callback = 2; cfg->cbhup = (p->local.flags & ISDN_NET_CBHUP) ? 1 : 0; 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->cbdelay = p->local.cbdelay; + cfg->dialmax = p->local.dialmax; + 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; if (p->local.slave) strcpy(cfg->slave, ((isdn_net_local *) p->local.slave->priv)->name); @@ -2208,7 +2345,8 @@ /* * Add a phone-number to an interface. */ -int isdn_net_addphone(isdn_net_ioctl_phone * phone) +int +isdn_net_addphone(isdn_net_ioctl_phone * phone) { isdn_net_dev *p = isdn_net_findif(phone->name); isdn_net_phone *n; @@ -2229,7 +2367,8 @@ /* * Return a string of all phone-numbers of an interface. */ -int isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones) +int +isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones) { isdn_net_dev *p = isdn_net_findif(phone->name); int inout = phone->outgoing & 1; @@ -2244,22 +2383,21 @@ save_flags(flags); cli(); inout &= 1; - for (n = p->local.phone[inout]; n; n = n->next) { + for (n = p->local.phone[inout]; n; n = n->next) { if (more) { put_user(' ', phones++); count++; } - if ((ret = verify_area(VERIFY_WRITE, (void *) phones, strlen(n->num) + 1))) { + if ((ret = copy_to_user(phones, n->num, strlen(n->num) + 1))) { restore_flags(flags); return ret; } - copy_to_user(phones, n->num, strlen(n->num) + 1); phones += strlen(n->num); count += strlen(n->num); more = 1; } - put_user(0,phones); - count++; + put_user(0, phones); + count++; restore_flags(flags); return count; } @@ -2268,18 +2406,24 @@ * Delete a phone-number from an interface. */ -int isdn_net_delphone(isdn_net_ioctl_phone * phone) +int +isdn_net_delphone(isdn_net_ioctl_phone * phone) { isdn_net_dev *p = isdn_net_findif(phone->name); int inout = phone->outgoing & 1; isdn_net_phone *n; isdn_net_phone *m; + int flags; if (p) { + save_flags(flags); + cli(); n = p->local.phone[inout]; m = NULL; while (n) { if (!strcmp(n->num, phone->phone)) { + if (p->local.dial == n) + p->local.dial = n->next; if (m) m->next = n->next; else @@ -2290,6 +2434,7 @@ m = n; n = (isdn_net_phone *) n->next; } + restore_flags(flags); return -EINVAL; } return -ENODEV; @@ -2298,7 +2443,8 @@ /* * Delete all phone-numbers of an interface. */ -static int isdn_net_rmallphone(isdn_net_dev * p) +static int +isdn_net_rmallphone(isdn_net_dev * p) { isdn_net_phone *n; isdn_net_phone *m; @@ -2316,6 +2462,7 @@ } p->local.phone[i] = NULL; } + p->local.dial = NULL; restore_flags(flags); return 0; } @@ -2323,7 +2470,8 @@ /* * Force a hangup of a network-interface. */ -int isdn_net_force_hangup(char *name) +int +isdn_net_force_hangup(char *name) { isdn_net_dev *p = isdn_net_findif(name); struct device *q; @@ -2331,13 +2479,13 @@ if (p) { if (p->local.isdn_device < 0) return 1; - isdn_net_hangup(&p->dev); q = p->local.slave; /* If this interface has slaves, do a hangup for them also. */ while (q) { isdn_net_hangup(q); q = (((isdn_net_local *) q->priv)->slave); } + isdn_net_hangup(&p->dev); return 0; } return -ENODEV; @@ -2346,7 +2494,8 @@ /* * Helper-function for isdn_net_rm: Do the real work. */ -static int isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q) +static int +isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q) { int flags; @@ -2399,9 +2548,6 @@ isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0); restore_flags(flags); -#ifdef CONFIG_ISDN_MPP - isdn_ppp_free_mpqueue(p); /* still necessary? */ -#endif kfree(p); return 0; @@ -2410,7 +2556,8 @@ /* * Remove a single network-interface. */ -int isdn_net_rm(char *name) +int +isdn_net_rm(char *name) { isdn_net_dev *p; isdn_net_dev *q; @@ -2433,7 +2580,8 @@ /* * Remove all network-interfaces */ -int isdn_net_rmall(void) +int +isdn_net_rmall(void) { int flags; int ret; @@ -2446,7 +2594,7 @@ /* Remove master-devices only, slaves get removed with their master */ if ((ret = isdn_net_realrm(dev->netdev, NULL))) { restore_flags(flags); - return ret; + return ret; } } } @@ -2455,17 +2603,18 @@ return 0; } -/* +/* * helper function to flush device queues * the better place would be net/core/dev.c */ -void dev_purge_queues(struct device *dev) +static void +dev_purge_queues(struct device *dev) { int i; - for(i=0;ibuffs[i]))) - dev_kfree_skb(skb,FREE_WRITE); - } - + while ((skb = skb_dequeue(&dev->buffs[i]))) + dev_kfree_skb(skb, FREE_WRITE); + } + } diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_net.h linux/drivers/isdn/isdn_net.h --- v2.0.30/linux/drivers/isdn/isdn_net.h Sun Apr 21 01:56:14 1996 +++ linux/drivers/isdn/isdn_net.h Mon Aug 4 17:34:00 1997 @@ -1,11 +1,11 @@ -/* $Id: isdn_net.h,v 1.2 1996/04/20 16:29:43 fritz Exp $ - * +/* $Id: isdn_net.h,v 1.5 1997/02/10 20:12:47 fritz Exp $ + * header for Linux ISDN subsystem, network related functions (linklevel). * * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.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) @@ -18,9 +18,18 @@ * * 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. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_net.h,v $ + * Revision 1.5 1997/02/10 20:12:47 fritz + * Changed interface for reporting incoming calls. + * + * Revision 1.4 1997/02/03 23:16:48 fritz + * Removed isdn_net_receive_callback prototype. + * + * Revision 1.3 1997/01/17 01:19:30 fritz + * Applied chargeint patch. + * * Revision 1.2 1996/04/20 16:29:43 fritz * Misc. typos * @@ -29,24 +38,30 @@ * */ -extern char* isdn_net_new(char *, struct device *); -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_receive_callback(int, u_char *, int); -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, char *); -extern void isdn_net_hangup(struct device *); -extern void isdn_net_dial(void); -extern void isdn_net_autohup(void); -extern int isdn_net_force_hangup(char *); -extern int isdn_net_force_dial(char *); -extern isdn_net_dev* isdn_net_findif(char *); -extern int isdn_net_send_skb(struct device *, isdn_net_local *, - struct sk_buff *); -extern int isdn_net_rcv_skb(int, struct sk_buff *); + /* Definitions for hupflags: */ +#define ISDN_WAITCHARGE 1 /* did not get a charge info yet */ +#define ISDN_HAVECHARGE 2 /* We know a charge info */ +#define ISDN_CHARGEHUP 4 /* We want to use the charge mechanism */ +#define ISDN_INHUP 8 /* Even if incoming, close after huptimeout */ +#define ISDN_MANCHARGE 16 /* Charge Interval manually set */ + +extern char *isdn_net_new(char *, struct device *); +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_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 void isdn_net_hangup(struct device *); +extern void isdn_net_dial(void); +extern void isdn_net_autohup(void); +extern int isdn_net_force_hangup(char *); +extern int isdn_net_force_dial(char *); +extern isdn_net_dev *isdn_net_findif(char *); +extern int isdn_net_send_skb(struct device *, isdn_net_local *, + struct sk_buff *); +extern int isdn_net_rcv_skb(int, struct sk_buff *); diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c --- v2.0.30/linux/drivers/isdn/isdn_ppp.c Tue Nov 12 22:36:19 1996 +++ linux/drivers/isdn/isdn_ppp.c Mon Aug 4 17:34:00 1997 @@ -1,9 +1,9 @@ -/* $Id: isdn_ppp.c,v 1.20 1996/10/30 12:21:58 fritz Exp $ +/* $Id: isdn_ppp.c,v 1.28 1997/06/17 13:05:57 hipp Exp $ * * Linux ISDN subsystem, functions for synchronous PPP (linklevel). * * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.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) @@ -16,9 +16,42 @@ * * 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. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_ppp.c,v $ + * 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) + * changed one copy_to_user() to run with enabled IRQs + * a few MP changes + * changed 'proto' handling in the isdn_ppp receive code + * + * Revision 1.27 1997/03/30 16:51:17 calle + * changed calls to copy_from_user/copy_to_user and removed verify_area + * were possible. + * + * Revision 1.26 1997/02/23 16:53:44 hipp + * minor cleanup + * some initial changes for future PPP compresion + * added AC,PC compression for outgoing frames + * + * Revision 1.25 1997/02/12 20:37:35 hipp + * New ioctl() PPPIOCGCALLINFO, minor cleanup + * + * Revision 1.24 1997/02/11 18:32:56 fritz + * Bugfix in isdn_ppp_free_mpqueue(). + * + * Revision 1.23 1997/02/10 11:12:19 fritz + * More changes for Kernel 2.1.X compatibility. + * + * Revision 1.22 1997/02/06 15:03:51 hipp + * changed GFP_KERNEL kmalloc to GFP_ATOMIC in isdn_ppp_fill_mpqueue() + * + * Revision 1.21 1997/02/03 23:29:38 fritz + * Reformatted according CodingStyle + * Bugfix: removed isdn_ppp_skb_destructor, used by upper layers. + * Misc changes for Kernel 2.1.X compatibility. + * * Revision 1.20 1996/10/30 12:21:58 fritz * Cosmetic fix: Compiler warning when compiling without MPP. * @@ -92,70 +125,91 @@ /* TODO: right tbusy handling when using MP */ +/* + * experimental for dynamic addressing: readdress IP frames + */ #undef ISDN_SYNCPPP_READDRESS #include #define __NO_VERSION__ #include +#include #include +#if (LINUX_VERSION_CODE >= 0x020117) +#include +#endif #include "isdn_common.h" #include "isdn_ppp.h" #include "isdn_net.h" #ifndef PPP_IPX -#define PPP_IPX 0x002b +#define PPP_IPX 0x002b #endif /* set this if you use dynamic addressing */ - + /* Prototypes */ -static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot); +static int isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot); static int isdn_ppp_closewait(int slot); static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, - struct sk_buff *skb, int proto); + struct sk_buff *skb, int proto); static int isdn_ppp_if_get_unit(char *namebuf); +static int isdn_ppp_set_compressor(struct ippp_struct *is,int num); +static struct sk_buff *isdn_ppp_decompress(struct sk_buff *, + struct ippp_struct *,struct ippp_struct *); +static void isdn_ppp_receive_ccp(isdn_net_dev * net_dev, isdn_net_local * lp, + struct sk_buff *skb); +static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto, + struct ippp_struct *is,struct ippp_struct *master,int type); #ifdef CONFIG_ISDN_MPP static int isdn_ppp_bundle(struct ippp_struct *, int unit); static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask); static void isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min); -static void isdn_ppp_cleanup_sqqueue(isdn_net_dev * dev,isdn_net_local *, long min); +static void isdn_ppp_cleanup_sqqueue(isdn_net_dev * dev, isdn_net_local *, long min); +static void isdn_ppp_free_sqqueue(isdn_net_dev *); static int isdn_ppp_fill_mpqueue(isdn_net_dev *, struct sk_buff **skb, - int BEbyte, long *sqno, int min_sqno); + int BEbyte, long *sqno, int min_sqno); +static void isdn_ppp_free_mpqueue(isdn_net_dev *); #endif -char *isdn_ppp_revision = "$Revision: 1.20 $"; +char *isdn_ppp_revision = "$Revision: 1.28 $"; -struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS]; +static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS]; +static struct isdn_ppp_compressor *ipc_head = NULL; extern int isdn_net_force_dial_lp(isdn_net_local *); /* * frame log (debug) */ -static void isdn_ppp_frame_log(char *info,char *data,int len,int maxlen) +static void +isdn_ppp_frame_log(char *info, char *data, int len, int maxlen) { - int cnt,j,i; + int cnt, + j, + i; char buf[80]; - if(len < maxlen) + if (len < maxlen) maxlen = len; - - for(i=0,cnt=0;cnt ippp-device + * unbind isdn_net_local <=> ippp-device * note: it can happen, that we hangup/free the master before the slaves */ -int isdn_ppp_free(isdn_net_local *lp) +int +isdn_ppp_free(isdn_net_local * lp) { #ifdef CONFIG_ISDN_MPP - isdn_net_local *master_lp=lp; + isdn_net_local *master_lp = lp; #endif unsigned long flags; struct ippp_struct *is; @@ -168,14 +222,14 @@ save_flags(flags); cli(); #ifdef CONFIG_ISDN_MPP - if(lp->master) + if (lp->master) master_lp = (isdn_net_local *) lp->master->priv; lp->last->next = lp->next; lp->next->last = lp->last; - if(master_lp->netdev->queue == lp) { + if (master_lp->netdev->queue == lp) { master_lp->netdev->queue = lp->next; - if(lp->next == lp) { /* last link in queue? */ + if (lp->next == lp) { /* last link in queue? */ master_lp->netdev->ib.bundled = 0; isdn_ppp_free_mpqueue(master_lp->netdev); isdn_ppp_free_sqqueue(master_lp->netdev); @@ -184,21 +238,21 @@ lp->next = lp->last = lp; /* (re)set own pointers */ #endif - if( (is->state & IPPP_CONNECT) ) + if ((is->state & IPPP_CONNECT)) isdn_ppp_closewait(lp->ppp_slot); /* force wakeup on ippp device */ - else if(is->state & IPPP_ASSIGNED) + else if (is->state & IPPP_ASSIGNED) is->state = IPPP_OPEN; /* fallback to 'OPEN but not ASSIGEND' staet */ - - if(is->debug & 0x1) - printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp,(long) is->lp); - is->lp = NULL; /* link is down .. set lp to NULL */ + if (is->debug & 0x1) + printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp, (long) is->lp); + + is->lp = NULL; /* link is down .. set lp to NULL */ #ifdef ISDN_SYNCPPP_READDRESS is->old_pa_addr = 0x0; is->old_pa_dstaddr = 0x0; #endif - lp->ppp_slot = -1; /* is this OK ?? */ + lp->ppp_slot = -1; /* is this OK ?? */ restore_flags(flags); return 0; @@ -207,7 +261,8 @@ /* * bind isdn_net_local <=> ippp-device */ -int isdn_ppp_bind(isdn_net_local * lp) +int +isdn_ppp_bind(isdn_net_local * lp) { int i; int unit = 0; @@ -220,14 +275,13 @@ save_flags(flags); cli(); - if(lp->pppbind < 0) /* device bounded to ippp device ? */ - { - isdn_net_dev *net_dev = dev->netdev; + if (lp->pppbind < 0) { /* device bounded to ippp device ? */ + isdn_net_dev *net_dev = dev->netdev; char exclusive[ISDN_MAX_CHANNELS]; /* exclusive flags */ - memset(exclusive,0,ISDN_MAX_CHANNELS); + memset(exclusive, 0, ISDN_MAX_CHANNELS); while (net_dev) { /* step through net devices to find exclusive minors */ isdn_net_local *lp = &net_dev->local; - if(lp->pppbind >= 0) + if (lp->pppbind >= 0) exclusive[lp->pppbind] = 1; net_dev = net_dev->next; } @@ -235,14 +289,13 @@ * search a free device / slot */ for (i = 0; i < ISDN_MAX_CHANNELS; i++) { - if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) { /* OPEN, but not connected! */ + if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) { /* OPEN, but not connected! */ break; } } - } - else { - for(i=0;iminor == lp->pppbind && ippp_table[i]->state == IPPP_OPEN) + } else { + for (i = 0; i < ISDN_MAX_CHANNELS; i++) + if (ippp_table[i]->minor == lp->pppbind && ippp_table[i]->state == IPPP_OPEN) break; } @@ -251,13 +304,11 @@ printk(KERN_WARNING "isdn_ppp_bind: Can't find usable ippp device.\n"); return -1; } - - unit = isdn_ppp_if_get_unit(lp->name); /* get unit number from interface name .. ugly! */ - if(unit < 0) { - printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n",lp->name); + unit = isdn_ppp_if_get_unit(lp->name); /* get unit number from interface name .. ugly! */ + if (unit < 0) { + printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n", lp->name); return -1; } - lp->ppp_slot = i; is = ippp_table[i]; is->lp = lp; @@ -274,9 +325,10 @@ * (wakes up daemon after B-channel connect) */ -void isdn_ppp_wakeup_daemon(isdn_net_local *lp) +void +isdn_ppp_wakeup_daemon(isdn_net_local * lp) { - if(lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) + if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) return; ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK; @@ -287,10 +339,11 @@ /* * there was a hangup on the netdevice - * force wakeup of the ippp device + * force wakeup of the ippp device * go into 'device waits for release' state */ -static int isdn_ppp_closewait(int slot) +static int +isdn_ppp_closewait(int slot) { struct ippp_struct *is; @@ -309,56 +362,64 @@ * isdn_ppp_find_slot / isdn_ppp_free_slot */ -static int isdn_ppp_get_slot(void) +static int +isdn_ppp_get_slot(void) { int i; - for(i=0;istate) + for (i = 0; i < ISDN_MAX_CHANNELS; i++) { + if (!ippp_table[i]->state) return i; } return -1; } /* - * isdn_ppp_open + * isdn_ppp_open */ -int isdn_ppp_open(int min, struct file *file) +int +isdn_ppp_open(int min, struct file *file) { int slot; struct ippp_struct *is; - if(min < 0 || min > ISDN_MAX_CHANNELS) + if (min < 0 || min > ISDN_MAX_CHANNELS) return -ENODEV; slot = isdn_ppp_get_slot(); - if(slot < 0) { + if (slot < 0) { return -EBUSY; } is = file->private_data = ippp_table[slot]; - if(is->debug & 0x1) - printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n",slot, min,is->state); + if (is->debug & 0x1) + printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n", slot, min, is->state); - is->lp = 0; - is->mp_seqno = 0; /* MP sequence number */ - is->pppcfg = 0; /* ppp configuration */ - is->mpppcfg = 0; /* mppp configuration */ - is->range = 0x1000000; /* MP: 24 bit range */ + /* compression stuff */ + is->compressor = NULL; + is->decomp_stat = is->comp_stat = NULL; + is->link_compressor = NULL; + is->link_decomp_stat = is->link_comp_stat = NULL; + + is->lp = NULL; + is->mp_seqno = 0; /* MP sequence number */ + is->pppcfg = 0; /* ppp configuration */ + is->mpppcfg = 0; /* mppp configuration */ + is->range = 0x1000000; /* MP: 24 bit range */ is->last_link_seqno = -1; /* MP: maybe set to Bundle-MIN, when joining a bundle ?? */ - is->unit = -1; /* set, when we have our interface */ - is->mru = 1524; /* MRU, default 1524 */ - is->maxcid = 16; /* VJ: maxcid */ + is->unit = -1; /* set, when we have our interface */ + is->mru = 1524; /* MRU, default 1524 */ + is->maxcid = 16; /* VJ: maxcid */ is->tk = current; - is->wq = NULL; /* read() wait queue */ - is->wq1 = NULL; /* select() wait queue */ - is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */ + is->wq = NULL; /* read() wait queue */ + is->wq1 = NULL; /* select() wait queue */ + is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */ is->last = is->rq; is->minor = min; #ifdef CONFIG_ISDN_PPP_VJ - /* - * VJ header compression init - */ + /* + * VJ header compression init + */ is->slcomp = slhc_init(16, 16); /* not necessary for 2. link in bundle */ #endif @@ -370,7 +431,8 @@ /* * release ippp device */ -void isdn_ppp_release(int min, struct file *file) +void +isdn_ppp_release(int min, struct file *file) { int i; struct ippp_struct *is; @@ -379,14 +441,14 @@ return; is = file->private_data; - if(is->debug & 0x1) + if (is->debug & 0x1) printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", min, (long) is->lp); - if (is->lp) { /* a lp address says: this link is still up */ + if (is->lp) { /* a lp address says: this link is still up */ isdn_net_dev *p = is->lp->netdev; - - is->state &= ~IPPP_CONNECT; /* -> effect: no call of wakeup */ - /* + + is->state &= ~IPPP_CONNECT; /* -> effect: no call of wakeup */ + /* * isdn_net_hangup() calls isdn_ppp_free() * isdn_ppp_free() sets is->lp to NULL and lp->ppp_slot to -1 * removing the IPPP_CONNECT flag omits calling of isdn_ppp_wakeup_daemon() @@ -399,8 +461,8 @@ is->rq[i].buf = NULL; } } - is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */ - is->last = is->rq; + is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */ + is->last = is->rq; #ifdef CONFIG_ISDN_PPP_VJ slhc_free(is->slcomp); @@ -413,216 +475,286 @@ /* * get_arg .. ioctl helper */ -static int get_arg(void *b,void *val,int len) +static int +get_arg(void *b, void *val, int len) { int r; - if(len <= 0) - len = sizeof(unsigned long); - if ((r = verify_area(VERIFY_READ, (void *) b, len ))) - return r; - copy_from_user((void *) val, b, len ); + if (len <= 0) + len = sizeof(unsigned long); + if ((r = copy_from_user((void *) val, b, len))) + return r; return 0; } /* * set arg .. ioctl helper */ -static int set_arg(void *b, unsigned long val,void *str) +static int +set_arg(void *b, unsigned long val, void *str) { int r; - if(!str) { - if ((r = verify_area(VERIFY_WRITE, b, 4 ))) - return r; - copy_to_user(b, (void *) &val, 4 ); - } - else { - if ((r = verify_area(VERIFY_WRITE, b,val))) + if (!str) { + if ((r = copy_to_user(b, (void *) &val, 4))) + return r; + } else { + if ((r = copy_to_user(b, str, val))) return r; - copy_to_user(b,str,val); } return 0; } /* - * ippp device ioctl + * ippp device ioctl */ -int isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg) +int +isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg) { unsigned long val; - int r; + int num,r; struct ippp_struct *is; + isdn_net_local *lp; - is = file->private_data; + is = (struct ippp_struct *) file->private_data; + lp = is->lp; - if(is->debug & 0x1) - printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n",min,cmd,is->state); + if (is->debug & 0x1) + printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n", min, cmd, is->state); if (!(is->state & IPPP_OPEN)) return -EINVAL; switch (cmd) { - case PPPIOCBUNDLE: + case PPPIOCBUNDLE: #ifdef CONFIG_ISDN_MPP - if( !(is->state & IPPP_CONNECT) ) - return -EINVAL; - if ((r = get_arg((void *) arg, &val,0))) - return r; - printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n", - (int) min, (int) is->unit, (int) val); - return isdn_ppp_bundle(is, val); + if (!(is->state & IPPP_CONNECT)) + return -EINVAL; + if ((r = get_arg((void *) arg, &val, 0))) + return r; + printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n", + (int) min, (int) is->unit, (int) val); + return isdn_ppp_bundle(is, val); #else - return -1; + return -1; #endif - break; - case PPPIOCGUNIT: /* get ppp/isdn unit number */ - if ((r = set_arg((void *) arg, is->unit,NULL))) - return r; - break; - case PPPIOCGMPFLAGS: /* get configuration flags */ - if ((r = set_arg((void *) arg, is->mpppcfg,NULL))) - return r; - break; - case PPPIOCSMPFLAGS: /* set configuration flags */ - if ((r = get_arg((void *) arg, &val,0))) - return r; - is->mpppcfg = val; - break; - case PPPIOCGFLAGS: /* get configuration flags */ - if ((r = set_arg((void *) arg, is->pppcfg,NULL))) - return r; - break; - case PPPIOCSFLAGS: /* set configuration flags */ - if ((r = get_arg((void *) arg, &val,0))) { - return r; - } - if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT) ) { - isdn_net_local *lp = is->lp; - if(lp) { - lp->netdev->dev.tbusy = 0; - mark_bh(NET_BH); /* OK .. we are ready to send buffers */ + break; + case PPPIOCGUNIT: /* get ppp/isdn unit number */ + if ((r = set_arg((void *) arg, is->unit, NULL))) + return r; + break; + case PPPIOCGMPFLAGS: /* get configuration flags */ + if ((r = set_arg((void *) arg, is->mpppcfg, NULL))) + return r; + break; + case PPPIOCSMPFLAGS: /* set configuration flags */ + if ((r = get_arg((void *) arg, &val, 0))) + return r; + is->mpppcfg = val; + break; + case PPPIOCGFLAGS: /* get configuration flags */ + if ((r = set_arg((void *) arg, is->pppcfg, NULL))) + return r; + break; + case PPPIOCSFLAGS: /* set configuration flags */ + if ((r = get_arg((void *) arg, &val, 0))) { + return r; } - } - is->pppcfg = val; - break; + if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) { + if (lp) { + lp->netdev->dev.tbusy = 0; + mark_bh(NET_BH); /* OK .. we are ready to send buffers */ + } + } + is->pppcfg = val; + break; #if 0 - case PPPIOCGSTAT: /* read PPP statistic information */ - break; + case PPPIOCGSTAT: /* read PPP statistic information */ + break; #endif - case PPPIOCGIDLE: /* get idle time information */ - if(is->lp) - { - struct ppp_idle pidle; - pidle.xmit_idle = pidle.recv_idle = is->lp->huptimer; - if((r = set_arg((void *) arg,sizeof(struct ppp_idle),&pidle))) - return r; - } - break; - case PPPIOCSMRU: /* set receive unit size for PPP */ - if ((r = get_arg((void *) arg, &val,0))) - return r; - is->mru = val; - break; - case PPPIOCSMPMRU: - break; - case PPPIOCSMPMTU: - break; - case PPPIOCSMAXCID: /* set the maximum compression slot id */ - if ((r = get_arg((void *) arg, &val,0))) - return r; - val++; - if(is->maxcid != val) { + case PPPIOCGIDLE: /* get idle time information */ + if (lp) { + struct ppp_idle pidle; + pidle.xmit_idle = pidle.recv_idle = lp->huptimer; + if ((r = set_arg((void *) arg, sizeof(struct ppp_idle), &pidle))) + return r; + } + break; + case PPPIOCSMRU: /* set receive unit size for PPP */ + if ((r = get_arg((void *) arg, &val, 0))) + return r; + is->mru = val; + break; + case PPPIOCSMPMRU: + break; + case PPPIOCSMPMTU: + break; + case PPPIOCSMAXCID: /* set the maximum compression slot id */ + if ((r = get_arg((void *) arg, &val, 0))) + return r; + val++; + if (is->maxcid != val) { #ifdef CONFIG_ISDN_PPP_VJ - struct slcompress *sltmp; + struct slcompress *sltmp; #endif - if(is->debug & 0x1) - printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n",val); - is->maxcid = val; + if (is->debug & 0x1) + printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n", val); + is->maxcid = val; #ifdef CONFIG_ISDN_PPP_VJ - sltmp = slhc_init(16,val); - if(!sltmp) { - printk(KERN_ERR "ippp, can't realloc slhc struct\n"); - return -ENOMEM; - } - if(is->slcomp) - slhc_free(is->slcomp); - is->slcomp = sltmp; + sltmp = slhc_init(16, val); + if (!sltmp) { + printk(KERN_ERR "ippp, can't realloc slhc struct\n"); + return -ENOMEM; + } + if (is->slcomp) + slhc_free(is->slcomp); + is->slcomp = sltmp; #endif - } - break; - case PPPIOCGDEBUG: - if ((r = set_arg((void *) arg, is->debug,0))) - return r; - break; - case PPPIOCSDEBUG: - if ((r = get_arg((void *) arg, &val,0))) - return r; - is->debug = val; - break; - case PPPIOCSCOMPRESS: -#if 0 - { - struct ppp_option_data pod; - r = get_arg((void *) arg,&pod,sizeof(struct ppp_option_data)); - if(r) + } + break; + case PPPIOCGDEBUG: + if ((r = set_arg((void *) arg, is->debug, 0))) return r; - ippp_set_compression(is,&pod); - } -#endif - break; - default: - break; + break; + case PPPIOCSDEBUG: + if ((r = get_arg((void *) arg, &val, 0))) + return r; + is->debug = val; + break; + case PPPIOCGCOMPRESSORS: + { + unsigned long protos = 0; + struct isdn_ppp_compressor *ipc = ipc_head; + while(ipc) { + protos |= (0x1<num); + ipc = ipc->next; + } + if ((r = set_arg((void *) arg, protos, 0))) + return r; + } + break; + case PPPIOCSCOMPRESSOR: + if ((r = get_arg((void *) arg, &num, sizeof(int)))) + return r; + return isdn_ppp_set_compressor(is, num); + break; + case PPPIOCGCALLINFO: + { + struct pppcallinfo pci; + memset((char *) &pci,0,sizeof(struct pppcallinfo)); + if(lp) + { + strncpy(pci.local_num,lp->msn,63); + if(lp->dial) { + strncpy(pci.remote_num,lp->dial->num,63); + } + pci.charge_units = lp->charge; + if(lp->outgoing) + pci.calltype = CALLTYPE_OUTGOING; + else + pci.calltype = CALLTYPE_INCOMING; + if(lp->flags & ISDN_NET_CALLBACK) + pci.calltype |= CALLTYPE_CALLBACK; + } + return set_arg((void *)arg,sizeof(struct pppcallinfo),&pci); + } + default: + break; } return 0; } -int isdn_ppp_select(int min, struct file *file, int type, select_table * st) +#if (LINUX_VERSION_CODE < 0x020117) +int +isdn_ppp_select(int min, struct file *file, int type, select_table * st) { - struct ippp_buf_queue *bf, *bl; + struct ippp_buf_queue *bf, + *bl; unsigned long flags; struct ippp_struct *is; is = file->private_data; - if(is->debug & 0x2) - printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n",min,type); + if (is->debug & 0x2) + printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n", min, type); if (!(is->state & IPPP_OPEN)) return -EINVAL; switch (type) { - case SEL_IN: - save_flags(flags); - cli(); - bl = is->last; - bf = is->first; - /* - * if IPPP_NOBLOCK is set we return even if we have nothing to read - */ - if (bf->next == bl && !(is->state & IPPP_NOBLOCK)) { - select_wait(&is->wq, st); + case SEL_IN: + save_flags(flags); + cli(); + bl = is->last; + bf = is->first; + /* + * if IPPP_NOBLOCK is set we return even if we have nothing to read + */ + if (bf->next == bl && !(is->state & IPPP_NOBLOCK)) { + select_wait(&is->wq, st); + restore_flags(flags); + return 0; + } + is->state &= ~IPPP_NOBLOCK; restore_flags(flags); + return 1; + case SEL_OUT: + /* we're always ready to send .. */ + return 1; + case SEL_EX: + select_wait(&is->wq1, st); return 0; - } - is->state &= ~IPPP_NOBLOCK; - restore_flags(flags); - return 1; - case SEL_OUT: - /* we're always ready to send .. */ - return 1; - case SEL_EX: - select_wait(&is->wq1, st); - return 0; } return 1; } +#else +unsigned int +isdn_ppp_poll(struct file *file, poll_table * wait) +{ + unsigned int mask; + struct ippp_buf_queue *bf; + struct ippp_buf_queue *bl; + unsigned long flags; + struct ippp_struct *is; + + is = file->private_data; + + if (is->debug & 0x2) + printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", MINOR(file->f_inode->i_rdev)); + + poll_wait(&is->wq, wait); + + if (!(is->state & IPPP_OPEN)) { + printk(KERN_DEBUG "isdn_ppp: device not open\n"); + return POLLERR; + } + /* we're always ready to send .. */ + mask = POLLOUT | POLLWRNORM; + + save_flags(flags); + cli(); + bl = is->last; + bf = is->first; + /* + * if IPPP_NOBLOCK is set we return even if we have nothing to read + */ + if (bf->next != bl || (is->state & IPPP_NOBLOCK)) { + is->state &= ~IPPP_NOBLOCK; + mask |= POLLIN | POLLRDNORM; + } + restore_flags(flags); + return mask; +} +#endif + /* * fill up isdn_ppp_read() queue .. */ -static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot) +static int +isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot) { - struct ippp_buf_queue *bf, *bl; + struct ippp_buf_queue *bf, + *bl; unsigned long flags; unsigned char *nbuf; struct ippp_struct *is; @@ -637,9 +769,8 @@ printk(KERN_DEBUG "ippp: device not activated.\n"); return 0; } - - nbuf = (unsigned char *) kmalloc(len+4, GFP_ATOMIC); - if(!nbuf) { + nbuf = (unsigned char *) kmalloc(len + 4, GFP_ATOMIC); + if (!nbuf) { printk(KERN_WARNING "ippp: Can't alloc buf\n"); return 0; } @@ -647,7 +778,7 @@ nbuf[1] = PPP_UI; nbuf[2] = proto >> 8; nbuf[3] = proto & 0xff; - memcpy(nbuf+4, buf, len); + memcpy(nbuf + 4, buf, len); save_flags(flags); cli(); @@ -662,7 +793,7 @@ is->first = bf; } bl->buf = (char *) nbuf; - bl->len = len+4; + bl->len = len + 4; is->last = bl->next; restore_flags(flags); @@ -678,12 +809,14 @@ * reports, that there is data */ -int isdn_ppp_read(int min, struct file *file, char *buf, int count) +int +isdn_ppp_read(int min, struct file *file, char *buf, int count) { struct ippp_struct *is; struct ippp_buf_queue *b; int r; unsigned long flags; + unsigned char *save_buf; is = file->private_data; @@ -697,26 +830,30 @@ cli(); b = is->first->next; - if (!b->buf) { + save_buf = b->buf; + if (!save_buf) { restore_flags(flags); return -EAGAIN; } if (b->len < count) count = b->len; - copy_to_user(buf, b->buf, count); - kfree(b->buf); b->buf = NULL; is->first = b; + restore_flags(flags); + copy_to_user(buf, save_buf, count); + kfree(save_buf); + return count; } /* * ipppd wanna write a packet to the card .. non-blocking */ - -int isdn_ppp_write(int min, struct file *file, const char *buf, int count) + +int +isdn_ppp_write(int min, struct file *file, const char *buf, int count) { isdn_net_local *lp; struct ippp_struct *is; @@ -735,15 +872,16 @@ if (!lp) printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n"); else { - /* - * Don't reset huptimer for - * LCP packets. (Echo requests). - */ - copy_from_user(protobuf, buf, 4); - proto = PPP_PROTOCOL(protobuf); - if (proto != PPP_LCP) + /* + * Don't reset huptimer for + * LCP packets. (Echo requests). + */ + if (copy_from_user(protobuf, buf, 4)) + return -EFAULT; + proto = PPP_PROTOCOL(protobuf); + if (proto != PPP_LCP) lp->huptimer = 0; - + if (lp->isdn_device < 0 || lp->isdn_channel < 0) return 0; @@ -752,23 +890,23 @@ int cnt; struct sk_buff *skb; skb = dev_alloc_skb(count); - if(!skb) { + if (!skb) { printk(KERN_WARNING "isdn_ppp_write: out of memory!\n"); return count; } - skb->free = 1; - copy_from_user(skb_put(skb, count), buf, count); - if(is->debug & 0x40) { - printk(KERN_DEBUG "ppp xmit: len %ld\n",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); - printk(KERN_INFO "isdn_ppp_write: freeing sav_skb (%d,%d)!\n",cnt,count); - } - else - printk(KERN_INFO "isdn_ppp_write: Can't write PPP frame to LL (%d,%d)!\n",cnt,count); + SET_SKB_FREE(skb); + if (copy_from_user(skb_put(skb, count), buf, count)) + return -EFAULT; + if (is->debug & 0x40) { + 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); + printk(KERN_INFO "isdn_ppp_write: freeing sav_skb (%d,%d)!\n", cnt, count); + } else + printk(KERN_INFO "isdn_ppp_write: Can't write PPP frame to LL (%d,%d)!\n", cnt, count); lp->sav_skb = skb; } } @@ -777,21 +915,23 @@ } /* - * init memory, structures etc. + * init memory, structures etc. */ -int isdn_ppp_init(void) +int +isdn_ppp_init(void) { - int i, j; + int i, + j; for (i = 0; i < ISDN_MAX_CHANNELS; i++) { - if (!(ippp_table[i] = (struct ippp_struct *) - kmalloc(sizeof(struct ippp_struct), GFP_KERNEL))) { - printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n"); + if (!(ippp_table[i] = (struct ippp_struct *) + kmalloc(sizeof(struct ippp_struct), GFP_KERNEL))) { + printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n"); for (j = 0; j < i; j++) kfree(ippp_table[i]); - return -1; - } + return -1; + } memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct)); ippp_table[i]->state = 0; ippp_table[i]->first = ippp_table[i]->rq + NUM_RCV_BUFFS - 1; @@ -807,78 +947,109 @@ return 0; } -void isdn_ppp_cleanup(void) +void +isdn_ppp_cleanup(void) { - int i; + int i; - for (i = 0; i < ISDN_MAX_CHANNELS; i++) - kfree(ippp_table[i]); + for (i = 0; i < ISDN_MAX_CHANNELS; i++) + kfree(ippp_table[i]); } /* + * get the PPP protocol header and pull skb + */ +static int isdn_ppp_strip_proto(struct sk_buff *skb) +{ + int proto; + if (skb->data[0] & 0x1) { + proto = skb->data[0]; + skb_pull(skb, 1); /* protocol ID is only 8 bit */ + } else { + proto = ((int) skb->data[0] << 8) + skb->data[1]; + skb_pull(skb, 2); + } + return proto; +} + + +/* * handler for incoming packets on a syncPPP interface */ void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb) { struct ippp_struct *is; + int proto; + is = ippp_table[lp->ppp_slot]; - if(is->debug & 0x4) { - printk(KERN_DEBUG "ippp_receive: len: %ld\n",skb->len); - isdn_ppp_frame_log("receive",skb->data,skb->len,32); + if (is->debug & 0x4) { + printk(KERN_DEBUG "ippp_receive: len: %d\n", (int) skb->len); + isdn_ppp_frame_log("receive", skb->data, skb->len, 32); } - - if(net_dev->local.master) { + if (net_dev->local.master) { printk(KERN_WARNING "isdn_ppp_receice: net_dev != master\n"); - net_dev = ((isdn_net_local*) net_dev->local.master->priv)->netdev; + net_dev = ((isdn_net_local *) net_dev->local.master->priv)->netdev; } - - if(skb->data[0] == 0xff && skb->data[1] == 0x03) - skb_pull(skb,2); + if (skb->data[0] == 0xff && skb->data[1] == 0x03) + skb_pull(skb, 2); else if (is->pppcfg & SC_REJ_COMP_AC) { - skb->free = 1; - dev_kfree_skb(skb,0 /* FREE_READ */ ); - return; /* discard it silently */ + SET_SKB_FREE(skb); + dev_kfree_skb(skb, 0 /* FREE_READ */ ); + return; /* discard it silently */ } + proto = isdn_ppp_strip_proto(skb); + #ifdef CONFIG_ISDN_MPP if (!(is->mpppcfg & SC_REJ_MP_PROT)) { - int proto; int sqno_end; - if (skb->data[0] & 0x1) { - proto = skb->data[0]; - skb_pull(skb,1); /* protocol ID is only 8 bit */ - } else { - proto = ((int) skb->data[0] << 8) + skb->data[1]; - skb_pull(skb,2); + + if(proto == PPP_LINK_COMP) { + printk(KERN_DEBUG "received single link compressed frame\n"); + skb = isdn_ppp_decompress(skb,is,NULL); + if(!skb) + return; + proto = isdn_ppp_strip_proto(skb); } + if (proto == PPP_MP) { isdn_net_local *lpq; long sqno, min_sqno, tseq; + u_char BEbyte = skb->data[0]; - if(is->debug & 0x8) - printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_slot, proto , - (int) skb->len, (int) skb->data[0], (int) skb->data[1], (int) skb->data[2], - (int) skb->data[3], (int) skb->data[4], (int) skb->data[5]); + if (is->debug & 0x8) + printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_slot, proto, + (int) skb->len, (int) skb->data[0], (int) skb->data[1], (int) skb->data[2], + (int) skb->data[3], (int) skb->data[4], (int) skb->data[5]); if (!(is->mpppcfg & SC_IN_SHORT_SEQ)) { sqno = ((int) skb->data[1] << 16) + ((int) skb->data[2] << 8) + (int) skb->data[3]; - skb_pull(skb,4); + skb_pull(skb, 4); } else { sqno = (((int) skb->data[0] & 0xf) << 8) + (int) skb->data[1]; - skb_pull(skb,2); + skb_pull(skb, 2); } + /* + * new sequence number lower than last number? (this is only allowed + * for overflow case) + */ if ((tseq = is->last_link_seqno) >= sqno) { int range = is->range; - if (tseq + 1024 < range + sqno) /* redundancy check .. not MP conform */ + if (tseq + 1024 < range + sqno) /* redundancy check .. not MP conform */ printk(KERN_WARNING "isdn_ppp_receive, MP, detected overflow with sqno: %ld, last: %ld !!!\n", sqno, tseq); else { sqno += range; is->last_link_seqno = sqno; } - } else + } else { + /* here, we should also add an redundancy check */ is->last_link_seqno = sqno; + } + /* + * step over all links to find lowest link number + */ for (min_sqno = LONG_MAX, lpq = net_dev->queue;;) { long lls = ippp_table[lpq->ppp_slot]->last_link_seqno; if (lls >= 0 && lls < min_sqno) @@ -887,11 +1058,14 @@ if (lpq == net_dev->queue) break; } - if (min_sqno >= ippp_table[lpq->ppp_slot]->range) { /* OK, every link overflowed */ - int mask = ippp_table[lpq->ppp_slot]->range - 1; /* range is a power of 2 */ -#if 0 - isdn_ppp_cleanup_queue(net_dev, min_sqno); -#endif + + /* + * for the case, that the last frame numbers of all + * links are overflowed: mask/reduce the sequenece number to + * 'normal' numbering. + */ + if (min_sqno >= ippp_table[lpq->ppp_slot]->range) { + int mask = ippp_table[lpq->ppp_slot]->range-1; /* range is power of two, so a mask will do the job */ isdn_ppp_mask_queue(net_dev, mask); net_dev->ib.next_num &= mask; { @@ -910,18 +1084,22 @@ } } if ((BEbyte & (MP_BEGIN_FRAG | MP_END_FRAG)) != (MP_BEGIN_FRAG | MP_END_FRAG)) { - printk(KERN_DEBUG "ippp: trying ;) to fill mp_queue %d .. UNTESTED!!\n", lp->ppp_slot); - if ((sqno_end = isdn_ppp_fill_mpqueue(net_dev, &skb , BEbyte, &sqno, min_sqno)) < 0) { + static int dmes = 0; + if( !dmes ) { + printk(KERN_DEBUG "ippp: trying ;) to fill mp_queue %d .. UNTESTED!!\n", lp->ppp_slot); + dmes = 1; + } + if ((sqno_end = isdn_ppp_fill_mpqueue(net_dev, &skb, BEbyte, &sqno, min_sqno)) < 0) { net_dev->ib.modify = 1; /* block timeout-timer */ - isdn_ppp_cleanup_sqqueue(net_dev,lp,min_sqno); + isdn_ppp_cleanup_sqqueue(net_dev, lp, min_sqno); net_dev->ib.modify = 0; - return; /* no packet complete */ + return; /* no packet complete */ } } else sqno_end = sqno; - if(is->debug & 0x40) - printk(KERN_DEBUG "min_sqno: %ld sqno_end %d next: %ld\n",min_sqno,sqno_end,net_dev->ib.next_num ); + if (is->debug & 0x40) + printk(KERN_DEBUG "min_sqno: %ld sqno_end %d next: %ld\n", min_sqno, sqno_end, net_dev->ib.next_num); /* * MP buffer management .. reorders incoming packets .. @@ -930,7 +1108,7 @@ * first check whether there is more than one link in the bundle * then check whether the number is in order */ - net_dev->ib.modify = 1; /* block timeout-timer */ + net_dev->ib.modify = 1; /* block timeout-timer */ if (net_dev->ib.bundled && net_dev->ib.next_num != sqno) { /* * packet is not 'in order' @@ -941,9 +1119,9 @@ if (!q) { net_dev->ib.modify = 0; printk(KERN_WARNING "ippp/MPPP: Bad! Can't alloc sq node!\n"); - skb->free = 1; + SET_SKB_FREE(skb); dev_kfree_skb(skb, 0 /* FREE_READ */ ); - return; /* discard */ + return; /* discard */ } q->skb = skb; q->sqno_end = sqno_end; @@ -966,123 +1144,128 @@ } } } else { - /* - * packet was 'in order' .. push it higher + /* + * packet was 'in order' .. push it higher */ net_dev->ib.next_num = sqno_end + 1; - isdn_ppp_push_higher(net_dev, lp, skb, -1); + proto = isdn_ppp_strip_proto(skb); + isdn_ppp_push_higher(net_dev, lp, skb, proto); } - isdn_ppp_cleanup_sqqueue(net_dev,lp,min_sqno); + isdn_ppp_cleanup_sqqueue(net_dev, lp, min_sqno); net_dev->ib.modify = 0; } else - isdn_ppp_push_higher(net_dev, lp, skb , proto); + isdn_ppp_push_higher(net_dev, lp, skb, proto); } else #endif - isdn_ppp_push_higher(net_dev, lp, skb , -1); + isdn_ppp_push_higher(net_dev, lp, skb, proto); } /* * push frame to higher layers * note: net_dev has to be master net_dev */ -static void isdn_ppp_push_higher(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb,int proto) +static void +isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb, int proto) { struct device *dev = &net_dev->dev; struct ippp_struct *is = ippp_table[lp->ppp_slot]; - if (proto < 0) { /* MP, oder normales Paket bei REJ_MP, MP Pakete gehen bei REJ zum pppd */ - if (skb->data[0] & 0x01) { /* is it odd? */ - proto = (unsigned char) skb->data[0]; - skb_pull(skb,1); /* protocol ID is only 8 bit */ - } else { - proto = ((int) (unsigned char) skb->data[0] << 8) + (unsigned char) skb->data[1]; - skb_pull(skb,2); - } + if (is->debug & 0x10) { + printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto); + isdn_ppp_frame_log("rpush", skb->data, skb->len, 32); } - if(is->debug & 0x10) { - printk(KERN_DEBUG "push, skb %ld %04x\n",skb->len,proto); - isdn_ppp_frame_log("rpush",skb->data,skb->len,32); + if(proto == PPP_COMP) { + if(!lp->master) + skb = isdn_ppp_decompress(skb,is,is); + else + skb = isdn_ppp_decompress(skb,is,ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot]); + if(!skb) + return; + proto = isdn_ppp_strip_proto(skb); } switch (proto) { - case PPP_IPX: /* untested */ - if(is->debug & 0x20) - printk(KERN_DEBUG "isdn_ppp: IPX\n"); - skb->dev = dev; - skb->mac.raw = skb->data; - skb->protocol = htons(ETH_P_IPX); - break; -#ifdef CONFIG_ISDN_PPP_VJ - case PPP_VJC_UNCOMP: - if(is->debug & 0x20) - printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n"); - if(slhc_remember(ippp_table[net_dev->local.ppp_slot]->slcomp, skb->data, skb->len) <= 0) { - printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n"); - net_dev->local.stats.rx_dropped++; - skb->free = 1; - dev_kfree_skb(skb,0 /* FREE_READ */ ); - return; - } -#endif - case PPP_IP: - if(is->debug & 0x20) - printk(KERN_DEBUG "isdn_ppp: IP\n"); - skb->dev = dev; - skb->mac.raw = skb->data; - skb->protocol = htons(ETH_P_IP); - break; - case PPP_VJC_COMP: - if(is->debug & 0x20) - printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n"); + case PPP_IPX: /* untested */ + if (is->debug & 0x20) + printk(KERN_DEBUG "isdn_ppp: IPX\n"); + skb->dev = dev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IPX); + break; #ifdef CONFIG_ISDN_PPP_VJ - { - struct sk_buff *skb_old = skb; - int pkt_len; - skb = dev_alloc_skb(skb_old->len + 40); - - skb_old->free = 1; - - if (!skb) { - printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name); + case PPP_VJC_UNCOMP: + if (is->debug & 0x20) + printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n"); + if (slhc_remember(ippp_table[net_dev->local.ppp_slot]->slcomp, skb->data, skb->len) <= 0) { + printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n"); net_dev->local.stats.rx_dropped++; - dev_kfree_skb(skb_old,0 /* FREE_READ */ ); + SET_SKB_FREE(skb); + dev_kfree_skb(skb, 0 /* FREE_READ */ ); return; } +#endif + case PPP_IP: + if (is->debug & 0x20) + printk(KERN_DEBUG "isdn_ppp: IP\n"); skb->dev = dev; - skb_put(skb,skb_old->len + 40); - memcpy(skb->data, skb_old->data, skb_old->len); skb->mac.raw = skb->data; - pkt_len = slhc_uncompress(ippp_table[net_dev->local.ppp_slot]->slcomp, - skb->data, skb_old->len); - dev_kfree_skb(skb_old,0 /* FREE_READ */ ); - if(pkt_len < 0) { - skb->free = 1; - dev_kfree_skb(skb, 0 /* FREE_READ */ ); - lp->stats.rx_dropped++; - return; - } - skb_trim(skb, pkt_len); skb->protocol = htons(ETH_P_IP); - } + break; + case PPP_VJC_COMP: + if (is->debug & 0x20) + printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n"); +#ifdef CONFIG_ISDN_PPP_VJ + { + struct sk_buff *skb_old = skb; + int pkt_len; + skb = dev_alloc_skb(skb_old->len + 40); + + SET_SKB_FREE(skb_old); + + if (!skb) { + printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name); + net_dev->local.stats.rx_dropped++; + dev_kfree_skb(skb_old, 0 /* FREE_READ */ ); + return; + } + skb->dev = dev; + skb_put(skb, skb_old->len + 40); + memcpy(skb->data, skb_old->data, skb_old->len); + skb->mac.raw = skb->data; + pkt_len = slhc_uncompress(ippp_table[net_dev->local.ppp_slot]->slcomp, + skb->data, skb_old->len); + dev_kfree_skb(skb_old, 0 /* FREE_READ */ ); + if (pkt_len < 0) { + SET_SKB_FREE(skb); + dev_kfree_skb(skb, 0 /* FREE_READ */ ); + lp->stats.rx_dropped++; + return; + } + skb_trim(skb, pkt_len); + skb->protocol = htons(ETH_P_IP); + } #else - printk(KERN_INFO "isdn: Ooopsa .. VJ-Compression support not compiled into isdn driver.\n"); - lp->stats.rx_dropped++; - skb->free = 1; - dev_kfree_skb(skb,0 /* FREE_READ */ ); - return; + printk(KERN_INFO "isdn: Ooopsa .. VJ-Compression support not compiled into isdn driver.\n"); + lp->stats.rx_dropped++; + SET_SKB_FREE(skb); + dev_kfree_skb(skb, 0 /* FREE_READ */ ); + return; #endif - break; - default: - isdn_ppp_fill_rq(skb->data, skb->len,proto, lp->ppp_slot); /* push data to pppd device */ - skb->free = 1; - dev_kfree_skb(skb,0 /* FREE_READ */ ); - return; + break; + case PPP_CCP: + isdn_ppp_receive_ccp(net_dev,lp,skb); + /* fall through */ + default: + isdn_ppp_fill_rq(skb->data, skb->len, proto, lp->ppp_slot); /* push data to pppd device */ + SET_SKB_FREE(skb); + dev_kfree_skb(skb, 0 /* FREE_READ */ ); + return; } netif_rx(skb); -/* net_dev->local.stats.rx_packets++; */ /* done in isdn_net.c */ + /* net_dev->local.stats.rx_packets++; *//* done in isdn_net.c */ /* Reset hangup-timer */ lp->huptimer = 0; @@ -1090,89 +1273,95 @@ } /* - * send ppp frame .. we expect a PIDCOMPressable proto -- - * (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP) - * - * VJ compression may change skb pointer!!! .. requeue with old - * skb isn't allowed!! + * isdn_ppp_skb_push .. + * checks whether we have enough space at the beginning of the SKB + * and allocs a new SKB if necessary */ - -static void isdn_ppp_skb_destructor(struct sk_buff *skb) /* debug function */ +static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len) { - char outstr[80],*outpnt=outstr; - int i; + struct sk_buff *skb = *skb_p; - *outpnt = 0; - for(i=0;i<24 && ilen;i++) { - sprintf(outpnt,"%02x ",skb->data[i]); - outpnt += 3; + if(skb_headroom(skb) < len) { + printk(KERN_ERR "isdn_ppp_skb_push:under %d %d\n",skb_headroom(skb),len); + dev_kfree_skb(skb,FREE_WRITE); + return NULL; } - printk(KERN_DEBUG "ippp_dstrct: %s\n",outstr); + return skb_push(skb,len); } -int isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) + +/* + * send ppp frame .. we expect a PIDCOMPressable proto -- + * (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP) + * + * VJ compression may change skb pointer!!! .. requeue with old + * skb isn't allowed!! + */ + +int +isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) { - struct device *mdev = ((isdn_net_local *) (dev->priv) )->master; /* get master (for redundancy) */ + struct device *mdev = ((isdn_net_local *) (dev->priv))->master; /* get master (for redundancy) */ isdn_net_local *lp,*mlp; isdn_net_dev *nd; - int proto = PPP_IP; /* 0x21 */ + unsigned int proto = PPP_IP; /* 0x21 */ struct ippp_struct *ipt,*ipts; - if(mdev) - mlp = (isdn_net_local *) (mdev->priv); + if (mdev) + mlp = (isdn_net_local *) (mdev->priv); else { mdev = dev; mlp = (isdn_net_local *) (dev->priv); } - nd = mlp->netdev; /* get master lp */ + nd = mlp->netdev; /* get master lp */ ipts = ippp_table[mlp->ppp_slot]; - if (!(ipts->pppcfg & SC_ENABLE_IP)) { /* PPP connected ? */ + if (!(ipts->pppcfg & SC_ENABLE_IP)) { /* PPP connected ? */ #ifdef ISDN_SYNCPPP_READDRESS - if(!ipts->old_pa_addr) + if (!ipts->old_pa_addr) ipts->old_pa_addr = mdev->pa_addr; - if(!ipts->old_pa_dstaddr) + if (!ipts->old_pa_dstaddr) ipts->old_pa_dstaddr = mdev->pa_dstaddr; #endif - if(ipts->debug & 0x1) { - printk(KERN_INFO "%s: IP frame delayed.\n",dev->name); - skb->destructor = isdn_ppp_skb_destructor; - } - return 1; + if (ipts->debug & 0x1) + printk(KERN_INFO "%s: IP frame delayed.\n", dev->name); + return 1; } - skb->destructor = NULL; - - switch(ntohs(skb->protocol)) { + switch (ntohs(skb->protocol)) { case ETH_P_IP: proto = PPP_IP; #ifdef ISDN_SYNCPPP_READDRESS - if(ipts->old_pa_addr != mdev->pa_addr) - { + if (ipts->old_pa_addr != mdev->pa_addr) { struct iphdr *ipfr; ipfr = (struct iphdr *) skb->data; -printk(KERN_DEBUG "IF-address changed from %lx to %lx\n",ipts->old_pa_addr,mdev->pa_addr); - if(ipfr->version == 4) { - if(ipfr->saddr == ipts->old_pa_addr) { -printk(KERN_DEBUG "readdressing %lx to %lx\n",ipfr->saddr,mdev->pa_addr); + if(ipts->debug & 0x4) + printk(KERN_DEBUG "IF-address changed from %lx to %lx\n", ipts->old_pa_addr, mdev->pa_addr); + if (ipfr->version == 4) { + if (ipfr->saddr == ipts->old_pa_addr) { + printk(KERN_DEBUG "readdressing %lx to %lx\n", ipfr->saddr, mdev->pa_addr); ipfr->saddr = mdev->pa_addr; } } } - /* dstaddr change not so improtant */ + /* dstaddr change not so important */ #endif break; case ETH_P_IPX: proto = PPP_IPX; /* untested */ break; + default: + dev_kfree_skb(skb, FREE_WRITE); + printk(KERN_ERR "isdn_ppp: skipped frame with unsupported protocoll: %#x.\n", skb->protocol); + return 0; } - lp = nd->queue; /* get lp on top of queue */ + lp = nd->queue; /* get lp on top of queue */ - if(lp->sav_skb) { /* find a non-busy device */ + if (lp->sav_skb) { /* find a non-busy device */ isdn_net_local *nlp = lp->next; - while(lp->sav_skb) { - if(lp == nlp) + while (lp->sav_skb) { + if (lp == nlp) return 1; nlp = nd->queue = nd->queue->next; } @@ -1180,43 +1369,49 @@ } ipt = ippp_table[lp->ppp_slot]; - lp->huptimer = 0; + lp->huptimer = 0; /* - * after this line .. requeueing in the device queue is no longer allowed!!! + * after this line .. requeueing in the device queue is no longer allowed!!! */ - if(ipt->debug & 0x4) - printk(KERN_DEBUG "xmit skb, len %ld\n",skb->len); + /* 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 + */ + skb_pull(skb,IPPP_MAX_HEADER); + + if (ipt->debug & 0x4) + printk(KERN_DEBUG "xmit skb, len %d\n", (int) skb->len); #ifdef CONFIG_ISDN_PPP_VJ - if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) { /* ipts here? probably yes .. but this check again */ + if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) { /* ipts here? probably yes, but check this again */ struct sk_buff *new_skb; new_skb = dev_alloc_skb(skb->len); - if(new_skb) { + if (new_skb) { u_char *buf; int pktlen; new_skb->dev = skb->dev; - new_skb->free = 1; - skb_put(new_skb,skb->len); + SET_SKB_FREE(new_skb); + skb_put(new_skb, skb->len); buf = skb->data; pktlen = slhc_compress(ipts->slcomp, skb->data, skb->len, new_skb->data, - &buf, !(ipts->pppcfg & SC_NO_TCP_CCID)); + &buf, !(ipts->pppcfg & SC_NO_TCP_CCID)); - if(buf != skb->data) { /* copied to new buffer ??? (btw: WHY must slhc copy it?? *sigh*) */ - if(new_skb->data != buf) + if (buf != skb->data) { + if (new_skb->data != buf) printk(KERN_ERR "isdn_ppp: FATAL error after slhc_compress!!\n"); - dev_kfree_skb(skb,FREE_WRITE); + dev_kfree_skb(skb, FREE_WRITE); skb = new_skb; - } - else { - dev_kfree_skb(new_skb,0 /* FREE_WRITE */ ); + } else { + dev_kfree_skb(new_skb, 0 /* FREE_WRITE */ ); } - skb_trim(skb,pktlen); + skb_trim(skb, pktlen); if (skb->data[0] & SL_TYPE_COMPRESSED_TCP) { /* cslip? style -> PPP */ proto = PPP_VJC_COMP; skb->data[0] ^= SL_TYPE_COMPRESSED_TCP; @@ -1229,8 +1424,13 @@ } #endif - if(ipt->debug & 0x24) - printk(KERN_DEBUG "xmit2 skb, len %ld, proto %04x\n",skb->len,proto); + /* + * normal or bundle compression + */ + skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0); + + if (ipt->debug & 0x24) + printk(KERN_DEBUG "xmit2 skb, len %d, proto %04x\n", (int) skb->len, proto); #ifdef CONFIG_ISDN_MPP if (ipt->mpppcfg & SC_MP_PROT) { @@ -1239,41 +1439,64 @@ ipts->mp_seqno++; nd->queue = nd->queue->next; if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) { - skb_push(skb, 3); + unsigned char *data = isdn_ppp_skb_push(&skb, 3); + if(!data) + return 0; mp_seqno &= 0xfff; - skb->data[0] = MP_BEGIN_FRAG | MP_END_FRAG | (mp_seqno >> 8); /* (B)egin & (E)ndbit .. */ - skb->data[1] = mp_seqno & 0xff; - skb->data[2] = proto; /* PID compression */ + data[0] = MP_BEGIN_FRAG | MP_END_FRAG | ((mp_seqno >> 8) & 0xf); /* (B)egin & (E)ndbit .. */ + data[1] = mp_seqno & 0xff; + data[2] = proto; /* PID compression */ } else { - skb_push(skb, 5); - skb->data[0] = MP_BEGIN_FRAG | MP_END_FRAG; /* (B)egin & (E)ndbit .. */ - skb->data[1] = (mp_seqno >> 16) & 0xff; /* sequence number: 24bit */ - skb->data[2] = (mp_seqno >> 8) & 0xff; - skb->data[3] = (mp_seqno >> 0) & 0xff; - skb->data[4] = proto; /* PID compression */ + unsigned char *data = isdn_ppp_skb_push(&skb, 5); + if(!data) + return 0; + data[0] = MP_BEGIN_FRAG | MP_END_FRAG; /* (B)egin & (E)ndbit .. */ + data[1] = (mp_seqno >> 16) & 0xff; /* sequence number: 24bit */ + data[2] = (mp_seqno >> 8) & 0xff; + data[3] = (mp_seqno >> 0) & 0xff; + data[4] = proto; /* PID compression */ } proto = PPP_MP; /* MP Protocol, 0x003d */ } #endif - skb_push(skb,4); - skb->data[0] = 0xff; /* All Stations */ - skb->data[1] = 0x03; /* Unnumbered information */ - skb->data[2] = proto >> 8; - skb->data[3] = proto & 0xff; - - /* tx-stats are now updated via BSENT-callback */ - - if(ipts->debug & 0x40) { - printk(KERN_DEBUG "skb xmit: len: %ld\n",skb->len); - isdn_ppp_frame_log("xmit",skb->data,skb->len,32); - } - - if(isdn_net_send_skb(dev , lp , skb)) { - if(lp->sav_skb) { /* whole sav_skb processing with disabled IRQs ?? */ - printk(KERN_ERR "%s: whoops .. there is another stored skb!\n",dev->name); - dev_kfree_skb(skb,FREE_WRITE); - } - else + + /* + * 'link' compression + */ + skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1); + + if( (ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff) ) { + unsigned char *data = isdn_ppp_skb_push(&skb,1); + if(!data) + return 0; + data[0] = proto & 0xff; + } + else { + unsigned char *data = isdn_ppp_skb_push(&skb,2); + if(!data) + return 0; + data[0] = (proto >> 8) & 0xff; + data[1] = proto & 0xff; + } + if(!(ipt->pppcfg & SC_COMP_AC)) { + unsigned char *data = isdn_ppp_skb_push(&skb,2); + if(!data) + return 0; + data[0] = 0xff; /* All Stations */ + data[1] = 0x03; /* Unnumbered information */ + } + + /* tx-stats are now updated via BSENT-callback */ + + if (ipts->debug & 0x40) { + printk(KERN_DEBUG "skb xmit: len: %d\n", (int) skb->len); + isdn_ppp_frame_log("xmit", skb->data, skb->len, 32); + } + if (isdn_net_send_skb(dev, lp, skb)) { + if (lp->sav_skb) { /* whole sav_skb processing with disabled IRQs ?? */ + printk(KERN_ERR "%s: whoops .. there is another stored skb!\n", dev->name); + dev_kfree_skb(skb, FREE_WRITE); + } else lp->sav_skb = skb; } return 0; @@ -1281,16 +1504,17 @@ #ifdef CONFIG_ISDN_MPP -void isdn_ppp_free_sqqueue(isdn_net_dev * p) +static void +isdn_ppp_free_sqqueue(isdn_net_dev * p) { struct sqqueue *q = p->ib.sq; p->ib.sq = NULL; - while(q) { + while (q) { struct sqqueue *qn = q->next; - if(q->skb) { - q->skb->free = 1; - dev_kfree_skb(q->skb,0 /* FREE_READ */ ); + if (q->skb) { + SET_SKB_FREE(q->skb); + dev_kfree_skb(q->skb, 0 /* FREE_READ */ ); } kfree(q); q = qn; @@ -1298,24 +1522,29 @@ } -void isdn_ppp_free_mpqueue(isdn_net_dev * p) +static void +isdn_ppp_free_mpqueue(isdn_net_dev * p) { - struct mpqueue *ql, *q = p->mp_last; + struct mpqueue *q = p->mp_last; + p->mp_last = NULL; + while (q) { - ql = q->next; - q->skb->free = 1; - dev_kfree_skb(q->skb,0 /* FREE_READ */ ); + struct mpqueue *ql = q->next; + SET_SKB_FREE(q->skb); + dev_kfree_skb(q->skb, 0 /* FREE_READ */ ); kfree(q); q = ql; } } -static int isdn_ppp_bundle(struct ippp_struct *is, int unit) +static int +isdn_ppp_bundle(struct ippp_struct *is, int unit) { char ifn[IFNAMSIZ + 1]; long flags; isdn_net_dev *p; - isdn_net_local *lp,*nlp; + isdn_net_local *lp, + *nlp; sprintf(ifn, "ippp%d", unit); p = isdn_net_findif(ifn); @@ -1356,7 +1585,8 @@ } -static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask) +static void +isdn_ppp_mask_queue(isdn_net_dev * dev, long mask) { struct mpqueue *q = dev->mp_last; while (q) { @@ -1365,14 +1595,19 @@ } } -static int isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff ** skb, int BEbyte, long *sqnop, int min_sqno) +static int +isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff **skb, int BEbyte, long *sqnop, int min_sqno) { - struct mpqueue *qe, *q1, *q; - long cnt, flags; - int pktlen, sqno_end; + struct mpqueue *qe, + *q1, + *q; + long cnt, + flags; + int pktlen, + sqno_end; int sqno = *sqnop; - q1 = (struct mpqueue *) kmalloc(sizeof(struct mpqueue), GFP_KERNEL); + q1 = (struct mpqueue *) kmalloc(sizeof(struct mpqueue), GFP_ATOMIC); if (!q1) { printk(KERN_WARNING "isdn_ppp_fill_mpqueue: Can't alloc struct memory.\n"); save_flags(flags); @@ -1395,9 +1630,9 @@ q1->last = NULL; isdn_ppp_cleanup_mpqueue(dev, min_sqno); /* not necessary */ restore_flags(flags); - return -1; + return -1; /* -1 is not an error. Just says, that this fragment hasn't complete a full frame */ } - for (;;) { /* the faster way would be to step from the queue-end to the start */ + for (;;) { /* the faster way would be to step from the queue-end to the start */ if (sqno > q->sqno) { if (q->next) { q = q->next; @@ -1465,26 +1700,26 @@ isdn_ppp_cleanup_mpqueue(dev, min_sqno); restore_flags(flags); - *skb = dev_alloc_skb(pktlen + 40); /* not needed: +40 for VJ compression .. */ + *skb = dev_alloc_skb(pktlen + 40); /* not needed: +40 for VJ compression .. */ if (!(*skb)) { while (q) { struct mpqueue *ql = q->next; - q->skb->free = 1; - dev_kfree_skb(q->skb,0 /* FREE_READ */ ); + SET_SKB_FREE(q->skb); + dev_kfree_skb(q->skb, 0 /* FREE_READ */ ); kfree(q); q = ql; } return -2; } cnt = 0; - skb_put(*skb,pktlen); + skb_put(*skb, pktlen); while (q) { struct mpqueue *ql = q->next; memcpy((*skb)->data + cnt, q->skb->data, q->skb->len); cnt += q->skb->len; - q->skb->free = 1; - dev_kfree_skb(q->skb,0 /* FREE_READ */ ); + SET_SKB_FREE(q->skb); + dev_kfree_skb(q->skb, 0 /* FREE_READ */ ); kfree(q); q = ql; } @@ -1497,28 +1732,32 @@ * or packets with a sqno less or equal to min_sqno * net_dev: master netdevice , lp: 'real' local connection */ -static void isdn_ppp_cleanup_sqqueue(isdn_net_dev *net_dev, isdn_net_local *lp,long min_sqno) +static void +isdn_ppp_cleanup_sqqueue(isdn_net_dev * net_dev, isdn_net_local * lp, long min_sqno) { struct sqqueue *q; - while ((q = net_dev->ib.sq) && (q->sqno_start == net_dev->ib.next_num || q->sqno_end <= min_sqno) ) { - if(q->sqno_start != net_dev->ib.next_num) { - printk(KERN_DEBUG "ippp: MP, stepping over missing frame: %ld\n",net_dev->ib.next_num); + while ((q = net_dev->ib.sq) && (q->sqno_start == net_dev->ib.next_num || q->sqno_end <= min_sqno)) { + int proto; + if (q->sqno_start != net_dev->ib.next_num) { + printk(KERN_DEBUG "ippp: MP, stepping over missing frame: %ld\n", net_dev->ib.next_num); #ifdef CONFIG_ISDN_PPP_VJ slhc_toss(ippp_table[net_dev->local.ppp_slot]->slcomp); #endif } - isdn_ppp_push_higher(net_dev, lp, q->skb, -1); + proto = isdn_ppp_strip_proto(q->skb); + isdn_ppp_push_higher(net_dev, lp, q->skb, proto); net_dev->ib.sq = q->next; net_dev->ib.next_num = q->sqno_end + 1; kfree(q); - } + } } /* * remove stale packets from list */ -static void isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min_sqno) +static void +isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min_sqno) { #ifdef CONFIG_ISDN_PPP_VJ int toss = 0; @@ -1526,36 +1765,36 @@ /* z.z einfaches aussortieren gammeliger pakete. Fuer die Zukunft: eventuell, solange vorne kein B-paket ist und sqno<=min_sqno: auch rauswerfen wenn sqnomp_last; - while (q) { - if (q->sqno < min_sqno) { - if (q->BEbyte & MP_END_FRAG) { - printk(KERN_DEBUG "ippp: freeing stale packet!\n"); - if ((dev->mp_last = q->next)) - q->next->last = NULL; - while (q) { - ql = q->last; - q->skb->free = 1; - dev_kfree_skb(q->skb,0 /* FREE_READ */ ); - kfree(q); + struct mpqueue *ql, + *q = dev->mp_last; + while(q && (q->sqno < min_sqno) ) { + if ( (q->BEbyte & MP_END_FRAG) || + (q->next && (q->next->sqno <= min_sqno) && (q->next->BEbyte & MP_BEGIN_FRAG)) ) { + printk(KERN_DEBUG "ippp: freeing stale packet(s), min_sq: %ld!\n",min_sqno); + if ((dev->mp_last = q->next)) + q->next->last = NULL; + while (q) { + ql = q->last; + SET_SKB_FREE(q->skb); + printk(KERN_DEBUG "ippp, freeing packet with sqno: %ld\n",q->sqno); + dev_kfree_skb(q->skb, 0 /* FREE_READ */ ); + kfree(q); #ifdef CONFIG_ISDN_PPP_VJ - toss = 1; + toss = 1; #endif - q = ql; - } - q = dev->mp_last; - } else - q = q->next; + q = ql; + } + q = dev->mp_last; } else - break; + q = q->next; } #ifdef CONFIG_ISDN_PPP_VJ /* did we free a stale frame ? */ - if(toss) + if (toss) slhc_toss(ippp_table[dev->local.ppp_slot]->slcomp); #endif } @@ -1566,26 +1805,28 @@ #endif -void isdn_ppp_timer_timeout(void) +void +isdn_ppp_timer_timeout(void) { #ifdef CONFIG_ISDN_MPP isdn_net_dev *net_dev = dev->netdev; - struct sqqueue *q, *ql = NULL, *qn; + struct sqqueue *q, + *ql = NULL, + *qn; while (net_dev) { isdn_net_local *lp = &net_dev->local; - if (net_dev->ib.modify || lp->master) { /* interface locked or slave?*/ + if (net_dev->ib.modify || lp->master) { /* interface locked or slave? */ net_dev = net_dev->next; continue; } - q = net_dev->ib.sq; while (q) { if (q->sqno_start == net_dev->ib.next_num || q->timer < jiffies) { #ifdef CONFIG_ISDN_PPP_VJ /* did we step over a missing frame ? */ - if(q->sqno_start != net_dev->ib.next_num) + if (q->sqno_start != net_dev->ib.next_num) slhc_toss(ippp_table[lp->ppp_slot]->slcomp); #endif @@ -1594,7 +1835,8 @@ net_dev->ib.next_num = q->sqno_end + 1; q->next = NULL; for (; ql;) { - isdn_ppp_push_higher(net_dev, lp, ql->skb, -1); + int proto = isdn_ppp_strip_proto(ql->skb); + isdn_ppp_push_higher(net_dev, lp, ql->skb, proto); qn = ql->next; kfree(ql); ql = qn; @@ -1612,46 +1854,47 @@ * network device ioctl handlers */ -static int isdn_ppp_dev_ioctl_stats(int slot,struct ifreq *ifr,struct device *dev) +static int +isdn_ppp_dev_ioctl_stats(int slot, struct ifreq *ifr, struct device *dev) { - struct ppp_stats *res, t; + struct ppp_stats *res, + t; isdn_net_local *lp = (isdn_net_local *) dev->priv; int err; - res = (struct ppp_stats *) ifr->ifr_ifru.ifru_data; - err = verify_area (VERIFY_WRITE, res,sizeof(struct ppp_stats)); + res = (struct ppp_stats *) ifr->ifr_ifru.ifru_data; + err = verify_area(VERIFY_WRITE, res, sizeof(struct ppp_stats)); - if(err) + if (err) return err; /* build a temporary stat struct and copy it to user space */ - memset (&t, 0, sizeof(struct ppp_stats)); - if(dev->flags & IFF_UP) { + memset(&t, 0, sizeof(struct ppp_stats)); + if (dev->flags & IFF_UP) { t.p.ppp_ipackets = lp->stats.rx_packets; t.p.ppp_ierrors = lp->stats.rx_errors; t.p.ppp_opackets = lp->stats.tx_packets; t.p.ppp_oerrors = lp->stats.tx_errors; #ifdef CONFIG_ISDN_PPP_VJ - if(slot >= 0 && ippp_table[slot]->slcomp) { + if (slot >= 0 && ippp_table[slot]->slcomp) { struct slcompress *slcomp = ippp_table[slot]->slcomp; - t.vj.vjs_packets = slcomp->sls_o_compressed+slcomp->sls_o_uncompressed; + t.vj.vjs_packets = slcomp->sls_o_compressed + slcomp->sls_o_uncompressed; t.vj.vjs_compressed = slcomp->sls_o_compressed; t.vj.vjs_searches = slcomp->sls_o_searches; - t.vj.vjs_misses = slcomp->sls_o_misses; - t.vj.vjs_errorin = slcomp->sls_i_error; - t.vj.vjs_tossed = slcomp->sls_i_tossed; + t.vj.vjs_misses = slcomp->sls_o_misses; + t.vj.vjs_errorin = slcomp->sls_i_error; + t.vj.vjs_tossed = slcomp->sls_i_tossed; t.vj.vjs_uncompressedin = slcomp->sls_i_uncompressed; t.vj.vjs_compressedin = slcomp->sls_i_compressed; } #endif } - copy_to_user (res, &t, sizeof (struct ppp_stats)); - return 0; - + return copy_to_user(res, &t, sizeof(struct ppp_stats)); } -int isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +int +isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd) { int error; char *r; @@ -1659,7 +1902,7 @@ isdn_net_local *lp = (isdn_net_local *) dev->priv; #if 0 - printk(KERN_DEBUG "ippp, dev_ioctl: cmd %#08x , %d \n",cmd,lp->ppp_slot); + printk(KERN_DEBUG "ippp, dev_ioctl: cmd %#08x , %d \n", cmd, lp->ppp_slot); #endif if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) @@ -1669,12 +1912,10 @@ case SIOCGPPPVER: r = (char *) ifr->ifr_ifru.ifru_data; len = strlen(PPP_VERSION) + 1; - error = verify_area(VERIFY_WRITE, r, len); - if (!error) - copy_to_user(r, PPP_VERSION, len); + error = copy_to_user(r, PPP_VERSION, len); break; case SIOCGPPPSTATS: - error = isdn_ppp_dev_ioctl_stats (lp->ppp_slot, ifr, dev); + error = isdn_ppp_dev_ioctl_stats(lp->ppp_slot, ifr, dev); break; default: error = -EINVAL; @@ -1683,51 +1924,55 @@ return error; } -static int isdn_ppp_if_get_unit(char *name) +static int +isdn_ppp_if_get_unit(char *name) { - int len, i, unit = 0, deci; + int len, + i, + unit = 0, + deci; len = strlen(name); - if(strncmp("ippp",name,4) || len > 8) + if (strncmp("ippp", name, 4) || len > 8) return -1; for (i = 0, deci = 1; i < len; i++, deci *= 10) { - char a = name[len-i-1]; + char a = name[len - i - 1]; if (a >= '0' && a <= '9') unit += (a - '0') * deci; else break; } - if (!i || len-i != 4) + if (!i || len - i != 4) unit = -1; return unit; } -int isdn_ppp_dial_slave(char *name) +int +isdn_ppp_dial_slave(char *name) { #ifdef CONFIG_ISDN_MPP isdn_net_dev *ndev; isdn_net_local *lp; struct device *sdev; - if(!(ndev = isdn_net_findif(name))) + if (!(ndev = isdn_net_findif(name))) return 1; lp = &ndev->local; - if(!(lp->flags & ISDN_NET_CONNECTED)) + if (!(lp->flags & ISDN_NET_CONNECTED)) return 5; sdev = lp->slave; - while(sdev) - { + while (sdev) { isdn_net_local *mlp = (isdn_net_local *) sdev->priv; - if(!(mlp->flags & ISDN_NET_CONNECTED)) + if (!(mlp->flags & ISDN_NET_CONNECTED)) break; sdev = mlp->slave; } - if(!sdev) + if (!sdev) return 2; isdn_net_force_dial_lp((isdn_net_local *) sdev->priv); @@ -1737,28 +1982,28 @@ #endif } -int isdn_ppp_hangup_slave(char *name) +int +isdn_ppp_hangup_slave(char *name) { #ifdef CONFIG_ISDN_MPP - isdn_net_dev *ndev; - isdn_net_local *lp; - struct device *sdev; + isdn_net_dev *ndev; + isdn_net_local *lp; + struct device *sdev; - if(!(ndev = isdn_net_findif(name))) + if (!(ndev = isdn_net_findif(name))) return 1; lp = &ndev->local; - if(!(lp->flags & ISDN_NET_CONNECTED)) + if (!(lp->flags & ISDN_NET_CONNECTED)) return 5; sdev = lp->slave; - while(sdev) - { + while (sdev) { isdn_net_local *mlp = (isdn_net_local *) sdev->priv; - if((mlp->flags & ISDN_NET_CONNECTED)) + if ((mlp->flags & ISDN_NET_CONNECTED)) break; sdev = mlp->slave; } - if(!sdev) + if (!sdev) return 2; isdn_net_hangup(sdev); @@ -1768,13 +2013,182 @@ #endif } +/* + * PPP compression stuff + */ +static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master) +{ +#if 1 + printk(KERN_ERR "compression not included!\n"); + SET_SKB_FREE(skb); + dev_kfree_skb(skb,FREE_WRITE); + return NULL; +#else + if(!master) { + /* + * single link compression + */ + if(!is->link_compressor) { + printk(KERN_ERR "ippp: no (link) compressor defined!\n"); + SET_SKB_FREE(skb); + dev_kfree_skb(skb,FREE_WRITE); + return NULL; + } + if(!is->link_decomp_stat) { + printk(KERN_DEBUG "ippp: initialize link compressor\n"); + } +/* + -> decompress link +*/ + } + else { + /* + * 'normal' or bundle-compression + */ + if(!master->compressor) { + printk(KERN_ERR "ippp: no (link) compressor defined!\n"); + SET_SKB_FREE(skb); + dev_kfree_skb(skb,FREE_WRITE); + return NULL; + } + if(!master->decomp_stat) { +#if 0 + master->decomp_stat = (master->compressor->decomp_alloc)( .. ); +#endif + printk(KERN_DEBUG "ippp: initialize compressor\n"); + } + } + + return skb; +#endif +} + +/* + * compress a frame + * type=0: normal/bundle compression + * =1: link compression + * returns original skb if we haven't compressed the frame + * and a new skb pointer if we've done it + */ +static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto, + struct ippp_struct *is,struct ippp_struct *master,int type) +{ +#if 1 + return skb_in; +#else + int ret; + int new_proto; + struct isdn_ppp_compressor *compressor; + void *stat; + struct sk_buff *skb_out; + + if(type) { /* type=1 => Link compression */ + compressor = is->link_compressor; + stat = is->link_comp_stat; + new_proto = PPP_LINK_COMP; + } + else { + if(!master) { + compressor = is->compressor; + stat = is->comp_stat; + } + else { + compressor = master->compressor; + stat = master->comp_stat; + } + new_proto = PPP_COMP; + } + + if(!compressor) { + printk(KERN_ERR "No compressor set!\n"); + return skb_in; + } + if(!stat) { + /* init here ? */ + return skb_in; + } + + skb_out = dev_alloc_skb(skb_in->len); + if(!skb_out) + return skb_in; + + ret = (compressor->compress)(stat,skb_in,skb_out,*proto); + if(!ret) { + SET_SKB_FREE(skb_out); + dev_kfree_skb(skb_out,0); + return skb_in; + } + + dev_kfree_skb(skb_in,FREE_WRITE); + *proto = new_proto; + return skb_out; +#endif + +} + +/* + * we received a CCP frame .. + * not a clean solution, but we SHOULD handle a few cased in the kernel + */ +static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, + struct sk_buff *skb) +{ +#if 0 + printk(KERN_DEBUG "isdn_ppp_receive_cpp: %02x %02x %02x %02x %02x %02x %02x %02x\n", + skb->data[0],skb->data[1],skb->data[2],skb->data[3], + skb->data[4],skb->data[5],skb->data[6],skb->data[7] ); +#endif +} + +int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc) +{ + ipc->next = ipc_head; + ipc->prev = NULL; + if(ipc_head) { + ipc_head->prev = ipc; + } + ipc_head = ipc; + return 0; +} + +int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc) +{ + if(ipc->prev) + ipc->prev->next = ipc->next; + else + ipc_head = ipc->next; + if(ipc->next) + ipc->next->prev = ipc->prev; + ipc->prev = ipc->next = NULL; + return 0; +} + +static int isdn_ppp_set_compressor(struct ippp_struct *is,int num) +{ + struct isdn_ppp_compressor *ipc = ipc_head; + + while(ipc) { + if(ipc->num == num) { + return 0; + is->compressor = ipc; + is->link_compressor = ipc; + } + ipc = ipc->next; + } + return -EINVAL; +} + + #if 0 -static struct symbol_table isdn_ppp_syms = { +static struct symbol_table isdn_ppp_syms = +{ #include - X(isdn_ppp_register_compressor), - X(isdn_ppp_unregister_compressor), + X(isdn_ppp_register_compressor), + X(isdn_ppp_unregister_compressor), #include }; #endif + + diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_ppp.h linux/drivers/isdn/isdn_ppp.h --- v2.0.30/linux/drivers/isdn/isdn_ppp.h Tue Nov 12 22:36:20 1996 +++ linux/drivers/isdn/isdn_ppp.h Mon Aug 4 17:34:00 1997 @@ -1,9 +1,9 @@ -/* $Id: isdn_ppp.h,v 1.6 1996/09/23 01:58:11 fritz Exp $ - * +/* $Id: isdn_ppp.h,v 1.10 1997/06/17 13:06:00 hipp Exp $ + * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel). * * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.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) @@ -16,9 +16,26 @@ * * 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. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_ppp.h,v $ + * Revision 1.10 1997/06/17 13:06:00 hipp + * Applied Eric's underflow-patches (slightly modified) + * more compression changes (but disabled at the moment) + * changed one copy_to_user() to run with enabled IRQs + * a few MP changes + * changed 'proto' handling in the isdn_ppp receive code + * + * Revision 1.9 1997/02/11 18:32:59 fritz + * Bugfix in isdn_ppp_free_mpqueue(). + * + * Revision 1.8 1997/02/10 10:11:33 fritz + * More changes for Kernel 2.1.X compatibility. + * + * Revision 1.7 1997/02/03 23:18:57 fritz + * Removed isdn_ppp_free_sqqueue prototype + * and ippp_table (both static in isdn_ppp.c). + * * Revision 1.6 1996/09/23 01:58:11 fritz * Fix: With syncPPP encapsulation, discard LCP packets * when calculating hangup timeout. @@ -41,31 +58,34 @@ * */ -#include /* for PPP_PROTOCOL */ +#include /* for PPP_PROTOCOL */ extern void isdn_ppp_timer_timeout(void); -extern int isdn_ppp_read(int , struct file *, char *, int); -extern int isdn_ppp_write(int , struct file *, const char *, int); -extern int isdn_ppp_open(int , struct file *); -extern int isdn_ppp_init(void); +extern int isdn_ppp_read(int, struct file *, char *, int); +extern int isdn_ppp_write(int, struct file *, const char *, int); +extern int isdn_ppp_open(int, struct file *); +extern int isdn_ppp_init(void); extern void isdn_ppp_cleanup(void); -extern int isdn_ppp_free(isdn_net_local *); -extern int isdn_ppp_bind(isdn_net_local *); -extern int isdn_ppp_xmit(struct sk_buff *, struct device *); +extern int isdn_ppp_free(isdn_net_local *); +extern int isdn_ppp_bind(isdn_net_local *); +extern int isdn_ppp_xmit(struct sk_buff *, struct device *); extern void isdn_ppp_receive(isdn_net_dev *, isdn_net_local *, struct sk_buff *); -extern int isdn_ppp_dev_ioctl(struct device *, struct ifreq *, int); -extern void isdn_ppp_free_mpqueue(isdn_net_dev *); -extern void isdn_ppp_free_sqqueue(isdn_net_dev *); -extern int isdn_ppp_select(int, struct file *, int, select_table *); -extern int isdn_ppp_ioctl(int, struct file *, unsigned int, unsigned long); +extern int isdn_ppp_dev_ioctl(struct device *, struct ifreq *, int); +#if (LINUX_VERSION_CODE < 0x020117) +extern int isdn_ppp_select(int, struct file *, int, select_table *); +#else +extern unsigned int isdn_ppp_poll(struct file *, poll_table *); +#endif +extern int isdn_ppp_ioctl(int, struct file *, unsigned int, unsigned long); extern void isdn_ppp_release(int, struct file *); -extern int isdn_ppp_dial_slave(char *); +extern int isdn_ppp_dial_slave(char *); extern void isdn_ppp_wakeup_daemon(isdn_net_local *); -extern struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS]; - #define IPPP_OPEN 0x01 #define IPPP_CONNECT 0x02 #define IPPP_CLOSEWAIT 0x04 #define IPPP_NOBLOCK 0x08 #define IPPP_ASSIGNED 0x10 + +#define IPPP_MAX_HEADER 10 + diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_syms.c linux/drivers/isdn/isdn_syms.c --- v2.0.30/linux/drivers/isdn/isdn_syms.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/isdn_syms.c Mon Aug 4 17:34:00 1997 @@ -0,0 +1,55 @@ +/* $Id: isdn_syms.c,v 1.3 1997/02/16 01:02:47 fritz Exp $ + + * Linux ISDN subsystem, exported symbols (linklevel). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: isdn_syms.c,v $ + * Revision 1.3 1997/02/16 01:02:47 fritz + * Added GPL-Header, Id and Log + * + */ +#include +#include +#include + +#ifndef __GENKSYMS__ /* Don't want genksyms report unneeded structs */ +#include +#endif +#include "isdn_common.h" + +#if (LINUX_VERSION_CODE < 0x020111) +static int has_exported; + +static struct symbol_table isdn_syms = { +#include + X(register_isdn), +#include +}; + +void +isdn_export_syms(void) +{ + if (has_exported) + return; + register_symtab(&isdn_syms); + has_exported = 1; +} + +#else + +EXPORT_SYMBOL(register_isdn); + +#endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c --- v2.0.30/linux/drivers/isdn/isdn_tty.c Tue Nov 12 22:36:20 1996 +++ linux/drivers/isdn/isdn_tty.c Thu Aug 14 11:19:48 1997 @@ -1,10 +1,10 @@ -/* $Id: isdn_tty.c,v 1.23 1996/10/22 23:14:02 fritz Exp $ - * +/* $Id: isdn_tty.c,v 1.41 1997/05/27 15:17:31 fritz Exp $ + * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel). * * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg - * + * * 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) @@ -17,9 +17,80 @@ * * 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. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_tty.c,v $ + * Revision 1.41 1997/05/27 15:17:31 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.40 1997/03/24 22:55:27 fritz + * Added debug code for status callbacks. + * + * Revision 1.39 1997/03/21 18:25:56 fritz + * Corrected CTS handling. + * + * Revision 1.38 1997/03/07 12:13:35 fritz + * Bugfix: Send audio in adpcm format was broken. + * Bugfix: CTS handling was wrong. + * + * Revision 1.37 1997/03/07 01:37:34 fritz + * Bugfix: Did not compile with CONFIG_ISDN_AUDIO disabled. + * Bugfix: isdn_tty_tint() did not handle lowlevel errors correctly. + * Bugfix: conversion was wrong when sending ulaw audio. + * Added proper ifdef's for CONFIG_ISDN_AUDIO + * + * Revision 1.36 1997/03/04 21:41:55 fritz + * Fix: Excessive stack usage of isdn_tty_senddown() + * and isdn_tty_end_vrx() could lead to problems. + * + * Revision 1.35 1997/03/02 19:05:52 fritz + * Bugfix: Avoid recursion. + * + * Revision 1.34 1997/03/02 14:29:22 fritz + * More ttyI related cleanup. + * + * Revision 1.33 1997/02/28 02:32:45 fritz + * Cleanup: Moved some tty related stuff from isdn_common.c + * to isdn_tty.c + * Bugfix: Bisync protocol did not behave like documented. + * + * Revision 1.32 1997/02/23 15:43:03 fritz + * Small change in handling of incoming calls + * documented in newest version of ttyI.4 + * + * Revision 1.31 1997/02/21 13:05:57 fritz + * Bugfix: Remote hangup did not set location-info on ttyI's + * + * Revision 1.30 1997/02/18 09:41:05 fritz + * Added support for bitwise access to modem registers (ATSx.y=n, ATSx.y?). + * Beautified output of AT&V. + * + * Revision 1.29 1997/02/16 12:11:51 fritz + * Added S13,Bit4 option. + * + * Revision 1.28 1997/02/10 22:07:08 fritz + * Added 2 modem registers for numbering plan and screening info. + * + * Revision 1.27 1997/02/10 21:31:14 fritz + * Changed setup-interface (incoming and outgoing). + * + * Revision 1.26 1997/02/10 20:12:48 fritz + * Changed interface for reporting incoming calls. + * + * Revision 1.25 1997/02/03 23:04:30 fritz + * Reformatted according CodingStyle. + * skb->free stuff replaced by macro. + * Finished full-duplex audio. + * + * Revision 1.24 1997/01/14 01:32:42 fritz + * Changed audio receive not to rely on skb->users and skb->lock. + * Added ATI2 and related variables. + * Started adding full-duplex audio capability. + * * Revision 1.23 1996/10/22 23:14:02 fritz * Changes for compatibility to 2.0.X and 2.1.X kernels. * @@ -114,6 +185,7 @@ * Initial revision * */ +#undef ISDN_TTY_STAT_DEBUG #define __NO_VERSION__ #include @@ -129,29 +201,35 @@ /* Prototypes */ -static int isdn_tty_edit_at(const char *, int, modem_info *, int); +static int isdn_tty_edit_at(const char *, int, modem_info *, int); static void isdn_tty_check_esc(const u_char *, u_char, int, int *, int *, int); static void isdn_tty_modem_reset_regs(modem_info *, int); static void isdn_tty_cmd_ATA(modem_info *); static void isdn_tty_at_cout(char *, modem_info *); static void isdn_tty_flush_buffer(struct tty_struct *); +static void isdn_tty_modem_result(int, modem_info *); +#ifdef CONFIG_ISDN_AUDIO +static int isdn_tty_countDLE(unsigned char *, int); +#endif /* Leave this unchanged unless you know what you do! */ #define MODEM_PARANOIA_CHECK #define MODEM_DO_RESTART static char *isdn_ttyname_ttyI = "ttyI"; -static char *isdn_ttyname_cui = "cui"; -static int bit2si[8] = {1,5,7,7,7,7,7,7}; -static int si2bit[8] = {4,1,4,4,4,4,4,4}; - -char *isdn_tty_revision = "$Revision: 1.23 $"; +static char *isdn_ttyname_cui = "cui"; +static int bit2si[8] = +{1, 5, 7, 7, 7, 7, 7, 7}; +static int si2bit[8] = +{4, 1, 4, 4, 4, 4, 4, 4}; + +char *isdn_tty_revision = "$Revision: 1.41 $"; #define DLE 0x10 #define ETX 0x03 #define DC4 0x14 -/* isdn_tty_try_read() is called from within isdn_receive_callback() +/* isdn_tty_try_read() is called from within isdn_tty_rcv_skb() * to stuff incoming data directly into a tty's flip-buffer. This * is done to speed up tty-receiving if the receive-queue is empty. * This routine MUST be called with interrupts off. @@ -160,37 +238,45 @@ * 0 = Failure, data has to be buffered and later processed by * isdn_tty_readmodem(). */ -int isdn_tty_try_read(modem_info *info, struct sk_buff *skb) +static int +isdn_tty_try_read(modem_info * info, struct sk_buff *skb) { - int c; - int len; - struct tty_struct *tty; + int c; + int len; + struct tty_struct *tty; if (info->online) { if ((tty = info->tty)) { if (info->mcr & UART_MCR_RTS) { c = TTY_FLIPBUF_SIZE - tty->flip.count; - len = skb->len + skb->users; + len = skb->len +#ifdef CONFIG_ISDN_AUDIO + + ISDN_AUDIO_SKB_DLECOUNT(skb) +#endif + ; if (c >= len) { - if (skb->users) - while (skb->len--) { - if (*skb->data == DLE) - tty_insert_flip_char(tty, DLE, 0); - tty_insert_flip_char(tty, *skb->data++, 0); - } - else { - memcpy(tty->flip.char_buf_ptr, - skb->data, len); - tty->flip.count += len; - tty->flip.char_buf_ptr += len; - memset(tty->flip.flag_buf_ptr, 0, len); - tty->flip.flag_buf_ptr += len; - } - if (info->emu.mdmreg[12] & 128) - tty->flip.flag_buf_ptr[len - 1] = 0xff; - queue_task_irq_off(&tty->flip.tqueue, &tq_timer); - skb->free = 1; - kfree_skb(skb, FREE_READ); +#ifdef CONFIG_ISDN_AUDIO + if (ISDN_AUDIO_SKB_DLECOUNT(skb)) + while (skb->len--) { + if (*skb->data == DLE) + tty_insert_flip_char(tty, DLE, 0); + tty_insert_flip_char(tty, *skb->data++, 0); + } else { +#endif + memcpy(tty->flip.char_buf_ptr, + skb->data, len); + tty->flip.count += len; + tty->flip.char_buf_ptr += len; + memset(tty->flip.flag_buf_ptr, 0, len); + tty->flip.flag_buf_ptr += len; +#ifdef CONFIG_ISDN_AUDIO + } +#endif + if (info->emu.mdmreg[12] & 128) + tty->flip.flag_buf_ptr[len - 1] = 0xff; + queue_task(&tty->flip.tqueue, &tq_timer); + SET_SKB_FREE(skb); + kfree_skb(skb, FREE_READ); return 1; } } @@ -203,7 +289,8 @@ * It tries getting received data from the receive queue an stuff it into * the tty's flip-buffer. */ -void isdn_tty_readmodem(void) +void +isdn_tty_readmodem(void) { int resched = 0; int midx; @@ -216,7 +303,7 @@ for (i = 0; i < ISDN_MAX_CHANNELS; i++) { if ((midx = dev->m_idx[i]) >= 0) { - info = &dev->mdm.info[midx]; + info = &dev->mdm.info[midx]; if (info->online) { r = 0; #ifdef CONFIG_ISDN_AUDIO @@ -226,20 +313,20 @@ if (info->mcr & UART_MCR_RTS) { c = TTY_FLIPBUF_SIZE - tty->flip.count; if (c > 0) { - save_flags(flags); - cli(); + save_flags(flags); + cli(); r = isdn_readbchan(info->isdn_driver, info->isdn_channel, - tty->flip.char_buf_ptr, - tty->flip.flag_buf_ptr, c, 0); - /* CISCO AsyncPPP Hack */ + tty->flip.char_buf_ptr, + tty->flip.flag_buf_ptr, c, 0); + /* CISCO AsyncPPP Hack */ if (!(info->emu.mdmreg[12] & 128)) memset(tty->flip.flag_buf_ptr, 0, r); tty->flip.count += r; tty->flip.flag_buf_ptr += r; tty->flip.char_buf_ptr += r; if (r) - queue_task_irq_off(&tty->flip.tqueue, &tq_timer); - restore_flags(flags); + queue_task(&tty->flip.tqueue, &tq_timer); + restore_flags(flags); } } else r = 1; @@ -251,257 +338,383 @@ } else info->rcvsched = 1; } - } + } } if (!resched) isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0); } -void isdn_tty_cleanup_xmit(modem_info *info) +int +isdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb) { - struct sk_buff *skb; - unsigned long flags; + ulong flags; + int midx; +#ifdef CONFIG_ISDN_AUDIO + int ifmt; +#endif + modem_info *info; - save_flags(flags); - cli(); - if (skb_queue_len(&info->xmit_queue)) - while ((skb = skb_dequeue(&info->xmit_queue))) { - skb->free = 1; - kfree_skb(skb, FREE_WRITE); - } - if (skb_queue_len(&info->dtmf_queue)) - while ((skb = skb_dequeue(&info->dtmf_queue))) { - skb->free = 1; - kfree_skb(skb, FREE_WRITE); - } - restore_flags(flags); -} - -static void isdn_tty_tint(modem_info *info) -{ - struct sk_buff *skb = skb_dequeue(&info->xmit_queue); - int len, slen; - - if (!skb) - return; - len = skb->len; - if ((slen = isdn_writebuf_skb_stub(info->isdn_driver, - info->isdn_channel, skb)) == len) { - struct tty_struct *tty = info->tty; - info->send_outstanding++; - info->msr |= UART_MSR_CTS; - info->lsr |= UART_LSR_TEMT; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); - wake_up_interruptible(&tty->write_wait); - return; - } - if (slen > 0) - skb_pull(skb,slen); - skb_queue_head(&info->xmit_queue, skb); + if ((midx = dev->m_idx[i]) < 0) { + /* if midx is invalid, packet is not for tty */ + return 0; + } + info = &dev->mdm.info[midx]; +#ifdef CONFIG_ISDN_AUDIO + ifmt = 1; + + if (info->vonline) + isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt); +#endif + if ((info->online < 2) +#ifdef CONFIG_ISDN_AUDIO + && (!(info->vonline & 1)) +#endif + ) { + /* If Modem not listening, drop data */ + SET_SKB_FREE(skb); + kfree_skb(skb, FREE_READ); + return 1; + } + if (info->emu.mdmreg[13] & 2) + /* T.70 decoding: Simply throw away the T.70 header (4 bytes) */ + if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1))) + skb_pull(skb, 4); +#ifdef CONFIG_ISDN_AUDIO + if (skb_headroom(skb) < sizeof(isdn_audio_skb)) { + printk(KERN_WARNING + "isdn_audio: insufficient skb_headroom, dropping\n"); + SET_SKB_FREE(skb); + kfree_skb(skb, FREE_READ); + return 1; + } + ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; + ISDN_AUDIO_SKB_LOCK(skb) = 0; + if (info->vonline & 1) { + /* voice conversion/compression */ + switch (info->emu.vpar[3]) { + case 2: + case 3: + case 4: + /* adpcm + * Since compressed data takes less + * space, we can overwrite the buffer. + */ + skb_trim(skb, isdn_audio_xlaw2adpcm(info->adpcmr, + ifmt, + skb->data, + skb->data, + skb->len)); + break; + case 5: + /* a-law */ + if (!ifmt) + isdn_audio_ulaw2alaw(skb->data, skb->len); + break; + case 6: + /* u-law */ + if (ifmt) + isdn_audio_alaw2ulaw(skb->data, skb->len); + break; + } + ISDN_AUDIO_SKB_DLECOUNT(skb) = + isdn_tty_countDLE(skb->data, skb->len); + } +#endif + /* Try to deliver directly via tty-flip-buf if queue is empty */ + save_flags(flags); + cli(); + if (skb_queue_empty(&dev->drv[di]->rpqueue[channel])) + if (isdn_tty_try_read(info, skb)) { + restore_flags(flags); + return 1; + } + /* Direct deliver failed or queue wasn't empty. + * Queue up for later dequeueing via timer-irq. + */ + __skb_queue_tail(&dev->drv[di]->rpqueue[channel], skb); + dev->drv[di]->rcvcount[channel] += + (skb->len +#ifdef CONFIG_ISDN_AUDIO + + ISDN_AUDIO_SKB_DLECOUNT(skb) +#endif + ); + restore_flags(flags); + /* Schedule dequeuing */ + if ((dev->modempoll) && (info->rcvsched)) + isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); + return 1; } +void +isdn_tty_cleanup_xmit(modem_info * info) +{ + struct sk_buff *skb; + unsigned long flags; + + save_flags(flags); + cli(); + if (skb_queue_len(&info->xmit_queue)) + while ((skb = skb_dequeue(&info->xmit_queue))) { + SET_SKB_FREE(skb); + kfree_skb(skb, FREE_WRITE); + } #ifdef CONFIG_ISDN_AUDIO -int isdn_tty_countDLE(unsigned char *buf, int len) -{ - int count = 0; - - while (len--) - if (*buf++ == DLE) - count++; - return count; + if (skb_queue_len(&info->dtmf_queue)) + while ((skb = skb_dequeue(&info->dtmf_queue))) { + SET_SKB_FREE(skb); + kfree_skb(skb, FREE_WRITE); + } +#endif + restore_flags(flags); +} + +static void +isdn_tty_tint(modem_info * info) +{ + struct sk_buff *skb = skb_dequeue(&info->xmit_queue); + int len, + slen; + + if (!skb) + return; + len = skb->len; + if ((slen = isdn_writebuf_skb_stub(info->isdn_driver, + info->isdn_channel, skb)) == len) { + struct tty_struct *tty = info->tty; + info->send_outstanding++; + info->msr |= UART_MSR_CTS; + info->lsr |= UART_LSR_TEMT; + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup) (tty); + wake_up_interruptible(&tty->write_wait); + return; + } + if (slen < 0) { + /* Error: no channel, already shutdown, or wrong parameter */ + SET_SKB_FREE(skb); + dev_kfree_skb(skb, FREE_WRITE); + return; + } + if (slen) + skb_pull(skb, slen); + skb_queue_head(&info->xmit_queue, skb); +} + +#ifdef CONFIG_ISDN_AUDIO +static int +isdn_tty_countDLE(unsigned char *buf, int len) +{ + int count = 0; + + while (len--) + if (*buf++ == DLE) + count++; + return count; } /* This routine is called from within isdn_tty_write() to perform * DLE-decoding when sending audio-data. */ -static int isdn_tty_handleDLEdown(modem_info *info, atemu *m, int len) +static int +isdn_tty_handleDLEdown(modem_info * info, atemu * m, int len) { - unsigned char *p = &info->xmit_buf[info->xmit_count]; - int count = 0; + unsigned char *p = &info->xmit_buf[info->xmit_count]; + int count = 0; - while (len>0) { - if (m->lastDLE) { - m->lastDLE = 0; - switch (*p) { - case DLE: - /* Escape code */ - if (len>1) - memmove(p,p+1,len-1); - p--; - count++; - break; - case ETX: - /* End of data */ - info->vonline |= 4; - return count; - case DC4: - /* Abort RX */ - info->vonline &= ~1; - isdn_tty_at_cout("\020\003", info); - if (!info->vonline) - isdn_tty_at_cout("\r\nVCON\r\n", info); - /* Fall through */ - case 'q': - case 's': - /* Silence */ - if (len>1) - memmove(p,p+1,len-1); - p--; - break; - } - } else { - if (*p == DLE) - m->lastDLE = 1; - else - count++; - } - p++; - len--; - } - if (len<0) { - printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n"); - return 0; - } - return count; + while (len > 0) { + if (m->lastDLE) { + m->lastDLE = 0; + switch (*p) { + case DLE: + /* Escape code */ + if (len > 1) + memmove(p, p + 1, len - 1); + p--; + count++; + break; + case ETX: + /* End of data */ + info->vonline |= 4; + return count; + case DC4: + /* Abort RX */ + info->vonline &= ~1; +#ifdef ISDN_DEBUG_MODEM_VOICE + printk(KERN_DEBUG + "DLEdown: got DLE-DC4, send DLE-ETX on ttyI%d\n", + info->line); +#endif + isdn_tty_at_cout("\020\003", info); + if (!info->vonline) { +#ifdef ISDN_DEBUG_MODEM_VOICE + printk(KERN_DEBUG + "DLEdown: send VCON on ttyI%d\n", + info->line); +#endif + isdn_tty_at_cout("\r\nVCON\r\n", info); + } + /* Fall through */ + case 'q': + case 's': + /* Silence */ + if (len > 1) + memmove(p, p + 1, len - 1); + p--; + break; + } + } else { + if (*p == DLE) + m->lastDLE = 1; + else + count++; + } + p++; + len--; + } + if (len < 0) { + printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n"); + return 0; + } + return count; } /* This routine is called from within isdn_tty_write() when receiving * audio-data. It interrupts receiving, if an character other than * ^S or ^Q is sent. */ -static int isdn_tty_end_vrx(const char *buf, int c, int from_user) +static int +isdn_tty_end_vrx(const char *buf, int c, int from_user) { - char tmpbuf[VBUF]; - char *p; + char ch; - if (c > VBUF) { - printk(KERN_ERR "isdn_tty: (end_vrx) BUFFER OVERFLOW!!!\n"); - return 1; - } - if (from_user) { - copy_from_user(tmpbuf, buf, c); - p = tmpbuf; - } else - p = (char *)buf; - while (c--) { - if ((*p != 0x11) && (*p != 0x13)) - return 1; - p++; - } - return 0; + while (c--) { + if (from_user) + GET_USER(ch, buf); + else + ch = *buf; + if ((ch != 0x11) && (ch != 0x13)) + return 1; + buf++; + } + return 0; } -static int voice_cf[7] = { 1, 1, 4, 3, 2, 1, 1 }; +static int voice_cf[7] = +{0, 0, 4, 3, 2, 0, 0}; -#endif /* CONFIG_ISDN_AUDIO */ +#endif /* CONFIG_ISDN_AUDIO */ /* isdn_tty_senddown() is called either directly from within isdn_tty_write() * or via timer-interrupt from within isdn_tty_modem_xmit(). It pulls * outgoing data from the tty's xmit-buffer, handles voice-decompression or * T.70 if necessary, and finally queues it up for sending via isdn_tty_tint. */ -static void isdn_tty_senddown(modem_info * info) +static void +isdn_tty_senddown(modem_info * info) { - unsigned char *buf = info->xmit_buf; - int buflen; - int skb_res; - struct sk_buff *skb; - unsigned long flags; - - save_flags(flags); - cli(); - if (!(buflen = info->xmit_count)) { - restore_flags(flags); - return; - } - if (info->isdn_driver < 0) { - info->xmit_count = 0; - restore_flags(flags); - return; - } - skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4; -#ifdef CONFIG_ISDN_AUDIO - if (info->vonline & 2) { - /* For now, ifmt is fixed to 1 (alaw), since this - * is used with ISDN everywhere in the world, except - * US, Canada and Japan. - * Later, when US-ISDN protocols are implemented, - * this setting will depend on the D-channel protocol. - */ - int ifmt = 1; - int skb_len; - unsigned char hbuf[VBUF]; - - memcpy(hbuf,info->xmit_buf,buflen); - info->xmit_count = 0; - restore_flags(flags); - /* voice conversion/decompression */ - skb_len = buflen * voice_cf[info->emu.vpar[3]]; - skb = dev_alloc_skb(skb_len + skb_res); - if (!skb) { - printk(KERN_WARNING - "isdn_tty: Out of memory in ttyI%d senddown\n", info->line); - return; - } - skb_reserve(skb, skb_res); - switch (info->emu.vpar[3]) { - case 2: - case 3: - case 4: - /* adpcm, compatible to ZyXel 1496 modem - * with ROM revision 6.01 - */ - buflen = isdn_audio_adpcm2xlaw(info->adpcms, - ifmt, - hbuf, - skb_put(skb,skb_len), - buflen); - skb_trim(skb, buflen); - break; - case 5: - /* a-law */ - if (!ifmt) - isdn_audio_alaw2ulaw(hbuf,buflen); - memcpy(skb_put(skb,buflen),hbuf,buflen); - break; - case 6: - /* u-law */ - if (ifmt) - isdn_audio_ulaw2alaw(hbuf,buflen); - memcpy(skb_put(skb,buflen),hbuf,buflen); - break; - } - if (info->vonline & 4) { - info->vonline &= ~6; - if (!info->vonline) - isdn_tty_at_cout("\r\nVCON\r\n",info); - } - } else { -#endif /* CONFIG_ISDN_AUDIO */ - skb = dev_alloc_skb(buflen + skb_res); - if (!skb) { - printk(KERN_WARNING - "isdn_tty: Out of memory in ttyI%d senddown\n", info->line); - restore_flags(flags); - return; - } - skb_reserve(skb, skb_res); - memcpy(skb_put(skb,buflen),buf,buflen); - info->xmit_count = 0; - restore_flags(flags); -#ifdef CONFIG_ISDN_AUDIO - } -#endif - skb->free = 1; - if (info->emu.mdmreg[13] & 2) - /* Add T.70 simplified header */ - memcpy(skb_push(skb, 4), "\1\0\1\0", 4); - skb_queue_tail(&info->xmit_queue, skb); - if ((info->emu.mdmreg[12] & 0x10) != 0) - info->msr &= UART_MSR_CTS; - info->lsr &= UART_LSR_TEMT; + int buflen; + int skb_res; +#ifdef CONFIG_ISDN_AUDIO + int audio_len; +#endif + struct sk_buff *skb; + unsigned long flags; + +#ifdef CONFIG_ISDN_AUDIO + if (info->vonline & 4) { + info->vonline &= ~6; + if (!info->vonline) { +#ifdef ISDN_DEBUG_MODEM_VOICE + printk(KERN_DEBUG + "senddown: send VCON on ttyI%d\n", + info->line); +#endif + isdn_tty_at_cout("\r\nVCON\r\n", info); + } + } +#endif + save_flags(flags); + cli(); + if (!(buflen = info->xmit_count)) { + restore_flags(flags); + return; + } + if ((info->emu.mdmreg[12] & 0x10) != 0) + info->msr &= ~UART_MSR_CTS; + info->lsr &= ~UART_LSR_TEMT; + if (info->isdn_driver < 0) { + info->xmit_count = 0; + restore_flags(flags); + return; + } + skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4; +#ifdef CONFIG_ISDN_AUDIO + if (info->vonline & 2) + audio_len = buflen * voice_cf[info->emu.vpar[3]]; + else + audio_len = 0; + skb = dev_alloc_skb(skb_res + buflen + audio_len); +#else + skb = dev_alloc_skb(skb_res + buflen); +#endif + if (!skb) { + restore_flags(flags); + printk(KERN_WARNING + "isdn_tty: Out of memory in ttyI%d senddown\n", + info->line); + return; + } + skb_reserve(skb, skb_res); + memcpy(skb_put(skb, buflen), info->xmit_buf, buflen); + info->xmit_count = 0; + restore_flags(flags); +#ifdef CONFIG_ISDN_AUDIO + if (info->vonline & 2) { + /* For now, ifmt is fixed to 1 (alaw), since this + * is used with ISDN everywhere in the world, except + * US, Canada and Japan. + * Later, when US-ISDN protocols are implemented, + * this setting will depend on the D-channel protocol. + */ + int ifmt = 1; + + /* voice conversion/decompression */ + switch (info->emu.vpar[3]) { + case 2: + case 3: + case 4: + /* adpcm, compatible to ZyXel 1496 modem + * with ROM revision 6.01 + */ + audio_len = isdn_audio_adpcm2xlaw(info->adpcms, + ifmt, + skb->data, + skb_put(skb, audio_len), + buflen); + skb_pull(skb, buflen); + skb_trim(skb, audio_len); + break; + case 5: + /* a-law */ + if (!ifmt) + isdn_audio_alaw2ulaw(skb->data, + buflen); + break; + case 6: + /* u-law */ + if (ifmt) + isdn_audio_ulaw2alaw(skb->data, + buflen); + break; + } + } +#endif /* CONFIG_ISDN_AUDIO */ + SET_SKB_FREE(skb); + if (info->emu.mdmreg[13] & 2) + /* Add T.70 simplified header */ + memcpy(skb_push(skb, 4), "\1\0\1\0", 4); + skb_queue_tail(&info->xmit_queue, skb); } /************************************************************ @@ -517,53 +730,55 @@ * isdn_tty_modem_result() to stuff a "NO CARRIER" Message * into the tty's flip-buffer. */ -static void isdn_tty_modem_do_ncarrier(unsigned long data) +static void +isdn_tty_modem_do_ncarrier(unsigned long data) { - modem_info * info = (modem_info *)data; - isdn_tty_modem_result(3, info); + modem_info *info = (modem_info *) data; + isdn_tty_modem_result(3, info); } /* Next routine is called, whenever the DTR-signal is raised. * It checks the ncarrier-flag, and triggers the above routine * when necessary. The ncarrier-flag is set, whenever DTR goes * low. - */ -static void isdn_tty_modem_ncarrier(modem_info * info) + */ +static void +isdn_tty_modem_ncarrier(modem_info * info) { - if (info->ncarrier) { - info->ncarrier = 0; - info->nc_timer.expires = jiffies + HZ; - info->nc_timer.function = isdn_tty_modem_do_ncarrier; - info->nc_timer.data = (unsigned long)info; - add_timer(&info->nc_timer); - } + if (info->ncarrier) { + info->nc_timer.expires = jiffies + HZ; + info->nc_timer.function = isdn_tty_modem_do_ncarrier; + info->nc_timer.data = (unsigned long) info; + add_timer(&info->nc_timer); + } } /* isdn_tty_dial() performs dialing of a tty an the necessary * setup of the lower levels before that. */ -static void isdn_tty_dial(char *n, modem_info * info, atemu * m) +static void +isdn_tty_dial(char *n, modem_info * info, atemu * m) { - int usg = ISDN_USAGE_MODEM; - int si = 7; - int l2 = m->mdmreg[14]; + int usg = ISDN_USAGE_MODEM; + int si = 7; + int l2 = m->mdmreg[14]; isdn_ctrl cmd; ulong flags; int i; - int j; + int j; - for (j=7;j>=0;j--) - if (m->mdmreg[18] & (1<= 0; j--) + if (m->mdmreg[18] & (1 << j)) { + si = bit2si[j]; + break; + } +#ifdef CONFIG_ISDN_AUDIO + if (si == 1) { + l2 = 4; + usg = ISDN_USAGE_VOICE; + } #endif - m->mdmreg[20] = si2bit[si]; + m->mdmreg[20] = si2bit[si]; save_flags(flags); cli(); i = isdn_get_free_channel(usg, l2, m->mdmreg[15], -1, -1); @@ -571,38 +786,44 @@ restore_flags(flags); isdn_tty_modem_result(6, info); } else { - info->isdn_driver = dev->drvmap[i]; - info->isdn_channel = dev->chanmap[i]; - info->drv_index = i; - dev->m_idx[i] = info->line; - dev->usage[i] |= ISDN_USAGE_OUTGOING; - isdn_info_update(); - restore_flags(flags); - cmd.driver = info->isdn_driver; - cmd.arg = info->isdn_channel; - cmd.command = ISDN_CMD_CLREAZ; - dev->drv[info->isdn_driver]->interface->command(&cmd); - strcpy(cmd.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); - cmd.driver = info->isdn_driver; - cmd.command = ISDN_CMD_SETL2; - cmd.arg = info->isdn_channel + (l2 << 8); - dev->drv[info->isdn_driver]->interface->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); - cmd.driver = info->isdn_driver; - cmd.arg = info->isdn_channel; - sprintf(cmd.num, "%s,%s,%d,%d", n, isdn_map_eaz2msn(m->msn, info->isdn_driver), - si, m->mdmreg[19]); - cmd.command = ISDN_CMD_DIAL; - info->dialing = 1; - strcpy(dev->num[i], n); - isdn_info_update(); - dev->drv[info->isdn_driver]->interface->command(&cmd); + info->isdn_driver = dev->drvmap[i]; + info->isdn_channel = dev->chanmap[i]; + info->drv_index = i; + dev->m_idx[i] = info->line; + dev->usage[i] |= ISDN_USAGE_OUTGOING; + info->last_dir = 1; + strcpy(info->last_num, n); + isdn_info_update(); + restore_flags(flags); + cmd.driver = info->isdn_driver; + cmd.arg = info->isdn_channel; + cmd.command = ISDN_CMD_CLREAZ; + dev->drv[info->isdn_driver]->interface->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); + 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); + 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); + cmd.driver = info->isdn_driver; + cmd.arg = info->isdn_channel; + sprintf(cmd.parm.setup.phone, "%s", n); + sprintf(cmd.parm.setup.eazmsn, "%s", + isdn_map_eaz2msn(m->msn, info->isdn_driver)); + cmd.parm.setup.si1 = si; + cmd.parm.setup.si2 = m->mdmreg[19]; + cmd.command = ISDN_CMD_DIAL; + info->dialing = 1; + strcpy(dev->num[i], n); + isdn_info_update(); + dev->drv[info->isdn_driver]->interface->command(&cmd); } } @@ -610,64 +831,66 @@ * ISDN-line (hangup). The usage-status is cleared * and some cleanup is done also. */ -void isdn_tty_modem_hup(modem_info * info) +static void +isdn_tty_modem_hup(modem_info * info, int local) { isdn_ctrl cmd; - int usage; + int usage; - if (!info) - return; + if (!info) + return; #ifdef ISDN_DEBUG_MODEM_HUP - printk(KERN_DEBUG "Mhup ttyI%d\n", info->line); + printk(KERN_DEBUG "Mhup ttyI%d\n", info->line); #endif - info->rcvsched = 0; - info->online = 0; - isdn_tty_flush_buffer(info->tty); - if (info->vonline & 1) { - /* voice-recording, add DLE-ETX */ - isdn_tty_at_cout("\020\003", info); - } - if (info->vonline & 2) { - /* voice-playing, add DLE-DC4 */ - isdn_tty_at_cout("\020\024", info); - } - info->vonline = 0; -#ifdef CONFIG_ISDN_AUDIO - if (info->dtmf_state) { - kfree(info->dtmf_state); - info->dtmf_state = NULL; - } - if (info->adpcms) { - kfree(info->adpcms); - info->adpcms = NULL; - } - if (info->adpcmr) { - kfree(info->adpcmr); - info->adpcmr = NULL; - } + info->rcvsched = 0; + isdn_tty_flush_buffer(info->tty); + if (info->online) { + info->last_lhup = local; + info->online = 0; + /* NO CARRIER message */ + isdn_tty_modem_result(3, info); + } +#ifdef CONFIG_ISDN_AUDIO + info->vonline = 0; + if (info->dtmf_state) { + kfree(info->dtmf_state); + info->dtmf_state = NULL; + } + if (info->adpcms) { + kfree(info->adpcms); + info->adpcms = NULL; + } + if (info->adpcmr) { + kfree(info->adpcmr); + info->adpcmr = NULL; + } #endif - info->msr &= ~(UART_MSR_DCD | UART_MSR_RI); - info->lsr |= UART_LSR_TEMT; + info->msr &= ~(UART_MSR_DCD | UART_MSR_RI); + info->lsr |= UART_LSR_TEMT; if (info->isdn_driver >= 0) { - cmd.driver = info->isdn_driver; - cmd.command = ISDN_CMD_HANGUP; - cmd.arg = info->isdn_channel; - dev->drv[info->isdn_driver]->interface->command(&cmd); + if (local) { + cmd.driver = info->isdn_driver; + cmd.command = ISDN_CMD_HANGUP; + cmd.arg = info->isdn_channel; + dev->drv[info->isdn_driver]->interface->command(&cmd); + } isdn_all_eaz(info->isdn_driver, info->isdn_channel); - usage = (info->emu.mdmreg[20] == 1)? - ISDN_USAGE_VOICE:ISDN_USAGE_MODEM; + info->emu.mdmreg[1] = 0; + usage = (info->emu.mdmreg[20] == 1) ? + ISDN_USAGE_VOICE : ISDN_USAGE_MODEM; isdn_free_channel(info->isdn_driver, info->isdn_channel, - usage); + usage); } info->isdn_driver = -1; info->isdn_channel = -1; - if (info->drv_index >= 0) { - dev->m_idx[info->drv_index] = -1; - info->drv_index = -1; - } + if (info->drv_index >= 0) { + dev->m_idx[info->drv_index] = -1; + info->drv_index = -1; + } } -static inline int isdn_tty_paranoia_check(modem_info * info, kdev_t device, const char *routine) +static inline int +isdn_tty_paranoia_check(modem_info * info, kdev_t device, const char *routine) { #ifdef MODEM_PARANOIA_CHECK if (!info) { @@ -688,9 +911,13 @@ * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */ -static void isdn_tty_change_speed(modem_info * info) +static void +isdn_tty_change_speed(modem_info * info) { - uint cflag, cval, fcr, quot; + uint cflag, + cval, + fcr, + quot; int i; if (!info->tty || !info->tty->termios) @@ -707,19 +934,19 @@ } if (quot) { info->mcr |= UART_MCR_DTR; - isdn_tty_modem_ncarrier(info); + isdn_tty_modem_ncarrier(info); } else { info->mcr &= ~UART_MCR_DTR; - if (info->emu.mdmreg[13] & 4) { + if (info->emu.mdmreg[13] & 4) { #ifdef ISDN_DEBUG_MODEM_HUP - printk(KERN_DEBUG "Mhup in changespeed\n"); + printk(KERN_DEBUG "Mhup in changespeed\n"); #endif - if (info->online) - info->ncarrier = 1; - isdn_tty_modem_reset_regs(info, 0); - isdn_tty_modem_hup(info); - } - return; + if (info->online) + info->ncarrier = 1; + isdn_tty_modem_reset_regs(info, 0); + isdn_tty_modem_hup(info, 1); + } + return; } /* byte size and parity */ cval = cflag & (CSIZE | CSTOPB); @@ -742,7 +969,8 @@ } } -static int isdn_tty_startup(modem_info * info) +static int +isdn_tty_startup(modem_info * info) { ulong flags; @@ -750,12 +978,12 @@ return 0; save_flags(flags); cli(); - isdn_MOD_INC_USE_COUNT(); + isdn_MOD_INC_USE_COUNT(); #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "starting up ttyi%d ...\n", info->line); #endif /* - * Now, initialize the UART + * Now, initialize the UART */ info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2; if (info->tty) @@ -776,7 +1004,8 @@ * 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 isdn_tty_shutdown(modem_info * info) +static void +isdn_tty_shutdown(modem_info * info) { ulong flags; @@ -786,17 +1015,18 @@ printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line); #endif save_flags(flags); - cli(); /* Disable interrupts */ - isdn_MOD_DEC_USE_COUNT(); + cli(); /* Disable interrupts */ + isdn_MOD_DEC_USE_COUNT(); + info->msr &= ~UART_MSR_RI; if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS); - if (info->emu.mdmreg[13] & 4) { - isdn_tty_modem_reset_regs(info, 0); + if (info->emu.mdmreg[13] & 4) { + isdn_tty_modem_reset_regs(info, 0); #ifdef ISDN_DEBUG_MODEM_HUP - printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n"); + printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n"); #endif - isdn_tty_modem_hup(info); - } + isdn_tty_modem_hup(info, 1); + } } if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); @@ -814,9 +1044,11 @@ * - If receiving audio-data, call isdn_tty_end_vrx() to abort if needed. * - If dialing, abort dial. */ -static int isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count) +static int +isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count) { - int c, total = 0; + int c, + total = 0; ulong flags; modem_info *info = (modem_info *) tty->driver_data; @@ -824,70 +1056,82 @@ return 0; if (!tty) return 0; - save_flags(flags); - cli(); + save_flags(flags); + cli(); while (1) { c = MIN(count, info->xmit_size - info->xmit_count); if (info->isdn_driver >= 0) c = MIN(c, dev->drv[info->isdn_driver]->maxbufsize); if (c <= 0) break; - if ((info->online > 1) || - (info->vonline & 2)) { - atemu *m = &info->emu; - - if (!(info->vonline & 2)) - isdn_tty_check_esc(buf, m->mdmreg[2], c, - &(m->pluscount), - &(m->lastplus), - from_user); - if (from_user) - copy_from_user(&(info->xmit_buf[info->xmit_count]), buf, c); - else - memcpy(&(info->xmit_buf[info->xmit_count]), buf, c); -#ifdef CONFIG_ISDN_AUDIO - if (info->vonline & 2) { - int cc; - - if (!(cc = isdn_tty_handleDLEdown(info,m,c))) { - /* If DLE decoding results in zero-transmit, but - * c originally was non-zero, do a wakeup. - */ - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); - wake_up_interruptible(&tty->write_wait); - info->msr |= UART_MSR_CTS; - info->lsr |= UART_LSR_TEMT; - } - info->xmit_count += cc; - } else + if ((info->online > 1) +#ifdef CONFIG_ISDN_AUDIO + || (info->vonline & 3) #endif - info->xmit_count += c; - if (m->mdmreg[13] & 1) { - isdn_tty_senddown(info); - isdn_tty_tint(info); - } - } else { - info->msr |= UART_MSR_CTS; - info->lsr |= UART_LSR_TEMT; + ) { + atemu *m = &info->emu; + #ifdef CONFIG_ISDN_AUDIO - if (info->vonline & 1) { - if (isdn_tty_end_vrx(buf, c, from_user)) { - info->vonline &= ~1; - isdn_tty_at_cout("\020\003\r\nVCON\r\n", info); - } - } else + if (!info->vonline) +#endif + isdn_tty_check_esc(buf, m->mdmreg[2], c, + &(m->pluscount), + &(m->lastplus), + from_user); + if (from_user) + copy_from_user(&(info->xmit_buf[info->xmit_count]), buf, c); + else + memcpy(&(info->xmit_buf[info->xmit_count]), buf, c); +#ifdef CONFIG_ISDN_AUDIO + if (info->vonline) { + int cc = isdn_tty_handleDLEdown(info, m, c); + if (info->vonline & 2) { + if (!cc) { + /* If DLE decoding results in zero-transmit, but + * c originally was non-zero, do a wakeup. + */ + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup) (tty); + wake_up_interruptible(&tty->write_wait); + info->msr |= UART_MSR_CTS; + info->lsr |= UART_LSR_TEMT; + } + info->xmit_count += cc; + } + if ((info->vonline & 3) == 1) { + /* Do NOT handle Ctrl-Q or Ctrl-S + * when in full-duplex audio mode. + */ + if (isdn_tty_end_vrx(buf, c, from_user)) { + info->vonline &= ~1; +#ifdef ISDN_DEBUG_MODEM_VOICE + printk(KERN_DEBUG + "got !^Q/^S, send DLE-ETX,VCON on ttyI%d\n", + info->line); +#endif + isdn_tty_at_cout("\020\003\r\nVCON\r\n", info); + } + } + } else #endif - if (info->dialing) { - info->dialing = 0; + info->xmit_count += c; + if (m->mdmreg[13] & 1) { + isdn_tty_senddown(info); + isdn_tty_tint(info); + } + } else { + info->msr |= UART_MSR_CTS; + info->lsr |= UART_LSR_TEMT; + if (info->dialing) { + info->dialing = 0; #ifdef ISDN_DEBUG_MODEM_HUP - printk(KERN_DEBUG "Mhup in isdn_tty_write\n"); + printk(KERN_DEBUG "Mhup in isdn_tty_write\n"); #endif - isdn_tty_modem_result(3, info); - isdn_tty_modem_hup(info); - } else - c = isdn_tty_edit_at(buf, c, info, from_user); + isdn_tty_modem_result(3, info); + isdn_tty_modem_hup(info, 1); + } else + c = isdn_tty_edit_at(buf, c, info, from_user); } buf += c; count -= c; @@ -895,11 +1139,12 @@ } if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue))) isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1); - restore_flags(flags); + restore_flags(flags); return total; } -static int isdn_tty_write_room(struct tty_struct *tty) +static int +isdn_tty_write_room(struct tty_struct *tty) { modem_info *info = (modem_info *) tty->driver_data; int ret; @@ -912,7 +1157,8 @@ return (ret < 0) ? 0 : ret; } -static int isdn_tty_chars_in_buffer(struct tty_struct *tty) +static int +isdn_tty_chars_in_buffer(struct tty_struct *tty) { modem_info *info = (modem_info *) tty->driver_data; @@ -923,32 +1169,34 @@ return (info->xmit_count); } -static void isdn_tty_flush_buffer(struct tty_struct *tty) +static void +isdn_tty_flush_buffer(struct tty_struct *tty) { modem_info *info; - unsigned long flags; + unsigned long flags; - save_flags(flags); - cli(); - if (!tty) { - restore_flags(flags); - return; - } - info = (modem_info *) tty->driver_data; + save_flags(flags); + cli(); + if (!tty) { + restore_flags(flags); + return; + } + info = (modem_info *) tty->driver_data; if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_buffer")) { - restore_flags(flags); + restore_flags(flags); return; - } - isdn_tty_cleanup_xmit(info); - info->xmit_count = 0; - restore_flags(flags); + } + isdn_tty_cleanup_xmit(info); + info->xmit_count = 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 void isdn_tty_flush_chars(struct tty_struct *tty) +static void +isdn_tty_flush_chars(struct tty_struct *tty) { modem_info *info = (modem_info *) tty->driver_data; @@ -961,12 +1209,13 @@ /* * ------------------------------------------------------------ * isdn_tty_throttle() - * + * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */ -static void isdn_tty_throttle(struct tty_struct *tty) +static void +isdn_tty_throttle(struct tty_struct *tty) { modem_info *info = (modem_info *) tty->driver_data; @@ -977,7 +1226,8 @@ info->mcr &= ~UART_MCR_RTS; } -static void isdn_tty_unthrottle(struct tty_struct *tty) +static void +isdn_tty_unthrottle(struct tty_struct *tty) { modem_info *info = (modem_info *) tty->driver_data; @@ -1006,9 +1256,10 @@ * release the bus after transmitting. This must be done when * the transmit shift register is empty, not be done when the * transmit holding register is empty. This functionality - * allows RS485 driver to be written in user space. + * allows RS485 driver to be written in user space. */ -static int isdn_tty_get_lsr_info(modem_info * info, uint * value) +static int +isdn_tty_get_lsr_info(modem_info * info, uint * value) { u_char status; uint result; @@ -1024,9 +1275,11 @@ } -static int isdn_tty_get_modem_info(modem_info * info, uint * value) +static int +isdn_tty_get_modem_info(modem_info * info, uint * value) { - u_char control, status; + u_char control, + status; uint result; ulong flags; @@ -1045,76 +1298,78 @@ return 0; } -static int isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value) +static int +isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value) { uint arg; - int pre_dtr; + int pre_dtr; - GET_USER(arg, (uint *)value); + GET_USER(arg, (uint *) value); switch (cmd) { - case TIOCMBIS: + case TIOCMBIS: #ifdef ISDN_DEBUG_MODEM_IOCTL - printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIS\n", info->line); + printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIS\n", info->line); #endif - if (arg & TIOCM_RTS) { - info->mcr |= UART_MCR_RTS; - } - if (arg & TIOCM_DTR) { - info->mcr |= UART_MCR_DTR; - isdn_tty_modem_ncarrier(info); - } - break; - case TIOCMBIC: + if (arg & TIOCM_RTS) { + info->mcr |= UART_MCR_RTS; + } + if (arg & TIOCM_DTR) { + info->mcr |= UART_MCR_DTR; + isdn_tty_modem_ncarrier(info); + } + break; + case TIOCMBIC: #ifdef ISDN_DEBUG_MODEM_IOCTL - printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIC\n", info->line); + printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIC\n", info->line); #endif - if (arg & TIOCM_RTS) { - info->mcr &= ~UART_MCR_RTS; - } - if (arg & TIOCM_DTR) { - info->mcr &= ~UART_MCR_DTR; - if (info->emu.mdmreg[13] & 4) { - isdn_tty_modem_reset_regs(info, 0); + if (arg & TIOCM_RTS) { + info->mcr &= ~UART_MCR_RTS; + } + if (arg & TIOCM_DTR) { + info->mcr &= ~UART_MCR_DTR; + if (info->emu.mdmreg[13] & 4) { + isdn_tty_modem_reset_regs(info, 0); #ifdef ISDN_DEBUG_MODEM_HUP - printk(KERN_DEBUG "Mhup in TIOCMBIC\n"); + printk(KERN_DEBUG "Mhup in TIOCMBIC\n"); #endif - if (info->online) - info->ncarrier = 1; - isdn_tty_modem_hup(info); - } - } - break; - case TIOCMSET: + if (info->online) + info->ncarrier = 1; + isdn_tty_modem_hup(info, 1); + } + } + break; + case TIOCMSET: #ifdef ISDN_DEBUG_MODEM_IOCTL - printk(KERN_DEBUG "ttyI%d ioctl TIOCMSET\n", info->line); + printk(KERN_DEBUG "ttyI%d ioctl TIOCMSET\n", info->line); #endif - pre_dtr = (info->mcr & UART_MCR_DTR); - info->mcr = ((info->mcr & ~(UART_MCR_RTS | UART_MCR_DTR)) - | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) - | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); - if (pre_dtr |= (info->mcr & UART_MCR_DTR)) { - if (!(info->mcr & UART_MCR_DTR)) { - if (info->emu.mdmreg[13] & 4) { - isdn_tty_modem_reset_regs(info, 0); + pre_dtr = (info->mcr & UART_MCR_DTR); + info->mcr = ((info->mcr & ~(UART_MCR_RTS | UART_MCR_DTR)) + | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) + | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); + if (pre_dtr |= (info->mcr & UART_MCR_DTR)) { + if (!(info->mcr & UART_MCR_DTR)) { + if (info->emu.mdmreg[13] & 4) { + isdn_tty_modem_reset_regs(info, 0); #ifdef ISDN_DEBUG_MODEM_HUP - printk(KERN_DEBUG "Mhup in TIOCMSET\n"); + printk(KERN_DEBUG "Mhup in TIOCMSET\n"); #endif - if (info->online) - info->ncarrier = 1; - isdn_tty_modem_hup(info); - } - } else - isdn_tty_modem_ncarrier(info); - } - break; - default: - return -EINVAL; + if (info->online) + info->ncarrier = 1; + isdn_tty_modem_hup(info, 1); + } + } else + isdn_tty_modem_ncarrier(info); + } + break; + default: + return -EINVAL; } return 0; } -static int isdn_tty_ioctl(struct tty_struct *tty, struct file *file, - uint cmd, ulong arg) +static int +isdn_tty_ioctl(struct tty_struct *tty, struct file *file, + uint cmd, ulong arg) { modem_info *info = (modem_info *) tty->driver_data; int error; @@ -1122,97 +1377,98 @@ if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_ioctl")) return -ENODEV; - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ + case TCSBRK: /* SVID version: non-zero arg --> no break */ #ifdef ISDN_DEBUG_MODEM_IOCTL - printk(KERN_DEBUG "ttyI%d ioctl TCSBRK\n", info->line); + printk(KERN_DEBUG "ttyI%d ioctl TCSBRK\n", info->line); #endif - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + return 0; + case TCSBRKP: /* support for POSIX tcsendbreak() */ #ifdef ISDN_DEBUG_MODEM_IOCTL - printk(KERN_DEBUG "ttyI%d ioctl TCSBRKP\n", info->line); + printk(KERN_DEBUG "ttyI%d ioctl TCSBRKP\n", info->line); #endif - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - return 0; - case TIOCGSOFTCAR: + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + return 0; + case TIOCGSOFTCAR: #ifdef ISDN_DEBUG_MODEM_IOCTL - printk(KERN_DEBUG "ttyI%d ioctl TIOCGSOFTCAR\n", info->line); + printk(KERN_DEBUG "ttyI%d ioctl TIOCGSOFTCAR\n", info->line); #endif - error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long)); - if (error) - return error; - put_user(C_CLOCAL(tty) ? 1 : 0, (ulong *) arg); - return 0; - case TIOCSSOFTCAR: + error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long)); + if (error) + return error; + put_user(C_CLOCAL(tty) ? 1 : 0, (ulong *) arg); + return 0; + case TIOCSSOFTCAR: #ifdef ISDN_DEBUG_MODEM_IOCTL - printk(KERN_DEBUG "ttyI%d ioctl TIOCSSOFTCAR\n", info->line); + printk(KERN_DEBUG "ttyI%d ioctl TIOCSSOFTCAR\n", info->line); #endif - error = verify_area(VERIFY_READ, (void *) arg, sizeof(long)); - if (error) - return error; - GET_USER(arg, (ulong *) arg); - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - return 0; - case TIOCMGET: + error = verify_area(VERIFY_READ, (void *) arg, sizeof(long)); + if (error) + return error; + GET_USER(arg, (ulong *) arg); + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + return 0; + case TIOCMGET: #ifdef ISDN_DEBUG_MODEM_IOCTL - printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line); + printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line); #endif - error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint)); - if (error) - return error; - return isdn_tty_get_modem_info(info, (uint *) arg); - case TIOCMBIS: - case TIOCMBIC: - case TIOCMSET: - error = verify_area(VERIFY_READ, (void *) arg, sizeof(uint)); - if (error) - return error; - return isdn_tty_set_modem_info(info, cmd, (uint *) arg); - case TIOCSERGETLSR: /* Get line status register */ + error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint)); + if (error) + return error; + return isdn_tty_get_modem_info(info, (uint *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + error = verify_area(VERIFY_READ, (void *) arg, sizeof(uint)); + if (error) + return error; + return isdn_tty_set_modem_info(info, cmd, (uint *) arg); + case TIOCSERGETLSR: /* Get line status register */ #ifdef ISDN_DEBUG_MODEM_IOCTL - printk(KERN_DEBUG "ttyI%d ioctl TIOCSERGETLSR\n", info->line); + printk(KERN_DEBUG "ttyI%d ioctl TIOCSERGETLSR\n", info->line); #endif - error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint)); - if (error) - return error; - else - return isdn_tty_get_lsr_info(info, (uint *) arg); - - default: + error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint)); + if (error) + 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); + printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line); #endif - return -ENOIOCTLCMD; + return -ENOIOCTLCMD; } return 0; } -static void isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_termios) +static void +isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_termios) { modem_info *info = (modem_info *) tty->driver_data; - if (!old_termios) - isdn_tty_change_speed(info); - else { - if (tty->termios->c_cflag == old_termios->c_cflag) - return; - isdn_tty_change_speed(info); - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - } - } + if (!old_termios) + isdn_tty_change_speed(info); + else { + if (tty->termios->c_cflag == old_termios->c_cflag) + return; + isdn_tty_change_speed(info); + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + } + } } /* @@ -1220,9 +1476,11 @@ * isdn_tty_open() and friends * ------------------------------------------------------------ */ -static int isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info) +static int +isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info) { - struct wait_queue wait = {current, NULL}; + struct wait_queue wait = + {current, NULL}; int do_clocal = 0; unsigned long flags; int retval; @@ -1233,8 +1491,8 @@ */ if (tty_hung_up_p(filp) || (info->flags & ISDN_ASYNC_CLOSING)) { - if (info->flags & ISDN_ASYNC_CLOSING) - interruptible_sleep_on(&info->close_wait); + if (info->flags & ISDN_ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); #ifdef MODEM_DO_RESTART if (info->flags & ISDN_ASYNC_HUP_NOTIFY) return -EAGAIN; @@ -1267,7 +1525,7 @@ * and then exit. */ if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { + (tty->flags & (1 << TTY_IO_ERROR))) { if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) return -EBUSY; info->flags |= ISDN_ASYNC_NORMAL_ACTIVE; @@ -1293,11 +1551,11 @@ printk(KERN_DEBUG "isdn_tty_block_til_ready before block: ttyi%d, count = %d\n", info->line, info->count); #endif - save_flags(flags); - cli(); - if (!(tty_hung_up_p(filp))) - info->count--; - restore_flags(flags); + save_flags(flags); + cli(); + if (!(tty_hung_up_p(filp))) + info->count--; + restore_flags(flags); info->blocked_open++; while (1) { current->state = TASK_INTERRUPTIBLE; @@ -1349,10 +1607,12 @@ * the IRQ chain. It also performs the serial-specific * initialization for the tty structure. */ -static int isdn_tty_open(struct tty_struct *tty, struct file *filp) +static int +isdn_tty_open(struct tty_struct *tty, struct file *filp) { modem_info *info; - int retval, line; + int retval, + line; line = MINOR(tty->device) - tty->driver.minor_start; if (line < 0 || line > ISDN_MAX_CHANNELS) @@ -1403,7 +1663,8 @@ return 0; } -static void isdn_tty_close(struct tty_struct *tty, struct file *filp) +static void +isdn_tty_close(struct tty_struct *tty, struct file *filp) { modem_info *info = (modem_info *) tty->driver_data; ulong flags; @@ -1454,7 +1715,7 @@ if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) info->callout_termios = *tty->termios; - tty->closing = 1; + tty->closing = 1; /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the @@ -1462,7 +1723,7 @@ * line status register. */ if (info->flags & ISDN_ASYNC_INITIALIZED) { - tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ + tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially @@ -1484,12 +1745,12 @@ if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); info->tty = 0; - info->ncarrier = 0; + info->ncarrier = 0; tty->closing = 0; if (info->blocked_open) { - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 50; - schedule(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 50; + schedule(); wake_up_interruptible(&info->open_wait); } info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE | @@ -1504,7 +1765,8 @@ /* * isdn_tty_hangup() --- called by tty_hangup() when a hangup is signaled. */ -static void isdn_tty_hangup(struct tty_struct *tty) +static void +isdn_tty_hangup(struct tty_struct *tty) { modem_info *info = (modem_info *) tty->driver_data; @@ -1519,7 +1781,8 @@ /* This routine initializes all emulator-data. */ -static void isdn_tty_reset_profile(atemu * m) +static void +isdn_tty_reset_profile(atemu * m) { m->profile[0] = 0; m->profile[1] = 0; @@ -1545,27 +1808,34 @@ m->pmsn[0] = '\0'; } -static void isdn_tty_modem_reset_vpar(atemu *m) +#ifdef CONFIG_ISDN_AUDIO +static void +isdn_tty_modem_reset_vpar(atemu * m) { - m->vpar[0] = 2; /* Voice-device (2 = phone line) */ - m->vpar[1] = 0; /* Silence detection level (0 = none ) */ - m->vpar[2] = 70; /* Silence interval (7 sec. ) */ - m->vpar[3] = 2; /* Compression type (1 = ADPCM-2 ) */ + m->vpar[0] = 2; /* Voice-device (2 = phone line) */ + m->vpar[1] = 0; /* Silence detection level (0 = none ) */ + m->vpar[2] = 70; /* Silence interval (7 sec. ) */ + m->vpar[3] = 2; /* Compression type (1 = ADPCM-2 ) */ } +#endif -static void isdn_tty_modem_reset_regs(modem_info *info, int force) +static void +isdn_tty_modem_reset_regs(modem_info * info, int force) { - atemu *m = &info->emu; + atemu *m = &info->emu; if ((m->mdmreg[12] & 32) || force) { memcpy(m->mdmreg, m->profile, ISDN_MODEM_ANZREG); memcpy(m->msn, m->pmsn, ISDN_MSNLEN); - info->xmit_size = m->mdmreg[16] * 16; + info->xmit_size = m->mdmreg[16] * 16; } - isdn_tty_modem_reset_vpar(m); +#ifdef CONFIG_ISDN_AUDIO + isdn_tty_modem_reset_vpar(m); +#endif m->mdmcmdl = 0; } -static void modem_write_profile(atemu * m) +static void +modem_write_profile(atemu * m) { memcpy(m->profile, m->mdmreg, ISDN_MODEM_ANZREG); memcpy(m->pmsn, m->msn, ISDN_MSNLEN); @@ -1573,7 +1843,8 @@ send_sig(SIGIO, dev->profd, 1); } -int isdn_tty_modem_init(void) +int +isdn_tty_modem_init(void) { modem *m; int i; @@ -1630,6 +1901,12 @@ } for (i = 0; i < ISDN_MAX_CHANNELS; i++) { info = &m->info[i]; + sprintf(info->last_cause, "0000"); + sprintf(info->last_num, "none"); + info->last_dir = 0; + info->last_lhup = 1; + info->last_l2 = 0; + info->last_si = 0; isdn_tty_reset_profile(&info->emu); isdn_tty_modem_reset_regs(info, 1); info->magic = ISDN_ASYNC_MAGIC; @@ -1646,14 +1923,16 @@ info->isdn_channel = -1; info->drv_index = -1; info->xmit_size = ISDN_SERIAL_XMIT_SIZE; - skb_queue_head_init(&info->xmit_queue); - skb_queue_head_init(&info->dtmf_queue); - if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_SIZE + 5, GFP_KERNEL))) { - printk(KERN_ERR "Could not allocate modem xmit-buffer\n"); - return -3; - } - /* Make room for T.70 header */ - info->xmit_buf += 4; + skb_queue_head_init(&info->xmit_queue); +#ifdef CONFIG_ISDN_AUDIO + skb_queue_head_init(&info->dtmf_queue); +#endif + if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_SIZE + 5, GFP_KERNEL))) { + printk(KERN_ERR "Could not allocate modem xmit-buffer\n"); + return -3; + } + /* Make room for T.70 header */ + info->xmit_buf += 4; } return 0; } @@ -1664,61 +1943,46 @@ * it to the ISDN-Channel. * Return Index to dev->mdm or -1 if none found. */ -int isdn_tty_find_icall(int di, int ch, char *num) +int +isdn_tty_find_icall(int di, int ch, setup_parm setup) { char *eaz; int i; int idx; int si1; int si2; - char *s; - char nr[31]; + char nr[32]; ulong flags; save_flags(flags); cli(); - if (num[0] == ',') { + if (!setup.phone[0]) { nr[0] = '0'; - strncpy(&nr[1], num, 29); + nr[1] = '\0'; printk(KERN_INFO "isdn_tty: Incoming call without OAD, assuming '0'\n"); } else - strncpy(nr, num, 30); - s = strtok(nr, ","); - s = strtok(NULL, ","); - if (!s) { - printk(KERN_WARNING "isdn_tty: Incoming callinfo garbled, ignored: %s\n", - num); - restore_flags(flags); - return -1; - } - si1 = (int)simple_strtoul(s,NULL,10); - s = strtok(NULL, ","); - if (!s) { - printk(KERN_WARNING "isdn_tty: Incoming callinfo garbled, ignored: %s\n", - num); - restore_flags(flags); - return -1; - } - si2 = (int)simple_strtoul(s,NULL,10); - eaz = strtok(NULL, ","); - if (!eaz) { + 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; #ifdef ISDN_DEBUG_MODEM_ICALL printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2); #endif for (i = 0; i < ISDN_MAX_CHANNELS; i++) { - modem_info *info = &dev->mdm.info[i]; + modem_info *info = &dev->mdm.info[i]; #ifdef ISDN_DEBUG_MODEM_ICALL printk(KERN_DEBUG "m_fi: i=%d msn=%s mmsn=%s mreg18=%d mreg19=%d\n", i, info->emu.msn, isdn_map_eaz2msn(info->emu.msn, di), info->emu.mdmreg[18], info->emu.mdmreg[19]); #endif if ((!strcmp(isdn_map_eaz2msn(info->emu.msn, di), - eaz)) && /* EAZ is matching */ - (info->emu.mdmreg[18] & si2bit[si1]) && /* SI1 is matching */ - ((info->emu.mdmreg[19] == si2) || !si2)) { /* SI2 is matching or 0 */ + eaz)) && /* EAZ is matching */ + (info->emu.mdmreg[18] & si2bit[si1]) && /* SI1 is matching */ + ((info->emu.mdmreg[19] == si2) || !si2)) { /* SI2 is matching or 0 */ idx = isdn_dc2minor(di, ch); #ifdef ISDN_DEBUG_MODEM_ICALL printk(KERN_DEBUG "m_fi: match1\n"); @@ -1735,13 +1999,18 @@ info->drv_index = idx; dev->m_idx[idx] = info->line; dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE; - dev->usage[idx] |= (si1==1)?ISDN_USAGE_VOICE:ISDN_USAGE_MODEM; + 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[20] = si2bit[si1]; + 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, info->line); + info->msr |= UART_MSR_RI; + isdn_tty_modem_result(2, info); + isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1); return info->line; } } @@ -1752,6 +2021,140 @@ return -1; } +#define TTY_IS_ACTIVE(info) \ + (info->flags & (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) + +int +isdn_tty_stat_callback(int i, isdn_ctrl * c) +{ + int mi; + modem_info *info; + + if (i < 0) + return 0; + if ((mi = dev->m_idx[i]) >= 0) { + info = &dev->mdm.info[mi]; + switch (c->command) { + case ISDN_STAT_BSENT: +#ifdef ISDN_TTY_STAT_DEBUG + printk(KERN_DEBUG "tty_STAT_BSENT ttyI%d\n", info->line); +#endif + if ((info->isdn_driver == c->driver) && + (info->isdn_channel == c->arg)) { + info->msr |= UART_MSR_CTS; + if (info->send_outstanding) + if (!(--info->send_outstanding)) + info->lsr |= UART_LSR_TEMT; + isdn_tty_tint(info); + return 1; + } + break; + case ISDN_STAT_CAUSE: +#ifdef ISDN_TTY_STAT_DEBUG + printk(KERN_DEBUG "tty_STAT_CAUSE ttyI%d\n", info->line); +#endif + /* Signal cause to tty-device */ + strncpy(info->last_cause, c->parm.num, 5); + return 1; + case ISDN_STAT_DCONN: +#ifdef ISDN_TTY_STAT_DEBUG + printk(KERN_DEBUG "tty_STAT_DCONN ttyI%d\n", info->line); +#endif + if (TTY_IS_ACTIVE(info)) { + if (info->dialing == 1) { + info->dialing = 2; + return 1; + } + } + break; + case ISDN_STAT_DHUP: +#ifdef ISDN_TTY_STAT_DEBUG + printk(KERN_DEBUG "tty_STAT_DHUP ttyI%d\n", info->line); +#endif + if (TTY_IS_ACTIVE(info)) { + if (info->dialing == 1) { + info->dialing = 0; + isdn_tty_modem_result(7, info); + } +#ifdef ISDN_DEBUG_MODEM_HUP + printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n"); +#endif + isdn_tty_modem_hup(info, 0); + return 1; + } + break; + case ISDN_STAT_BCONN: +#ifdef ISDN_TTY_STAT_DEBUG + printk(KERN_DEBUG "tty_STAT_BCONN ttyI%d\n", info->line); +#endif + /* Schedule CONNECT-Message to any tty + * waiting for it and + * set DCD-bit of its modem-status. + */ + if (TTY_IS_ACTIVE(info)) { + info->msr |= UART_MSR_DCD; + if (info->dialing) { + info->dialing = 0; + info->last_dir = 1; + } else + info->last_dir = 0; + info->rcvsched = 1; + if (USG_MODEM(dev->usage[i])) + isdn_tty_modem_result(5, info); + if (USG_VOICE(dev->usage[i])) + isdn_tty_modem_result(11, info); + return 1; + } + break; + case ISDN_STAT_BHUP: +#ifdef ISDN_TTY_STAT_DEBUG + printk(KERN_DEBUG "tty_STAT_BHUP ttyI%d\n", info->line); +#endif + if (TTY_IS_ACTIVE(info)) { +#ifdef ISDN_DEBUG_MODEM_HUP + printk(KERN_DEBUG "Mhup in ISDN_STAT_BHUP\n"); +#endif + isdn_tty_modem_hup(info, 0); + return 1; + } + break; + case ISDN_STAT_NODCH: +#ifdef ISDN_TTY_STAT_DEBUG + printk(KERN_DEBUG "tty_STAT_NODCH ttyI%d\n", info->line); +#endif + if (TTY_IS_ACTIVE(info)) { + if (info->dialing) { + info->dialing = 0; + info->last_l2 = -1; + info->last_si = 0; + sprintf(info->last_cause, "0000"); + isdn_tty_modem_result(6, info); + } + info->msr &= ~UART_MSR_DCD; + if (info->online) { + isdn_tty_modem_result(3, info); + info->online = 0; + } + return 1; + } + break; + case ISDN_STAT_UNLOAD: +#ifdef ISDN_TTY_STAT_DEBUG + printk(KERN_DEBUG "tty_STAT_UNLOAD ttyI%d\n", info->line); +#endif + for (i = 0; i < ISDN_MAX_CHANNELS; i++) { + info = &dev->mdm.info[i]; + if (info->isdn_driver == c->driver) { + if (info->online) + isdn_tty_modem_hup(info, 1); + } + } + return 1; + } + } + return 0; +} + /********************************************************************* Modem-Emulator-Routines *********************************************************************/ @@ -1762,7 +2165,8 @@ * Put a message from the AT-emulator into receive-buffer of tty, * convert CR, LF, and BS to values in modem-registers 3, 4 and 5. */ -static void isdn_tty_at_cout(char *msg, modem_info * info) +static void +isdn_tty_at_cout(char *msg, modem_info * info) { struct tty_struct *tty; atemu *m = &info->emu; @@ -1779,17 +2183,17 @@ tty = info->tty; for (p = msg; *p; p++) { switch (*p) { - case '\r': - c = m->mdmreg[3]; - break; - case '\n': - c = m->mdmreg[4]; - break; - case '\b': - c = m->mdmreg[5]; - break; - default: - c = *p; + case '\r': + c = m->mdmreg[3]; + break; + case '\n': + c = m->mdmreg[4]; + break; + case '\b': + c = m->mdmreg[5]; + break; + default: + c = *p; } if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) { restore_flags(flags); @@ -1806,24 +2210,25 @@ /* * Perform ATH Hangup */ -static void isdn_tty_on_hook(modem_info * info) +static void +isdn_tty_on_hook(modem_info * info) { if (info->isdn_channel >= 0) { #ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "Mhup in isdn_tty_on_hook\n"); #endif - isdn_tty_modem_result(3, info); - isdn_tty_modem_hup(info); + isdn_tty_modem_hup(info, 1); } } -static void isdn_tty_off_hook(void) +static void +isdn_tty_off_hook(void) { printk(KERN_DEBUG "isdn_tty_off_hook\n"); } -#define PLUSWAIT1 (HZ/2) /* 0.5 sec. */ -#define PLUSWAIT2 (HZ*3/2) /* 1.5 sec */ +#define PLUSWAIT1 (HZ/2) /* 0.5 sec. */ +#define PLUSWAIT2 (HZ*3/2) /* 1.5 sec */ /* * Check Buffer for Modem-escape-sequence, activate timer-callback to @@ -1836,8 +2241,9 @@ * pluscount count of valid escape-characters so far * lastplus timestamp of last character */ -static void isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pluscount, - int *lastplus, int from_user) +static void +isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pluscount, + int *lastplus, int from_user) { char cbuf[3]; @@ -1880,7 +2286,8 @@ * For CONNECT-messages also switch to online-mode. * For RING-message handle auto-ATA if register 0 != 0 */ -void isdn_tty_modem_result(int code, modem_info * info) +static void +isdn_tty_modem_result(int code, modem_info * info) { atemu *m = &info->emu; static char *msg[] = @@ -1888,47 +2295,66 @@ "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER", "RINGING", "NO MSN/EAZ", "VCON"}; ulong flags; - char s[4]; + char s[10]; switch (code) { - case 2: - m->mdmreg[1]++; /* RING */ - if (m->mdmreg[1] == m->mdmreg[0]) - /* Automatically accept incoming call */ - isdn_tty_cmd_ATA(info); - break; - case 3: - /* NO CARRIER */ - save_flags(flags); - cli(); + case 2: + m->mdmreg[1]++; /* RING */ + if (m->mdmreg[1] == m->mdmreg[0]) + /* Automatically accept incoming call */ + isdn_tty_cmd_ATA(info); + break; + case 3: + /* NO CARRIER */ #ifdef ISDN_DEBUG_MODEM_HUP - printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n", - (info->flags & ISDN_ASYNC_CLOSING), - (!info->tty)); -#endif - if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) { - restore_flags(flags); - return; - } - restore_flags(flags); - if (info->vonline & 1) { - /* voice-recording, add DLE-ETX */ - isdn_tty_at_cout("\020\003", info); - } - if (info->vonline & 2) { - /* voice-playing, add DLE-DC4 */ - isdn_tty_at_cout("\020\024", info); - } - break; - case 1: - case 5: - if (!info->online) - info->online = 2; - break; - case 11: - if (!info->online) - info->online = 1; - break; + printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n", + (info->flags & ISDN_ASYNC_CLOSING), + (!info->tty)); +#endif + save_flags(flags); + cli(); + m->mdmreg[1] = 0; + del_timer(&info->nc_timer); + info->ncarrier = 0; + if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) { + restore_flags(flags); + return; + } + restore_flags(flags); +#ifdef CONFIG_ISDN_AUDIO + if (info->vonline & 1) { +#ifdef ISDN_DEBUG_MODEM_VOICE + printk(KERN_DEBUG "res3: send DLE-ETX on ttyI%d\n", + info->line); +#endif + /* voice-recording, add DLE-ETX */ + isdn_tty_at_cout("\020\003", info); + } + if (info->vonline & 2) { +#ifdef ISDN_DEBUG_MODEM_VOICE + printk(KERN_DEBUG "res3: send DLE-DC4 on ttyI%d\n", + info->line); +#endif + /* voice-playing, add DLE-DC4 */ + isdn_tty_at_cout("\020\024", info); + } +#endif + break; + case 1: + case 5: + sprintf(info->last_cause, "0000"); + if (!info->online) + info->online = 2; + break; + case 11: +#ifdef ISDN_DEBUG_MODEM_VOICE + printk(KERN_DEBUG "res3: send VCON on ttyI%d\n", + info->line); +#endif + sprintf(info->last_cause, "0000"); + if (!info->online) + info->online = 1; + break; } if (m->mdmreg[12] & 1) { /* Show results */ @@ -1938,17 +2364,38 @@ sprintf(s, "%d", code); isdn_tty_at_cout(s, info); } else { - if (code == 2) { + if ((code == 2) && (!(m->mdmreg[13] & 16))) { isdn_tty_at_cout("CALLER NUMBER: ", info); isdn_tty_at_cout(dev->num[info->drv_index], info); isdn_tty_at_cout("\r\n", info); } isdn_tty_at_cout(msg[code], info); - if (code == 5) { - /* Append Protocol to CONNECT message */ - isdn_tty_at_cout((m->mdmreg[14] != 3) ? "/X.75" : "/HDLC", info); - if (m->mdmreg[13] & 2) - isdn_tty_at_cout("/T.70", info); + switch (code) { + case 2: + /* Print CID only once, _after_ 1.st RING */ + if ((m->mdmreg[13] & 16) && (m->mdmreg[1] == 1)) { + isdn_tty_at_cout("\r\n", info); + isdn_tty_at_cout("CALLER NUMBER: ", info); + isdn_tty_at_cout(dev->num[info->drv_index], info); + } + break; + case 3: + case 6: + case 7: + case 8: + m->mdmreg[1] = 0; + /* Append Cause-Message if enabled */ + if (m->mdmreg[13] & 8) { + sprintf(s, "/%s", info->last_cause); + isdn_tty_at_cout(s, info); + } + break; + case 5: + /* Append Protocol to CONNECT message */ + isdn_tty_at_cout((m->mdmreg[14] != 3) ? "/X.75" : "/HDLC", info); + if (m->mdmreg[13] & 2) + isdn_tty_at_cout("/T.70", info); + break; } } isdn_tty_at_cout("\r\n", info); @@ -1960,13 +2407,13 @@ restore_flags(flags); return; } - if (info->tty->ldisc.flush_buffer) - info->tty->ldisc.flush_buffer(info->tty); + if (info->tty->ldisc.flush_buffer) + info->tty->ldisc.flush_buffer(info->tty); if ((info->flags & ISDN_ASYNC_CHECK_CD) && (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) && (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) { tty_hangup(info->tty); - } + } restore_flags(flags); } } @@ -1974,7 +2421,8 @@ /* * Display a modem-register-value. */ -static void isdn_tty_show_profile(int ridx, modem_info * info) +static void +isdn_tty_show_profile(int ridx, modem_info * info) { char v[6]; @@ -1985,7 +2433,8 @@ /* * Get MSN-string from char-pointer, set pointer to end of number */ -static void isdn_tty_get_msnstr(char *n, char **p) +static void +isdn_tty_get_msnstr(char *n, char **p) { while ((*p[0] >= '0' && *p[0] <= '9') || (*p[0] == ',')) *n++ = *p[0]++; @@ -1995,13 +2444,17 @@ /* * Get phone-number from modem-commandbuffer */ -static void isdn_tty_getdial(char *p, char *q) +static void +isdn_tty_getdial(char *p, char *q, int max) { int first = 1; - while (strchr("0123456789,#.*WPTS-", *p) && *p) { - if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first)) + max--; + while (strchr("0123456789,#.*WPTS-", *p) && *p && (max > 0)) { + if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first)) { *q++ = *p; + max--; + } p++; first = 0; } @@ -2011,648 +2464,775 @@ #define PARSE_ERROR { isdn_tty_modem_result(4, info); return; } #define PARSE_ERROR1 { isdn_tty_modem_result(4, info); return 1; } +static void +isdn_tty_report(modem_info * info) +{ + atemu *m = &info->emu; + char s[80]; + + isdn_tty_at_cout("\r\nStatistics of last connection:\r\n\r\n", info); + sprintf(s, " Remote Number: %s\r\n", info->last_num); + isdn_tty_at_cout(s, info); + sprintf(s, " Direction: %s\r\n", info->last_dir ? "outgoing" : "incoming"); + isdn_tty_at_cout(s, info); + isdn_tty_at_cout(" Layer-2 Protocol: ", info); + switch (info->last_l2) { + case ISDN_PROTO_L2_X75I: + isdn_tty_at_cout("x75i", info); + break; + case ISDN_PROTO_L2_X75UI: + isdn_tty_at_cout("x75ui", info); + break; + case ISDN_PROTO_L2_X75BUI: + isdn_tty_at_cout("x75bui", info); + break; + case ISDN_PROTO_L2_HDLC: + isdn_tty_at_cout("hdlc", info); + break; + case ISDN_PROTO_L2_TRANS: + isdn_tty_at_cout("transparent", info); + break; + default: + isdn_tty_at_cout("unknown", info); + break; + } + isdn_tty_at_cout((m->mdmreg[13] & 2) ? "/t.70\r\n" : "\r\n", info); + isdn_tty_at_cout(" Service: ", info); + switch (info->last_si) { + case 1: + isdn_tty_at_cout("audio\r\n", info); + break; + case 5: + isdn_tty_at_cout("btx\r\n", info); + break; + case 7: + isdn_tty_at_cout("data\r\n", info); + break; + default: + sprintf(s, "%d\r\n", info->last_si); + isdn_tty_at_cout(s, info); + break; + } + sprintf(s, " Hangup location: %s\r\n", info->last_lhup ? "local" : "remote"); + isdn_tty_at_cout(s, info); + sprintf(s, " Last cause: %s\r\n", info->last_cause); + isdn_tty_at_cout(s, info); +} + /* * Parse AT&.. commands. */ -static int isdn_tty_cmd_ATand(char **p, modem_info * info) +static int +isdn_tty_cmd_ATand(char **p, modem_info * info) { - atemu *m = &info->emu; - int i; - char rb[100]; - - switch (*p[0]) { - case 'B': - /* &B - Set Buffersize */ - p[0]++; - i = isdn_getnum(p); - if ((i < 0) || (i > ISDN_SERIAL_XMIT_SIZE)) - PARSE_ERROR1; -#ifdef CONFIG_ISDN_AUDIO - if ((m->mdmreg[18] & 1) && (i > VBUF)) - PARSE_ERROR1; -#endif - m->mdmreg[16] = i / 16; - info->xmit_size = m->mdmreg[16] * 16; - break; - case 'D': - /* &D - Set DCD-Low-behavior */ - p[0]++; - switch (isdn_getnum(p)) { - case 0: - m->mdmreg[13] &= ~4; - m->mdmreg[12] &= ~32; - break; - case 2: - m->mdmreg[13] |= 4; - m->mdmreg[12] &= ~32; - break; - case 3: - m->mdmreg[13] |= 4; - m->mdmreg[12] |= 32; - break; - default: - PARSE_ERROR1 - } - break; - case 'E': - /* &E -Set EAZ/MSN */ - p[0]++; - isdn_tty_get_msnstr(m->msn, p); - break; - case 'F': - /* &F -Set Factory-Defaults */ - p[0]++; - isdn_tty_reset_profile(m); - isdn_tty_modem_reset_regs(info, 1); - break; - case 'S': - /* &S - Set Windowsize */ - p[0]++; - i = isdn_getnum(p); - if ((i > 0) && (i < 9)) - m->mdmreg[17] = i; - else - PARSE_ERROR1; - break; - case 'V': - /* &V - Show registers */ - p[0]++; - for (i = 0; i < ISDN_MODEM_ANZREG; i++) { - sprintf(rb, "S%d=%d%s", i, - m->mdmreg[i], (i == 6) ? "\r\n" : " "); - isdn_tty_at_cout(rb, info); - } - sprintf(rb, "\r\nEAZ/MSN: %s\r\n", - strlen(m->msn) ? m->msn : "None"); - isdn_tty_at_cout(rb, info); - break; - case 'W': - /* &W - Write Profile */ - p[0]++; - switch (*p[0]) { - case '0': - p[0]++; - modem_write_profile(m); - break; - default: - PARSE_ERROR1; - } - break; - case 'X': - /* &X - Switch to BTX-Mode */ - p[0]++; - switch (isdn_getnum(p)) { - case 0: - m->mdmreg[13] &= ~2; - info->xmit_size = m->mdmreg[16] * 16; - break; - case 1: - m->mdmreg[13] |= 2; - m->mdmreg[14] = 0; - info->xmit_size = 112; - m->mdmreg[18] = 4; - m->mdmreg[19] = 0; - break; - default: - PARSE_ERROR1; - } - break; - default: - PARSE_ERROR1; - } - return 0; + atemu *m = &info->emu; + int i; + char rb[100]; + + switch (*p[0]) { + case 'B': + /* &B - Set Buffersize */ + p[0]++; + i = isdn_getnum(p); + if ((i < 0) || (i > ISDN_SERIAL_XMIT_SIZE)) + PARSE_ERROR1; +#ifdef CONFIG_ISDN_AUDIO + if ((m->mdmreg[18] & 1) && (i > VBUF)) + PARSE_ERROR1; +#endif + m->mdmreg[16] = i / 16; + info->xmit_size = m->mdmreg[16] * 16; + break; + case 'D': + /* &D - Set DCD-Low-behavior */ + p[0]++; + switch (isdn_getnum(p)) { + case 0: + m->mdmreg[13] &= ~4; + m->mdmreg[12] &= ~32; + break; + case 2: + m->mdmreg[13] |= 4; + m->mdmreg[12] &= ~32; + break; + case 3: + m->mdmreg[13] |= 4; + m->mdmreg[12] |= 32; + break; + default: + PARSE_ERROR1 + } + break; + case 'E': + /* &E -Set EAZ/MSN */ + p[0]++; + isdn_tty_get_msnstr(m->msn, p); + break; + case 'F': + /* &F -Set Factory-Defaults */ + p[0]++; + isdn_tty_reset_profile(m); + isdn_tty_modem_reset_regs(info, 1); + break; + case 'S': + /* &S - Set Windowsize */ + p[0]++; + i = isdn_getnum(p); + if ((i > 0) && (i < 9)) + m->mdmreg[17] = i; + else + PARSE_ERROR1; + break; + case 'V': + /* &V - Show registers */ + p[0]++; + isdn_tty_at_cout("\r\n", info); + for (i = 0; i < ISDN_MODEM_ANZREG; i++) { + sprintf(rb, "S%02d=%03d%s", i, + m->mdmreg[i], ((i + 1) % 10) ? " " : "\r\n"); + isdn_tty_at_cout(rb, info); + } + sprintf(rb, "\r\nEAZ/MSN: %s\r\n", + strlen(m->msn) ? m->msn : "None"); + isdn_tty_at_cout(rb, info); + break; + case 'W': + /* &W - Write Profile */ + p[0]++; + switch (*p[0]) { + case '0': + p[0]++; + modem_write_profile(m); + break; + default: + PARSE_ERROR1; + } + break; + case 'X': + /* &X - Switch to BTX-Mode */ + p[0]++; + switch (isdn_getnum(p)) { + case 0: + m->mdmreg[13] &= ~2; + info->xmit_size = m->mdmreg[16] * 16; + break; + case 1: + m->mdmreg[13] |= 2; + m->mdmreg[14] = 0; + info->xmit_size = 112; + m->mdmreg[18] = 4; + m->mdmreg[19] = 0; + break; + default: + PARSE_ERROR1; + } + break; + default: + PARSE_ERROR1; + } + return 0; +} + +static int +isdn_tty_check_ats(int mreg, int mval, modem_info * info, atemu * m) +{ + /* Some plausibility checks */ + switch (mreg) { + case 14: + if (mval > ISDN_PROTO_L2_TRANS) + return 1; + break; + case 16: + if ((mval * 16) > ISDN_SERIAL_XMIT_SIZE) + return 1; +#ifdef CONFIG_ISDN_AUDIO + if ((m->mdmreg[18] & 1) && (mval > VBUFX)) + return 1; +#endif + info->xmit_size = mval * 16; + break; + case 20: + case 21: + case 22: + /* readonly registers */ + return 1; + } + return 0; } /* * Perform ATS command */ -static int isdn_tty_cmd_ATS(char **p, modem_info * info) +static int +isdn_tty_cmd_ATS(char **p, modem_info * info) { - atemu *m = &info->emu; - int mreg; - int mval; - - mreg = isdn_getnum(p); - if (mreg < 0 || mreg > ISDN_MODEM_ANZREG) - PARSE_ERROR1; - switch (*p[0]) { - case '=': - p[0]++; - mval = isdn_getnum(p); - if (mval < 0 || mval > 255) - PARSE_ERROR1; - switch (mreg) { - /* Some plausibility checks */ - case 14: - if (mval > ISDN_PROTO_L2_TRANS) - PARSE_ERROR1; - break; - case 16: - if ((mval * 16) > ISDN_SERIAL_XMIT_SIZE) - PARSE_ERROR1; -#ifdef CONFIG_ISDN_AUDIO - if ((m->mdmreg[18] & 1) && (mval > VBUFX)) - PARSE_ERROR1; -#endif - info->xmit_size = mval * 16; - break; - case 20: - PARSE_ERROR1; - } - m->mdmreg[mreg] = mval; - break; - case '?': - p[0]++; - isdn_tty_show_profile(mreg, info); - break; - default: - PARSE_ERROR1; - break; - } - return 0; + atemu *m = &info->emu; + int bitpos; + int mreg; + int mval; + int bval; + + mreg = isdn_getnum(p); + if (mreg < 0 || mreg > ISDN_MODEM_ANZREG) + PARSE_ERROR1; + switch (*p[0]) { + case '=': + p[0]++; + mval = isdn_getnum(p); + if (mval < 0 || mval > 255) + PARSE_ERROR1; + if (isdn_tty_check_ats(mreg, mval, info, m)) + PARSE_ERROR1; + m->mdmreg[mreg] = mval; + break; + case '.': + /* Set/Clear a single bit */ + p[0]++; + bitpos = isdn_getnum(p); + if ((bitpos < 0) || (bitpos > 7)) + PARSE_ERROR1; + switch (*p[0]) { + case '=': + p[0]++; + bval = isdn_getnum(p); + if (bval < 0 || bval > 1) + PARSE_ERROR1; + if (bval) + mval = m->mdmreg[mreg] | (1 << bitpos); + else + mval = m->mdmreg[mreg] & ~(1 << bitpos); + if (isdn_tty_check_ats(mreg, mval, info, m)) + PARSE_ERROR1; + m->mdmreg[mreg] = mval; + break; + case '?': + p[0]++; + isdn_tty_at_cout("\r\n", info); + isdn_tty_at_cout((m->mdmreg[mreg] & (1 << bitpos)) ? "1" : "0", + info); + break; + default: + PARSE_ERROR1; + } + break; + case '?': + p[0]++; + isdn_tty_show_profile(mreg, info); + break; + default: + PARSE_ERROR1; + break; + } + return 0; } /* * Perform ATA command */ -static void isdn_tty_cmd_ATA(modem_info * info) +static void +isdn_tty_cmd_ATA(modem_info * info) { - atemu *m = &info->emu; - isdn_ctrl cmd; - int l2; - - if (info->msr & UART_MSR_RI) { - /* Accept incoming call */ - m->mdmreg[1] = 0; - info->msr &= ~UART_MSR_RI; - l2 = m->mdmreg[14]; -#ifdef CONFIG_ISDN_AUDIO - /* If more than one bit set in reg18, autoselect Layer2 */ - if ((m->mdmreg[18] & m->mdmreg[20]) != m->mdmreg[18]) - if (m->mdmreg[20] == 1) l2 = 4; -#endif - cmd.driver = info->isdn_driver; - cmd.command = ISDN_CMD_SETL2; - cmd.arg = info->isdn_channel + (l2 << 8); - dev->drv[info->isdn_driver]->interface->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); - cmd.driver = info->isdn_driver; - cmd.arg = info->isdn_channel; - cmd.command = ISDN_CMD_ACCEPTD; - dev->drv[info->isdn_driver]->interface->command(&cmd); - } else - isdn_tty_modem_result(8, info); + atemu *m = &info->emu; + isdn_ctrl cmd; + int l2; + + if (info->msr & UART_MSR_RI) { + /* Accept incoming call */ + info->last_dir = 0; + strcpy(info->last_num, dev->num[info->drv_index]); + m->mdmreg[1] = 0; + info->msr &= ~UART_MSR_RI; + l2 = m->mdmreg[14]; +#ifdef CONFIG_ISDN_AUDIO + /* If more than one bit set in reg18, autoselect Layer2 */ + if ((m->mdmreg[18] & m->mdmreg[20]) != m->mdmreg[18]) { + if (m->mdmreg[20] == 1) + l2 = 4; + else + l2 = 0; + } +#endif + cmd.driver = info->isdn_driver; + cmd.command = ISDN_CMD_SETL2; + cmd.arg = info->isdn_channel + (l2 << 8); + info->last_l2 = l2; + dev->drv[info->isdn_driver]->interface->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); + cmd.driver = info->isdn_driver; + cmd.arg = info->isdn_channel; + cmd.command = ISDN_CMD_ACCEPTD; + dev->drv[info->isdn_driver]->interface->command(&cmd); + } else + isdn_tty_modem_result(8, info); } #ifdef CONFIG_ISDN_AUDIO /* * Parse AT+F.. commands */ -static int isdn_tty_cmd_PLUSF(char **p, modem_info * info) +static int +isdn_tty_cmd_PLUSF(char **p, modem_info * info) { - atemu *m = &info->emu; - int par; + atemu *m = &info->emu; + int par; char rs[20]; - if (!strncmp(p[0],"CLASS",5)) { - p[0] += 5; - switch (*p[0]) { - case '?': - p[0]++; - sprintf(rs,"\r\n%d", - (m->mdmreg[18]&1)?8:0); - isdn_tty_at_cout(rs, info); - break; - case '=': - p[0]++; - switch (*p[0]) { - case '0': - p[0]++; - m->mdmreg[18] = 4; - info->xmit_size = - m->mdmreg[16] * 16; - break; - case '8': - p[0]++; - m->mdmreg[18] = 5; - info->xmit_size = VBUF; - break; - case '?': - p[0]++; - isdn_tty_at_cout("\r\n0,8", - info); - break; - default: - PARSE_ERROR1; - } - break; - default: - PARSE_ERROR1; - } - return 0; - } - if (!strncmp(p[0],"AA",2)) { - p[0] += 2; - switch (*p[0]) { - case '?': - p[0]++; - sprintf(rs,"\r\n%d", - m->mdmreg[0]); - isdn_tty_at_cout(rs, info); - break; - case '=': - p[0]++; - par = isdn_getnum(p); - if ((par < 0) || (par > 255)) - PARSE_ERROR1; - m->mdmreg[0]=par; - break; - default: - PARSE_ERROR1; - } - return 0; - } - PARSE_ERROR1; + if (!strncmp(p[0], "CLASS", 5)) { + p[0] += 5; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", + (m->mdmreg[18] & 1) ? 8 : 0); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + switch (*p[0]) { + case '0': + p[0]++; + m->mdmreg[18] = 4; + info->xmit_size = + m->mdmreg[16] * 16; + break; + case '8': + p[0]++; + m->mdmreg[18] = 5; + info->xmit_size = VBUF; + break; + case '?': + p[0]++; + isdn_tty_at_cout("\r\n0,8", + info); + break; + default: + PARSE_ERROR1; + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + if (!strncmp(p[0], "AA", 2)) { + p[0] += 2; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", + m->mdmreg[0]); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + m->mdmreg[0] = par; + break; + default: + PARSE_ERROR1; + } + return 0; + } + PARSE_ERROR1; } /* * Parse AT+V.. commands */ -static int isdn_tty_cmd_PLUSV(char **p, modem_info * info) +static int +isdn_tty_cmd_PLUSV(char **p, modem_info * info) { - atemu *m = &info->emu; - static char *vcmd[] = {"NH","IP","LS","RX","SD","SM","TX",NULL}; - int i; + atemu *m = &info->emu; + static char *vcmd[] = + {"NH", "IP", "LS", "RX", "SD", "SM", "TX", NULL}; + int i; int par1; int par2; char rs[20]; - i = 0; - while (vcmd[i]) { - if (!strncmp(vcmd[i],p[0],2)) { - p[0] += 2; - break; - } - i++; - } - switch (i) { - case 0: - /* AT+VNH - Auto hangup feature */ - switch (*p[0]) { - case '?': - p[0]++; - isdn_tty_at_cout("\r\n1", info); - break; - case '=': - p[0]++; - switch (*p[0]) { - case '1': - p[0]++; - break; - case '?': - p[0]++; - isdn_tty_at_cout("\r\n1", info); - break; - default: - PARSE_ERROR1; - } - break; - default: - PARSE_ERROR1; - } - break; - case 1: - /* AT+VIP - Reset all voice parameters */ - isdn_tty_modem_reset_vpar(m); - break; - case 2: - /* AT+VLS - Select device, accept incoming call */ - switch (*p[0]) { - case '?': - p[0]++; - sprintf(rs,"\r\n%d",m->vpar[0]); - isdn_tty_at_cout(rs, info); - break; - case '=': - p[0]++; - switch (*p[0]) { - case '0': - p[0]++; - m->vpar[0] = 0; - break; - case '2': - p[0]++; - m->vpar[0] = 2; - break; - case '?': - p[0]++; - isdn_tty_at_cout("\r\n0,2", info); - break; - default: - PARSE_ERROR1; - } - break; - default: - PARSE_ERROR1; - } - break; - case 3: - /* AT+VRX - Start recording */ - if (!m->vpar[0]) - PARSE_ERROR1; - if (info->online != 1) { - isdn_tty_modem_result(8, info); - return 1; - } - info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state); - if (!info->dtmf_state) { - printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n"); - PARSE_ERROR1; - } - if (m->vpar[3] < 5) { - info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]); - if (!info->adpcmr) { - printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n"); - PARSE_ERROR1; - } - } - info->vonline = 1; - isdn_tty_modem_result(1, info); - return 1; - break; - case 4: - /* AT+VSD - Silence detection */ - switch (*p[0]) { - case '?': - p[0]++; - sprintf(rs,"\r\n<%d>,<%d>", - m->vpar[1], - m->vpar[2]); - 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; - default: - PARSE_ERROR1; - } - break; - case 5: - /* AT+VSM - Select compression */ - switch (*p[0]) { - case '?': - p[0]++; - sprintf(rs,"\r\n<%d>,<%d><8000>", - m->vpar[3], - m->vpar[1]); - isdn_tty_at_cout(rs, info); - break; - case '=': - p[0]++; - switch (*p[0]) { - case '2': - case '3': - case '4': - case '5': - case '6': - par1 = isdn_getnum(p); - if ((par1 < 2) || (par1 > 6)) - PARSE_ERROR1; - m->vpar[3] = par1; - break; - case '?': - p[0]++; - isdn_tty_at_cout("\r\n2;ADPCM;2;0;(8000)\r\n", - info); - isdn_tty_at_cout("3;ADPCM;3;0;(8000)\r\n", - info); - isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n", - info); - isdn_tty_at_cout("5;ALAW;8;0;(8000)", - info); - isdn_tty_at_cout("6;ULAW;8;0;(8000)", - info); - break; - default: - PARSE_ERROR1; - } - break; - default: - PARSE_ERROR1; - } - break; - case 6: - /* AT+VTX - Start sending */ - if (!m->vpar[0]) - PARSE_ERROR1; - if (info->online != 1) { - isdn_tty_modem_result(8, info); - return 1; - } - info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state); - if (!info->dtmf_state) { - printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n"); - PARSE_ERROR1; - } - if (m->vpar[3] < 5) { - info->adpcms = isdn_audio_adpcm_init(info->adpcms, m->vpar[3]); - if (!info->adpcms) { - printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n"); - PARSE_ERROR1; - } - } - m->lastDLE = 0; - info->vonline = 2; - isdn_tty_modem_result(1, info); - return 1; - break; - default: - PARSE_ERROR1; - } - return 0; + i = 0; + while (vcmd[i]) { + if (!strncmp(vcmd[i], p[0], 2)) { + p[0] += 2; + break; + } + i++; + } + switch (i) { + case 0: + /* AT+VNH - Auto hangup feature */ + switch (*p[0]) { + case '?': + p[0]++; + isdn_tty_at_cout("\r\n1", info); + break; + case '=': + p[0]++; + switch (*p[0]) { + case '1': + p[0]++; + break; + case '?': + p[0]++; + isdn_tty_at_cout("\r\n1", info); + break; + default: + PARSE_ERROR1; + } + break; + default: + PARSE_ERROR1; + } + break; + case 1: + /* AT+VIP - Reset all voice parameters */ + isdn_tty_modem_reset_vpar(m); + break; + case 2: + /* AT+VLS - Select device, accept incoming call */ + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", m->vpar[0]); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + switch (*p[0]) { + case '0': + p[0]++; + m->vpar[0] = 0; + break; + case '2': + p[0]++; + m->vpar[0] = 2; + break; + case '?': + p[0]++; + isdn_tty_at_cout("\r\n0,2", info); + break; + default: + PARSE_ERROR1; + } + break; + default: + PARSE_ERROR1; + } + break; + case 3: + /* AT+VRX - Start recording */ + if (!m->vpar[0]) + PARSE_ERROR1; + if (info->online != 1) { + isdn_tty_modem_result(8, info); + return 1; + } + info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state); + if (!info->dtmf_state) { + printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n"); + PARSE_ERROR1; + } + if (m->vpar[3] < 5) { + info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]); + if (!info->adpcmr) { + printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n"); + PARSE_ERROR1; + } + } +#ifdef ISDN_DEBUG_AT + printk(KERN_DEBUG "AT: +VRX\n"); +#endif + info->vonline |= 1; + isdn_tty_modem_result(1, info); + return 0; + break; + case 4: + /* AT+VSD - Silence detection */ + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n<%d>,<%d>", + m->vpar[1], + m->vpar[2]); + 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; + default: + PARSE_ERROR1; + } + break; + case 5: + /* AT+VSM - Select compression */ + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n<%d>,<%d><8000>", + m->vpar[3], + m->vpar[1]); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + switch (*p[0]) { + case '2': + case '3': + case '4': + case '5': + case '6': + par1 = isdn_getnum(p); + if ((par1 < 2) || (par1 > 6)) + PARSE_ERROR1; + m->vpar[3] = par1; + break; + case '?': + p[0]++; + isdn_tty_at_cout("\r\n2;ADPCM;2;0;(8000)\r\n", + info); + isdn_tty_at_cout("3;ADPCM;3;0;(8000)\r\n", + info); + isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n", + info); + isdn_tty_at_cout("5;ALAW;8;0;(8000)", + info); + isdn_tty_at_cout("6;ULAW;8;0;(8000)", + info); + break; + default: + PARSE_ERROR1; + } + break; + default: + PARSE_ERROR1; + } + break; + case 6: + /* AT+VTX - Start sending */ + if (!m->vpar[0]) + PARSE_ERROR1; + if (info->online != 1) { + isdn_tty_modem_result(8, info); + return 1; + } + info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state); + if (!info->dtmf_state) { + printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n"); + PARSE_ERROR1; + } + if (m->vpar[3] < 5) { + info->adpcms = isdn_audio_adpcm_init(info->adpcms, m->vpar[3]); + if (!info->adpcms) { + printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n"); + PARSE_ERROR1; + } + } +#ifdef ISDN_DEBUG_AT + printk(KERN_DEBUG "AT: +VTX\n"); +#endif + m->lastDLE = 0; + info->vonline |= 2; + isdn_tty_modem_result(1, info); + return 0; + break; + default: + PARSE_ERROR1; + } + return 0; } -#endif /* CONFIG_ISDN_AUDIO */ +#endif /* CONFIG_ISDN_AUDIO */ /* * Parse and perform an AT-command-line. */ -static void isdn_tty_parse_at(modem_info * info) +static void +isdn_tty_parse_at(modem_info * info) { - atemu *m = &info->emu; - char *p; - char ds[40]; + atemu *m = &info->emu; + char *p; + char ds[40]; #ifdef ISDN_DEBUG_AT - printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd); + printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd); +#endif + for (p = &m->mdmcmd[2]; *p;) { + switch (*p) { + case 'A': + /* A - Accept incoming call */ + p++; + isdn_tty_cmd_ATA(info); + return; + break; + case 'D': + /* D - Dial */ + isdn_tty_getdial(++p, ds, sizeof(ds)); + p += strlen(p); + if (!strlen(m->msn)) + isdn_tty_modem_result(10, info); + else if (strlen(ds)) + isdn_tty_dial(ds, info, m); + else + PARSE_ERROR; + return; + case 'E': + /* E - Turn Echo on/off */ + p++; + switch (isdn_getnum(&p)) { + case 0: + m->mdmreg[12] &= ~4; + break; + case 1: + m->mdmreg[12] |= 4; + break; + default: + PARSE_ERROR; + } + break; + case 'H': + /* H - On/Off-hook */ + p++; + switch (*p) { + case '0': + p++; + isdn_tty_on_hook(info); + break; + case '1': + p++; + isdn_tty_off_hook(); + break; + default: + isdn_tty_on_hook(info); + break; + } + break; + case 'I': + /* I - Information */ + p++; + isdn_tty_at_cout("\r\nLinux ISDN", info); + switch (*p) { + case '0': + case '1': + p++; + break; + case '2': + p++; + isdn_tty_report(info); + break; + default: + } + break; + case 'O': + /* O - Go online */ + p++; + if (info->msr & UART_MSR_DCD) + /* if B-Channel is up */ + isdn_tty_modem_result(5, info); + else + isdn_tty_modem_result(3, info); + return; + case 'Q': + /* Q - Turn Emulator messages on/off */ + p++; + switch (isdn_getnum(&p)) { + case 0: + m->mdmreg[12] |= 1; + break; + case 1: + m->mdmreg[12] &= ~1; + break; + default: + PARSE_ERROR; + } + break; + case 'S': + /* S - Set/Get Register */ + p++; + if (isdn_tty_cmd_ATS(&p, info)) + return; + break; + case 'V': + /* V - Numeric or ASCII Emulator-messages */ + p++; + switch (isdn_getnum(&p)) { + case 0: + m->mdmreg[12] |= 2; + break; + case 1: + m->mdmreg[12] &= ~2; + break; + default: + PARSE_ERROR; + } + break; + case 'Z': + /* Z - Load Registers from Profile */ + p++; + isdn_tty_modem_reset_regs(info, 1); + break; +#ifdef CONFIG_ISDN_AUDIO + case '+': + p++; + switch (*p) { + case 'F': + p++; + if (isdn_tty_cmd_PLUSF(&p, info)) + return; + break; + case 'V': + if (!(m->mdmreg[18] & 1)) + PARSE_ERROR; + p++; + if (isdn_tty_cmd_PLUSV(&p, info)) + return; + break; + default: + PARSE_ERROR; + } + break; +#endif /* CONFIG_ISDN_AUDIO */ + case '&': + p++; + if (isdn_tty_cmd_ATand(&p, info)) + return; + break; + default: + PARSE_ERROR; + } + } +#ifdef CONFIG_ISDN_AUDIO + if (!info->vonline) #endif - for (p = &m->mdmcmd[2]; *p;) { - switch (*p) { - case 'A': - /* A - Accept incoming call */ - p++; - isdn_tty_cmd_ATA(info); - return; - break; - case 'D': - /* D - Dial */ - isdn_tty_getdial(++p, ds); - p += strlen(p); - if (!strlen(m->msn)) - isdn_tty_modem_result(10, info); - else if (strlen(ds)) - isdn_tty_dial(ds, info, m); - else - isdn_tty_modem_result(4, info); - return; - case 'E': - /* E - Turn Echo on/off */ - p++; - switch (isdn_getnum(&p)) { - case 0: - m->mdmreg[12] &= ~4; - break; - case 1: - m->mdmreg[12] |= 4; - break; - default: - PARSE_ERROR; - } - break; - case 'H': - /* H - On/Off-hook */ - p++; - switch (*p) { - case '0': - p++; - isdn_tty_on_hook(info); - break; - case '1': - p++; - isdn_tty_off_hook(); - break; - default: - isdn_tty_on_hook(info); - break; - } - break; - case 'I': - /* I - Information */ - p++; - isdn_tty_at_cout("\r\nLinux ISDN", info); - switch (*p) { - case '0': - case '1': - p++; - break; - default: - } - break; - case 'O': - /* O - Go online */ - p++; - if (info->msr & UART_MSR_DCD) - /* if B-Channel is up */ - isdn_tty_modem_result(5, info); - else - isdn_tty_modem_result(3, info); - return; - case 'Q': - /* Q - Turn Emulator messages on/off */ - p++; - switch (isdn_getnum(&p)) { - case 0: - m->mdmreg[12] |= 1; - break; - case 1: - m->mdmreg[12] &= ~1; - break; - default: - PARSE_ERROR; - } - break; - case 'S': - /* S - Set/Get Register */ - p++; - if (isdn_tty_cmd_ATS(&p, info)) - return; - break; - case 'V': - /* V - Numeric or ASCII Emulator-messages */ - p++; - switch (isdn_getnum(&p)) { - case 0: - m->mdmreg[12] |= 2; - break; - case 1: - m->mdmreg[12] &= ~2; - break; - default: - PARSE_ERROR; - } - break; - case 'Z': - /* Z - Load Registers from Profile */ - p++; - isdn_tty_modem_reset_regs(info, 1); - break; -#ifdef CONFIG_ISDN_AUDIO - case '+': - p++; - switch (*p) { - case 'F': - p++; - if (isdn_tty_cmd_PLUSF(&p, info)) - return; - break; - case 'V': - if (!(m->mdmreg[18] & 1)) - PARSE_ERROR; - p++; - if (isdn_tty_cmd_PLUSV(&p, info)) - return; - break; - } - break; -#endif /* CONFIG_ISDN_AUDIO */ - case '&': - p++; - if (isdn_tty_cmd_ATand(&p, info)) - return; - break; - default: - isdn_tty_modem_result(4, info); - return; - } - } - isdn_tty_modem_result(0, info); + isdn_tty_modem_result(0, info); } /* Need own toupper() because standard-toupper is not available @@ -2669,7 +3249,8 @@ * channel index to line (minor-device) * user flag: buffer is in userspace */ -static int isdn_tty_edit_at(const char *p, int count, modem_info * info, int user) +static int +isdn_tty_edit_at(const char *p, int count, modem_info * info, int user) { atemu *m = &info->emu; int total = 0; @@ -2715,16 +3296,16 @@ if (m->mdmcmdl < 255) { c = my_toupper(c); switch (m->mdmcmdl) { - case 0: - if (c == 'A') - m->mdmcmd[m->mdmcmdl++] = c; - break; - case 1: - if (c == 'T') - m->mdmcmd[m->mdmcmdl++] = c; - break; - default: - m->mdmcmd[m->mdmcmdl++] = c; + case 0: + if (c == 'A') + m->mdmcmd[m->mdmcmdl++] = c; + break; + case 1: + if (c == 'T') + m->mdmcmd[m->mdmcmdl++] = c; + break; + default: + m->mdmcmd[m->mdmcmdl++] = c; } } } @@ -2735,10 +3316,11 @@ /* * Switch all modem-channels who are online and got a valid * escape-sequence 1.5 seconds ago, to command-mode. - * This function is called every second via timer-interrupt from within + * This function is called every second via timer-interrupt from within * timer-dispatcher isdn_timer_function() */ -void isdn_tty_modem_escape(void) +void +isdn_tty_modem_escape(void) { int ton = 0; int i; @@ -2747,7 +3329,7 @@ for (i = 0; i < ISDN_MAX_CHANNELS; i++) if (USG_MODEM(dev->usage[i])) if ((midx = dev->m_idx[i]) >= 0) { - modem_info *info = &dev->mdm.info[midx]; + modem_info *info = &dev->mdm.info[midx]; if (info->online) { ton = 1; if ((info->emu.pluscount == 3) && @@ -2757,27 +3339,28 @@ isdn_tty_modem_result(0, info); } } - } + } isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, ton); } /* * Put a RING-message to all modem-channels who have the RI-bit set. - * This function is called every second via timer-interrupt from within + * This function is called every second via timer-interrupt from within * timer-dispatcher isdn_timer_function() */ -void isdn_tty_modem_ring(void) +void +isdn_tty_modem_ring(void) { int ton = 0; int i; for (i = 0; i < ISDN_MAX_CHANNELS; i++) { - modem_info *info = &dev->mdm.info[i]; - if (info->msr & UART_MSR_RI) { - ton = 1; - isdn_tty_modem_result(2, info); - } - } + modem_info *info = &dev->mdm.info[i]; + if (info->msr & UART_MSR_RI) { + ton = 1; + isdn_tty_modem_result(2, info); + } + } isdn_timer_ctrl(ISDN_TIMER_MODEMRING, ton); } @@ -2785,41 +3368,19 @@ * For all online tty's, try sending data to * the lower levels. */ -void isdn_tty_modem_xmit(void) +void +isdn_tty_modem_xmit(void) { int ton = 1; int i; for (i = 0; i < ISDN_MAX_CHANNELS; i++) { - modem_info *info = &dev->mdm.info[i]; - if (info->online) { - ton = 1; - isdn_tty_senddown(info); - isdn_tty_tint(info); - } - } + modem_info *info = &dev->mdm.info[i]; + if (info->online) { + ton = 1; + isdn_tty_senddown(info); + isdn_tty_tint(info); + } + } isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton); -} - -/* - * A packet has been output successfully. - * Search the tty-devices for an appropriate device, decrement its - * counter for outstanding packets, and set CTS. - */ -void isdn_tty_bsent(int drv, int chan) -{ - int i; - - for (i = 0; i < ISDN_MAX_CHANNELS; i++) { - modem_info *info = &dev->mdm.info[i]; - if ((info->isdn_driver == drv) && - (info->isdn_channel == chan) ) { - info->msr |= UART_MSR_CTS; - if (info->send_outstanding) - if (!(--info->send_outstanding)) - info->lsr |= UART_LSR_TEMT; - isdn_tty_tint(info); - } - } - return; } diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_tty.h linux/drivers/isdn/isdn_tty.h --- v2.0.30/linux/drivers/isdn/isdn_tty.h Sun May 19 05:29:29 1996 +++ linux/drivers/isdn/isdn_tty.h Mon Aug 4 17:34:00 1997 @@ -1,10 +1,10 @@ -/* $Id: isdn_tty.h,v 1.5 1996/05/17 03:52:31 fritz Exp $ - * +/* $Id: isdn_tty.h,v 1.10 1997/03/02 14:29:26 fritz Exp $ + * header for Linux ISDN subsystem, tty related functions (linklevel). * * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg - * + * * 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) @@ -17,9 +17,26 @@ * * 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. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_tty.h,v $ + * Revision 1.10 1997/03/02 14:29:26 fritz + * More ttyI related cleanup. + * + * Revision 1.9 1997/02/28 02:32:49 fritz + * Cleanup: Moved some tty related stuff from isdn_common.c + * to isdn_tty.c + * Bugfix: Bisync protocol did not behave like documented. + * + * Revision 1.8 1997/02/10 20:12:50 fritz + * Changed interface for reporting incoming calls. + * + * Revision 1.7 1997/02/03 23:06:10 fritz + * Reformatted according CodingStyle + * + * Revision 1.6 1997/01/14 01:35:19 fritz + * Changed prototype of isdn_tty_modem_hup. + * * Revision 1.5 1996/05/17 03:52:31 fritz * Changed DLE handling for audio receive. * @@ -37,15 +54,12 @@ * */ -extern void isdn_tty_modem_result(int, modem_info *); -extern void isdn_tty_modem_escape(void); -extern void isdn_tty_modem_ring(void); -extern void isdn_tty_modem_xmit(void); -extern void isdn_tty_modem_hup(modem_info *); -extern int isdn_tty_modem_init(void); -extern void isdn_tty_readmodem(void); -extern int isdn_tty_try_read(modem_info *, struct sk_buff *); -extern int isdn_tty_find_icall(int, int, char *); -extern int isdn_tty_countDLE(unsigned char *, int); -extern void isdn_tty_bsent(int, int); -extern void isdn_tty_cleanup_xmit(modem_info *); +extern void isdn_tty_modem_escape(void); +extern void isdn_tty_modem_ring(void); +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 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.30/linux/drivers/isdn/pcbit/callbacks.c linux/drivers/isdn/pcbit/callbacks.c --- v2.0.30/linux/drivers/isdn/pcbit/callbacks.c Sat Jun 29 10:36:22 1996 +++ linux/drivers/isdn/pcbit/callbacks.c Mon Aug 4 17:34:00 1997 @@ -164,18 +164,12 @@ * ictl.num >= strlen() + strlen() + 5 */ - if (cbdata->data.setup.CalledPN) - sprintf(ictl.num, "%s,%d,%d,%s", - cbdata->data.setup.CallingPN, - 7, 0, - cbdata->data.setup.CalledPN); - - else - sprintf(ictl.num, "%s,%d,%d,%s", - cbdata->data.setup.CallingPN, - 7, 0, - "0"); - + strcpy(ictl.parm.setup.phone, cbdata->data.setup.CallingPN); + 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; #ifdef DEBUG printk(KERN_DEBUG "statstr: %s\n", ictl.num); diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/pcbit/capi.c linux/drivers/isdn/pcbit/capi.c --- v2.0.30/linux/drivers/isdn/pcbit/capi.c Sat Jun 29 10:36:22 1996 +++ linux/drivers/isdn/pcbit/capi.c Mon Aug 4 17:34:01 1997 @@ -147,7 +147,7 @@ return -1; } - (*skb)->free = 1; + SET_SKB_FREE((*skb)); *((ushort*) skb_put(*skb, 2) ) = chan->callref; @@ -170,7 +170,7 @@ return -1; } - (*skb)->free = 1; + SET_SKB_FREE((*skb)); *((ushort*) skb_put(*skb, 2) ) = chan->callref; @@ -200,7 +200,7 @@ return -1; } - (*skb)->free = 1; + SET_SKB_FREE((*skb)); *((ushort*) skb_put(*skb, 2) ) = chan->callref; @@ -222,7 +222,7 @@ return -1; } - (*skb)->free = 1; + SET_SKB_FREE((*skb)); *((ushort*) skb_put(*skb, 2) ) = chan->callref; @@ -285,7 +285,7 @@ return -1; } - (*skb)->free = 1; + SET_SKB_FREE((*skb)); *((ushort*) skb_put(*skb, 2) ) = chan->callref; @@ -338,7 +338,7 @@ return -1; } - (*skb)->free = 1; + SET_SKB_FREE((*skb)); *((ushort*) skb_put(*skb, 2) ) = chan->callref; @@ -357,7 +357,7 @@ return -1; } - (*skb)->free = 1; + SET_SKB_FREE((*skb)); *((ushort*) skb_put(*skb, 2) ) = callref; @@ -382,7 +382,7 @@ return -1; } - (*skb)->free = 1; + SET_SKB_FREE((*skb)); *((ushort*) skb_put(*skb, 2)) = chan->callref; diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/pcbit/drv.c linux/drivers/isdn/pcbit/drv.c --- v2.0.30/linux/drivers/isdn/pcbit/drv.c Tue Nov 12 22:36:20 1996 +++ linux/drivers/isdn/pcbit/drv.c Mon Aug 4 17:34:01 1997 @@ -226,7 +226,6 @@ struct pcbit_dev *dev; struct pcbit_chan *chan; struct callb_data info; - char *cp; dev = finddev(ctl->driver); @@ -245,14 +244,7 @@ break; case ISDN_CMD_DIAL: info.type = EV_USR_SETUP_REQ; - info.data.setup.CalledPN = (char *) &ctl->num; - cp = strchr(info.data.setup.CalledPN, ','); - if (cp) - *cp = 0; - else { - printk(KERN_DEBUG "DIAL: error in CalledPN\n"); - return -1; - } + info.data.setup.CalledPN = (char *) &ctl->parm.setup.phone; pcbit_fsm_event(dev, chan, EV_USR_SETUP_REQ, &info); break; case ISDN_CMD_ACCEPTD: @@ -280,7 +272,7 @@ pcbit_clear_msn(dev); break; case ISDN_CMD_SETEAZ: - pcbit_set_msn(dev, ctl->num); + pcbit_set_msn(dev, ctl->parm.num); break; case ISDN_CMD_SETL3: if ((ctl->arg >> 8) != ISDN_PROTO_L3_TRANS) @@ -457,15 +449,9 @@ for (i=0; i < len; i++) { for(j=0; j < LOAD_RETRY; j++) - { - __volatile__ unsigned char * ptr; - - ptr = dev->sh_mem + dev->loadptr; - if (*ptr == 0) + if (!(readb(dev->sh_mem + dev->loadptr))) break; - } - if (j == LOAD_RETRY) { errstat = -ETIME; @@ -745,7 +731,7 @@ #endif } - skb->free = 1; + SET_SKB_FREE(skb); kfree_skb(skb, FREE_READ); @@ -944,7 +930,7 @@ return -ENODEV; } - cmd = (struct pcbit_ioctl *) ctl->num; + cmd = (struct pcbit_ioctl *) ctl->parm.num; switch(ctl->arg) { case PCBIT_IOCTL_GETSTAT: diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/pcbit/layer2.c linux/drivers/isdn/pcbit/layer2.c --- v2.0.30/linux/drivers/isdn/pcbit/layer2.c Sat Jun 29 10:36:22 1996 +++ linux/drivers/isdn/pcbit/layer2.c Mon Aug 4 17:34:01 1997 @@ -1,13 +1,13 @@ /* * Copyright (C) 1996 Universidade de Lisboa - * + * * Written by Pedro Roque Marques (roque@di.fc.ul.pt) * - * This software may be used and distributed according to the terms of + * This software may be used and distributed according to the terms of * the GNU Public License, incorporated herein by reference. */ -/* +/* * PCBIT-D low-layer interface */ @@ -19,7 +19,7 @@ /* * TODO: better handling of errors * re-write/remove debug printks - */ + */ #define __NO_VERSION__ @@ -57,7 +57,7 @@ /* * task queue struct - */ + */ @@ -66,16 +66,16 @@ * drv.c */ -extern void pcbit_l3_receive(struct pcbit_dev * dev, ulong msg, - struct sk_buff * skb, +extern void pcbit_l3_receive(struct pcbit_dev *dev, ulong msg, + struct sk_buff *skb, ushort hdr_len, ushort refnum); /* * Prototypes */ -void pcbit_deliver(void * data); -static void pcbit_transmit(struct pcbit_dev * dev); +void pcbit_deliver(void *data); +static void pcbit_transmit(struct pcbit_dev *dev); static void pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack); @@ -83,9 +83,10 @@ static void pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info); static void pcbit_l2_err_recover(unsigned long data); -static void pcbit_firmware_bug(struct pcbit_dev * dev); +static void pcbit_firmware_bug(struct pcbit_dev *dev); -static __inline__ void pcbit_sched_delivery(struct pcbit_dev *dev) +static __inline__ void +pcbit_sched_delivery(struct pcbit_dev *dev) { queue_task(&dev->qdelivery, &tq_immediate); mark_bh(IMMEDIATE_BH); @@ -96,71 +97,67 @@ * Called from layer3 */ -int pcbit_l2_write(struct pcbit_dev * dev, ulong msg, ushort refnum, - struct sk_buff *skb, unsigned short hdr_len) - -{ - struct frame_buf * frame, * ptr; - unsigned long flags; - - if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) { - dev_kfree_skb(skb, FREE_WRITE); - return -1; - } - - if ( (frame = (struct frame_buf *) kmalloc(sizeof(struct frame_buf), - GFP_ATOMIC)) == NULL ) { - printk(KERN_WARNING "pcbit_2_write: kmalloc failed\n"); +int +pcbit_l2_write(struct pcbit_dev *dev, ulong msg, ushort refnum, + struct sk_buff *skb, unsigned short hdr_len) +{ + struct frame_buf *frame, + *ptr; + unsigned long flags; + + if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) { + dev_kfree_skb(skb, FREE_WRITE); + return -1; + } + if ((frame = (struct frame_buf *) kmalloc(sizeof(struct frame_buf), + GFP_ATOMIC)) == NULL) { + printk(KERN_WARNING "pcbit_2_write: kmalloc failed\n"); dev_kfree_skb(skb, FREE_WRITE); - return -1; - } + return -1; + } + frame->msg = msg; + frame->refnum = refnum; + frame->copied = 0; + frame->hdr_len = hdr_len; + + if (skb) + frame->dt_len = skb->len - hdr_len; + else + frame->dt_len = 0; - frame->msg = msg; - frame->refnum = refnum; - frame->copied = 0; - frame->hdr_len = hdr_len; - - if (skb) { - frame->dt_len = skb->len - hdr_len; - if (frame->dt_len == 0) - skb->lock++; - } - else - frame->dt_len = 0; - - frame->skb = skb; + frame->skb = skb; - frame->next = NULL; + frame->next = NULL; - save_flags(flags); - cli(); + save_flags(flags); + cli(); + + if (dev->write_queue == NULL) { + dev->write_queue = frame; + restore_flags(flags); + pcbit_transmit(dev); + } else { + for (ptr = dev->write_queue; ptr->next; ptr = ptr->next); + ptr->next = frame; - if (dev->write_queue == NULL) { - dev->write_queue = frame; restore_flags(flags); - pcbit_transmit(dev); - } - else { - for(ptr=dev->write_queue; ptr->next; ptr=ptr->next); - ptr->next = frame; - - restore_flags(flags); - } - return 0; + } + return 0; } -static __inline__ void pcbit_tx_update(struct pcbit_dev *dev, ushort len) +static __inline__ void +pcbit_tx_update(struct pcbit_dev *dev, ushort len) { - u_char info; + u_char info; - dev->send_seq = (dev->send_seq + 1) % 8; + dev->send_seq = (dev->send_seq + 1) % 8; - dev->fsize[dev->send_seq] = len; - info = 0; - info |= dev->rcv_seq << 3; - info |= dev->send_seq; + dev->fsize[dev->send_seq] = len; + info = 0; + info |= dev->rcv_seq << 3; + info |= dev->send_seq; - writeb(info, dev->sh_mem + BANK4); + writeb(info, dev->sh_mem + BANK4); } @@ -168,46 +165,47 @@ * called by interrupt service routine or by write_2 */ -static void pcbit_transmit(struct pcbit_dev * dev) +static void +pcbit_transmit(struct pcbit_dev *dev) { - struct frame_buf * frame = NULL; - unsigned char unacked; - int flen; /* fragment frame length including all headers */ - int totlen; /* non-fragmented frame length */ - int free; - int count, cp_len; - unsigned long flags; + struct frame_buf *frame = NULL; + unsigned char unacked; + int flen; /* fragment frame length including all headers */ + int totlen; /* non-fragmented frame length */ + int free; + int count, + cp_len; + unsigned long flags; unsigned short tt; - if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) - return; + if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) + return; - unacked = (dev->send_seq + (8 - dev->unack_seq) ) & 0x07; + unacked = (dev->send_seq + (8 - dev->unack_seq)) & 0x07; save_flags(flags); cli(); - if (dev->free > 16 && dev->write_queue && unacked < 7) { + if (dev->free > 16 && dev->write_queue && unacked < 7) { - if (!dev->w_busy) - dev->w_busy = 1; - else - { - restore_flags(flags); - return; - } + if (!dev->w_busy) + dev->w_busy = 1; + else { + restore_flags(flags); + return; + } - frame = dev->write_queue; - free = dev->free; + frame = dev->write_queue; + free = dev->free; - restore_flags(flags); + restore_flags(flags); if (frame->copied == 0) { - /* Type 0 frame */ + /* Type 0 frame */ - struct msg_fmt * msg; + struct msg_fmt *msg; if (frame->skb) totlen = FRAME_HDR_LEN + PREHDR_LEN + frame->skb->len; @@ -216,67 +214,65 @@ flen = MIN(totlen, free); - msg = (struct msg_fmt *) &(frame->msg); + msg = (struct msg_fmt *) &(frame->msg); + + /* + * Board level 2 header + */ - /* - * Board level 2 header - */ + pcbit_writew(dev, flen - FRAME_HDR_LEN); - pcbit_writew(dev, flen - FRAME_HDR_LEN); + pcbit_writeb(dev, msg->cpu); - pcbit_writeb(dev, msg->cpu); + pcbit_writeb(dev, msg->proc); - pcbit_writeb(dev, msg->proc); + /* TH */ + pcbit_writew(dev, frame->hdr_len + PREHDR_LEN); - /* TH */ - pcbit_writew(dev, frame->hdr_len + PREHDR_LEN); + /* TD */ + pcbit_writew(dev, frame->dt_len); - /* TD */ - pcbit_writew(dev, frame->dt_len); + /* + * Board level 3 fixed-header + */ - /* - * Board level 3 fixed-header - */ - - /* LEN = TH */ - pcbit_writew(dev, frame->hdr_len + PREHDR_LEN); - - /* XX */ - pcbit_writew(dev, 0); + /* LEN = TH */ + pcbit_writew(dev, frame->hdr_len + PREHDR_LEN); - /* C + S */ - pcbit_writeb(dev, msg->cmd); - pcbit_writeb(dev, msg->scmd); + /* XX */ + pcbit_writew(dev, 0); + + /* C + S */ + pcbit_writeb(dev, msg->cmd); + pcbit_writeb(dev, msg->scmd); + + /* NUM */ + pcbit_writew(dev, frame->refnum); - /* NUM */ - pcbit_writew(dev, frame->refnum); - count = FRAME_HDR_LEN + PREHDR_LEN; - } - else { + } else { /* Type 1 frame */ - totlen = 2 + (frame->skb->len - frame->copied); - + totlen = 2 + (frame->skb->len - frame->copied); + flen = MIN(totlen, free); - /* TT */ - tt = ((ushort) (flen - 2)) | 0x8000U; /* Type 1 */ - pcbit_writew(dev, tt); + /* TT */ + tt = ((ushort) (flen - 2)) | 0x8000U; /* Type 1 */ + pcbit_writew(dev, tt); count = 2; } if (frame->skb) { - cp_len = MIN(frame->skb->len - frame->copied, - flen - count); - - memcpy_topcbit(dev, frame->skb->data + frame->copied, - cp_len); + cp_len = MIN(frame->skb->len - frame->copied, + flen - count); + + memcpy_topcbit(dev, frame->skb->data + frame->copied, + cp_len); frame->copied += cp_len; } - /* bookkeeping */ dev->free -= flen; pcbit_tx_update(dev, flen); @@ -285,28 +281,24 @@ cli(); - if (frame->skb == NULL || frame->copied == frame->skb->len) { - - dev->write_queue = frame->next; + if (frame->skb == NULL || frame->copied == frame->skb->len) { - if (frame->skb != NULL) { - /* free frame */ - dev_kfree_skb(frame->skb, FREE_WRITE); - } - - kfree(frame); - } + dev->write_queue = frame->next; + if (frame->skb != NULL) { + /* free frame */ + dev_kfree_skb(frame->skb, FREE_WRITE); + } + kfree(frame); + } dev->w_busy = 0; - restore_flags(flags); - } - else - { restore_flags(flags); -#ifdef DEBUG - printk(KERN_DEBUG "unacked %d free %d write_queue %s\n", - unacked, dev->free, dev->write_queue ? "not empty" : - "empty"); + } else { + restore_flags(flags); +#ifdef DEBUG + printk(KERN_DEBUG "unacked %d free %d write_queue %s\n", + unacked, dev->free, dev->write_queue ? "not empty" : + "empty"); #endif } } @@ -316,18 +308,18 @@ * deliver a queued frame to the upper layer */ -void pcbit_deliver(void * data) -{ - struct frame_buf *frame; - unsigned long flags; +void +pcbit_deliver(void *data) +{ + struct frame_buf *frame; + unsigned long flags; struct msg_fmt msg; - struct pcbit_dev *dev = (struct pcbit_dev *) data; + struct pcbit_dev *dev = (struct pcbit_dev *) data; save_flags(flags); - cli(); + cli(); - while((frame=dev->read_queue)) - { + while ((frame = dev->read_queue)) { dev->read_queue = frame->next; restore_flags(flags); @@ -336,12 +328,12 @@ msg.cmd = frame->skb->data[2]; msg.scmd = frame->skb->data[3]; - frame->refnum = *((ushort*) frame->skb->data + 4); - frame->msg = *((ulong*) &msg); - + frame->refnum = *((ushort *) frame->skb->data + 4); + frame->msg = *((ulong *) & msg); + skb_pull(frame->skb, 6); - pcbit_l3_receive(dev, frame->msg, frame->skb, frame->hdr_len, + pcbit_l3_receive(dev, frame->msg, frame->skb, frame->hdr_len, frame->refnum); kfree(frame); @@ -354,110 +346,104 @@ } /* - * Reads BANK 2 & Reassembles + * Reads BANK 2 & Reassembles */ -static void pcbit_receive(struct pcbit_dev * dev) +static void +pcbit_receive(struct pcbit_dev *dev) { - unsigned short tt; - u_char cpu, proc; - struct frame_buf * frame = NULL; - unsigned long flags; - u_char type1; + unsigned short tt; + u_char cpu, + proc; + struct frame_buf *frame = NULL; + unsigned long flags; + u_char type1; - if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) - return; + if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) + return; - tt = pcbit_readw(dev); + tt = pcbit_readw(dev); - if ((tt & 0x7fffU) > 511) { - printk(KERN_INFO "pcbit: invalid frame length -> TT=%04x\n", + if ((tt & 0x7fffU) > 511) { + printk(KERN_INFO "pcbit: invalid frame length -> TT=%04x\n", tt); - pcbit_l2_error(dev); - return; - } - - if (!(tt & 0x8000U)) - { /* Type 0 */ - type1 = 0; + pcbit_l2_error(dev); + return; + } + if (!(tt & 0x8000U)) { /* Type 0 */ + type1 = 0; - if (dev->read_frame) { - printk(KERN_DEBUG "pcbit_receive: Type 0 frame and read_frame != NULL\n"); + if (dev->read_frame) { + printk(KERN_DEBUG "pcbit_receive: Type 0 frame and read_frame != NULL\n"); #if 0 - pcbit_l2_error(dev); - return; + pcbit_l2_error(dev); + return; #else /* discard previous queued frame */ if (dev->read_frame->skb) { - dev->read_frame->skb->free = 1; + SET_SKB_FREE(dev->read_frame->skb); kfree_skb(dev->read_frame->skb, FREE_READ); } kfree(dev->read_frame); dev->read_frame = NULL; #endif - } + } + frame = kmalloc(sizeof(struct frame_buf), GFP_ATOMIC); + + if (frame == NULL) { + printk(KERN_WARNING "kmalloc failed\n"); + return; + } + memset(frame, 0, sizeof(struct frame_buf)); + + cpu = pcbit_readb(dev); + proc = pcbit_readb(dev); - frame = kmalloc(sizeof(struct frame_buf), GFP_ATOMIC); - - if (frame == NULL) { - printk(KERN_WARNING "kmalloc failed\n"); - return; - } - memset(frame, 0, sizeof(struct frame_buf)); - - cpu = pcbit_readb(dev); - proc = pcbit_readb(dev); - - - if (cpu != 0x06 && cpu != 0x02) - { - printk (KERN_DEBUG "pcbit: invalid cpu value\n"); + + if (cpu != 0x06 && cpu != 0x02) { + printk(KERN_DEBUG "pcbit: invalid cpu value\n"); kfree(frame); pcbit_l2_error(dev); - return; - } - - /* - * we discard cpu & proc on receiving - * but we read it to update the pointer - */ - - frame->hdr_len = pcbit_readw(dev); - frame->dt_len = pcbit_readw(dev); - - /* - * 0 sized packet - * I don't know if they are an error or not... - * But they are very frequent - * Not documented - */ + return; + } + /* + * we discard cpu & proc on receiving + * but we read it to update the pointer + */ + + frame->hdr_len = pcbit_readw(dev); + frame->dt_len = pcbit_readw(dev); + + /* + * 0 sized packet + * I don't know if they are an error or not... + * But they are very frequent + * Not documented + */ if (frame->hdr_len == 0) { - kfree(frame); + kfree(frame); #ifdef DEBUG - printk(KERN_DEBUG "0 sized frame\n"); + printk(KERN_DEBUG "0 sized frame\n"); #endif pcbit_firmware_bug(dev); - return; + return; } - - /* sanity check the length values */ - if (frame->hdr_len > 1024 || frame->dt_len > 2048) - { + /* sanity check the length values */ + if (frame->hdr_len > 1024 || frame->dt_len > 2048) { #ifdef DEBUG - printk(KERN_DEBUG "length problem: "); - printk(KERN_DEBUG "TH=%04x TD=%04x\n", - frame->hdr_len, - frame->dt_len); + printk(KERN_DEBUG "length problem: "); + printk(KERN_DEBUG "TH=%04x TD=%04x\n", + frame->hdr_len, + frame->dt_len); #endif pcbit_l2_error(dev); kfree(frame); - return; - } - - /* minimum frame read */ + return; + } + /* minimum frame read */ - frame->skb = dev_alloc_skb(frame->hdr_len + frame->dt_len + + frame->skb = dev_alloc_skb(frame->hdr_len + frame->dt_len + ((frame->hdr_len + 15) & ~15)); if (!frame->skb) { @@ -465,62 +451,57 @@ kfree(frame); return; } - - /* 16 byte alignment for IP */ + /* 16 byte alignment for IP */ if (frame->dt_len) - skb_reserve(frame->skb, (frame->hdr_len + 15) & ~15); + skb_reserve(frame->skb, (frame->hdr_len + 15) & ~15); - } - else { + } else { /* Type 1 */ - type1 = 1; - tt &= 0x7fffU; + type1 = 1; + tt &= 0x7fffU; - if (!(frame = dev->read_frame)) { - printk("Type 1 frame and no frame queued\n"); + if (!(frame = dev->read_frame)) { + printk("Type 1 frame and no frame queued\n"); #if 1 /* usually after an error: toss frame */ dev->readptr += tt; if (dev->readptr > dev->sh_mem + BANK2 + BANKLEN) dev->readptr -= BANKLEN; #else - pcbit_l2_error(dev); + pcbit_l2_error(dev); #endif - return; + return; - } - } + } + } memcpy_frompcbit(dev, skb_put(frame->skb, tt), tt); frame->copied += tt; - if (frame->copied == frame->hdr_len + frame->dt_len) { - - save_flags(flags); - cli(); - - if (type1) { - dev->read_frame = NULL; - } - - if (dev->read_queue) { - struct frame_buf *ptr; - for(ptr=dev->read_queue;ptr->next;ptr=ptr->next); - ptr->next = frame; - } - else - dev->read_queue = frame; - - restore_flags(flags); - - } - else { - save_flags(flags); - cli(); - dev->read_frame = frame; - restore_flags(flags); - } + if (frame->copied == frame->hdr_len + frame->dt_len) { + + save_flags(flags); + cli(); + + if (type1) { + dev->read_frame = NULL; + } + if (dev->read_queue) { + struct frame_buf *ptr; + for (ptr = dev->read_queue; ptr->next; ptr = ptr->next); + ptr->next = frame; + } else + dev->read_queue = frame; + + restore_flags(flags); + + } else { + save_flags(flags); + cli(); + dev->read_frame = frame; + restore_flags(flags); + } } /* @@ -529,194 +510,173 @@ * gotta send a fake acknowledgment to the upper layer somehow */ -static __inline__ void pcbit_fake_conf(struct pcbit_dev *dev, struct pcbit_chan * chan) +static __inline__ void +pcbit_fake_conf(struct pcbit_dev *dev, struct pcbit_chan *chan) { - isdn_ctrl ictl; + isdn_ctrl ictl; - if (chan->queued) { - chan->queued--; - - ictl.driver = dev->id; - ictl.command = ISDN_STAT_BSENT; - ictl.arg = chan->id; - dev->dev_if->statcallb(&ictl); - } + if (chan->queued) { + chan->queued--; + + ictl.driver = dev->id; + ictl.command = ISDN_STAT_BSENT; + ictl.arg = chan->id; + dev->dev_if->statcallb(&ictl); + } } -static void pcbit_firmware_bug(struct pcbit_dev * dev) +static void +pcbit_firmware_bug(struct pcbit_dev *dev) { - struct pcbit_chan *chan; - - chan = dev->b1; + struct pcbit_chan *chan; - if (chan->fsm_state == ST_ACTIVE) { - pcbit_fake_conf(dev, chan); - } + chan = dev->b1; - chan = dev->b2; + if (chan->fsm_state == ST_ACTIVE) { + pcbit_fake_conf(dev, chan); + } + chan = dev->b2; - if (chan->fsm_state == ST_ACTIVE) { - pcbit_fake_conf(dev, chan); - } - + if (chan->fsm_state == ST_ACTIVE) { + pcbit_fake_conf(dev, chan); + } } -void pcbit_irq_handler(int interrupt, void * devptr, struct pt_regs *regs) +void +pcbit_irq_handler(int interrupt, void *devptr, struct pt_regs *regs) { - struct pcbit_dev * dev; - u_char info, ack_seq, read_seq; + struct pcbit_dev *dev; + u_char info, + ack_seq, + read_seq; dev = (struct pcbit_dev *) devptr; - if (!dev) - { - printk(KERN_WARNING "pcbit_irq_handler: wrong device\n"); - return; - } - + if (!dev) { + printk(KERN_WARNING "pcbit_irq_handler: wrong device\n"); + return; + } if (dev->interrupt) { - printk(KERN_DEBUG "pcbit: reentering interrupt hander\n"); + printk(KERN_DEBUG "pcbit: reentering interrupt hander\n"); return; } - dev->interrupt = 1; - info = readb(dev->sh_mem + BANK3); + info = readb(dev->sh_mem + BANK3); - if (dev->l2_state == L2_STARTING || dev->l2_state == L2_ERROR) - { - pcbit_l2_active_conf(dev, info); + if (dev->l2_state == L2_STARTING || dev->l2_state == L2_ERROR) { + pcbit_l2_active_conf(dev, info); dev->interrupt = 0; - return; - } - - if (info & 0x40U) /* E bit set */ - { + return; + } + if (info & 0x40U) { /* E bit set */ #ifdef DEBUG printk(KERN_DEBUG "pcbit_irq_handler: E bit on\n"); #endif - pcbit_l2_error(dev); + pcbit_l2_error(dev); dev->interrupt = 0; - return; - } - - if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) - { - dev->interrupt = 0; - return; + return; } + if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) { + dev->interrupt = 0; + return; + } + ack_seq = (info >> 3) & 0x07U; + read_seq = (info & 0x07U); - ack_seq = (info >> 3) & 0x07U; - read_seq = (info & 0x07U); - dev->interrupt = 0; - if (read_seq != dev->rcv_seq) - { - while (read_seq != dev->rcv_seq) - { + if (read_seq != dev->rcv_seq) { + while (read_seq != dev->rcv_seq) { pcbit_receive(dev); dev->rcv_seq = (dev->rcv_seq + 1) % 8; } pcbit_sched_delivery(dev); - } - - if (ack_seq != dev->unack_seq) - { - pcbit_recv_ack(dev, ack_seq); - } - - + } + if (ack_seq != dev->unack_seq) { + pcbit_recv_ack(dev, ack_seq); + } info = dev->rcv_seq << 3; info |= dev->send_seq; - - writeb(info, dev->sh_mem + BANK4); + + writeb(info, dev->sh_mem + BANK4); } -static void pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info) +static void +pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info) { - u_char state; + u_char state; - state = dev->l2_state; + state = dev->l2_state; #ifdef DEBUG - printk(KERN_DEBUG "layer2_active_confirm\n"); + printk(KERN_DEBUG "layer2_active_confirm\n"); #endif - - if (info & 0x80U ) { - dev->rcv_seq = info & 0x07U; - dev->l2_state = L2_RUNNING; - } - else - dev->l2_state = L2_DOWN; - - if (state == L2_STARTING) - wake_up_interruptible(&dev->set_running_wq); - if (state == L2_ERROR && dev->l2_state == L2_RUNNING) { - pcbit_transmit(dev); - } + if (info & 0x80U) { + dev->rcv_seq = info & 0x07U; + dev->l2_state = L2_RUNNING; + } else + dev->l2_state = L2_DOWN; + + if (state == L2_STARTING) + wake_up_interruptible(&dev->set_running_wq); + if (state == L2_ERROR && dev->l2_state == L2_RUNNING) { + pcbit_transmit(dev); + } } -static void pcbit_l2_err_recover(unsigned long data) +static void +pcbit_l2_err_recover(unsigned long data) { - struct pcbit_dev * dev; - struct frame_buf *frame; + struct pcbit_dev *dev; + struct frame_buf *frame; - dev = (struct pcbit_dev *) data; + dev = (struct pcbit_dev *) data; - del_timer(&dev->error_recover_timer); - if (dev->w_busy || dev->r_busy) - { + del_timer(&dev->error_recover_timer); + if (dev->w_busy || dev->r_busy) { init_timer(&dev->error_recover_timer); dev->error_recover_timer.expires = jiffies + ERRTIME; add_timer(&dev->error_recover_timer); return; } - dev->w_busy = dev->r_busy = 1; - if (dev->read_frame) - { - if (dev->read_frame->skb) - { - dev->read_frame->skb->free = 1; - kfree_skb(dev->read_frame->skb, FREE_READ); - } - kfree(dev->read_frame); - dev->read_frame = NULL; - } - - - if (dev->write_queue) - { - frame = dev->write_queue; + if (dev->read_frame) { + if (dev->read_frame->skb) { + SET_SKB_FREE(dev->read_frame->skb); + kfree_skb(dev->read_frame->skb, FREE_READ); + } + kfree(dev->read_frame); + dev->read_frame = NULL; + } + if (dev->write_queue) { + frame = dev->write_queue; #ifdef FREE_ON_ERROR - dev->write_queue = dev->write_queue->next; + dev->write_queue = dev->write_queue->next; if (frame->skb) { - dev_kfree_skb(frame->skb, FREE_WRITE); + dev_kfree_skb(frame->skb, FREE_WRITE); } - - kfree(frame); -#else - frame->copied = 0; + kfree(frame); +#else + frame->copied = 0; #endif - } - - dev->rcv_seq = dev->send_seq = dev->unack_seq = 0; - dev->free = 511; - dev->l2_state = L2_ERROR; + } + dev->rcv_seq = dev->send_seq = dev->unack_seq = 0; + dev->free = 511; + dev->l2_state = L2_ERROR; /* this is an hack... */ pcbit_firmware_bug(dev); - dev->writeptr = dev->sh_mem; - dev->readptr = dev->sh_mem + BANK2; + dev->writeptr = dev->sh_mem; + dev->readptr = dev->sh_mem + BANK2; writeb((0x80U | ((dev->rcv_seq & 0x07) << 3) | (dev->send_seq & 0x07)), dev->sh_mem + BANK4); @@ -724,24 +684,25 @@ } -static void pcbit_l2_error(struct pcbit_dev *dev) +static void +pcbit_l2_error(struct pcbit_dev *dev) { - if (dev->l2_state == L2_RUNNING) { + if (dev->l2_state == L2_RUNNING) { - printk(KERN_INFO "pcbit: layer 2 error\n"); + printk(KERN_INFO "pcbit: layer 2 error\n"); #ifdef DEBUG - log_state(dev); + log_state(dev); #endif - - dev->l2_state = L2_DOWN; - init_timer(&dev->error_recover_timer); - dev->error_recover_timer.function = &pcbit_l2_err_recover; - dev->error_recover_timer.data = (ulong) dev; - dev->error_recover_timer.expires = jiffies + ERRTIME; - add_timer(&dev->error_recover_timer); - } + dev->l2_state = L2_DOWN; + + init_timer(&dev->error_recover_timer); + dev->error_recover_timer.function = &pcbit_l2_err_recover; + dev->error_recover_timer.data = (ulong) dev; + dev->error_recover_timer.expires = jiffies + ERRTIME; + add_timer(&dev->error_recover_timer); + } } /* @@ -751,58 +712,52 @@ * call pcbit_transmit to write possible queued frames */ -static void pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack) +static void +pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack) { - int i, count; - int unacked; + int i, + count; + int unacked; - unacked = (dev->send_seq + (8 - dev->unack_seq) ) & 0x07; + unacked = (dev->send_seq + (8 - dev->unack_seq)) & 0x07; - /* dev->unack_seq < ack <= dev->send_seq; */ + /* dev->unack_seq < ack <= dev->send_seq; */ - if (unacked) - { + if (unacked) { if (dev->send_seq > dev->unack_seq) - if (ack <= dev->unack_seq || ack > dev->send_seq) - { - printk(KERN_DEBUG - "layer 2 ack unacceptable - dev %d", + if (ack <= dev->unack_seq || ack > dev->send_seq) { + printk(KERN_DEBUG + "layer 2 ack unacceptable - dev %d", dev->id); - pcbit_l2_error(dev); - } - else - if (ack > dev->send_seq && ack <= dev->unack_seq) - { - printk(KERN_DEBUG - "layer 2 ack unacceptable - dev %d", - dev->id); - pcbit_l2_error(dev); - } - - /* ack is acceptable */ - - - i = dev->unack_seq; - - do { - dev->unack_seq = i = (i + 1) % 8; - dev->free += dev->fsize[i]; - } while (i != ack); - - count = 0; - while (count < 7 && dev->write_queue) - { + pcbit_l2_error(dev); + } else if (ack > dev->send_seq && ack <= dev->unack_seq) { + printk(KERN_DEBUG + "layer 2 ack unacceptable - dev %d", + dev->id); + pcbit_l2_error(dev); + } + /* ack is acceptable */ + + + i = dev->unack_seq; + + do { + dev->unack_seq = i = (i + 1) % 8; + dev->free += dev->fsize[i]; + } while (i != ack); + + count = 0; + while (count < 7 && dev->write_queue) { u8 lsend_seq = dev->send_seq; - pcbit_transmit(dev); + pcbit_transmit(dev); if (dev->send_seq == lsend_seq) break; - count++; - } - } - else - printk(KERN_DEBUG "recv_ack: unacked = 0\n"); + count++; + } + } else + printk(KERN_DEBUG "recv_ack: unacked = 0\n"); } diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/pcbit/module.c linux/drivers/isdn/pcbit/module.c --- v2.0.30/linux/drivers/isdn/pcbit/module.c Tue May 14 23:09:00 1996 +++ linux/drivers/isdn/pcbit/module.c Mon Aug 4 17:34:01 1997 @@ -22,8 +22,8 @@ #include #include "pcbit.h" -int mem[MAX_PCBIT_CARDS] = {0, }; -int irq[MAX_PCBIT_CARDS] = {0, }; +static int mem[MAX_PCBIT_CARDS] = {0, }; +static int irq[MAX_PCBIT_CARDS] = {0, }; int num_boards; struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS] = {0, 0, 0, 0}; @@ -35,6 +35,10 @@ extern int pcbit_init_dev(int board, int mem_base, int irq); #ifdef MODULE +#if (LINUX_VERSION_CODE > 0x020111) +MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_PCBIT_CARDS) "i"); +MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_PCBIT_CARDS) "i"); +#endif #define pcbit_init init_module #endif @@ -83,7 +87,11 @@ } /* No symbols to export, hide all symbols */ +#if (LINUX_VERSION_CODE < 0x020111) register_symtab(NULL); +#else + EXPORT_NO_SYMBOLS; +#endif return 0; } diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/Makefile linux/drivers/isdn/sc/Makefile --- v2.0.30/linux/drivers/isdn/sc/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/Makefile Mon Aug 4 17:34:01 1997 @@ -0,0 +1,44 @@ +# +# $Id: Makefile,v 1.1 1997/03/22 02:01:22 fritz Exp $ +# Copyright (C) 1996 SpellCaster Telecommunications 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. +# +# 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. +# +# For more information, please contact gpl-info@spellcast.com or write: +# +# SpellCaster Telecommunications Inc. +# 5621 Finch Avenue East, Unit #3 +# Scarborough, Ontario Canada +# M1B 2T9 +# +1 (416) 297-8565 +# +1 (416) 297-6433 Facsimile +# + +L_OBJS := +M_OBJS := +O_OBJS := shmem.o init.o debug.o packet.o command.o event.o \ + ioctl.o interrupt.o message.o timer.o + +O_TARGET := +ifeq ($(CONFIG_ISDN_DRV_SC),y) + O_TARGET += sc.o +else + ifeq ($(CONFIG_ISDN_DRV_SC),m) + O_TARGET += sc.o + M_OBJS += sc.o + endif +endif + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/card.h linux/drivers/isdn/sc/card.h --- v2.0.30/linux/drivers/isdn/sc/card.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/card.h Mon Aug 4 17:34:01 1997 @@ -0,0 +1,110 @@ +/* + * $Id: card.h,v 1.1 1996/11/07 13:07:40 fritz Exp $ + * Copyright (C) 1996 SpellCaster Telecommunications Inc. + * + * card.h - Driver parameters for SpellCaster ISA ISDN adapters + * + * 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. + * + * For more information, please contact gpl-info@spellcast.com or write: + * + * SpellCaster Telecommunications Inc. + * 5621 Finch Avenue East, Unit #3 + * Scarborough, Ontario Canada + * M1B 2T9 + * +1 (416) 297-8565 + * +1 (416) 297-6433 Facsimile + */ + +#ifndef CARD_H +#define CARD_H + +/* + * We need these if they're not already included + */ +#include +#include +#include "message.h" + +/* + * Amount of time to wait for a reset to complete + */ +#define CHECKRESET_TIME milliseconds(4000) + +/* + * Amount of time between line status checks + */ +#define CHECKSTAT_TIME milliseconds(8000) + +/* + * The maximum amount of time to wait for a message response + * to arrive. Use exclusively by send_and_receive + */ +#define SAR_TIMEOUT milliseconds(10000) + +/* + * Macro to determine is a card id is valid + */ +#define IS_VALID_CARD(x) ((x >= 0) && (x <= cinst)) + +/* + * Per channel status and configuration + */ +typedef struct { + int l2_proto; + int l3_proto; + char dn[50]; + unsigned long first_sendbuf; /* Offset of first send buffer */ + unsigned int num_sendbufs; /* Number of send buffers */ + unsigned int free_sendbufs; /* Number of free sendbufs */ + unsigned int next_sendbuf; /* Next sequential buffer */ + char eazlist[50]; /* Set with SETEAZ */ + char sillist[50]; /* Set with SETSIL */ + int eazclear; /* Don't accept calls if TRUE */ +} bchan; + +/* + * Everything you want to know about the adapter ... + */ +typedef struct { + int model; + int driverId; /* LL Id */ + char devicename[20]; /* The device name */ + isdn_if *card; /* ISDN4Linux structure */ + bchan *channel; /* status of the B channels */ + char nChannels; /* Number of channels */ + unsigned int interrupt; /* Interrupt number */ + int iobase; /* I/O Base address */ + int ioport[MAX_IO_REGS]; /* Index to I/O ports */ + int shmem_pgport; /* port for the exp mem page reg. */ + int shmem_magic; /* adapter magic number */ + unsigned int rambase; /* Shared RAM base address */ + unsigned int ramsize; /* Size of shared memory */ + RspMessage async_msg; /* Async response message */ + int want_async_messages; /* Snoop the Q ? */ + unsigned char seq_no; /* Next send seq. number */ + struct timer_list reset_timer; /* Check reset timer */ + struct timer_list stat_timer; /* Check startproc timer */ + unsigned char nphystat; /* Latest PhyStat info */ + unsigned char phystat; /* Last PhyStat info */ + HWConfig_pl hwconfig; /* Hardware config info */ + char load_ver[11]; /* CommManage Version string */ + char proc_ver[11]; /* CommEngine Version */ + int StartOnReset; /* Indicates startproc after reset */ + int EngineUp; /* Indicates CommEngine Up */ + int trace_mode; /* Indicate if tracing is on */ +} board; + +#endif /* CARD_H */ diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/command.c linux/drivers/isdn/sc/command.c --- v2.0.30/linux/drivers/isdn/sc/command.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/command.c Mon Aug 4 17:34:01 1997 @@ -0,0 +1,563 @@ +/* + * $Id: command.c,v 1.4 1997/03/30 16:51:34 calle Exp $ + * Copyright (C) 1996 SpellCaster Telecommunications 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. + * + * 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. + * + * For more information, please contact gpl-info@spellcast.com or write: + * + * SpellCaster Telecommunications Inc. + * 5621 Finch Avenue East, Unit #3 + * Scarborough, Ontario Canada + * M1B 2T9 + * +1 (416) 297-8565 + * +1 (416) 297-6433 Facsimile + */ + +#define __NO_VERSION__ +#include "includes.h" /* This must be first */ +#include "hardware.h" +#include "message.h" +#include "card.h" +#include "scioc.h" + +int dial(int card, unsigned long channel, setup_parm setup); +int hangup(int card, unsigned long channel); +int answer(int card, unsigned long channel); +int clreaz(int card, unsigned long channel); +int seteaz(int card, unsigned long channel, char *); +int geteaz(int card, unsigned long channel, char *); +int setsil(int card, unsigned long channel, char *); +int getsil(int card, unsigned long channel, char *); +int setl2(int card, unsigned long arg); +int getl2(int card, unsigned long arg); +int setl3(int card, unsigned long arg); +int getl3(int card, unsigned long arg); +int lock(void); +int unlock(void); +int acceptb(int card, unsigned long channel); + +extern int cinst; +extern board *adapter[]; + +extern int sc_ioctl(int, scs_ioctl *); +extern int setup_buffers(int, int, unsigned int); +extern int indicate_status(int, int,ulong,char*); +extern void check_reset(unsigned long); +extern int send_and_receive(int, unsigned int, unsigned char, unsigned char, + unsigned char, unsigned char, unsigned char, unsigned char *, + RspMessage *, int); +extern int sendmessage(int, unsigned int, unsigned int, unsigned int, + unsigned int, unsigned int, unsigned int, unsigned int *); +extern inline void pullphone(char *, char *); + +#ifdef DEBUG +/* + * Translate command codes to strings + */ +static char *commands[] = { "ISDN_CMD_IOCTL", + "ISDN_CMD_DIAL", + "ISDN_CMD_ACCEPTB", + "ISDN_CMD_ACCEPTB", + "ISDN_CMD_HANGUP", + "ISDN_CMD_CLREAZ", + "ISDN_CMD_SETEAZ", + "ISDN_CMD_GETEAZ", + "ISDN_CMD_SETSIL", + "ISDN_CMD_GETSIL", + "ISDN_CMD_SETL2", + "ISDN_CMD_GETL2", + "ISDN_CMD_SETL3", + "ISDN_CMD_GETL3", + "ISDN_CMD_LOCK", + "ISDN_CMD_UNLOCK", + "ISDN_CMD_SUSPEND", + "ISDN_CMD_RESUME" }; + +/* + * Translates ISDN4Linux protocol codes to strings for debug messages + */ +static char *l3protos[] = { "ISDN_PROTO_L3_TRANS" }; +static char *l2protos[] = { "ISDN_PROTO_L2_X75I", + "ISDN_PROTO_L2_X75UI", + "ISDN_PROTO_L2_X75BUI", + "ISDN_PROTO_L2_HDLC", + "ISDN_PROTO_L2_TRANS" }; +#endif + +int get_card_from_id(int driver) +{ + int i; + + for(i = 0 ; i < cinst ; i++) { + if(adapter[i]->driverId == driver) + return i; + } + return -NODEV; +} + +/* + * command + */ + +int command(isdn_ctrl *cmd) +{ + int card; + + card = get_card_from_id(cmd->driver); + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + pr_debug("%s: Received %s command from Link Layer\n", + adapter[card]->devicename, commands[cmd->command]); + + /* + * Dispatch the command + */ + switch(cmd->command) { + case ISDN_CMD_IOCTL: + { + unsigned long cmdptr; + scs_ioctl ioc; + int err; + + memcpy(&cmdptr, cmd->parm.num, sizeof(unsigned long)); + if((err = copy_from_user(&ioc, (scs_ioctl *) cmdptr, + sizeof(scs_ioctl)))) { + pr_debug("%s: Failed to verify user space 0x%x\n", + adapter[card]->devicename, cmdptr); + return err; + } + return sc_ioctl(card, &ioc); + } + case ISDN_CMD_DIAL: + return dial(card, cmd->arg, cmd->parm.setup); + case ISDN_CMD_HANGUP: + return hangup(card, cmd->arg); + case ISDN_CMD_ACCEPTD: + return answer(card, cmd->arg); + case ISDN_CMD_ACCEPTB: + return acceptb(card, cmd->arg); + case ISDN_CMD_CLREAZ: + return clreaz(card, cmd->arg); + case ISDN_CMD_SETEAZ: + return seteaz(card, cmd->arg, cmd->parm.num); + case ISDN_CMD_GETEAZ: + return geteaz(card, cmd->arg, cmd->parm.num); + case ISDN_CMD_SETSIL: + return setsil(card, cmd->arg, cmd->parm.num); + case ISDN_CMD_GETSIL: + return getsil(card, cmd->arg, cmd->parm.num); + case ISDN_CMD_SETL2: + return setl2(card, cmd->arg); + case ISDN_CMD_GETL2: + return getl2(card, cmd->arg); + case ISDN_CMD_SETL3: + return setl3(card, cmd->arg); + case ISDN_CMD_GETL3: + return getl3(card, cmd->arg); + case ISDN_CMD_LOCK: + return lock(); + case ISDN_CMD_UNLOCK: + return unlock(); + default: + return -EINVAL; + } + return 0; +} + +/* + * Confirm our ability to communicate with the board. This test assumes no + * other message activity is present + */ +int loopback(int card) +{ + + int status; + static char testmsg[] = "Test Message"; + RspMessage rspmsg; + + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + pr_debug("%s: Sending loopback message\n", adapter[card]->devicename); + + + /* + * Send the loopback message to confirm that memory transfer is + * operational + */ + status = send_and_receive(card, CMPID, cmReqType1, + cmReqClass0, + cmReqMsgLpbk, + 0, + (unsigned char) strlen(testmsg), + (unsigned char *)testmsg, + &rspmsg, SAR_TIMEOUT); + + + if (!status) { + pr_debug("%s: Loopback message successfully sent\n", + adapter[card]->devicename); + if(strcmp(rspmsg.msg_data.byte_array, testmsg)) { + pr_debug("%s: Loopback return != sent\n", + adapter[card]->devicename); + return -EIO; + } + return 0; + } + else { + pr_debug("%s: Send loopback message failed\n", + adapter[card]->devicename); + return -EIO; + } + +} + +/* + * start the onboard firmware + */ +int startproc(int card) +{ + int status; + + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + /* + * send start msg + */ + status = sendmessage(card, CMPID,cmReqType2, + cmReqClass0, + cmReqStartProc, + 0,0,0); + pr_debug("%s: Sent startProc\n", adapter[card]->devicename); + + return status; +} + + +int loadproc(int card, char *data) +{ + return -1; +} + + +/* + * Dials the number passed in + */ +int dial(int card, unsigned long channel, setup_parm setup) +{ + int status; + char Phone[48]; + + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + /*extract ISDN number to dial from eaz/msn string*/ + strcpy(Phone,setup.phone); + + /*send the connection message*/ + status = sendmessage(card, CEPID,ceReqTypePhy, + ceReqClass1, + ceReqPhyConnect, + (unsigned char) channel+1, + strlen(Phone), + (unsigned int *) Phone); + + pr_debug("%s: Dialing %s on channel %d\n", + adapter[card]->devicename, Phone, channel+1); + + return status; +} + +/* + * Answer an incoming call + */ +int answer(int card, unsigned long channel) +{ + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + if(setup_buffers(card, channel+1, BUFFER_SIZE)) { + hangup(card, channel+1); + return -ENOBUFS; + } + + indicate_status(card, ISDN_STAT_BCONN,channel,NULL); + pr_debug("%s: Answered incoming call on channel %s\n", + adapter[card]->devicename, channel+1); + return 0; +} + +/* + * Hangup up the call on specified channel + */ +int hangup(int card, unsigned long channel) +{ + int status; + + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + status = sendmessage(card, CEPID, ceReqTypePhy, + ceReqClass1, + ceReqPhyDisconnect, + (unsigned char) channel+1, + 0, + NULL); + pr_debug("%s: Sent HANGUP message to channel %d\n", + adapter[card]->devicename, channel+1); + return status; +} + +/* + * Set the layer 2 protocol (X.25, HDLC, Raw) + */ +int setl2(int card, unsigned long arg) +{ + int status =0; + int protocol,channel; + + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + protocol = arg >> 8; + channel = arg & 0xff; + adapter[card]->channel[channel].l2_proto = protocol; + pr_debug("%s: Level 2 protocol for channel %d set to %s from %d\n", + adapter[card]->devicename, channel+1,l2protos[adapter[card]->channel[channel].l2_proto],protocol); + + /* + * check that the adapter is also set to the correct protocol + */ + pr_debug("%s: Sending GetFrameFormat for channel %d\n", + adapter[card]->devicename, channel+1); + status = sendmessage(card, CEPID, ceReqTypeCall, + ceReqClass0, + ceReqCallGetFrameFormat, + (unsigned char)channel+1, + 1, + (unsigned int *) protocol); + if(status) + return status; + return 0; +} + +/* + * Get the layer 2 protocol + */ +int getl2(int card, unsigned long channel) { + + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + pr_debug("%s: Level 2 protocol for channel %d reported as %s\n", + adapter[card]->devicename, channel+1, + l2protos[adapter[card]->channel[channel].l2_proto]); + + return adapter[card]->channel[channel].l2_proto; +} + +/* + * Set the layer 3 protocol + */ +int setl3(int card, unsigned long channel) +{ + int protocol = channel >> 8; + + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + adapter[card]->channel[channel].l3_proto = protocol; + pr_debug("%s: Level 3 protocol for channel %d set to %s\n", + adapter[card]->devicename, channel+1, l3protos[protocol]); + return 0; +} + +/* + * Get the layer 3 protocol + */ +int getl3(int card, unsigned long arg) +{ + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + pr_debug("%s: Level 3 protocol for channel %d reported as %s\n", + adapter[card]->devicename, arg+1, + l3protos[adapter[card]->channel[arg].l3_proto]); + return adapter[card]->channel[arg].l3_proto; +} + + +int acceptb(int card, unsigned long channel) +{ + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + if(setup_buffers(card, channel+1, BUFFER_SIZE)) + { + hangup(card, channel+1); + return -ENOBUFS; + } + + pr_debug("%s: B-Channel connection accepted on channel %d\n", + adapter[card]->devicename, channel+1); + indicate_status(card, ISDN_STAT_BCONN, channel, NULL); + return 0; +} + +int clreaz(int card, unsigned long arg) +{ + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + strcpy(adapter[card]->channel[arg].eazlist, ""); + adapter[card]->channel[arg].eazclear = 1; + pr_debug("%s: EAZ List cleared for channel %d\n", + adapter[card]->devicename, arg+1); + return 0; +} + +int seteaz(int card, unsigned long arg, char *num) +{ + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + strcpy(adapter[card]->channel[arg].eazlist, num); + adapter[card]->channel[arg].eazclear = 0; + pr_debug("%s: EAZ list for channel %d set to: %s\n", + adapter[card]->devicename, arg+1, + adapter[card]->channel[arg].eazlist); + return 0; +} + +int geteaz(int card, unsigned long arg, char *num) +{ + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + strcpy(num, adapter[card]->channel[arg].eazlist); + pr_debug("%s: EAZ List for channel %d reported: %s\n", + adapter[card]->devicename, arg+1, + adapter[card]->channel[arg].eazlist); + return 0; +} + +int setsil(int card, unsigned long arg, char *num) +{ + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + strcpy(adapter[card]->channel[arg].sillist, num); + pr_debug("%s: Service Indicators for channel %d set: %s\n", + adapter[card]->devicename, arg+1, + adapter[card]->channel[arg].sillist); + return 0; +} + +int getsil(int card, unsigned long arg, char *num) +{ + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + strcpy(num, adapter[card]->channel[arg].sillist); + pr_debug("%s: SIL for channel %d reported: %s\n", + adapter[card]->devicename, arg+1, + adapter[card]->channel[arg].sillist); + return 0; +} + + +int lock() +{ + MOD_INC_USE_COUNT; + return 0; +} + +int unlock() +{ + MOD_DEC_USE_COUNT; + return 0; +} + +int reset(int card) +{ + unsigned long flags; + + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + indicate_status(card, ISDN_STAT_STOP, 0, NULL); + + if(adapter[card]->EngineUp) { + del_timer(&adapter[card]->stat_timer); + } + + adapter[card]->EngineUp = 0; + + save_flags(flags); + cli(); + init_timer(&adapter[card]->reset_timer); + adapter[card]->reset_timer.function = check_reset; + adapter[card]->reset_timer.data = card; + adapter[card]->reset_timer.expires = jiffies + CHECKRESET_TIME; + add_timer(&adapter[card]->reset_timer); + restore_flags(flags); + + outb(0x1,adapter[card]->ioport[SFT_RESET]); + + pr_debug("%s: Adapter Reset\n", adapter[card]->devicename); + return 0; +} + +void flushreadfifo (int card) +{ + while(inb(adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA) + inb(adapter[card]->ioport[FIFO_READ]); +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/debug.c linux/drivers/isdn/sc/debug.c --- v2.0.30/linux/drivers/isdn/sc/debug.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/debug.c Mon Aug 4 17:34:01 1997 @@ -0,0 +1,80 @@ +/* + * $Id: debug.c,v 1.2 1996/11/20 17:49:50 fritz Exp $ + * Copyright (C) 1996 SpellCaster Telecommunications 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. + * + * 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. + * + * For more information, please contact gpl-info@spellcast.com or write: + * + * SpellCaster Telecommunications Inc. + * 5621 Finch Avenue East, Unit #3 + * Scarborough, Ontario Canada + * M1B 2T9 + * +1 (416) 297-8565 + * +1 (416) 297-6433 Facsimile + */ +#include + +#define NULL 0x0 + +#if LINUX_VERSION_CODE < 66363 /* Linux 1.3.59 there was a change to interrupts */ + #define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d) + #define FREE_IRQ(a,b) free_irq(a) +#else + #define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e) + #define FREE_IRQ(a,b) free_irq(a,b) +#endif + +inline char *strcpy(char *, const char *); + +int dbg_level = 0; +static char dbg_funcname[255]; + +void dbg_endfunc(void) +{ + if (dbg_level) { + printk("<-- Leaving function %s\n", dbg_funcname); + strcpy(dbg_funcname, ""); + } +} + +void dbg_func(char *func) +{ + strcpy(dbg_funcname, func); + if(dbg_level) + printk("--> Entering function %s\n", dbg_funcname); +} + +inline char *strcpy(char *dest, const char *src) +{ + char *i = dest; + char *j = (char *) src; + + while(*j) { + *i = *j; + i++; j++; + } + *(++i) = NULL; + return dest; +} + +inline void pullphone(char *dn, char *str) +{ + int i = 0; + + while(dn[i] != ',') + str[i] = dn[i++]; + str[i] = 0x0; +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/debug.h linux/drivers/isdn/sc/debug.h --- v2.0.30/linux/drivers/isdn/sc/debug.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/debug.h Mon Aug 4 17:34:01 1997 @@ -0,0 +1,35 @@ +/* + * $Id: debug.h,v 1.1 1996/11/07 13:07:42 fritz Exp $ + * Copyright (C) 1996 SpellCaster Telecommunications 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. + * + * 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. + * + * For more information, please contact gpl-info@spellcast.com or write: + * + * SpellCaster Telecommunications Inc. + * 5621 Finch Avenue East, Unit #3 + * Scarborough, Ontario Canada + * M1B 2T9 + * +1 (416) 297-8565 + * +1 (416) 297-6433 Facsimile + */ + +#if LINUX_VERSION_CODE < 131072 + #error You cant use this driver on kernels older than 2.0 +#else + #define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e) + #define FREE_IRQ(a,b) free_irq(a,b) +#endif + diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/event.c linux/drivers/isdn/sc/event.c --- v2.0.30/linux/drivers/isdn/sc/event.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/event.c Mon Aug 4 17:34:01 1997 @@ -0,0 +1,75 @@ +/* + * $Id: event.c,v 1.3 1997/02/11 22:53:41 fritz Exp $ + * Copyright (C) 1996 SpellCaster Telecommunications 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. + * + * 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. + * + * For more information, please contact gpl-info@spellcast.com or write: + * + * SpellCaster Telecommunications Inc. + * 5621 Finch Avenue East, Unit #3 + * Scarborough, Ontario Canada + * M1B 2T9 + * +1 (416) 297-8565 + * +1 (416) 297-6433 Facsimile + */ + +#define __NO_VERSION__ +#include "includes.h" +#include "hardware.h" +#include "message.h" +#include "card.h" + +extern int cinst; +extern board *adapter[]; + +#ifdef DEBUG +static char *events[] = { "ISDN_STAT_STAVAIL", + "ISDN_STAT_ICALL", + "ISDN_STAT_RUN", + "ISDN_STAT_STOP", + "ISDN_STAT_DCONN", + "ISDN_STAT_BCONN", + "ISDN_STAT_DHUP", + "ISDN_STAT_BHUP", + "ISDN_STAT_CINF", + "ISDN_STAT_LOAD", + "ISDN_STAT_UNLOAD", + "ISDN_STAT_BSENT", + "ISDN_STAT_NODCH", + "ISDN_STAT_ADDCH", + "ISDN_STAT_CAUSE" }; +#endif + +int indicate_status(int card, int event,ulong Channel,char *Data) +{ + isdn_ctrl cmd; + + pr_debug("%s: Indicating event %s on Channel %d\n", + adapter[card]->devicename, events[event-256], Channel); + if (Data != NULL){ + pr_debug("%s: Event data: %s\n", adapter[card]->devicename, + Data); + if (event == ISDN_STAT_ICALL) + memcpy(&cmd.parm.setup, Data, sizeof(cmd.parm.setup)); + else + strcpy(cmd.parm.num, Data); + } + + cmd.command = event; + cmd.driver = adapter[card]->driverId; + cmd.arg = Channel; + return adapter[card]->card->statcallb(&cmd); +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/hardware.h linux/drivers/isdn/sc/hardware.h --- v2.0.30/linux/drivers/isdn/sc/hardware.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/hardware.h Mon Aug 4 17:34:01 1997 @@ -0,0 +1,117 @@ +/* + * Hardware specific macros, defines and structures + */ + +#ifndef HARDWARE_H +#define HARDWARE_H + +#include /* For HZ */ + +/* + * General hardware parameters common to all ISA adapters + */ + +#define MAX_CARDS 4 /* The maximum number of cards to + control or probe for. If you change + this, you must also change the number + of elements in io, irq, and ram to + match. Initialized in init.c */ +/* +extern unsigned int io[]; +extern unsigned char irq[]; +extern unsigned long ram[]; +*/ + +#define SIGNATURE 0x87654321 /* Board reset signature */ +#define SIG_OFFSET 0x1004 /* Where to find signature in shared RAM */ +#define TRACE_OFFSET 0x1008 /* Trace enable word offset in shared RAM */ +#define BUFFER_OFFSET 0x1800 /* Beginning of buffers */ + +/* I/O Port parameters */ +#define IOBASE_MIN 0x180 /* Lowest I/O port address */ +#define IOBASE_MAX 0x3C0 /* Highest I/O port address */ +#define IOBASE_OFFSET 0x20 /* Inter-board I/O port gap used during + probing */ +#define FIFORD_OFFSET 0x0 +#define FIFOWR_OFFSET 0x400 +#define FIFOSTAT_OFFSET 0x1000 +#define RESET_OFFSET 0x2800 +#define PG0_OFFSET 0x3000 /* Offset from I/O Base for Page 0 register */ +#define PG1_OFFSET 0x3400 /* Offset from I/O Base for Page 1 register */ +#define PG2_OFFSET 0x3800 /* Offset from I/O Base for Page 2 register */ +#define PG3_OFFSET 0x3C00 /* Offset from I/O Base for Page 3 register */ + +#define FIFO_READ 0 /* FIFO Read register */ +#define FIFO_WRITE 1 /* FIFO Write rgister */ +#define LO_ADDR_PTR 2 /* Extended RAM Low Addr Pointer */ +#define HI_ADDR_PTR 3 /* Extended RAM High Addr Pointer */ +#define NOT_USED_1 4 +#define FIFO_STATUS 5 /* FIFO Status Register */ +#define NOT_USED_2 6 +#define MEM_OFFSET 7 +#define SFT_RESET 10 /* Reset Register */ +#define EXP_BASE 11 /* Shared RAM Base address */ +#define EXP_PAGE0 12 /* Shared RAM Page0 register */ +#define EXP_PAGE1 13 /* Shared RAM Page1 register */ +#define EXP_PAGE2 14 /* Shared RAM Page2 register */ +#define EXP_PAGE3 15 /* Shared RAM Page3 register */ +#define IRQ_SELECT 16 /* IRQ selection register */ +#define MAX_IO_REGS 17 /* Total number of I/O ports */ + +/* FIFO register values */ +#define RF_HAS_DATA 0x01 /* fifo has data */ +#define RF_QUART_FULL 0x02 /* fifo quarter full */ +#define RF_HALF_FULL 0x04 /* fifo half full */ +#define RF_NOT_FULL 0x08 /* fifo not full */ +#define WF_HAS_DATA 0x10 /* fifo has data */ +#define WF_QUART_FULL 0x20 /* fifo quarter full */ +#define WF_HALF_FULL 0x40 /* fifo half full */ +#define WF_NOT_FULL 0x80 /* fifo not full */ + +/* Shared RAM parameters */ +#define SRAM_MIN 0xC0000 /* Lowest host shared RAM address */ +#define SRAM_MAX 0xEFFFF /* Highest host shared RAM address */ +#define SRAM_PAGESIZE 0x4000 /* Size of one RAM page (16K) */ + +/* Shared RAM buffer parameters */ +#define BUFFER_SIZE 0x800 /* The size of a buffer in bytes */ +#define BUFFER_BASE BUFFER_OFFSET /* Offset from start of shared RAM + where buffer start */ +#define BUFFERS_MAX 16 /* Maximum number of send/receive + buffers per channel */ +#define HDLC_PROTO 0x01 /* Frame Format for Layer 2 */ + +#define BRI_BOARD 0 +#define POTS_BOARD 1 +#define PRI_BOARD 2 + +/* + * Specific hardware parameters for the DataCommute/BRI + */ +#define BRI_CHANNELS 2 /* Number of B channels */ +#define BRI_BASEPG_VAL 0x98 +#define BRI_MAGIC 0x60000 /* Magic Number */ +#define BRI_MEMSIZE 0x10000 /* Ammount of RAM (64K) */ +#define BRI_PARTNO "72-029" +#define BRI_FEATURES ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS; +/* + * Specific hardware parameters for the DataCommute/PRI + */ +#define PRI_CHANNELS 23 /* Number of B channels */ +#define PRI_BASEPG_VAL 0x88 +#define PRI_MAGIC 0x20000 /* Magic Number */ +#define PRI_MEMSIZE 0x100000 /* Amount of RAM (1M) */ +#define PRI_PARTNO "72-030" +#define PRI_FEATURES ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS; + +/* + * Some handy macros + */ + +/* Return the number of jiffies in a given number of msecs */ +#define milliseconds(x) (x/(1000/HZ)) + +/* Determine if a channel number is valid for the adapter */ +#define IS_VALID_CHANNEL(y,x) ((x>0) && (x <= adapter[y]->channels)) + +#endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/includes.h linux/drivers/isdn/sc/includes.h --- v2.0.30/linux/drivers/isdn/sc/includes.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/includes.h Mon Aug 4 17:34:01 1997 @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "debug.h" diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/init.c linux/drivers/isdn/sc/init.c --- v2.0.30/linux/drivers/isdn/sc/init.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/init.c Mon Aug 4 17:34:01 1997 @@ -0,0 +1,599 @@ +#include "includes.h" +#include "hardware.h" +#include "card.h" + +board *adapter[MAX_CARDS]; +int cinst; + +static char devname[] = "scX"; +const char version[] = "2.0b1"; + +const char *boardname[] = { "DataCommute/BRI", "DataCommute/PRI", "TeleCommute/BRI" }; + +/* insmod set parameters */ +unsigned int io[] = {0,0,0,0}; +unsigned char irq[] = {0,0,0,0}; +unsigned long ram[] = {0,0,0,0}; +int do_reset = 0; + +static int sup_irq[] = { 11, 10, 9, 5, 12, 14, 7, 3, 4, 6 }; +#define MAX_IRQS 10 + +extern void interrupt_handler(int, void *, struct pt_regs *); +extern int sndpkt(int, int, struct sk_buff *); +extern int command(isdn_ctrl *); +extern int indicate_status(int, int, ulong, char*); +extern int reset(int); + +int identify_board(unsigned long, unsigned int); + +int irq_supported(int irq_x) +{ + int i; + for(i=0 ; i < MAX_IRQS ; i++) { + if(sup_irq[i] == irq_x) + return 1; + } + return 0; +} + +#ifdef MODULE +#if (LINUX_VERSION_CODE > 0x020111) +MODULE_PARM(io, "1-4i"); +MODULE_PARM(irq, "1-4i"); +MODULE_PARM(ram, "1-4i"); +MODULE_PARM(do_reset, "i"); +#endif +#define init_sc init_module +#else +/* +Initialization code for non-module version to be included + +void sc_setup(char *str, int *ints) +{ +} +*/ +#endif + +int init_sc(void) +{ + int b = -1; + int i, j; + int status = -ENODEV; + + unsigned long memsize = 0; + unsigned long features = 0; + isdn_if *interface; + unsigned char channels; + unsigned char pgport; + unsigned long magic; + int model; + int last_base = IOBASE_MIN; + int probe_exhasted = 0; + +#ifdef MODULE + pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s Loaded\n", version); +#else + pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s\n", version); +#endif + pr_info("Copyright (C) 1996 SpellCaster Telecommunications Inc.\n"); + + while(b++ < MAX_CARDS - 1) { + pr_debug("Probing for adapter #%d\n", b); + /* + * Initialize reusable variables + */ + model = -1; + magic = 0; + channels = 0; + pgport = 0; + + /* + * See if we should probe for IO base + */ + pr_debug("I/O Base for board %d is 0x%x, %s probe\n", b, io[b], + io[b] == 0 ? "will" : "won't"); + if(io[b]) { + /* + * No, I/O Base has been provided + */ + for (i = 0 ; i < MAX_IO_REGS - 1 ; i++) { + if(check_region(io[b] + i * 0x400, 1)) { + pr_debug("check_region for 0x%x failed\n", io[b] + i * 0x400); + io[b] = 0; + break; + } + } + + /* + * Confirm the I/O Address with a test + */ + if(io[b] == 0) { + pr_debug("I/O Address 0x%x is in use.\n"); + continue; + } + + outb(0x18, io[b] + 0x400 * EXP_PAGE0); + if(inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) { + pr_debug("I/O Base 0x%x fails test\n"); + continue; + } + } + else { + /* + * Yes, probe for I/O Base + */ + if(probe_exhasted) { + pr_debug("All probe addresses exhasted, skipping\n"); + continue; + } + pr_debug("Probing for I/O...\n"); + for (i = last_base ; i <= IOBASE_MAX ; i += IOBASE_OFFSET) { + int found_io = 1; + if (i == IOBASE_MAX) { + probe_exhasted = 1; /* No more addresses to probe */ + pr_debug("End of Probes\n"); + } + last_base = i + IOBASE_OFFSET; + pr_debug(" checking 0x%x...", i); + for ( j = 0 ; j < MAX_IO_REGS - 1 ; j++) { + if(check_region(i + j * 0x400, 1)) { + pr_debug("Failed\n"); + found_io = 0; + break; + } + } + + if(found_io) { + io[b] = i; + outb(0x18, io[b] + 0x400 * EXP_PAGE0); + if(inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) { + pr_debug("Failed by test\n"); + continue; + } + pr_debug("Passed\n"); + break; + } + } + if(probe_exhasted) { + continue; + } + } + + /* + * See if we should probe for shared RAM + */ + if(do_reset) { + pr_debug("Doing a SAFE probe reset\n"); + outb(0xFF, io[b] + RESET_OFFSET); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + milliseconds(10000); + schedule(); + } + pr_debug("RAM Base for board %d is 0x%x, %s probe\n", b, ram[b], + ram[b] == 0 ? "will" : "won't"); + + if(ram[b]) { + /* + * No, the RAM base has been provided + * Just look for a signature and ID the + * board model + */ + if(!check_region(ram[b], SRAM_PAGESIZE)) { + pr_debug("check_region for RAM base 0x%x succeeded\n", ram[b]); + model = identify_board(ram[b], io[b]); + } + } + else { + /* + * Yes, probe for free RAM and look for + * a signature and id the board model + */ + for (i = SRAM_MIN ; i < SRAM_MAX ; i += SRAM_PAGESIZE) { + pr_debug("Checking RAM address 0x%x...\n", i); + if(!check_region(i, SRAM_PAGESIZE)) { + pr_debug(" check_region succeeded\n"); + model = identify_board(i, io[b]); + if (model >= 0) { + pr_debug(" Identified a %s\n", + boardname[model]); + ram[b] = i; + break; + } + pr_debug(" Unidentifed or inaccessible\n"); + continue; + } + pr_debug(" check_region failed\n"); + } + } + /* + * See if we found free RAM and the board model + */ + if(!ram[b] || model < 0) { + /* + * Nope, there was no place in RAM for the + * board, or it couldn't be identified + */ + pr_debug("Failed to find an adapter at 0x%x\n", ram[b]); + continue; + } + + /* + * Set the board's magic number, memory size and page register + */ + switch(model) { + case PRI_BOARD: + channels = 23; + magic = 0x20000; + memsize = 0x100000; + features = PRI_FEATURES; + break; + + case BRI_BOARD: + case POTS_BOARD: + channels = 2; + magic = 0x60000; + memsize = 0x10000; + features = BRI_FEATURES; + break; + } + switch(ram[b] >> 12 & 0x0F) { + case 0x0: + pr_debug("RAM Page register set to EXP_PAGE0\n"); + pgport = EXP_PAGE0; + break; + + case 0x4: + pr_debug("RAM Page register set to EXP_PAGE1\n"); + pgport = EXP_PAGE1; + break; + + case 0x8: + pr_debug("RAM Page register set to EXP_PAGE2\n"); + pgport = EXP_PAGE2; + break; + + case 0xC: + pr_debug("RAM Page register set to EXP_PAGE3\n"); + pgport = EXP_PAGE3; + break; + + default: + pr_debug("RAM base address doesn't fall on 16K boundary\n"); + continue; + } + + pr_debug("current IRQ: %d b: %d\n",irq[b],b); + /* + * See if we should probe for an irq + */ + if(irq[b]) { + /* + * No we were given one + * See that it is supported and free + */ + pr_debug("Trying for IRQ: %d\n",irq[b]); + if (irq_supported(irq[b])) { + if(REQUEST_IRQ(irq[b], interrupt_handler, + SA_PROBE, "sc_probe", NULL)) { + pr_debug("IRQ %d is already in use\n", + irq[b]); + continue; + } + FREE_IRQ(irq[b], NULL); + } + } + else { + /* + * Yes, we need to probe for an IRQ + */ + pr_debug("Probing for IRQ...\n"); + for (i = 0; i < MAX_IRQS ; i++) { + if(!REQUEST_IRQ(sup_irq[i], interrupt_handler, SA_PROBE, "sc_probe", NULL)) { + pr_debug("Probed for and found IRQ %d\n", sup_irq[i]); + FREE_IRQ(sup_irq[i], NULL); + irq[b] = sup_irq[i]; + break; + } + } + } + + /* + * Make sure we got an IRQ + */ + if(!irq[b]) { + /* + * No interrupt could be used + */ + pr_debug("Failed to aquire an IRQ line\n"); + continue; + } + + /* + * Horray! We found a board, Make sure we can register + * it with ISDN4Linux + */ + interface = kmalloc(sizeof(isdn_if), GFP_KERNEL); + if (interface == NULL) { + /* + * Oops, can't malloc isdn_if + */ + continue; + } + memset(interface, 0, sizeof(isdn_if)); + + interface->hl_hdrlen = 0; + interface->channels = channels; + interface->maxbufsize = BUFFER_SIZE; + interface->features = features; + interface->writebuf_skb = sndpkt; + interface->writecmd = NULL; + interface->command = command; + strcpy(interface->id, devname); + interface->id[2] = '0' + cinst; + + /* + * Allocate the board structure + */ + adapter[cinst] = kmalloc(sizeof(board), GFP_KERNEL); + if (adapter[cinst] == NULL) { + /* + * Oops, can't alloc memory for the board + */ + kfree(interface); + continue; + } + memset(adapter[cinst], 0, sizeof(board)); + + if(!register_isdn(interface)) { + /* + * Oops, couldn't register for some reason + */ + kfree(interface); + kfree(adapter[cinst]); + continue; + } + + adapter[cinst]->card = interface; + adapter[cinst]->driverId = interface->channels; + strcpy(adapter[cinst]->devicename, interface->id); + adapter[cinst]->nChannels = channels; + adapter[cinst]->ramsize = memsize; + adapter[cinst]->shmem_magic = magic; + adapter[cinst]->shmem_pgport = pgport; + adapter[cinst]->StartOnReset = 1; + + /* + * Allocate channels status structures + */ + adapter[cinst]->channel = kmalloc(sizeof(bchan) * channels, GFP_KERNEL); + if (adapter[cinst]->channel == NULL) { + /* + * Oops, can't alloc memory for the channels + */ + indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL); /* Fix me */ + kfree(interface); + kfree(adapter[cinst]); + continue; + } + memset(adapter[cinst]->channel, 0, sizeof(bchan) * channels); + + /* + * Lock down the hardware resources + */ + adapter[cinst]->interrupt = irq[b]; + REQUEST_IRQ(adapter[cinst]->interrupt, interrupt_handler, SA_INTERRUPT, + interface->id, NULL); + adapter[cinst]->iobase = io[b]; + for(i = 0 ; i < MAX_IO_REGS - 1 ; i++) { + adapter[cinst]->ioport[i] = io[b] + i * 0x400; + request_region(adapter[cinst]->ioport[i], 1, interface->id); + pr_debug("Requesting I/O Port %#x\n", adapter[cinst]->ioport[i]); + } + adapter[cinst]->ioport[IRQ_SELECT] = io[b] + 0x2; + request_region(adapter[cinst]->ioport[IRQ_SELECT], 1, interface->id); + pr_debug("Requesting I/O Port %#x\n", adapter[cinst]->ioport[IRQ_SELECT]); + adapter[cinst]->rambase = ram[b]; + request_region(adapter[cinst]->rambase, SRAM_PAGESIZE, interface->id); + + pr_info(" %s (%d) - %s %d channels IRQ %d, I/O Base 0x%x, RAM Base 0x%lx\n", + adapter[cinst]->devicename, adapter[cinst]->driverId, + boardname[model], channels, irq[b], io[b], ram[b]); + + /* + * reset the adapter to put things in motion + */ + reset(cinst); + + cinst++; + status = 0; + } + if (status) + pr_info("Failed to find any adapters, driver unloaded\n"); + return status; +} + +#ifdef MODULE +void cleanup_module(void) +{ + int i, j; + + for(i = 0 ; i < cinst ; i++) { + pr_debug("Cleaning up after adapter %d\n", i); + /* + * kill the timers + */ + del_timer(&(adapter[i]->reset_timer)); + del_timer(&(adapter[i]->stat_timer)); + + /* + * Tell I4L we're toast + */ + indicate_status(i, ISDN_STAT_STOP, 0, NULL); + indicate_status(i, ISDN_STAT_UNLOAD, 0, NULL); + + /* + * Release shared RAM + */ + release_region(adapter[i]->rambase, SRAM_PAGESIZE); + + /* + * Release the IRQ + */ + FREE_IRQ(adapter[i]->interrupt, NULL); + + /* + * Reset for a clean start + */ + outb(0xFF, adapter[i]->ioport[SFT_RESET]); + + /* + * Release the I/O Port regions + */ + for(j = 0 ; j < MAX_IO_REGS - 1; j++) { + release_region(adapter[i]->ioport[j], 1); + pr_debug("Releasing I/O Port %#x\n", adapter[i]->ioport[j]); + } + release_region(adapter[i]->ioport[IRQ_SELECT], 1); + pr_debug("Releasing I/O Port %#x\n", adapter[i]->ioport[IRQ_SELECT]); + + /* + * Release any memory we alloced + */ + kfree(adapter[i]->channel); + kfree(adapter[i]->card); + kfree(adapter[i]); + } + pr_info("SpellCaster ISA ISDN Adapter Driver Unloaded.\n"); +} +#endif + +int identify_board(unsigned long rambase, unsigned int iobase) +{ + unsigned int pgport; + unsigned long sig; + DualPortMemory *dpm; + RspMessage rcvmsg; + ReqMessage sndmsg; + HWConfig_pl hwci; + int x; + + pr_debug("Attempting to identify adapter @ 0x%x io 0x%x\n", + rambase, iobase); + + /* + * Enable the base pointer + */ + outb(rambase >> 12, iobase + 0x2c00); + + switch(rambase >> 12 & 0x0F) { + case 0x0: + pgport = iobase + PG0_OFFSET; + pr_debug("Page Register offset is 0x%x\n", PG0_OFFSET); + break; + + case 0x4: + pgport = iobase + PG1_OFFSET; + pr_debug("Page Register offset is 0x%x\n", PG1_OFFSET); + break; + + case 0x8: + pgport = iobase + PG2_OFFSET; + pr_debug("Page Register offset is 0x%x\n", PG2_OFFSET); + break; + + case 0xC: + pgport = iobase + PG3_OFFSET; + pr_debug("Page Register offset is 0x%x\n", PG3_OFFSET); + break; + default: + pr_debug("Invalid rambase 0x%lx\n", rambase); + return -1; + } + + /* + * Try to identify a PRI card + */ + outb(PRI_BASEPG_VAL, pgport); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + HZ; + schedule(); + sig = readl(rambase + SIG_OFFSET); + pr_debug("Looking for a signature, got 0x%x\n", sig); +#if 0 +/* + * For Gary: + * If it's a timing problem, it should be gone with the above schedule() + * Another possible reason may be the missing volatile in the original + * code. readl() does this for us. + */ + printk(""); /* Hack! Doesn't work without this !!!??? */ +#endif + if(sig == SIGNATURE) + return PRI_BOARD; + + /* + * Try to identify a PRI card + */ + outb(BRI_BASEPG_VAL, pgport); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + HZ; + schedule(); + sig = readl(rambase + SIG_OFFSET); + pr_debug("Looking for a signature, got 0x%x\n", sig); +#if 0 + printk(""); /* Hack! Doesn't work without this !!!??? */ +#endif + if(sig == SIGNATURE) + return BRI_BOARD; + + return -1; + + /* + * Try to spot a card + */ + sig = readl(rambase + SIG_OFFSET); + pr_debug("Looking for a signature, got 0x%x\n", sig); + if(sig != SIGNATURE) + return -1; + + dpm = (DualPortMemory *) rambase; + + memset(&sndmsg, 0, MSG_LEN); + sndmsg.msg_byte_cnt = 3; + sndmsg.type = cmReqType1; + sndmsg.class = cmReqClass0; + sndmsg.code = cmReqHWConfig; + memcpy_toio(&(dpm->req_queue[dpm->req_head++]), &sndmsg, MSG_LEN); + outb(0, iobase + 0x400); + pr_debug("Sent HWConfig message\n"); + /* + * Wait for the response + */ + x = 0; + while((inb(iobase + FIFOSTAT_OFFSET) & RF_HAS_DATA) && x < 100) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + x++; + } + if(x == 100) { + pr_debug("Timeout waiting for response\n"); + return -1; + } + + memcpy_fromio(&rcvmsg, &(dpm->rsp_queue[dpm->rsp_tail]), MSG_LEN); + pr_debug("Got HWConfig response, status = 0x%x\n", rcvmsg.rsp_status); + memcpy(&hwci, &(rcvmsg.msg_data.HWCresponse), sizeof(HWConfig_pl)); + pr_debug("Hardware Config: Interface: %s, RAM Size: %d, Serial: %s\n" + " Part: %s, Rev: %s\n", + hwci.st_u_sense ? "S/T" : "U", hwci.ram_size, + hwci.serial_no, hwci.part_no, hwci.rev_no); + + if(!strncmp(PRI_PARTNO, hwci.part_no, 6)) + return PRI_BOARD; + if(!strncmp(BRI_PARTNO, hwci.part_no, 6)) + return BRI_BOARD; + + return -1; +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/interrupt.c linux/drivers/isdn/sc/interrupt.c --- v2.0.30/linux/drivers/isdn/sc/interrupt.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/interrupt.c Mon Aug 4 17:34:01 1997 @@ -0,0 +1,258 @@ +/* + * $Id: interrupt.c,v 1.3 1997/02/11 22:53:43 fritz Exp $ + * Copyright (C) 1996 SpellCaster Telecommunications 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. + * + * 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. + * + * For more information, please contact gpl-info@spellcast.com or write: + * + * SpellCaster Telecommunications Inc. + * 5621 Finch Avenue East, Unit #3 + * Scarborough, Ontario Canada + * M1B 2T9 + * +1 (416) 297-8565 + * +1 (416) 297-6433 Facsimile + */ + +#define __NO_VERSION__ +#include "includes.h" +#include "hardware.h" +#include "message.h" +#include "card.h" + +extern indicate_status(int, int, ulong, char *); +extern void check_phystat(unsigned long); +extern void dump_messages(int); +extern int receivemessage(int, RspMessage *); +extern int sendmessage(int, unsigned int, unsigned int, unsigned int, + unsigned int, unsigned int, unsigned int, unsigned int *); +extern void rcvpkt(int, RspMessage *); + +extern int cinst; +extern board *adapter[]; + +int get_card_from_irq(int irq) +{ + int i; + + for(i = 0 ; i < cinst ; i++) { + if(adapter[i]->interrupt == irq) + return i; + } + return -1; +} + +/* + * + */ +void interrupt_handler(int interrupt, void * cardptr, struct pt_regs *regs ) { + + RspMessage rcvmsg; + int channel; + int card; + + card = get_card_from_irq(interrupt); + + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return; + } + + pr_debug("%s: Entered Interrupt handler\n", adapter[card]->devicename); + + /* + * Pull all of the waiting messages off the response queue + */ + while (!receivemessage(card, &rcvmsg)) { + /* + * Push the message to the adapter structure for + * send_and_receive to snoop + */ + if(adapter[card]->want_async_messages) + memcpy(&(adapter[card]->async_msg), &rcvmsg, sizeof(RspMessage)); + + channel = (unsigned int) rcvmsg.phy_link_no; + + /* + * Trap Invalid request messages + */ + if(IS_CM_MESSAGE(rcvmsg, 0, 0, Invalid)) { + pr_debug("%s: Invalid request Message, rsp_status = %d\n", + adapter[card]->devicename, rcvmsg.rsp_status); + break; + } + + /* + * Check for a linkRead message + */ + if (IS_CE_MESSAGE(rcvmsg, Lnk, 1, Read)) + { + pr_debug("%s: Received packet 0x%x bytes long at 0x%x\n", + adapter[card]->devicename, + rcvmsg.msg_data.response.msg_len, + rcvmsg.msg_data.response.buff_offset); + rcvpkt(card, &rcvmsg); + continue; + + } + + /* + * Handle a write acknoledgement + */ + if(IS_CE_MESSAGE(rcvmsg, Lnk, 1, Write)) { + pr_debug("%s: Packet Send ACK on channel %d\n", adapter[card]->devicename, + rcvmsg.phy_link_no); + adapter[card]->channel[rcvmsg.phy_link_no-1].free_sendbufs++; + continue; + } + + /* + * Handle a connection message + */ + if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Connect)) + { + unsigned int callid; + setup_parm setup; + pr_debug("%s: Connect message: line %d: status %d: cause 0x%x\n", + adapter[card]->devicename, + rcvmsg.phy_link_no, + rcvmsg.rsp_status, + rcvmsg.msg_data.byte_array[2]); + + memcpy(&callid,rcvmsg.msg_data.byte_array,sizeof(int)); + if(callid>=0x8000 && callid<=0xFFFF) + { + pr_debug("%s: Got Dial-Out Rsp\n", adapter[card]->devicename); + indicate_status(card, ISDN_STAT_DCONN, + (unsigned long)rcvmsg.phy_link_no-1,NULL); + + } + else if(callid>=0x0000 && callid<=0x7FFF) + { + pr_debug("%s: Got Incomming Call\n", adapter[card]->devicename); + strcpy(setup.phone,&(rcvmsg.msg_data.byte_array[4])); + strcpy(setup.eazmsn,adapter[card]->channel[rcvmsg.phy_link_no-1].dn); + setup.si1 = 7; + setup.si2 = 0; + setup.plan = 0; + setup.screen = 0; + + indicate_status(card, ISDN_STAT_ICALL,(unsigned long)rcvmsg.phy_link_no-1,(char *)&setup); + indicate_status(card, ISDN_STAT_DCONN,(unsigned long)rcvmsg.phy_link_no-1,NULL); + } + continue; + } + + /* + * Handle a disconnection message + */ + if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Disconnect)) + { + pr_debug("%s: disconnect message: line %d: status %d: cause 0x%x\n", + adapter[card]->devicename, + rcvmsg.phy_link_no, + rcvmsg.rsp_status, + rcvmsg.msg_data.byte_array[2]); + + indicate_status(card, ISDN_STAT_BHUP,(unsigned long)rcvmsg.phy_link_no-1,NULL); + indicate_status(card, ISDN_STAT_DHUP,(unsigned long)rcvmsg.phy_link_no-1,NULL); + continue; + + } + + /* + * Handle a startProc engine up message + */ + if (IS_CM_MESSAGE(rcvmsg, 5, 0, MiscEngineUp)) { + pr_debug("%s: Received EngineUp message\n", adapter[card]->devicename); + adapter[card]->EngineUp = 1; + sendmessage(card, CEPID,ceReqTypeCall,ceReqClass0,ceReqCallGetMyNumber,1,0,NULL); + sendmessage(card, CEPID,ceReqTypeCall,ceReqClass0,ceReqCallGetMyNumber,2,0,NULL); + init_timer(&adapter[card]->stat_timer); + adapter[card]->stat_timer.function = check_phystat; + adapter[card]->stat_timer.data = card; + adapter[card]->stat_timer.expires = jiffies + CHECKSTAT_TIME; + add_timer(&adapter[card]->stat_timer); + continue; + } + + /* + * Start proc response + */ + if (IS_CM_MESSAGE(rcvmsg, 2, 0, StartProc)) { + pr_debug("%s: StartProc Response Status %d\n", adapter[card]->devicename, + rcvmsg.rsp_status); + continue; + } + + /* + * Handle a GetMyNumber Rsp + */ + if (IS_CE_MESSAGE(rcvmsg,Call,0,GetMyNumber)){ + strcpy(adapter[card]->channel[rcvmsg.phy_link_no-1].dn,rcvmsg.msg_data.byte_array); + continue; + } + + /* + * PhyStatus response + */ + if(IS_CE_MESSAGE(rcvmsg, Phy, 2, Status)) { + unsigned int b1stat, b2stat; + + /* + * Covert the message data to the adapter->phystat code + */ + b1stat = (unsigned int) rcvmsg.msg_data.byte_array[0]; + b2stat = (unsigned int) rcvmsg.msg_data.byte_array[1]; + + adapter[card]->nphystat = (b2stat >> 8) | b1stat; /* endian?? */ + pr_debug("%s: PhyStat is 0x%2x\n", adapter[card]->devicename, + adapter[card]->nphystat); + continue; + } + + + /* + * Handle a GetFramFormat + */ + if(IS_CE_MESSAGE(rcvmsg, Call, 0, GetFrameFormat)) { + if(rcvmsg.msg_data.byte_array[0] != HDLC_PROTO) { + unsigned int proto = HDLC_PROTO; + /* + * Set board format to HDLC if it wasn't already + */ + pr_debug("%s: current frame format: 0x%x, will change to HDLC\n", + adapter[card]->devicename, + rcvmsg.msg_data.byte_array[0]); + sendmessage(card, CEPID, ceReqTypeCall, + ceReqClass0, + ceReqCallSetFrameFormat, + (unsigned char) channel +1, + 1,&proto); + } + continue; + } + + /* + * Hmm... + */ + pr_debug("%s: Received unhandled message (%d,%d,%d) link %d\n", + adapter[card]->devicename, rcvmsg.type, rcvmsg.class, rcvmsg.code, + rcvmsg.phy_link_no); + + } /* while */ + + pr_debug("%s: Exiting Interrupt Handler\n", adapter[card]->devicename); +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/ioctl.c linux/drivers/isdn/sc/ioctl.c --- v2.0.30/linux/drivers/isdn/sc/ioctl.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/ioctl.c Mon Aug 4 17:34:01 1997 @@ -0,0 +1,513 @@ +#define __NO_VERSION__ +#include "includes.h" +#include "hardware.h" +#include "message.h" +#include "card.h" +#include "scioc.h" + +extern int indicate_status(int, int, unsigned long, char *); +extern int startproc(int); +extern int loadproc(int, char *record); +extern int reset(int); +extern int send_and_receive(int, unsigned int, unsigned char,unsigned char, + unsigned char,unsigned char, + unsigned char, unsigned char *, RspMessage *, int); + +extern board *adapter[]; + +#if 0 +static char *ChannelStates[] = { "Idle", "Ready", "Connecting", "Connected", "Disconnecting" }; +#endif + +int GetStatus(int card, boardInfo *); + +/* + * Process private IOCTL messages (typically from scctrl) + */ +int sc_ioctl(int card, scs_ioctl *data) +{ + switch(data->command) { + case SCIOCRESET: /* Perform a hard reset of the adapter */ + { + pr_debug("%s: SCIOCRESET: ioctl received\n", adapter[card]->devicename); + adapter[card]->StartOnReset = 0; + return (reset(card)); + } + + case SCIOCLOAD: + { + RspMessage rcvmsg; + char srec[SCIOC_SRECSIZE]; + int status, err; + + pr_debug("%s: SCIOLOAD: ioctl received\n", adapter[card]->devicename); + if(adapter[card]->EngineUp) { + pr_debug("%s: SCIOCLOAD: Command Failed, LoadProc while engine running.\n", + adapter[card]->devicename); + return -1; + } + + /* + * Get the SRec from user space + */ + if ((err = copy_from_user(srec, (char *) data->dataptr, sizeof(srec)))) + return err; + + status = send_and_receive(card, CMPID, cmReqType2, cmReqClass0, cmReqLoadProc, + 0, sizeof(srec), srec, &rcvmsg, SAR_TIMEOUT); + if(status) { + pr_debug("%s: SCIOCLOAD: Command Failed, status = %d\n", + adapter[card]->devicename, status); + return -1; + } + else { + pr_debug("%s: SCIOCLOAD: Command Sucessful\n", adapter[card]->devicename); + return 0; + } + } + + case SCIOCSTART: + { + pr_debug("%s: SCIOSTART: ioctl received\n", adapter[card]->devicename); + if(adapter[card]->EngineUp) { + pr_debug("%s: SCIOCSTART: Command Failed, Engine already running.\n", + adapter[card]->devicename); + return -1; + } + + adapter[card]->StartOnReset = 1; + startproc(card); + return 0; + } + + case SCIOCSETSWITCH: + { + RspMessage rcvmsg; + char switchtype; + int status, err; + + pr_debug("%s: SCIOSETSWITCH: ioctl received\n", adapter[card]->devicename); + + /* + * Get the switch type from user space + */ + if ((err = copy_from_user(&switchtype, (char *) data->dataptr, sizeof(char)))) + return err; + + pr_debug("%s: SCIOCSETSWITCH: Setting switch type to %d\n", adapter[card]->devicename, + switchtype); + status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallSetSwitchType, + 0, sizeof(char),&switchtype,&rcvmsg, SAR_TIMEOUT); + if(!status && !rcvmsg.rsp_status) { + pr_debug("%s: SCIOCSETSWITCH: Command Successful\n", adapter[card]->devicename); + return 0; + } + else { + pr_debug("%s: SCIOCSETSWITCH: Command Failed (status = %d)\n", + adapter[card]->devicename, status); + return status; + } + } + + case SCIOCGETSWITCH: + { + RspMessage rcvmsg; + char switchtype; + int status, err; + + pr_debug("%s: SCIOGETSWITCH: ioctl received\n", adapter[card]->devicename); + + /* + * Get the switch type from the board + */ + status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, + ceReqCallGetSwitchType, 0, 0, 0, &rcvmsg, SAR_TIMEOUT); + if (!status && !rcvmsg.rsp_status) { + pr_debug("%s: SCIOCGETSWITCH: Command Sucessful\n", adapter[card]->devicename); + } + else { + pr_debug("%s: SCIOCGETSWITCH: Command Failed (status = %d)\n", + adapter[card]->devicename, status); + return status; + } + + switchtype = rcvmsg.msg_data.byte_array[0]; + + /* + * Package the switch type and send to user space + */ + if ((err = copy_to_user((char *) data->dataptr, &switchtype, sizeof(char)))) + return err; + + return 0; + } + + case SCIOCGETSPID: + { + RspMessage rcvmsg; + char spid[SCIOC_SPIDSIZE]; + int status, err; + + pr_debug("%s: SCIOGETSPID: ioctl received\n", adapter[card]->devicename); + + /* + * Get the spid from the board + */ + status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetSPID, + data->channel, 0, 0, &rcvmsg, SAR_TIMEOUT); + if (!status) { + pr_debug("%s: SCIOCGETSPID: Command Sucessful\n", adapter[card]->devicename); + } + else { + pr_debug("%s: SCIOCGETSPID: Command Failed (status = %d)\n", + adapter[card]->devicename, status); + return status; + } + strcpy(spid, rcvmsg.msg_data.byte_array); + + /* + * Package the switch type and send to user space + */ + if ((err = copy_to_user((char *) data->dataptr, spid, sizeof(spid)))) + return err; + + return 0; + } + + case SCIOCSETSPID: + { + RspMessage rcvmsg; + char spid[SCIOC_SPIDSIZE]; + int status, err; + + pr_debug("%s: DCBIOSETSPID: ioctl received\n", adapter[card]->devicename); + + /* + * Get the spid from user space + */ + if ((err = copy_from_user(spid, (char *) data->dataptr, sizeof(spid)))) + return err; + + pr_debug("%s: SCIOCSETSPID: Setting channel %d spid to %s\n", + adapter[card]->devicename, data->channel, spid); + status = send_and_receive(card, CEPID, ceReqTypeCall, + ceReqClass0, ceReqCallSetSPID, data->channel, + strlen(spid), spid, &rcvmsg, SAR_TIMEOUT); + if(!status && !rcvmsg.rsp_status) { + pr_debug("%s: SCIOCSETSPID: Command Successful\n", + adapter[card]->devicename); + return 0; + } + else { + pr_debug("%s: SCIOCSETSPID: Command Failed (status = %d)\n", + adapter[card]->devicename, status); + return status; + } + } + + case SCIOCGETDN: + { + RspMessage rcvmsg; + char dn[SCIOC_DNSIZE]; + int status, err; + + pr_debug("%s: SCIOGETDN: ioctl received\n", adapter[card]->devicename); + + /* + * Get the dn from the board + */ + status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber, + data->channel, 0, 0, &rcvmsg, SAR_TIMEOUT); + if (!status) { + pr_debug("%s: SCIOCGETDN: Command Sucessful\n", adapter[card]->devicename); + } + else { + pr_debug("%s: SCIOCGETDN: Command Failed (status = %d)\n", + adapter[card]->devicename, status); + return status; + } + + strcpy(dn, rcvmsg.msg_data.byte_array); + + /* + * Package the dn and send to user space + */ + if ((err = copy_to_user((char *) data->dataptr, dn, sizeof(dn)))) + return err; + + return 0; + } + + case SCIOCSETDN: + { + RspMessage rcvmsg; + char dn[SCIOC_DNSIZE]; + int status, err; + + pr_debug("%s: SCIOSETDN: ioctl received\n", adapter[card]->devicename); + + /* + * Get the spid from user space + */ + if ((err = copy_from_user(dn, (char *) data->dataptr, sizeof(dn)))) + return err; + + pr_debug("%s: SCIOCSETDN: Setting channel %d dn to %s\n", + adapter[card]->devicename, data->channel, dn); + status = send_and_receive(card, CEPID, ceReqTypeCall, + ceReqClass0, ceReqCallSetMyNumber, data->channel, + strlen(dn),dn,&rcvmsg, SAR_TIMEOUT); + if(!status && !rcvmsg.rsp_status) { + pr_debug("%s: SCIOCSETDN: Command Successful\n", + adapter[card]->devicename); + return 0; + } + else { + pr_debug("%s: SCIOCSETDN: Command Failed (status = %d)\n", + adapter[card]->devicename, status); + return status; + } + } + + case SCIOCTRACE: + + pr_debug("%s: SCIOTRACE: ioctl received\n", adapter[card]->devicename); +/* adapter[card]->trace = !adapter[card]->trace; + pr_debug("%s: SCIOCTRACE: Tracing turned %s\n", adapter[card]->devicename, + adapter[card]->trace ? "ON" : "OFF"); */ + break; + + case SCIOCSTAT: + { + boardInfo bi; + int err; + + pr_debug("%s: SCIOSTAT: ioctl received\n", adapter[card]->devicename); + GetStatus(card, &bi); + + if ((err = copy_to_user((boardInfo *) data->dataptr, &bi, sizeof(boardInfo)))) + return err; + + return 0; + } + + case SCIOCGETSPEED: + { + RspMessage rcvmsg; + char speed; + int status, err; + + pr_debug("%s: SCIOGETSPEED: ioctl received\n", adapter[card]->devicename); + + /* + * Get the speed from the board + */ + status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, + ceReqCallGetCallType, data->channel, 0, 0, &rcvmsg, SAR_TIMEOUT); + if (!status && !rcvmsg.rsp_status) { + pr_debug("%s: SCIOCGETSPEED: Command Sucessful\n", + adapter[card]->devicename); + } + else { + pr_debug("%s: SCIOCGETSPEED: Command Failed (status = %d)\n", + adapter[card]->devicename, status); + return status; + } + + speed = rcvmsg.msg_data.byte_array[0]; + + /* + * Package the switch type and send to user space + */ + if ((err = copy_to_user((char *) data->dataptr, &speed, sizeof(char)))) + return err; + + return 0; + } + + case SCIOCSETSPEED: + pr_debug("%s: SCIOCSETSPEED: ioctl received\n", adapter[card]->devicename); + break; + + case SCIOCLOOPTST: + pr_debug("%s: SCIOCLOOPTST: ioctl received\n", adapter[card]->devicename); + break; + + default: + return -1; + } + + return 0; +} + +int GetStatus(int card, boardInfo *bi) +{ + RspMessage rcvmsg; + int i, status; + + /* + * Fill in some of the basic info about the board + */ + bi->modelid = adapter[card]->model; + strcpy(bi->serial_no, adapter[card]->hwconfig.serial_no); + strcpy(bi->part_no, adapter[card]->hwconfig.part_no); + bi->iobase = adapter[card]->iobase; + bi->rambase = adapter[card]->rambase; + bi->irq = adapter[card]->interrupt; + bi->ramsize = adapter[card]->hwconfig.ram_size; + bi->interface = adapter[card]->hwconfig.st_u_sense; + strcpy(bi->load_ver, adapter[card]->load_ver); + strcpy(bi->proc_ver, adapter[card]->proc_ver); + + /* + * Get the current PhyStats and LnkStats + */ + status = send_and_receive(card, CEPID, ceReqTypePhy, ceReqClass2, + ceReqPhyStatus, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); + if(!status) { + if(adapter[card]->model < PRI_BOARD) { + bi->l1_status = rcvmsg.msg_data.byte_array[2]; + for(i = 0 ; i < BRI_CHANNELS ; i++) + bi->status.bristats[i].phy_stat = + rcvmsg.msg_data.byte_array[i]; + } + else { + bi->l1_status = rcvmsg.msg_data.byte_array[0]; + bi->l2_status = rcvmsg.msg_data.byte_array[1]; + for(i = 0 ; i < PRI_CHANNELS ; i++) + bi->status.pristats[i].phy_stat = + rcvmsg.msg_data.byte_array[i+2]; + } + } + + /* + * Get the call types for each channel + */ + for (i = 0 ; i < adapter[card]->nChannels ; i++) { + status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, + ceReqCallGetCallType, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); + if(!status) { + if (adapter[card]->model == PRI_BOARD) { + bi->status.pristats[i].call_type = + rcvmsg.msg_data.byte_array[0]; + } + else { + bi->status.bristats[i].call_type = + rcvmsg.msg_data.byte_array[0]; + } + } + } + + /* + * If PRI, get the call states and service states for each channel + */ + if (adapter[card]->model == PRI_BOARD) { + /* + * Get the call states + */ + status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2, + ceReqPhyChCallState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); + if(!status) { + for( i = 0 ; i < PRI_CHANNELS ; i++ ) + bi->status.pristats[i].call_state = + rcvmsg.msg_data.byte_array[i]; + } + + /* + * Get the service states + */ + status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2, + ceReqPhyChServState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); + if(!status) { + for( i = 0 ; i < PRI_CHANNELS ; i++ ) + bi->status.pristats[i].serv_state = + rcvmsg.msg_data.byte_array[i]; + } + + /* + * Get the link stats for the channels + */ + for (i = 1 ; i <= PRI_CHANNELS ; i++) { + status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0, + ceReqLnkGetStats, i, 0, NULL, &rcvmsg, SAR_TIMEOUT); + if (!status) { + bi->status.pristats[i-1].link_stats.tx_good = + (unsigned long)rcvmsg.msg_data.byte_array[0]; + bi->status.pristats[i-1].link_stats.tx_bad = + (unsigned long)rcvmsg.msg_data.byte_array[4]; + bi->status.pristats[i-1].link_stats.rx_good = + (unsigned long)rcvmsg.msg_data.byte_array[8]; + bi->status.pristats[i-1].link_stats.rx_bad = + (unsigned long)rcvmsg.msg_data.byte_array[12]; + } + } + + /* + * Link stats for the D channel + */ + status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0, + ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); + if (!status) { + bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0]; + bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4]; + bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8]; + bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12]; + } + + return 0; + } + + /* + * If BRI or POTS, Get SPID, DN and call types for each channel + */ + + /* + * Get the link stats for the channels + */ + status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0, + ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); + if (!status) { + bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0]; + bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4]; + bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8]; + bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12]; + bi->status.bristats[0].link_stats.tx_good = + (unsigned long)rcvmsg.msg_data.byte_array[16]; + bi->status.bristats[0].link_stats.tx_bad = + (unsigned long)rcvmsg.msg_data.byte_array[20]; + bi->status.bristats[0].link_stats.rx_good = + (unsigned long)rcvmsg.msg_data.byte_array[24]; + bi->status.bristats[0].link_stats.rx_bad = + (unsigned long)rcvmsg.msg_data.byte_array[28]; + bi->status.bristats[1].link_stats.tx_good = + (unsigned long)rcvmsg.msg_data.byte_array[32]; + bi->status.bristats[1].link_stats.tx_bad = + (unsigned long)rcvmsg.msg_data.byte_array[36]; + bi->status.bristats[1].link_stats.rx_good = + (unsigned long)rcvmsg.msg_data.byte_array[40]; + bi->status.bristats[1].link_stats.rx_bad = + (unsigned long)rcvmsg.msg_data.byte_array[44]; + } + + /* + * Get the SPIDs + */ + for (i = 0 ; i < BRI_CHANNELS ; i++) { + status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, + ceReqCallGetSPID, i+1, 0, NULL, &rcvmsg, SAR_TIMEOUT); + if (!status) + strcpy(bi->status.bristats[i].spid, rcvmsg.msg_data.byte_array); + } + + /* + * Get the DNs + */ + for (i = 0 ; i < BRI_CHANNELS ; i++) { + status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, + ceReqCallGetMyNumber, i+1, 0, NULL, &rcvmsg, SAR_TIMEOUT); + if (!status) + strcpy(bi->status.bristats[i].dn, rcvmsg.msg_data.byte_array); + } + + return 0; +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/message.c linux/drivers/isdn/sc/message.c --- v2.0.30/linux/drivers/isdn/sc/message.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/message.c Mon Aug 4 17:34:01 1997 @@ -0,0 +1,300 @@ +/* + * $Id: message.c,v 1.2 1996/11/20 17:49:54 fritz Exp $ + * Copyright (C) 1996 SpellCaster Telecommunications Inc. + * + * message.c - functions for sending and receiving control messages + * + * 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. + * + * For more information, please contact gpl-info@spellcast.com or write: + * + * SpellCaster Telecommunications Inc. + * 5621 Finch Avenue East, Unit #3 + * Scarborough, Ontario Canada + * M1B 2T9 + * +1 (416) 297-8565 + * +1 (416) 297-6433 Facsimile + */ + +#define __NO_VERSION__ +#include "includes.h" +#include "hardware.h" +#include "message.h" +#include "card.h" + +extern board *adapter[]; +extern unsigned int cinst; + +/* + * Obligitory function prototypes + */ +extern int indicate_status(int,ulong,char*); +extern int scm_command(isdn_ctrl *); +extern void *memcpy_fromshmem(int, void *, const void *, size_t); + +/* + * Dump message queue in shared memory to screen + */ +void dump_messages(int card) +{ + DualPortMemory dpm; + unsigned long flags; + + int i =0; + + if (!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + } + + save_flags(flags); + cli(); + outb(adapter[card]->ioport[adapter[card]->shmem_pgport], + (adapter[card]->shmem_magic >> 14) | 0x80); + memcpy_fromshmem(card, &dpm, 0, sizeof(dpm)); + restore_flags(flags); + + pr_debug("%s: Dumping Request Queue\n", adapter[card]->devicename); + for (i = 0; i < dpm.req_head; i++) { + pr_debug("%s: Message #%d: (%d,%d,%d), link: %d\n", + adapter[card]->devicename, i, + dpm.req_queue[i].type, + dpm.req_queue[i].class, + dpm.req_queue[i].code, + dpm.req_queue[i].phy_link_no); + } + + pr_debug("%s: Dumping Response Queue\n", adapter[card]->devicename); + for (i = 0; i < dpm.rsp_head; i++) { + pr_debug("%s: Message #%d: (%d,%d,%d), link: %d, status: %d\n", + adapter[card]->devicename, i, + dpm.rsp_queue[i].type, + dpm.rsp_queue[i].class, + dpm.rsp_queue[i].code, + dpm.rsp_queue[i].phy_link_no, + dpm.rsp_queue[i].rsp_status); + } + +} + +/* + * receive a message from the board + */ +int receivemessage(int card, RspMessage *rspmsg) +{ + DualPortMemory *dpm; + unsigned long flags; + + if (!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -EINVAL; + } + + pr_debug("%s: Entered receivemessage\n",adapter[card]->devicename); + + /* + * See if there are messages waiting + */ + if (inb(adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA) { + /* + * Map in the DPM to the base page and copy the message + */ + save_flags(flags); + cli(); + outb((adapter[card]->shmem_magic >> 14) | 0x80, + adapter[card]->ioport[adapter[card]->shmem_pgport]); + dpm = (DualPortMemory *) adapter[card]->rambase; + memcpy_fromio(rspmsg, &(dpm->rsp_queue[dpm->rsp_tail]), + MSG_LEN); + dpm->rsp_tail = (dpm->rsp_tail+1) % MAX_MESSAGES; + inb(adapter[card]->ioport[FIFO_READ]); + restore_flags(flags); + + /* + * Tell the board that the message is received + */ + pr_debug("%s: Received Message seq:%d pid:%d time:%d cmd:%d " + "cnt:%d (type,class,code):(%d,%d,%d) " + "link:%d stat:0x%x\n", + adapter[card]->devicename, + rspmsg->sequence_no, + rspmsg->process_id, + rspmsg->time_stamp, + rspmsg->cmd_sequence_no, + rspmsg->msg_byte_cnt, + rspmsg->type, + rspmsg->class, + rspmsg->code, + rspmsg->phy_link_no, + rspmsg->rsp_status); + + return 0; + } + return -ENOMSG; +} + +/* + * send a message to the board + */ +int sendmessage(int card, + unsigned int procid, + unsigned int type, + unsigned int class, + unsigned int code, + unsigned int link, + unsigned int data_len, + unsigned int *data) +{ + DualPortMemory *dpm; + ReqMessage sndmsg; + unsigned long flags; + + if (!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -EINVAL; + } + + /* + * Make sure we only send CEPID messages when the engine is up + * and CMPID messages when it is down + */ + if(adapter[card]->EngineUp && procid == CMPID) { + pr_debug("%s: Attempt to send CM message with engine up\n", + adapter[card]->devicename); + return -ESRCH; + } + + if(!adapter[card]->EngineUp && procid == CEPID) { + pr_debug("%s: Attempt to send CE message with engine down\n", + adapter[card]->devicename); + return -ESRCH; + } + + memset(&sndmsg, 0, MSG_LEN); + sndmsg.msg_byte_cnt = 4; + sndmsg.type = type; + sndmsg.class = class; + sndmsg.code = code; + sndmsg.phy_link_no = link; + + if (data_len > 0) { + if (data_len > MSG_DATA_LEN) + data_len = MSG_DATA_LEN; + memcpy(&(sndmsg.msg_data), data, data_len); + sndmsg.msg_byte_cnt = data_len + 8; + } + + sndmsg.process_id = procid; + sndmsg.sequence_no = adapter[card]->seq_no++ % 256; + + /* + * wait for an empty slot in the queue + */ + while (!(inb(adapter[card]->ioport[FIFO_STATUS]) & WF_NOT_FULL)) + SLOW_DOWN_IO; + + /* + * Disable interrupts and map in shared memory + */ + save_flags(flags); + cli(); + outb((adapter[card]->shmem_magic >> 14) | 0x80, + adapter[card]->ioport[adapter[card]->shmem_pgport]); + dpm = (DualPortMemory *) adapter[card]->rambase; /* Fix me */ + memcpy_toio(&(dpm->req_queue[dpm->req_head]),&sndmsg,MSG_LEN); + dpm->req_head = (dpm->req_head+1) % MAX_MESSAGES; + outb(sndmsg.sequence_no, adapter[card]->ioport[FIFO_WRITE]); + restore_flags(flags); + + pr_debug("%s: Sent Message seq:%d pid:%d time:%d " + "cnt:%d (type,class,code):(%d,%d,%d) " + "link:%d\n ", + adapter[card]->devicename, + sndmsg.sequence_no, + sndmsg.process_id, + sndmsg.time_stamp, + sndmsg.msg_byte_cnt, + sndmsg.type, + sndmsg.class, + sndmsg.code, + sndmsg.phy_link_no); + + return 0; +} + +int send_and_receive(int card, + unsigned int procid, + unsigned char type, + unsigned char class, + unsigned char code, + unsigned char link, + unsigned char data_len, + unsigned char *data, + RspMessage *mesgdata, + int timeout) +{ + int retval; + int tries; + + if (!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -EINVAL; + } + + adapter[card]->want_async_messages = 1; + retval = sendmessage(card, procid, type, class, code, link, + data_len, (unsigned int *) data); + + if (retval) { + pr_debug("%s: SendMessage failed in SAR\n", + adapter[card]->devicename); + adapter[card]->want_async_messages = 0; + return -EIO; + } + + tries = 0; + /* wait for the response */ + while (tries < timeout) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + + pr_debug("SAR waiting..\n"); + + /* + * See if we got our message back + */ + if ((adapter[card]->async_msg.type == type) && + (adapter[card]->async_msg.class == class) && + (adapter[card]->async_msg.code == code) && + (adapter[card]->async_msg.phy_link_no == link)) { + + /* + * Got it! + */ + pr_debug("%s: Got ASYNC message\n", + adapter[card]->devicename); + memcpy(mesgdata, &(adapter[card]->async_msg), + sizeof(RspMessage)); + adapter[card]->want_async_messages = 0; + return 0; + } + + tries++; + } + + pr_debug("%s: SAR message timeout\n", adapter[card]->devicename); + adapter[card]->want_async_messages = 0; + return -ETIME; +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/message.h linux/drivers/isdn/sc/message.h --- v2.0.30/linux/drivers/isdn/sc/message.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/message.h Mon Aug 4 17:34:01 1997 @@ -0,0 +1,256 @@ +/* + * $Id: message.h,v 1.1 1996/11/07 13:07:47 fritz Exp $ + * Copyright (C) 1996 SpellCaster Telecommunications Inc. + * + * message.h - structures, macros and defines useful for sending + * messages to the adapter + * + * 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. + * + * For more information, please contact gpl-info@spellcast.com or write: + * + * SpellCaster Telecommunications Inc. + * 5621 Finch Avenue East, Unit #3 + * Scarborough, Ontario Canada + * M1B 2T9 + * +1 (416) 297-8565 + * +1 (416) 297-6433 Facsimile + */ + +/* + * Board message macros, defines and structures + */ + +#ifndef MESSAGE_H +#define MESSAGE_H + +#define MAX_MESSAGES 32 /* Maximum messages that can be + queued */ +#define MSG_DATA_LEN 48 /* Maximum size of message payload */ +#define MSG_LEN 64 /* Size of a message */ +#define CMPID 0 /* Loader message process ID */ +#define CEPID 64 /* Firmware message process ID */ + +/* + * Macro to determine if a message is a loader message + */ +#define IS_CM_MESSAGE(mesg, tx, cx, dx) \ + ((mesg.type == cmRspType##tx) \ + &&(mesg.class == cmRspClass##cx) \ + &&(mesg.code == cmRsp##dx)) + +/* + * Macro to determine if a message is a firmware message + */ +#define IS_CE_MESSAGE(mesg, tx, cx, dx) \ + ((mesg.type == ceRspType##tx) \ + &&(mesg.class == ceRspClass##cx) \ + &&(mesg.code == ceRsp##tx##dx)) + +/* + * Loader Request and Response Messages + */ + +/* message types */ +#define cmReqType1 1 +#define cmReqType2 2 +#define cmRspType0 0 +#define cmRspType1 1 +#define cmRspType2 2 +#define cmRspType5 5 + +/* message classes */ +#define cmReqClass0 0 +#define cmRspClass0 0 + +/* message codes */ +#define cmReqHWConfig 1 /* 1,0,1 */ +#define cmReqMsgLpbk 2 /* 1,0,2 */ +#define cmReqVersion 3 /* 1,0,3 */ +#define cmReqLoadProc 1 /* 2,0,1 */ +#define cmReqStartProc 2 /* 2,0,2 */ +#define cmReqReadMem 6 /* 2,0,6 */ +#define cmRspHWConfig cmReqHWConfig +#define cmRspMsgLpbk cmReqMsgLpbk +#define cmRspVersion cmReqVersion +#define cmRspLoadProc cmReqLoadProc +#define cmRspStartProc cmReqStartProc +#define cmRspReadMem cmReqReadMem +#define cmRspMiscEngineUp 1 /* 5,0,1 */ +#define cmRspInvalid 0 /* 0,0,0 */ + + +/* + * Firmware Request and Response Messages + */ + +/* message types */ +#define ceReqTypePhy 1 +#define ceReqTypeLnk 2 +#define ceReqTypeCall 3 +#define ceReqTypeStat 1 +#define ceRspTypeErr 0 +#define ceRspTypePhy ceReqTypePhy +#define ceRspTypeLnk ceReqTypeLnk +#define ceRspTypeCall ceReqTypeCall +#define ceRspTypeStat ceReqTypeStat + +/* message classes */ +#define ceReqClass0 0 +#define ceReqClass1 1 +#define ceReqClass2 2 +#define ceReqClass3 3 +#define ceRspClass0 ceReqClass0 +#define ceRspClass1 ceReqClass1 +#define ceRspClass2 ceReqClass2 +#define ceRspClass3 ceReqClass3 + +/* message codes (B) = BRI only, (P) = PRI only, (V) = POTS only */ +#define ceReqPhyProcInfo 1 /* 1,0,1 */ +#define ceReqPhyConnect 1 /* 1,1,1 */ +#define ceReqPhyDisconnect 2 /* 1,1,2 */ +#define ceReqPhySetParams 3 /* 1,1,3 (P) */ +#define ceReqPhyGetParams 4 /* 1,1,4 (P) */ +#define ceReqPhyStatus 1 /* 1,2,1 */ +#define ceReqPhyAcfaStatus 2 /* 1,2,2 (P) */ +#define ceReqPhyChCallState 3 /* 1,2,3 (P) */ +#define ceReqPhyChServState 4 /* 1,2,4 (P) */ +#define ceReqPhyRLoopBack 1 /* 1,3,1 */ +#define ceRspPhyProcInfo ceReqPhyProcInfo +#define ceRspPhyConnect ceReqPhyConnect +#define ceRspPhyDisconnect ceReqPhyDisconnect +#define ceRspPhySetParams ceReqPhySetParams +#define ceRspPhyGetParams ceReqPhyGetParams +#define ceRspPhyStatus ceReqPhyStatus +#define ceRspPhyAcfaStatus ceReqPhyAcfaStatus +#define ceRspPhyChCallState ceReqPhyChCallState +#define ceRspPhyChServState ceReqPhyChServState +#define ceRspPhyRLoopBack ceReqphyRLoopBack +#define ceReqLnkSetParam 1 /* 2,0,1 */ +#define ceReqLnkGetParam 2 /* 2,0,2 */ +#define ceReqLnkGetStats 3 /* 2,0,3 */ +#define ceReqLnkWrite 1 /* 2,1,1 */ +#define ceReqLnkRead 2 /* 2,1,2 */ +#define ceReqLnkFlush 3 /* 2,1,3 */ +#define ceReqLnkWrBufTrc 4 /* 2,1,4 */ +#define ceReqLnkRdBufTrc 5 /* 2,1,5 */ +#define ceRspLnkSetParam ceReqLnkSetParam +#define ceRspLnkGetParam ceReqLnkGetParam +#define ceRspLnkGetStats ceReqLnkGetStats +#define ceRspLnkWrite ceReqLnkWrite +#define ceRspLnkRead ceReqLnkRead +#define ceRspLnkFlush ceReqLnkFlush +#define ceRspLnkWrBufTrc ceReqLnkWrBufTrc +#define ceRspLnkRdBufTrc ceReqLnkRdBufTrc +#define ceReqCallSetSwitchType 1 /* 3,0,1 */ +#define ceReqCallGetSwitchType 2 /* 3,0,2 */ +#define ceReqCallSetFrameFormat 3 /* 3,0,3 */ +#define ceReqCallGetFrameFormat 4 /* 3,0,4 */ +#define ceReqCallSetCallType 5 /* 3,0,5 */ +#define ceReqCallGetCallType 6 /* 3,0,6 */ +#define ceReqCallSetSPID 7 /* 3,0,7 (!P) */ +#define ceReqCallGetSPID 8 /* 3,0,8 (!P) */ +#define ceReqCallSetMyNumber 9 /* 3,0,9 (!P) */ +#define ceReqCallGetMyNumber 10 /* 3,0,10 (!P) */ +#define ceRspCallSetSwitchType ceReqCallSetSwitchType +#define ceRspCallGetSwitchType ceReqCallSetSwitchType +#define ceRspCallSetFrameFormat ceReqCallSetFrameFormat +#define ceRspCallGetFrameFormat ceReqCallGetFrameFormat +#define ceRspCallSetCallType ceReqCallSetCallType +#define ceRspCallGetCallType ceReqCallGetCallType +#define ceRspCallSetSPID ceReqCallSetSPID +#define ceRspCallGetSPID ceReqCallGetSPID +#define ceRspCallSetMyNumber ceReqCallSetMyNumber +#define ceRspCallGetMyNumber ceReqCallGetMyNumber +#define ceRspStatAcfaStatus 2 +#define ceRspStat +#define ceRspErrError 0 /* 0,0,0 */ + +/* + * Call Types + */ +#define CALLTYPE_64K 0 +#define CALLTYPE_56K 1 +#define CALLTYPE_SPEECH 2 +#define CALLTYPE_31KHZ 3 + +/* + * Link Level data contains a pointer to and the length of + * a buffer in shared RAM. Used by LnkRead and LnkWrite message + * types. Part of RspMsgStruct and ReqMsgStruct. + */ +typedef struct { + unsigned long buff_offset; + unsigned short msg_len; +} LLData; + + +/* + * Message payload template for an HWConfig message + */ +typedef struct { + char st_u_sense; + char powr_sense; + char sply_sense; + unsigned char asic_id; + long ram_size; + char serial_no[13]; + char part_no[13]; + char rev_no[2]; +} HWConfig_pl; + +/* + * A Message + */ +struct message { + unsigned char sequence_no; + unsigned char process_id; + unsigned char time_stamp; + unsigned char cmd_sequence_no; /* Rsp messages only */ + unsigned char reserved1[3]; + unsigned char msg_byte_cnt; + unsigned char type; + unsigned char class; + unsigned char code; + unsigned char phy_link_no; + unsigned char rsp_status; /* Rsp messages only */ + unsigned char reseved2[3]; + union { + unsigned char byte_array[MSG_DATA_LEN]; + LLData response; + HWConfig_pl HWCresponse; + } msg_data; +}; + +typedef struct message ReqMessage; /* Request message */ +typedef struct message RspMessage; /* Response message */ + +/* + * The first 5010 bytes of shared memory contain the message queues, + * indexes and other data. This structure is its template + */ +typedef struct { + volatile ReqMessage req_queue[MAX_MESSAGES]; + volatile RspMessage rsp_queue[MAX_MESSAGES]; + volatile unsigned char req_head; + volatile unsigned char req_tail; + volatile unsigned char rsp_head; + volatile unsigned char rsp_tail; + volatile unsigned long signature; + volatile unsigned long trace_enable; + volatile unsigned char reserved[4]; +} DualPortMemory; + +#endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/packet.c linux/drivers/isdn/sc/packet.c --- v2.0.30/linux/drivers/isdn/sc/packet.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/packet.c Mon Aug 4 17:34:01 1997 @@ -0,0 +1,228 @@ +/* + * $Id: packet.c,v 1.2 1996/11/20 17:49:55 fritz Exp $ + * Copyright (C) 1996 SpellCaster Telecommunications 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. + * + * 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. + * + * For more information, please contact gpl-info@spellcast.com or write: + * + * SpellCaster Telecommunications Inc. + * 5621 Finch Avenue East, Unit #3 + * Scarborough, Ontario Canada + * M1B 2T9 + * +1 (416) 297-8565 + * +1 (416) 297-6433 Facsimile + */ + +#define __NO_VERSION__ +#include "includes.h" +#include "hardware.h" +#include "message.h" +#include "card.h" + +extern board *adapter[]; +extern unsigned int cinst; + +extern int get_card_from_id(int); +extern int indicate_status(int, int,ulong,char*); +extern void *memcpy_toshmem(int, void *, const void *, size_t); +extern void *memcpy_fromshmem(int, void *, const void *, size_t); +extern int sendmessage(int, unsigned int, unsigned int, unsigned int, + unsigned int, unsigned int, unsigned int, unsigned int *); + +int sndpkt(int devId, int channel, struct sk_buff *data) +{ + LLData ReqLnkWrite; + int status; + int card; + + card = get_card_from_id(devId); + + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + pr_debug("%s: Send Packet: frst = 0x%x nxt = %d f = %d n = %d\n", + adapter[card]->devicename, + adapter[card]->channel[channel].first_sendbuf, + adapter[card]->channel[channel].next_sendbuf, + adapter[card]->channel[channel].free_sendbufs, + adapter[card]->channel[channel].num_sendbufs); + + if(!adapter[card]->channel[channel].free_sendbufs) { + pr_debug("%s: Out out TX buffers\n", adapter[card]->devicename); + return -EINVAL; + } + + if(data->len > BUFFER_SIZE) { + pr_debug("%s: Data overflows buffer size (data > buffer)\n", adapter[card]->devicename); + return -EINVAL; + } + + ReqLnkWrite.buff_offset = adapter[card]->channel[channel].next_sendbuf * + BUFFER_SIZE + adapter[card]->channel[channel].first_sendbuf; + ReqLnkWrite.msg_len = data->len; /* sk_buff size */ + pr_debug("%s: Writing %d bytes to buffer offset 0x%x\n", adapter[card]->devicename, + ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset); + memcpy_toshmem(card, (char *)ReqLnkWrite.buff_offset, data->data, ReqLnkWrite.msg_len); + + /* + * sendmessage + */ + pr_debug("%s: Send Packet size=%d, buf_offset=0x%x buf_indx=%d\n", + adapter[card]->devicename, + ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset, + adapter[card]->channel[channel].next_sendbuf); + + status = sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkWrite, + channel+1, sizeof(LLData), (unsigned int*)&ReqLnkWrite); + if(status) { + pr_debug("%s: Failed to send packet, status = %d\n", adapter[card]->devicename, status); + return -1; + } + else { + adapter[card]->channel[channel].free_sendbufs--; + adapter[card]->channel[channel].next_sendbuf = + ++adapter[card]->channel[channel].next_sendbuf == + adapter[card]->channel[channel].num_sendbufs ? 0 : + adapter[card]->channel[channel].next_sendbuf; + pr_debug("%s: Packet sent successfully\n", adapter[card]->devicename); + dev_kfree_skb(data, FREE_WRITE); + indicate_status(card,ISDN_STAT_BSENT,channel,NULL); + } + return data->len; +} + +void rcvpkt(int card, RspMessage *rcvmsg) +{ + LLData newll; + struct sk_buff *skb; + + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return; + } + + switch(rcvmsg->rsp_status){ + case 0x01: + case 0x02: + case 0x70: + pr_debug("%s: Error status code: 0x%x\n", adapter[card]->devicename, rcvmsg->rsp_status); + return; + case 0x00: + if (!(skb = dev_alloc_skb(rcvmsg->msg_data.response.msg_len))) { + printk(KERN_WARNING "%s: rcvpkt out of memory, dropping packet\n", + adapter[card]->devicename); + return; + } + skb_put(skb, rcvmsg->msg_data.response.msg_len); + pr_debug("%s: getting data from offset: 0x%x\n", + adapter[card]->devicename,rcvmsg->msg_data.response.buff_offset); + memcpy_fromshmem(card, + skb_put(skb, rcvmsg->msg_data.response.msg_len), + (char *)rcvmsg->msg_data.response.buff_offset, + rcvmsg->msg_data.response.msg_len); + adapter[card]->card->rcvcallb_skb(adapter[card]->driverId, + rcvmsg->phy_link_no-1, skb); + + case 0x03: + /* + * Recycle the buffer + */ + pr_debug("%s: Buffer size : %d\n", adapter[card]->devicename, BUFFER_SIZE); +/* memset_shmem(card, rcvmsg->msg_data.response.buff_offset, 0, BUFFER_SIZE); */ + newll.buff_offset = rcvmsg->msg_data.response.buff_offset; + newll.msg_len = BUFFER_SIZE; + pr_debug("%s: recycled buffer at offset 0x%x size %d\n", adapter[card]->devicename, + newll.buff_offset, newll.msg_len); + sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead, + rcvmsg->phy_link_no, sizeof(LLData), (unsigned int *)&newll); + } + +} + +int setup_buffers(int card, int c) +{ + unsigned int nBuffers, i, cBase; + unsigned int buffer_size; + LLData RcvBuffOffset; + + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return -ENODEV; + } + + /* + * Calculate the buffer offsets (send/recv/send/recv) + */ + pr_debug("%s: Seting up channel buffer space in shared RAM\n", adapter[card]->devicename); + buffer_size = BUFFER_SIZE; + nBuffers = ((adapter[card]->ramsize - BUFFER_BASE) / buffer_size) / 2; + nBuffers = nBuffers > BUFFERS_MAX ? BUFFERS_MAX : nBuffers; + pr_debug("%s: Calculating buffer space: %d buffers, %d big\n", adapter[card]->devicename, + nBuffers, buffer_size); + if(nBuffers < 2) { + pr_debug("%s: Not enough buffer space\n", adapter[card]->devicename); + return -1; + } + cBase = (nBuffers * buffer_size) * (c - 1); + pr_debug("%s: Channel buffer offset from Shared RAM: 0x%x\n", adapter[card]->devicename, cBase); + adapter[card]->channel[c-1].first_sendbuf = BUFFER_BASE + cBase; + adapter[card]->channel[c-1].num_sendbufs = nBuffers / 2; + adapter[card]->channel[c-1].free_sendbufs = nBuffers / 2; + adapter[card]->channel[c-1].next_sendbuf = 0; + pr_debug("%s: Send buffer setup complete: first=0x%x n=%d f=%d, nxt=%d\n", + adapter[card]->devicename, + adapter[card]->channel[c-1].first_sendbuf, + adapter[card]->channel[c-1].num_sendbufs, + adapter[card]->channel[c-1].free_sendbufs, + adapter[card]->channel[c-1].next_sendbuf); + + /* + * Prep the receive buffers + */ + pr_debug("%s: Adding %d RecvBuffers:\n", adapter[card]->devicename, nBuffers /2); + for (i = 0 ; i < nBuffers / 2; i++) { + RcvBuffOffset.buff_offset = + ((adapter[card]->channel[c-1].first_sendbuf + + (nBuffers / 2) * buffer_size) + (buffer_size * i)); + RcvBuffOffset.msg_len = buffer_size; + pr_debug("%s: Adding RcvBuffer #%d offset=0x%x sz=%d buffsz:%d\n", + adapter[card]->devicename, + i + 1, RcvBuffOffset.buff_offset, + RcvBuffOffset.msg_len,buffer_size); + sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead, + c, sizeof(LLData), (unsigned int *)&RcvBuffOffset); + } + return 0; +} + +int print_skb(int card,char *skb_p, int len){ + int i,data; + pr_debug("%s: data at 0x%x len: 0x%x\n",adapter[card]->devicename, + skb_p,len); + for(i=1;i<=len;i++,skb_p++){ + data = (int) (0xff & (*skb_p)); + pr_debug("%s: data = 0x%x",adapter[card]->devicename,data); + if(!(i%4)) + pr_debug(" "); + if(!(i%32)) + pr_debug("\n"); + } + pr_debug("\n"); + return 0; +} + diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/scioc.h linux/drivers/isdn/sc/scioc.h --- v2.0.30/linux/drivers/isdn/sc/scioc.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/scioc.h Mon Aug 4 17:34:01 1997 @@ -0,0 +1,101 @@ + +/* + * IOCTL Command Codes + */ +#define SCIOCLOAD 0x01 /* Load a firmware record */ +#define SCIOCRESET 0x02 /* Perform hard reset */ +#define SCIOCDEBUG 0x03 /* Set debug level */ +#define SCIOCREV 0x04 /* Get driver revision(s) */ +#define SCIOCSTART 0x05 /* Start the firmware */ +#define SCIOCGETSWITCH 0x06 /* Get switch type */ +#define SCIOCSETSWITCH 0x07 /* Set switch type */ +#define SCIOCGETSPID 0x08 /* Get channel SPID */ +#define SCIOCSETSPID 0x09 /* Set channel SPID */ +#define SCIOCGETDN 0x0A /* Get channel DN */ +#define SCIOCSETDN 0x0B /* Set channel DN */ +#define SCIOCTRACE 0x0C /* Toggle trace mode */ +#define SCIOCSTAT 0x0D /* Get line status */ +#define SCIOCGETSPEED 0x0E /* Set channel speed */ +#define SCIOCSETSPEED 0x0F /* Set channel speed */ +#define SCIOCLOOPTST 0x10 /* Perform loopback test */ + +typedef struct { + int device; + int channel; + unsigned long command; + void *dataptr; +} scs_ioctl; + +/* Size of strings */ +#define SCIOC_SPIDSIZE 49 +#define SCIOC_DNSIZE SCIOC_SPIDSIZE +#define SCIOC_REVSIZE SCIOC_SPIDSIZE +#define SCIOC_SRECSIZE 49 + +typedef struct { + unsigned long tx_good; + unsigned long tx_bad; + unsigned long rx_good; + unsigned long rx_bad; +} ChLinkStats; + +typedef struct { + char spid[49]; + char dn[49]; + char call_type; + char phy_stat; + ChLinkStats link_stats; +} BRIStat; + +typedef BRIStat POTStat; + +typedef struct { + char call_type; + char call_state; + char serv_state; + char phy_stat; + ChLinkStats link_stats; +} PRIStat; + +typedef char PRIInfo; +typedef char BRIInfo; +typedef char POTInfo; + + +typedef struct { + char acfa_nos; + char acfa_ais; + char acfa_los; + char acfa_rra; + char acfa_slpp; + char acfa_slpn; + char acfa_fsrf; +} ACFAStat; + +typedef struct { + unsigned char modelid; + char serial_no[13]; + char part_no[13]; + char load_ver[11]; + char proc_ver[11]; + int iobase; + long rambase; + char irq; + long ramsize; + char interface; + char switch_type; + char l1_status; + char l2_status; + ChLinkStats dch_stats; + ACFAStat AcfaStats; + union { + PRIStat pristats[23]; + BRIStat bristats[2]; + POTStat potsstats[2]; + } status; + union { + PRIInfo priinfo; + BRIInfo briinfo; + POTInfo potsinfo; + } info; +} boardInfo; diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/shmem.c linux/drivers/isdn/sc/shmem.c --- v2.0.30/linux/drivers/isdn/sc/shmem.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/shmem.c Mon Aug 4 17:34:01 1997 @@ -0,0 +1,165 @@ +/* + * $Id: shmem.c,v 1.2 1996/11/20 17:49:56 fritz Exp $ + * Copyright (C) 1996 SpellCaster Telecommunications Inc. + * + * card.c - Card functions implementing ISDN4Linux functionality + * + * 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. + * + * For more information, please contact gpl-info@spellcast.com or write: + * + * SpellCaster Telecommunications Inc. + * 5621 Finch Avenue East, Unit #3 + * Scarborough, Ontario Canada + * M1B 2T9 + * +1 (416) 297-8565 + * +1 (416) 297-6433 Facsimile + */ + +#define __NO_VERSION__ +#include "includes.h" /* This must be first */ +#include "hardware.h" +#include "card.h" + +/* + * Main adapter array + */ +extern board *adapter[]; +extern int cinst; + +/* + * + */ +void *memcpy_toshmem(int card, void *dest, const void *src, size_t n) +{ + unsigned long flags; + void *ret; + unsigned char ch; + + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return NULL; + } + + if(n > SRAM_PAGESIZE) { + return NULL; + } + + /* + * determine the page to load from the address + */ + ch = (unsigned long) dest / SRAM_PAGESIZE; + pr_debug("%s: loaded page %d\n",adapter[card]->devicename,ch); + /* + * Block interrupts and load the page + */ + save_flags(flags); + cli(); + + outb(((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80, + adapter[card]->ioport[adapter[card]->shmem_pgport]); + pr_debug("%s: set page to %#x\n",adapter[card]->devicename, + ((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80); + ret = memcpy_toio(adapter[card]->rambase + + ((unsigned long) dest % 0x4000), src, n); + pr_debug("%s: copying %d bytes from %#x to %#x\n",adapter[card]->devicename, n, + (unsigned long) src, adapter[card]->rambase + ((unsigned long) dest %0x4000)); + restore_flags(flags); + + return ret; +} + +/* + * Reverse of above + */ +void *memcpy_fromshmem(int card, void *dest, const void *src, size_t n) +{ + unsigned long flags; + void *ret; + unsigned char ch; + + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return NULL; + } + + if(n > SRAM_PAGESIZE) { + return NULL; + } + + /* + * determine the page to load from the address + */ + ch = (unsigned long) src / SRAM_PAGESIZE; + pr_debug("%s: loaded page %d\n",adapter[card]->devicename,ch); + + + /* + * Block interrupts and load the page + */ + save_flags(flags); + cli(); + + outb(((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80, + adapter[card]->ioport[adapter[card]->shmem_pgport]); + pr_debug("%s: set page to %#x\n",adapter[card]->devicename, + ((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80); + ret = memcpy_fromio(dest,(void *)(adapter[card]->rambase + + ((unsigned long) src % 0x4000)), n); +/* pr_debug("%s: copying %d bytes from %#x to %#x\n", + adapter[card]->devicename, n, + adapter[card]->rambase + ((unsigned long) src %0x4000), (unsigned long) dest); */ + restore_flags(flags); + + return ret; +} + +void *memset_shmem(int card, void *dest, int c, size_t n) +{ + unsigned long flags; + unsigned char ch; + void *ret; + + if(!IS_VALID_CARD(card)) { + pr_debug("Invalid param: %d is not a valid card id\n", card); + return NULL; + } + + if(n > SRAM_PAGESIZE) { + return NULL; + } + + /* + * determine the page to load from the address + */ + ch = (unsigned long) dest / SRAM_PAGESIZE; + pr_debug("%s: loaded page %d\n",adapter[card]->devicename,ch); + + /* + * Block interrupts and load the page + */ + save_flags(flags); + cli(); + + outb(((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80, + adapter[card]->ioport[adapter[card]->shmem_pgport]); + pr_debug("%s: set page to %#x\n",adapter[card]->devicename, + ((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80); + ret = memset_io(adapter[card]->rambase + + ((unsigned long) dest % 0x4000), c, n); + restore_flags(flags); + + return ret; +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/sc/timer.c linux/drivers/isdn/sc/timer.c --- v2.0.30/linux/drivers/isdn/sc/timer.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/sc/timer.c Mon Aug 4 17:34:01 1997 @@ -0,0 +1,172 @@ +/* + * $Id: timer.c,v 1.2 1996/11/20 17:49:57 fritz Exp $ + * Copyright (C) 1996 SpellCaster Telecommunications 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. + * + * 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. + * + * For more information, please contact gpl-info@spellcast.com or write: + * + * SpellCaster Telecommunications Inc. + * 5621 Finch Avenue East, Unit #3 + * Scarborough, Ontario Canada + * M1B 2T9 + * +1 (416) 297-8565 + * +1 (416) 297-6433 Facsimile + */ + +#define __NO_VERSION__ +#include "includes.h" +#include "hardware.h" +#include "message.h" +#include "card.h" + +extern board *adapter[]; + +extern void flushreadfifo(int); +extern int startproc(int); +extern int indicate_status(int, int, unsigned long, char *); +extern int sendmessage(int, unsigned int, unsigned int, unsigned int, + unsigned int, unsigned int, unsigned int, unsigned int *); + + +/* + * Write the proper values into the I/O ports following a reset + */ +void setup_ports(int card) +{ + + outb((adapter[card]->rambase >> 12), adapter[card]->ioport[EXP_BASE]); + + /* And the IRQ */ + outb((adapter[card]->interrupt | 0x80), + adapter[card]->ioport[IRQ_SELECT]); +} + +/* + * Timed function to check the status of a previous reset + * Must be very fast as this function runs in the context of + * an interrupt handler. + * + * Setup the ioports for the board that were cleared by the reset. + * Then, check to see if the signate has been set. Next, set the + * signature to a known value and issue a startproc if needed. + */ +void check_reset(unsigned long data) +{ + unsigned long flags; + unsigned long sig; + int card = (unsigned int) data; + + pr_debug("%s: check_timer timer called\n", adapter[card]->devicename); + + /* Setup the io ports */ + setup_ports(card); + + save_flags(flags); + cli(); + outb(adapter[card]->ioport[adapter[card]->shmem_pgport], + (adapter[card]->shmem_magic>>14) | 0x80); + sig = (unsigned long) *((unsigned long *)(adapter[card]->rambase + SIG_OFFSET)); + + /* check the signature */ + if(sig == SIGNATURE) { + flushreadfifo(card); + restore_flags(flags); + /* See if we need to do a startproc */ + if (adapter[card]->StartOnReset) + startproc(card); + } + else { + pr_debug("%s: No signature yet, waiting another %d jiffies.\n", + adapter[card]->devicename, CHECKRESET_TIME); + del_timer(&adapter[card]->reset_timer); + adapter[card]->reset_timer.expires = jiffies + CHECKRESET_TIME; + add_timer(&adapter[card]->reset_timer); + } + restore_flags(flags); + +} + +/* + * Timed function to check the status of a previous reset + * Must be very fast as this function runs in the context of + * an interrupt handler. + * + * Send check adapter->phystat to see if the channels are up + * If they are, tell ISDN4Linux that the board is up. If not, + * tell IADN4Linux that it is up. Always reset the timer to + * fire again (endless loop). + */ +void check_phystat(unsigned long data) +{ + unsigned long flags; + int card = (unsigned int) data; + + pr_debug("%s: Checking status...\n", adapter[card]->devicename); + /* + * check the results of the last PhyStat and change only if + * has changed drastically + */ + if (adapter[card]->nphystat && !adapter[card]->phystat) { /* All is well */ + pr_debug("PhyStat transition to RUN\n"); + pr_info("%s: Switch contacted, transmitter enabled\n", + adapter[card]->devicename); + indicate_status(card, ISDN_STAT_RUN, 0, NULL); + } + else if (!adapter[card]->nphystat && adapter[card]->phystat) { /* All is not well */ + pr_debug("PhyStat transition to STOP\n"); + pr_info("%s: Switch connection lost, transmitter disabled\n", + adapter[card]->devicename); + + indicate_status(card, ISDN_STAT_STOP, 0, NULL); + } + + adapter[card]->phystat = adapter[card]->nphystat; + + /* Reinitialize the timer */ + save_flags(flags); + cli(); + del_timer(&adapter[card]->stat_timer); + adapter[card]->stat_timer.expires = jiffies + CHECKSTAT_TIME; + add_timer(&adapter[card]->stat_timer); + restore_flags(flags); + + /* Send a new cePhyStatus message */ + sendmessage(card, CEPID,ceReqTypePhy,ceReqClass2, + ceReqPhyStatus,0,0,NULL); +} + +/* + * When in trace mode, this callback is used to swap the working shared + * RAM page to the trace page(s) and process all received messages. It + * must be called often enough to get all of the messages out of RAM before + * it loops around. + * Trace messages are \n terminated strings. + * We output the messages in 64 byte chunks through readstat. Each chunk + * is scanned for a \n followed by a time stamp. If the timerstamp is older + * than the current time, scanning stops and the page and offset are recorded + * as the starting point the next time the trace timer is called. The final + * step is to restore the working page and reset the timer. + */ +void trace_timer(unsigned long data) +{ + unsigned long flags; + + /* + * Disable interrupts and swap the first page + */ + save_flags(flags); + cli(); +} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/Makefile linux/drivers/isdn/teles/Makefile --- v2.0.30/linux/drivers/isdn/teles/Makefile Mon Feb 26 01:58:05 1996 +++ linux/drivers/isdn/teles/Makefile Wed Dec 31 16:00:00 1969 @@ -1,17 +0,0 @@ -L_OBJS := -M_OBJS := -O_OBJS := mod.o card.o config.o buffers.o tei.o isdnl2.o isdnl3.o \ -llglue.o q931.o callc.o fsm.o - -O_TARGET := -ifeq ($(CONFIG_ISDN_DRV_TELES),y) - O_TARGET += teles.o -else - ifeq ($(CONFIG_ISDN_DRV_TELES),m) - O_TARGET += teles.o - M_OBJS += teles.o - endif -endif - -include $(TOPDIR)/Rules.make - diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/buffers.c linux/drivers/isdn/teles/buffers.c --- v2.0.30/linux/drivers/isdn/teles/buffers.c Sat Jun 1 01:56:51 1996 +++ linux/drivers/isdn/teles/buffers.c Wed Dec 31 16:00:00 1969 @@ -1,326 +0,0 @@ -/* $Id: buffers.c,v 1.3 1996/05/31 00:56:53 fritz Exp $ - * - * $Log: buffers.c,v $ - * Revision 1.3 1996/05/31 00:56:53 fritz - * removed cli() from BufPoolAdd, since it is called - * with interrupts off anyway. - * - * Revision 1.2 1996/04/29 22:48:14 fritz - * Removed compatibility-macros. No longer needed. - * - * Revision 1.1 1996/04/13 10:19:28 fritz - * Initial revision - * - * - */ -#define __NO_VERSION__ -#include "teles.h" -#include -#include - - -void -BufPoolInit(struct BufPool *bp, int order, int bpps, - int maxpages) -{ -#ifdef DEBUG_MAGIC - generateerror - bp->magic = 010167; -#endif - -#if 0 - printk(KERN_DEBUG "BufPoolInit bp %x\n", bp); -#endif - - bp->freelist = NULL; - bp->pageslist = NULL; - bp->pageorder = order; - bp->pagescount = 0; - bp->bpps = bpps; - bp->bufsize = BUFFER_SIZE(order, bpps); - bp->maxpages = maxpages; -} - -int -BufPoolAdd(struct BufPool *bp, int priority) -{ - struct Pages *ptr; - byte *bptr; - int i; - struct BufHeader *bh = NULL, *prev, *first; - -#if 0 - printk(KERN_DEBUG "BufPoolAdd bp %x\n", bp); -#endif - - ptr = (struct Pages *) __get_free_pages(priority, bp->pageorder, 0); - if (!ptr) { - printk(KERN_WARNING "BufPoolAdd couldn't get pages!\n"); - return (-1); - } -#if 0 - printk(KERN_DEBUG "Order %d pages allocated at %x\n", bp->pageorder, ptr); -#endif - - ptr->next = bp->pageslist; - bp->pageslist = ptr; - bp->pagescount++; - - bptr = (byte *) ptr + sizeof(struct Pages *); - - i = bp->bpps; - first = (struct BufHeader *) bptr; - prev = NULL; - while (i--) { - bh = (struct BufHeader *) bptr; -#ifdef DEBUG_MAGIC - bh->magic = 020167; -#endif - bh->next = prev; - prev = bh; - bh->bp = bp; - bptr += PART_SIZE(bp->pageorder, bp->bpps); - } - - first->next = bp->freelist; - bp->freelist = bh; - return (0); -} - -void -BufPoolFree(struct BufPool *bp) -{ - struct Pages *p; - -#if 0 - printk(KERN_DEBUG "BufPoolFree bp %x\n", bp); -#endif - - while (bp->pagescount--) { - p = bp->pageslist->next; - free_pages((unsigned long) bp->pageslist, bp->pageorder); -#if 0 - printk(KERN_DEBUG "Free pages %x order %d\n", bp->pageslist, bp->pageorder); -#endif - bp->pageslist = p; - } -} - -int -BufPoolGet(struct BufHeader **bh, - struct BufPool *bp, int priority, void *heldby, int where) -{ - long flags; - int i; - -#ifdef DEBUG_MAGIC - if (bp->magic != 010167) { - printk(KERN_DEBUG "BufPoolGet: not a BufHeader\n"); - return (-1); - } -#endif - - save_flags(flags); - cli(); - i = 0; - while (!0) { - if (bp->freelist) { - *bh = bp->freelist; - bp->freelist = bp->freelist->next; - (*bh)->heldby = heldby; - (*bh)->where = where; - restore_flags(flags); - return (0); - } - if ((i == 0) && (bp->pagescount < bp->maxpages)) { - if (BufPoolAdd(bp, priority)) { - restore_flags(flags); - return -1; - } - i++; - } else { - *bh = NULL; - restore_flags(flags); - return (-1); - } - } - -} - -void -BufPoolRelease(struct BufHeader *bh) -{ - struct BufPool *bp; - long flags; - -#ifdef DEBUG_MAGIC - if (bh->magic != 020167) { - printk(KERN_DEBUG "BufPoolRelease: not a BufHeader\n"); - printk(KERN_DEBUG "called from %x\n", __builtin_return_address(0)); - return; - } -#endif - - bp = bh->bp; - -#ifdef DEBUG_MAGIC - if (bp->magic != 010167) { - printk(KERN_DEBUG "BufPoolRelease: not a BufPool\n"); - return; - } -#endif - - save_flags(flags); - cli(); - bh->next = bp->freelist; - bp->freelist = bh; - restore_flags(flags); -} - -void -BufQueueLink(struct BufQueue *bq, - struct BufHeader *bh) -{ - unsigned long flags; - - save_flags(flags); - cli(); - if (!bq->head) - bq->head = bh; - if (bq->tail) - bq->tail->next = bh; - bq->tail = bh; - bh->next = NULL; - restore_flags(flags); -} - -void -BufQueueLinkFront(struct BufQueue *bq, - struct BufHeader *bh) -{ - unsigned long flags; - - save_flags(flags); - cli(); - bh->next = bq->head; - bq->head = bh; - if (!bq->tail) - bq->tail = bh; - restore_flags(flags); -} - -int -BufQueueUnlink(struct BufHeader **bh, struct BufQueue *bq) -{ - long flags; - - save_flags(flags); - cli(); - - if (bq->head) { - if (bq->tail == bq->head) - bq->tail = NULL; - *bh = bq->head; - bq->head = (*bh)->next; - restore_flags(flags); - return (0); - } else { - restore_flags(flags); - return (-1); - } -} - -void -BufQueueInit(struct BufQueue *bq) -{ -#ifdef DEBUG_MAGIC - bq->magic = 030167; -#endif - bq->head = NULL; - bq->tail = NULL; -} - -void -BufQueueRelease(struct BufQueue *bq) -{ - struct BufHeader *bh; - - while (bq->head) { - BufQueueUnlink(&bh, bq); - BufPoolRelease(bh); - } -} - -int -BufQueueLength(struct BufQueue *bq) -{ - int i = 0; - struct BufHeader *bh; - - bh = bq->head; - while (bh) { - i++; - bh = bh->next; - } - return (i); -} - -void -BufQueueDiscard(struct BufQueue *q, int pr, void *heldby, - int releasetoo) -{ - long flags; - struct BufHeader *sp; - - save_flags(flags); - cli(); - - while (!0) { - sp = q->head; - if (!sp) - break; - if ((sp->primitive == pr) && (sp->heldby == heldby)) { - q->head = sp->next; - if (q->tail == sp) - q->tail = NULL; - if (releasetoo) - BufPoolRelease(sp); - } else - break; - } - - sp = q->head; - if (sp) - while (sp->next) { - if ((sp->next->primitive == pr) && (sp->next->heldby == heldby)) { - if (q->tail == sp->next) - q->tail = sp; - if (releasetoo) - BufPoolRelease(sp->next); - sp->next = sp->next->next; - } else - sp = sp->next; - } - restore_flags(flags); -} - -void -Sfree(byte * ptr) -{ -#if 0 - printk(KERN_DEBUG "Sfree %x\n", ptr); -#endif - kfree(ptr); -} - -byte * -Smalloc(int size, int pr, char *why) -{ - byte *p; - - p = (byte *) kmalloc(size, pr); -#if 0 - printk(KERN_DEBUG "Smalloc %s size %d res %x\n", why, size, p); -#endif - return (p); -} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/callc.c linux/drivers/isdn/teles/callc.c --- v2.0.30/linux/drivers/isdn/teles/callc.c Tue Nov 12 22:36:20 1996 +++ linux/drivers/isdn/teles/callc.c Wed Dec 31 16:00:00 1969 @@ -1,1482 +0,0 @@ -/* $Id: callc.c,v 1.14 1996/10/22 23:14:14 fritz Exp $ - * - * $Log: callc.c,v $ - * Revision 1.14 1996/10/22 23:14:14 fritz - * Changes for compatibility to 2.0.X and 2.1.X kernels. - * - * Revision 1.13 1996/06/24 17:15:55 fritz - * corrected return code of teles_writebuf() - * - * Revision 1.12 1996/06/12 16:15:33 fritz - * Extended user-configurable debugging flags. - * - * Revision 1.11 1996/06/07 12:32:20 fritz - * More changes to support suspend/resume. - * - * Revision 1.10 1996/06/06 21:24:21 fritz - * Started adding support for suspend/resume. - * - * Revision 1.9 1996/05/31 12:23:57 jdenoud - * Jan: added channel open check to teles_writebuf - * - * Revision 1.8 1996/05/31 01:00:38 fritz - * Changed return code of teles_writebuf, when out of memory. - * - * Revision 1.7 1996/05/17 03:40:37 fritz - * General cleanup. - * - * Revision 1.6 1996/05/10 22:42:07 fritz - * Added entry for EV_RELEASE_CNF in ST_OUT (if no D-Channel avail.) - * - * Revision 1.5 1996/05/06 10:16:15 fritz - * Added voice stuff. - * - * Revision 1.4 1996/04/30 22:04:05 isdn4dev - * improved callback Karsten Keil - * - * Revision 1.3 1996/04/30 10:04:19 fritz - * Started voice support. - * Added printk() to debug-switcher for easier - * synchronization between printk()'s and output - * of /dev/isdnctrl. - * - * Revision 1.2 1996/04/20 16:42:29 fritz - * Changed statemachine to allow reject of incoming calls. - * - * Revision 1.1 1996/04/13 10:20:59 fritz - * Initial revision - * - * - */ -#define __NO_VERSION__ -#include "teles.h" - -extern struct IsdnCard cards[]; -extern int nrcards; -extern int drid; -extern isdn_if iif; -extern void teles_mod_dec_use_count(void); -extern void teles_mod_inc_use_count(void); - -static int init_ds(int chan, int incoming); -static void release_ds(int chan); -static char *strcpyupto(char *dest, char *src, char upto); - -static struct Fsm callcfsm = -{NULL, 0, 0}, lcfsm = -{NULL, 0, 0}; - -struct Channel *chanlist; -static int chancount = 0; -unsigned int debugflags = 0; - -#define TMR_DCHAN_EST 2000 - -static void -stat_debug(struct Channel *chanp, char *s) -{ - char tmp[100], tm[32]; - - jiftime(tm, jiffies); - sprintf(tmp, "%s Channel %d HL->LL %s\n", tm, chanp->chan, s); - teles_putstatus(tmp); -} - -enum { - ST_NULL, /* 0 inactive */ - ST_OUT, /* 1 outgoing, awaiting SETUP confirm */ - ST_CLEAR, /* 2 call release, awaiting RELEASE confirm */ - ST_OUT_W, /* 3 outgoing, awaiting d-channel establishment */ - ST_REL_W, /* 4 awaiting d-channel release */ - ST_IN_W, /* 5 incoming, awaiting d-channel establishment */ - ST_IN, /* 6 incoming call received */ - ST_IN_SETUP, /* 7 incoming, SETUP response sent */ - ST_IN_DACT, /* 8 incoming connected, no b-channel prot. */ - ST_OUT_ESTB, /* 10 outgoing connected, awaiting b-channel prot. estbl. */ - ST_ACTIVE, /* 11 active, b channel prot. established */ - ST_BC_HANGUP, /* 12 call clear. (initiator), awaiting b channel prot. rel. */ - ST_PRO_W, /* 13 call clear. (initiator), DISCONNECT req. sent */ - ST_ANT_W, /* 14 call clear. (receiver), awaiting DISCONNECT ind. */ - ST_DISC_BC_HANGUP, /* d channel gone, wait for b channel deactivation */ - ST_OUT_W_HANGUP, /* Outgoing waiting for D-Channel hangup received */ - ST_D_ERR, /* d channel released while active */ -}; - -#define STATE_COUNT (ST_D_ERR+1) - -static char *strState[] = -{ - "ST_NULL", - "ST_OUT", - "ST_CLEAR", - "ST_OUT_W", - "ST_REL_W", - "ST_IN_W", - "ST_IN", - "ST_IN_SETUP", - "ST_IN_DACT", - "ST_OUT_ESTB", - "ST_ACTIVE", - "ST_BC_HANGUP", - "ST_PRO_W", - "ST_ANT_W", - "ST_DISC_BC_HANGUP", - "ST_OUT_W_HANGUP", - "ST_D_ERR", -}; - -enum { - EV_DIAL, /* 0 */ - EV_SETUP_CNF, /* 1 */ - EV_ACCEPTB, /* 2 */ - EV_DISCONNECT_CNF, /* 5 */ - EV_DISCONNECT_IND, /* 6 */ - EV_RELEASE_CNF, /* 7 */ - EV_DLEST, /* 8 */ - EV_DLRL, /* 9 */ - EV_SETUP_IND, /* 10 */ - EV_RELEASE_IND, /* 11 */ - EV_ACCEPTD, /* 12 */ - EV_SETUP_CMPL_IND, /* 13 */ - EV_BC_EST, /* 14 */ - EV_WRITEBUF, /* 15 */ - EV_DATAIN, /* 16 */ - EV_HANGUP, /* 17 */ - EV_BC_REL, /* 18 */ - EV_CINF, /* 19 */ - EV_SUSPEND, /* 20 */ - EV_RESUME, /* 21 */ -}; - -#define EVENT_COUNT (EV_CINF+1) - -static char *strEvent[] = -{ - "EV_DIAL", - "EV_SETUP_CNF", - "EV_ACCEPTB", - "EV_DISCONNECT_CNF", - "EV_DISCONNECT_IND", - "EV_RELEASE_CNF", - "EV_DLEST", - "EV_DLRL", - "EV_SETUP_IND", - "EV_RELEASE_IND", - "EV_ACCEPTD", - "EV_SETUP_CMPL_IND", - "EV_BC_EST", - "EV_WRITEBUF", - "EV_DATAIN", - "EV_HANGUP", - "EV_BC_REL", - "EV_CINF", - "EV_SUSPEND", - "EV_RESUME", -}; - -enum { - ST_LC_NULL, - ST_LC_ACTIVATE_WAIT, - ST_LC_DELAY, - ST_LC_ESTABLISH_WAIT, - ST_LC_CONNECTED, - ST_LC_RELEASE_WAIT, -}; - -#define LC_STATE_COUNT (ST_LC_RELEASE_WAIT+1) - -static char *strLcState[] = -{ - "ST_LC_NULL", - "ST_LC_ACTIVATE_WAIT", - "ST_LC_DELAY", - "ST_LC_ESTABLISH_WAIT", - "ST_LC_CONNECTED", - "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_RELEASE, - EV_LC_RELEASE, -}; - -#define LC_EVENT_COUNT (EV_LC_RELEASE+1) - -static char *strLcEvent[] = -{ - "EV_LC_ESTABLISH", - "EV_LC_PH_ACTIVATE", - "EV_LC_PH_DEACTIVATE", - "EV_LC_DL_ESTABLISH", - "EV_LC_TIMER", - "EV_LC_DL_RELEASE", - "EV_LC_RELEASE", -}; - -#define LC_D 0 -#define LC_B 1 - -static int -my_atoi(char *s) -{ - int i, n; - - n = 0; - if (!s) - return -1; - for (i = 0; *s >= '0' && *s <= '9'; i++, s++) - n = 10 * n + (*s - '0'); - return n; -} - -/* - * Dial out - */ -static void -r1(struct FsmInst *fi, int event, void *arg) -{ - isdn_ctrl *ic = arg; - struct Channel *chanp = fi->userdata; - char *ptr; - char sis[3]; - - /* Destination Phone-Number */ - ptr = strcpyupto(chanp->para.called, ic->num, ','); - /* Source Phone-Number */ - ptr = strcpyupto(chanp->para.calling, ptr + 1, ','); - if (!strcmp(chanp->para.calling, "0")) - chanp->para.calling[0] = '\0'; - - /* Service-Indicator 1 */ - ptr = strcpyupto(sis, ptr + 1, ','); - chanp->para.info = my_atoi(sis); - - /* Service-Indicator 2 */ - ptr = strcpyupto(sis, ptr + 1, '\0'); - chanp->para.info2 = my_atoi(sis); - - 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 "r1 unknown protocol\n"); - break; - } - - FsmChangeState(fi, ST_OUT_W); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL); -} - -static void -ll_hangup(struct Channel *chanp, int bchantoo) -{ - isdn_ctrl ic; - - if (bchantoo) { - if (chanp->debug & 1) - stat_debug(chanp, "STAT_BHUP"); - ic.driver = drid; - ic.command = ISDN_STAT_BHUP; - ic.arg = chanp->chan; - iif.statcallb(&ic); - } - if (chanp->debug & 1) - stat_debug(chanp, "STAT_DHUP"); - ic.driver = drid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - iif.statcallb(&ic); -} - -static void -r2(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL); - - FsmChangeState(fi, ST_CLEAR); - ll_hangup(chanp, 0); -} - - -static void -r2_1(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL); - - FsmChangeState(fi, ST_OUT_W_HANGUP); -} - - -static void -r2_2(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - FsmChangeState(fi, ST_REL_W); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); - ll_hangup(chanp, 0); -} - - -static void -r3(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); - FsmChangeState(fi, ST_REL_W); -} - - -static void -r3_1(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL); - - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); - FsmChangeState(fi, ST_REL_W); - ll_hangup(chanp, 0); -} - - -static void -r4(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp=fi->userdata; - - chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL); - FsmChangeState(fi, ST_NULL); -} - -static void -r5(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - chanp->para.callref = chanp->outcallref; - - chanp->outcallref++; - if (chanp->outcallref == 128) - chanp->outcallref = 64; - - chanp->is.l4.l4l3(&chanp->is, CC_SETUP_REQ, NULL); - - FsmChangeState(fi, ST_OUT); -} - -static void -r6(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - FsmChangeState(fi, ST_IN_W); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL); -} - -static void -r7(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; - - /* - * Report incoming calls only once to linklevel, use octet 3 of - * channel identification information element. (it's value - * is copied to chanp->para.bchannel in l3s12(), file isdnl3.c) - */ - if (((chanp->chan & 1) + 1) & chanp->para.bchannel) { - chanp->is.l4.l4l3(&chanp->is, CC_ALERTING_REQ, NULL); - FsmChangeState(fi, ST_IN); - if (chanp->debug & 1) - stat_debug(chanp, "STAT_ICALL"); - ic.driver = drid; - 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') - */ - sprintf(ic.num, "%s,%d,0,%s", chanp->para.calling, chanp->para.info, - chanp->para.called); - iif.statcallb(&ic); - } else { - chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); - FsmChangeState(fi, ST_REL_W); - } -} - -static void -r8(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - FsmChangeState(fi, ST_IN_SETUP); - chanp->is.l4.l4l3(&chanp->is, CC_SETUP_RSP, NULL); - -} - -static void -r9(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - FsmChangeState(fi, ST_IN_DACT); - - 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->chan, !0); - - FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL); -} - -static void -r10(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - FsmChangeState(fi, ST_OUT_ESTB); - - init_ds(chanp->chan, 0); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL); - -} - -static void -r12(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; - - FsmChangeState(fi, ST_ACTIVE); - chanp->data_open = !0; - - if (chanp->debug & 1) - stat_debug(chanp, "STAT_DCONN"); - ic.driver = drid; - ic.command = ISDN_STAT_DCONN; - ic.arg = chanp->chan; - iif.statcallb(&ic); - - if (chanp->debug & 1) - stat_debug(chanp, "STAT_BCONN"); - ic.driver = drid; - ic.command = ISDN_STAT_BCONN; - ic.arg = chanp->chan; - iif.statcallb(&ic); -} - -static void -r15(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - chanp->data_open = 0; - FsmChangeState(fi, ST_BC_HANGUP); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); -} - -static void -r16(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - release_ds(chanp->chan); - - FsmChangeState(fi, ST_PRO_W); - chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL); -} - -static void -r17(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - chanp->data_open = 0; - release_ds(chanp->chan); - - FsmChangeState(fi, ST_ANT_W); -} - - -static void -r17_1(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - chanp->data_open = 0; - release_ds(chanp->chan); - - chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL); - - FsmEvent(&chanp->lc_d.lcfi,EV_LC_RELEASE,NULL); - - FsmChangeState(fi, ST_NULL); - - ll_hangup(chanp,!0); -} - -static void -r18(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - FsmChangeState(fi, ST_REL_W); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); - - ll_hangup(chanp, !0); -} - -static void -r19(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - FsmChangeState(fi, ST_CLEAR); - - chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL); - - ll_hangup(chanp, !0); -} - -static void -r20(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL); - - FsmEvent(&chanp->lc_d.lcfi,EV_LC_RELEASE,NULL); - - FsmChangeState(fi, ST_NULL); - - ll_hangup(chanp, 0); -} - - -static void -r21(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - chanp->data_open = 0; - FsmChangeState(fi, ST_DISC_BC_HANGUP); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); -} - -static void -r22(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - release_ds(chanp->chan); - - FsmChangeState(fi, ST_CLEAR); - - chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL); - - ll_hangup(chanp, !0); -} - -static void -r23(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - release_ds(chanp->chan); - - FsmChangeState(fi, ST_PRO_W); - chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL); -} - -static void -r23_1(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - release_ds(chanp->chan); - - chanp->is.l4.l4l3(&chanp->is, CC_DLRL,NULL); - - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE,NULL); - - FsmChangeState(fi, ST_NULL); - - ll_hangup(chanp,!0); -} - -static void -r24(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - chanp->data_open = 0; - FsmChangeState(fi, ST_D_ERR); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); -} - -static void -r25(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - release_ds(chanp->chan); - - FsmChangeState(fi, ST_NULL); - - ll_hangup(chanp, !0); -} - -static void -r26(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; - - - ic.driver = drid; - ic.command = ISDN_STAT_CINF; - ic.arg = chanp->chan; - sprintf(ic.num, "%d", chanp->para.chargeinfo); - iif.statcallb(&ic); -} - - - -static struct FsmNode fnlist[] = -{ - {ST_NULL, EV_DIAL, r1}, - {ST_OUT_W, EV_DLEST, r5}, - {ST_OUT_W, EV_DLRL, r20}, - {ST_OUT_W, EV_RELEASE_CNF, r2_2 }, - {ST_OUT, EV_DISCONNECT_IND, r2}, - {ST_OUT, EV_SETUP_CNF, r10}, - {ST_OUT, EV_HANGUP, r2_1}, - {ST_OUT, EV_RELEASE_IND, r20}, - {ST_OUT, EV_RELEASE_CNF, r20}, - {ST_OUT, EV_DLRL, r2_2}, - {ST_OUT_W_HANGUP, EV_RELEASE_IND, r2_2}, - {ST_OUT_W_HANGUP, EV_DLRL, r20}, - {ST_CLEAR, EV_RELEASE_CNF, r3}, - {ST_CLEAR, EV_DLRL, r20}, - {ST_REL_W, EV_DLRL, r4}, - {ST_NULL, EV_SETUP_IND, r6}, - {ST_IN_W, EV_DLEST, r7}, - {ST_IN_W, EV_DLRL, r3_1}, - {ST_IN, EV_DLRL, r3_1}, - {ST_IN, EV_HANGUP, r2_1}, - {ST_IN, EV_RELEASE_IND, r2_2}, - {ST_IN, EV_RELEASE_CNF, r2_2}, - {ST_IN, EV_ACCEPTD, r8}, - {ST_IN_SETUP, EV_HANGUP, r2_1}, - {ST_IN_SETUP, EV_SETUP_CMPL_IND, r9}, - {ST_IN_SETUP, EV_RELEASE_IND, r2_2}, - {ST_IN_SETUP, EV_DISCONNECT_IND, r2}, - {ST_IN_SETUP, EV_DLRL, r20}, - {ST_OUT_ESTB, EV_BC_EST, r12}, - {ST_OUT_ESTB, EV_BC_REL, r23}, - {ST_OUT_ESTB, EV_DLRL, r23_1}, - {ST_IN_DACT, EV_BC_EST, r12}, - {ST_IN_DACT, EV_BC_REL, r17}, - {ST_IN_DACT, EV_DLRL, r17_1}, - {ST_ACTIVE, EV_HANGUP, r15}, - {ST_ACTIVE, EV_BC_REL, r17}, - {ST_ACTIVE, EV_DISCONNECT_IND, r21}, - {ST_ACTIVE, EV_DLRL, r24}, - {ST_ACTIVE, EV_CINF, r26}, - {ST_ACTIVE, EV_RELEASE_IND, r17}, - {ST_BC_HANGUP, EV_BC_REL, r16}, - {ST_BC_HANGUP, EV_DISCONNECT_IND, r21}, - {ST_PRO_W, EV_RELEASE_IND, r18}, - {ST_ANT_W, EV_DISCONNECT_IND, r19}, - {ST_DISC_BC_HANGUP, EV_BC_REL, r22}, - {ST_D_ERR, EV_BC_REL, r25}, -}; - -#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_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); - } else { - FsmChangeState(fi, ST_LC_NULL); - lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL); - lf->lccall(lf, LC_RELEASE, NULL); - } -} - -static void -lc_r5(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - FsmChangeState(fi, ST_LC_NULL); - lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL); - lf->lccall(lf, LC_RELEASE, NULL); -} - -static struct FsmNode LcFnList[] = -{ - {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_ESTABLISH_WAIT, EV_LC_DL_ESTABLISH, lc_r3}, - {ST_LC_CONNECTED, EV_LC_RELEASE, lc_r4}, - {ST_LC_CONNECTED, EV_LC_DL_RELEASE, lc_r5}, - {ST_LC_RELEASE_WAIT, EV_LC_DL_RELEASE, lc_r5}, - {ST_LC_ACTIVATE_WAIT, EV_LC_TIMER, lc_r5}, - {ST_LC_ESTABLISH_WAIT, EV_LC_DL_RELEASE, lc_r5}, -}; - -#define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode)) - -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(int chan) -{ - struct PStack *st = &chanlist[chan].ds; - struct IsdnCardState *sp; - struct HscxState *hsp; - - sp = st->l1.hardware; - hsp = sp->hs + chanlist[chan].hscx; - - close_hscxstate(hsp); - - switch (chanlist[chan].l2_active_protocol) { - case (ISDN_PROTO_L2_X75I): - releasestack_isdnl2(st); - break; - case (ISDN_PROTO_L2_HDLC): - case (ISDN_PROTO_L2_TRANS): - releasestack_transl2(st); - break; - } -} - -static void -cc_l1man(struct PStack *st, int pr, void *arg) -{ - struct Channel *chanp = (struct Channel *) st->l4.userdata; - - 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; - } -} - -static void -cc_l2man(struct PStack *st, int pr, void *arg) -{ - struct Channel *chanp = (struct Channel *) st->l4.userdata; - - 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; - } -} - -static void -dcc_l1man(struct PStack *st, int pr, void *arg) -{ - struct Channel *chanp = (struct Channel *) st->l4.userdata; - - 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); - 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); - break; - case (DL_RELEASE): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_RELEASE, NULL); - break; - } -} - -static void -ll_handler(struct PStack *st, int pr, - struct BufHeader *ibh) -{ - struct Channel *chanp = (struct Channel *) st->l4.userdata; - - switch (pr) { - case (CC_DISCONNECT_IND): - FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL); - break; - case (CC_RELEASE_CNF): - FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL); - break; - case (CC_SETUP_IND): - FsmEvent(&chanp->fi, EV_SETUP_IND, NULL); - break; - case (CC_RELEASE_IND): - FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL); - break; - case (CC_SETUP_COMPLETE_IND): - FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL); - break; - case (CC_SETUP_CNF): - FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL); - break; - case (CC_INFO_CHARGE): - FsmEvent(&chanp->fi, EV_CINF, NULL); - break; - } -} - -static void -init_is(int chan, unsigned int ces) -{ - struct PStack *st = &(chanlist[chan].is); - struct IsdnCardState *sp = chanlist[chan].sp; - char tmp[128]; - - setstack_teles(st, sp); - - st->l2.sap = 0; - - st->l2.tei = 255; - - st->l2.ces = ces; - st->l2.extended = !0; - st->l2.laptype = LAPD; - 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", chan); - setstack_isdnl2(st, tmp); - setstack_isdnl3(st); - st->l2.debug = 2; - st->l3.debug = 2; - st->l2.debug = 0xff; - st->l3.debug = 0xff; - st->l4.userdata = chanlist + chan; - st->l4.l2writewakeup = NULL; - - st->l3.l3l4 = ll_handler; - st->l1.l1man = cc_l1man; - st->l2.l2man = cc_l2man; - - st->pa = &chanlist[chan].para; - teles_addlist(sp, st); -} - -static void -callc_debug(struct FsmInst *fi, char *s) -{ - char str[80], tm[32]; - struct Channel *chanp = fi->userdata; - - jiftime(tm, jiffies); - sprintf(str, "%s Channel %d callc %s\n", tm, chanp->chan, s); - teles_putstatus(str); -} - -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); - teles_putstatus(str); -} - -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); - teles_putstatus(str); -} - -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, int cardnr, int hscx, - unsigned int ces) -{ - struct IsdnCard *card = cards + cardnr; - struct Channel *chanp = chanlist + chan; - - chanp->sp = card->sp; - chanp->hscx = hscx; - chanp->chan = chan; - chanp->incoming = 0; - chanp->debug = 0; - init_is(chan, ces); - - chanp->fi.fsm = &callcfsm; - chanp->fi.state = ST_NULL; - chanp->fi.debug = 0; - chanp->fi.userdata = chanp; - chanp->fi.printdebug = callc_debug; - - 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; - chanp->data_open = 0; -} - -int -CallcNewChan(void) -{ - int i, ces, c; - - chancount = 0; - for (i = 0; i < nrcards; i++) - if (cards[i].sp) - chancount += 2; - - chanlist = (struct Channel *) Smalloc(sizeof(struct Channel) * - chancount, GFP_KERNEL, "chanlist"); - - c = 0; - ces = randomces(); - for (i = 0; i < nrcards; i++) - if (cards[i].sp) { - init_chan(c++, i, 1, ces++); - ces %= 0xffff; - init_chan(c++, i, 0, ces++); - ces %= 0xffff; - } - printk(KERN_INFO "channels %d\n", chancount); - return (chancount); - -} - -static void -release_is(int chan) -{ - struct PStack *st = &chanlist[chan].is; - - releasestack_isdnl2(st); - teles_rmlist(st->l1.hardware, st); - BufQueueRelease(&st->l2.i_queue); -} - -void -CallcFreeChan(void) -{ - int i; - - for (i = 0; i < chancount; i++) - release_is(i); - Sfree((void *) chanlist); -} - -static void -lldata_handler(struct PStack *st, int pr, - void *arg) -{ - struct Channel *chanp = (struct Channel *) st->l4.userdata; - byte *ptr; - int size; - struct BufHeader *ibh = arg; - - switch (pr) { - case (DL_DATA): - if (chanp->data_open) { - ptr = DATAPTR(ibh); - ptr += chanp->ds.l2.ihsize; - size = ibh->datasize - chanp->ds.l2.ihsize; - iif.rcvcallb(drid, chanp->chan, ptr, size); - } - BufPoolRelease(ibh); - break; - default: - printk(KERN_WARNING "lldata_handler unknown primitive\n"); - break; - } -} - -static void -lltrans_handler(struct PStack *st, int pr, - struct BufHeader *ibh) -{ - struct Channel *chanp = (struct Channel *) st->l4.userdata; - byte *ptr; - - switch (pr) { - case (PH_DATA): - if (chanp->data_open) { - ptr = DATAPTR(ibh); - iif.rcvcallb(drid, chanp->chan, ptr, ibh->datasize); - } - BufPoolRelease(ibh); - break; - default: - printk(KERN_WARNING "lltrans_handler unknown primitive\n"); - break; - } -} - -static void -ll_writewakeup(struct PStack *st) -{ - struct Channel *chanp = st->l4.userdata; - isdn_ctrl ic; - - ic.driver = drid; - ic.command = ISDN_STAT_BSENT; - ic.arg = chanp->chan; - iif.statcallb(&ic); -} - -static int -init_ds(int chan, int incoming) -{ - struct PStack *st = &(chanlist[chan].ds); - struct IsdnCardState *sp = (struct IsdnCardState *) - chanlist[chan].is.l1.hardware; - struct HscxState *hsp = sp->hs + chanlist[chan].hscx; - char tmp[128]; - - st->l1.hardware = sp; - - hsp->mode = 2; - hsp->transbufsize = 4000; - - if (setstack_hscx(st, hsp)) - return (-1); - - st->l2.extended = 0; - st->l2.laptype = LAPB; - st->l2.orig = !incoming; - st->l2.t200 = 1000; /* 1000 milliseconds */ - st->l2.window = 3; - st->l2.n200 = 4; /* try 4 times */ - st->l2.t203 = 5000; /* 5000 milliseconds */ - - st->l2.debug = 0xff; - st->l3.debug = 0xff; - switch (chanlist[chan].l2_active_protocol) { - case (ISDN_PROTO_L2_X75I): - sprintf(tmp, "Channel %d x.75", chan); - setstack_isdnl2(st, tmp); - st->l2.l2l3 = lldata_handler; - st->l1.l1man = dcc_l1man; - st->l2.l2man = dcc_l2man; - st->l4.userdata = chanlist + chan; - st->l4.l1writewakeup = NULL; - st->l4.l2writewakeup = ll_writewakeup; - st->l2.l2m.debug = debugflags & 16; - st->ma.manl2(st, MDL_NOTEIPROC, NULL); - st->l1.hscxmode = 2; /* Packet-Mode ? */ - st->l1.hscxchannel = chanlist[chan].para.bchannel - 1; - break; - case (ISDN_PROTO_L2_HDLC): - st->l1.l1l2 = lltrans_handler; - st->l1.l1man = dcc_l1man; - st->l4.userdata = chanlist + chan; - st->l4.l1writewakeup = ll_writewakeup; - st->l1.hscxmode = 2; - st->l1.hscxchannel = chanlist[chan].para.bchannel - 1; - break; - case (ISDN_PROTO_L2_TRANS): - st->l1.l1l2 = lltrans_handler; - st->l1.l1man = dcc_l1man; - st->l4.userdata = chanlist + chan; - st->l4.l1writewakeup = ll_writewakeup; - st->l1.hscxmode = 1; - st->l1.hscxchannel = chanlist[chan].para.bchannel - 1; - break; - } - - return (0); - -} - -static void -channel_report(int i) -{ -} - -static void -command_debug(struct Channel *chanp, char *s) -{ - char tmp[64], tm[32]; - - jiftime(tm, jiffies); - sprintf(tmp, "%s Channel %d LL->HL %s\n", tm, chanp->chan, s); - teles_putstatus(tmp); -} - -static void -distr_debug(void) -{ - int i; - - for (i = 0; i < chancount; i++) { - chanlist[i].debug = debugflags & 1; - chanlist[i].fi.debug = debugflags & 2; - chanlist[i].is.l2.l2m.debug = debugflags & 8; - chanlist[i].ds.l2.l2m.debug = debugflags & 16; - } - for (i = 0; i < nrcards; i++) - if (cards[i].sp) { - cards[i].sp->dlogflag = debugflags & 4; - cards[i].sp->debug = debugflags & 32; - } -} - -int -teles_command(isdn_ctrl * ic) -{ - struct Channel *chanp; - char tmp[64]; - int i; - unsigned int num; - - switch (ic->command) { - case (ISDN_CMD_SETEAZ): - chanp = chanlist + ic->arg; - if (chanp->debug & 1) - command_debug(chanp, "SETEAZ"); - return (0); - case (ISDN_CMD_DIAL): - chanp = chanlist + (ic->arg & 0xff); - if (chanp->debug & 1) { - sprintf(tmp, "DIAL %s", ic->num); - command_debug(chanp, tmp); - } - FsmEvent(&chanp->fi, EV_DIAL, ic); - return (0); - case (ISDN_CMD_ACCEPTB): - chanp = chanlist + ic->arg; - if (chanp->debug & 1) - command_debug(chanp, "ACCEPTB"); - FsmEvent(&chanp->fi, EV_ACCEPTB, NULL); - break; - case (ISDN_CMD_ACCEPTD): - chanp = chanlist + ic->arg; - if (chanp->debug & 1) - command_debug(chanp, "ACCEPTD"); - FsmEvent(&chanp->fi, EV_ACCEPTD, NULL); - break; - case (ISDN_CMD_HANGUP): - chanp = chanlist + ic->arg; - if (chanp->debug & 1) - command_debug(chanp, "HANGUP"); - FsmEvent(&chanp->fi, EV_HANGUP, NULL); - break; - case (ISDN_CMD_SUSPEND): - chanp = chanlist + ic->arg; - if (chanp->debug & 1) { - sprintf(tmp, "SUSPEND %s", ic->num); - command_debug(chanp, tmp); - } - FsmEvent(&chanp->fi, EV_SUSPEND, ic); - break; - case (ISDN_CMD_RESUME): - chanp = chanlist + ic->arg; - if (chanp->debug & 1) { - sprintf(tmp, "RESUME %s", ic->num); - command_debug(chanp, tmp); - } - FsmEvent(&chanp->fi, EV_RESUME, ic); - break; - case (ISDN_CMD_LOCK): - teles_mod_inc_use_count(); - break; - case (ISDN_CMD_UNLOCK): - teles_mod_dec_use_count(); - break; - case (ISDN_CMD_IOCTL): - switch (ic->arg) { - case (0): - for (i = 0; i < nrcards; i++) - if (cards[i].sp) - teles_reportcard(i); - for (i = 0; i < chancount; i++) - channel_report(i); - break; - case (1): - debugflags = *(unsigned int *) ic->num; - distr_debug(); - sprintf(tmp, "debugging flags set to %x\n", debugflags); - teles_putstatus(tmp); - printk(KERN_DEBUG "%s", tmp); - break; - case (2): - num = *(unsigned int *) ic->num; - i = num >> 8; - if (i >= chancount) - break; - chanp = chanlist + i; - chanp->impair = num & 0xff; - if (chanp->debug & 1) { - sprintf(tmp, "IMPAIR %x", chanp->impair); - command_debug(chanp, tmp); - } - break; - } - break; - case (ISDN_CMD_SETL2): - chanp = chanlist + (ic->arg & 0xff); - if (chanp->debug & 1) { - sprintf(tmp, "SETL2 %ld", ic->arg >> 8); - command_debug(chanp, tmp); - } - chanp->l2_protocol = ic->arg >> 8; - break; - default: - break; - } - - return (0); -} - -int -teles_writebuf(int id, int chan, const u_char * buf, int count, int user) -{ - struct Channel *chanp = chanlist + chan; - struct PStack *st = &chanp->ds; - struct BufHeader *ibh; - int err, i; - byte *ptr; - - if (!chanp->data_open) { - printk(KERN_DEBUG "teles_writebuf: channel not open\n"); - return -EIO; - } - - err = BufPoolGet(&ibh, st->l1.sbufpool, GFP_ATOMIC, st, 21); - if (err) - /* Must return 0 here, since this is not an error - * but a temporary lack of resources. - */ - return 0; - - ptr = DATAPTR(ibh); - if (chanp->lc_b.l2_establish) - i = st->l2.ihsize; - else - i = 0; - - if ((count+i) > BUFFER_SIZE(HSCX_SBUF_ORDER, HSCX_SBUF_BPPS)) { - printk(KERN_WARNING "teles_writebuf: packet too large!\n"); - return (-EINVAL); - } - - ptr += i; - - if (user) - copy_from_user(ptr, buf, count); - else - memcpy(ptr, buf, count); - ibh->datasize = count + i; - - if (chanp->data_open) { - if (chanp->lc_b.l2_establish) - chanp->ds.l3.l3l2(&chanp->ds, DL_DATA, ibh); - else - chanp->ds.l2.l2l1(&chanp->ds, PH_DATA, ibh); - return (count); - } else { - BufPoolRelease(ibh); - return (0); - } - -} - -static char * -strcpyupto(char *dest, char *src, char upto) -{ - while (*src && (*src != upto) && (*src != '\0')) - *dest++ = *src++; - *dest = '\0'; - return (src); -} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/card.c linux/drivers/isdn/teles/card.c --- v2.0.30/linux/drivers/isdn/teles/card.c Tue Nov 12 22:36:20 1996 +++ linux/drivers/isdn/teles/card.c Wed Dec 31 16:00:00 1969 @@ -1,1900 +0,0 @@ -/* $Id: card.c,v 1.16 1996/10/22 23:14:16 fritz Exp $ - * - * card.c low level stuff for the Teles S0 isdn card - * - * Author Jan den Ouden - * - * Beat Doebeli log all D channel traffic - * - * $Log: card.c,v $ - * Revision 1.16 1996/10/22 23:14:16 fritz - * Changes for compatibility to 2.0.X and 2.1.X kernels. - * - * Revision 1.15 1996/09/29 19:41:56 fritz - * Bugfix: ignore unknown frames. - * - * Revision 1.14 1996/09/23 01:53:49 fritz - * Bugfix: discard unknown frames (non-EDSS1 and non-1TR6). - * - * Revision 1.13 1996/07/18 11:21:24 jdenoud - * Use small buffers for incoming audio data - * - * Revision 1.12 1996/06/24 17:16:52 fritz - * Added check for misconfigured membase. - * - * Revision 1.11 1996/06/14 03:30:37 fritz - * Added recovery from EXIR 40 interrupt. - * Some cleanup. - * - * Revision 1.10 1996/06/11 14:57:20 hipp - * minor changes to ensure, that SKBs are sent in the right order - * - * Revision 1.9 1996/06/06 14:42:09 fritz - * Bugfix: forgot hsp-> in last change. - * - * Revision 1.7 1996/05/31 01:02:21 fritz - * Cosmetic changes. - * - * Revision 1.6 1996/05/26 14:58:10 fritz - * Bugfix: Did not show port correctly, when no card found. - * - * Revision 1.5 1996/05/17 03:45:02 fritz - * Made error messages more clearly. - * Bugfix: Only 31 bytes of 32-byte audio frames - * have been transfered to upper layers. - * - * Revision 1.4 1996/05/06 10:17:57 fritz - * Added voice-send stuff - * (Not reporting EXIR when in voice-mode, since it's normal). - * - * Revision 1.3 1996/04/30 22:02:40 isdn4dev - * Bugfixes for 16.3 - * -improved IO allocation - * -fix second B channel problem - * -correct ph_command patch - * - * Revision 1.2 1996/04/30 10:00:59 fritz - * Bugfix: Added ph_command(8) for 16.3. - * Bugfix: Ports did not get registered correctly - * when using a 16.3. - * Started voice support. - * Some experimental changes of waitforXFW(). - * - * Revision 1.1 1996/04/13 10:22:42 fritz - * Initial revision - * - * - */ - -#define __NO_VERSION__ -#include "teles.h" -#include "proto.h" - -#define INCLUDE_INLINE_FUNCS -#include -#include - -#undef DCHAN_VERBOSE - -extern void tei_handler(struct PStack *st, byte pr, - struct BufHeader *ibh); -extern struct IsdnCard cards[]; -extern int nrcards; - -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) - -static inline byte -readisac_0(byte * cardm, byte offset) -{ - return readb(cardm + 0x100 + ((offset & 1) ? 0x1ff : 0) + offset); -} - -static inline byte -readisac_3(int iobase, byte offset) -{ - return (bytein(iobase - 0x420 + offset)); -} - -#define READISAC(mbase,ibase,ofs) \ - ((mbase)?readisac_0(mbase,ofs):readisac_3(ibase,ofs)) - -static inline void -writeisac_0(byte * cardm, byte offset, byte value) -{ - writeb(value, cardm + 0x100 + ((offset & 1) ? 0x1ff : 0) + offset); -} - -static inline void -writeisac_3(int iobase, byte offset, byte value) -{ - byteout(iobase - 0x420 + offset, value); -} - -#define WRITEISAC(mbase,ibase,ofs,val) \ - ((mbase)?writeisac_0(mbase,ofs,val):writeisac_3(ibase,ofs,val)) - -static inline void -readisac_s(int iobase, byte offset, byte * dest, int count) -{ - insb(iobase - 0x420 + offset, dest, count); -} - -static inline void -writeisac_s(int iobase, byte offset, byte * src, int count) -{ - outsb(iobase - 0x420 + offset, src, count); -} - -static inline byte -readhscx_0(byte * base, byte hscx, byte offset) -{ - return readb(base + 0x180 + ((offset & 1) ? 0x1FF : 0) + - ((hscx & 1) ? 0x40 : 0) + offset); -} - -static inline byte -readhscx_3(int iobase, byte hscx, byte offset) -{ - return (bytein(iobase - (hscx ? 0x820 : 0xc20) + offset)); -} - -#define READHSCX(mbase,ibase,hscx,ofs) \ - ((mbase)?readhscx_0(mbase,hscx,ofs):readhscx_3(ibase,hscx,ofs)) - -static inline void -writehscx_0(byte * base, byte hscx, byte offset, byte data) -{ - writeb(data, base + 0x180 + ((offset & 1) ? 0x1FF : 0) + - ((hscx & 1) ? 0x40 : 0) + offset); -} - -static inline void -writehscx_3(int iobase, byte hscx, byte offset, byte data) -{ - byteout(iobase - (hscx ? 0x820 : 0xc20) + offset, data); -} - -static inline void -readhscx_s(int iobase, byte hscx, byte offset, byte * dest, int count) -{ - insb(iobase - (hscx ? 0x820 : 0xc20) + offset, dest, count); -} - -static inline void -writehscx_s(int iobase, byte hscx, byte offset, byte * src, int count) -{ - outsb(iobase - (hscx ? 0x820 : 0xc20) + offset, src, count); -} - -#define ISAC_MASK 0x20 -#define ISAC_ISTA 0x20 -#define ISAC_STAR 0x21 -#define ISAC_CMDR 0x21 -#define ISAC_EXIR 0x24 - -#define ISAC_RBCH 0x2a - -#define ISAC_ADF2 0x39 -#define ISAC_SPCR 0x30 -#define ISAC_ADF1 0x38 -#define ISAC_CIX0 0x31 -#define ISAC_STCR 0x37 -#define ISAC_MODE 0x22 -#define ISAC_RSTA 0x27 -#define ISAC_RBCL 0x25 -#define ISAC_TIMR 0x23 -#define ISAC_SQXR 0x3b - -#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 - -static inline void -waitforCEC_0(byte * base, byte hscx) -{ - long to = 10; - - while ((readhscx_0(base, hscx, HSCX_STAR) & 0x04) && to) { - udelay(5); - to--; - } - if (!to) - printk(KERN_WARNING "waitforCEC timeout\n"); -} - -static inline void -waitforCEC_3(int iobase, byte hscx) -{ - long to = 10; - - while ((readhscx_3(iobase, hscx, HSCX_STAR) & 0x04) && to) { - udelay(5); - to--; - } - if (!to) - printk(KERN_WARNING "waitforCEC timeout\n"); -} - -static inline void -waitforXFW_0(byte * base, byte hscx) -{ - long to = 20; - - while ((!(readhscx_0(base, hscx, HSCX_STAR) & 0x44)==0x40) && to) { - udelay(5); - to--; - } - if (!to) - printk(KERN_WARNING "waitforXFW timeout\n"); -} - -static inline void -waitforXFW_3(int iobase, byte hscx) -{ - long to = 20; - - while ((!(readhscx_3(iobase, hscx, HSCX_STAR) & 0x44)==0x40) && to) { - udelay(5); - to--; - } - if (!to) - printk(KERN_WARNING "waitforXFW timeout\n"); -} - -static inline void -writehscxCMDR_0(byte * base, byte hscx, byte data) -{ - long flags; - - save_flags(flags); - cli(); - waitforCEC_0(base, hscx); - writehscx_0(base, hscx, HSCX_CMDR, data); - restore_flags(flags); -} - -static inline void -writehscxCMDR_3(int iobase, byte hscx, byte data) -{ - long flags; - - save_flags(flags); - cli(); - waitforCEC_3(iobase, hscx); - writehscx_3(iobase, hscx, HSCX_CMDR, data); - restore_flags(flags); -} - -#define WRITEHSCX_CMDR(mbase,ibase,hscx,data) \ - ((mbase)?writehscxCMDR_0(mbase,hscx,data):writehscxCMDR_3(ibase,hscx,data)) - -/* - * fast interrupt here - */ - -#define ISAC_RCVBUFREADY 0 -#define ISAC_XMTBUFREADY 1 -#define ISAC_PHCHANGE 2 - -#define HSCX_RCVBUFREADY 0 -#define HSCX_XMTBUFREADY 1 - -void -teles_hscxreport(struct IsdnCardState *sp, int hscx) -{ - printk(KERN_DEBUG "HSCX %d\n", hscx); - if (sp->membase) { - printk(KERN_DEBUG " ISTA %x\n", readhscx_0(sp->membase, - hscx, HSCX_ISTA)); - printk(KERN_DEBUG " STAR %x\n", readhscx_0(sp->membase, - hscx, HSCX_STAR)); - printk(KERN_DEBUG " EXIR %x\n", readhscx_0(sp->membase, - hscx, HSCX_EXIR)); - } else { - printk(KERN_DEBUG " ISTA %x\n", readhscx_3(sp->iobase, - hscx, HSCX_ISTA)); - printk(KERN_DEBUG " STAR %x\n", readhscx_3(sp->iobase, - hscx, HSCX_STAR)); - printk(KERN_DEBUG " EXIR %x\n", readhscx_3(sp->iobase, - hscx, HSCX_EXIR)); - } -} - -void -teles_report(struct IsdnCardState *sp) -{ - printk(KERN_DEBUG "ISAC\n"); - if (sp->membase) { - printk(KERN_DEBUG " ISTA %x\n", readisac_0(sp->membase, - ISAC_ISTA)); - printk(KERN_DEBUG " STAR %x\n", readisac_0(sp->membase, - ISAC_STAR)); - printk(KERN_DEBUG " EXIR %x\n", readisac_0(sp->membase, - ISAC_EXIR)); - } else { - printk(KERN_DEBUG " ISTA %x\n", readisac_3(sp->iobase, - ISAC_ISTA)); - printk(KERN_DEBUG " STAR %x\n", readisac_3(sp->iobase, - ISAC_STAR)); - printk(KERN_DEBUG " EXIR %x\n", readisac_3(sp->iobase, - ISAC_EXIR)); - } - teles_hscxreport(sp, 0); - teles_hscxreport(sp, 1); -} - -/* - * HSCX stuff goes here - */ - -static void -hscx_sched_event(struct HscxState *hsp, int event) -{ - hsp->event |= 1 << event; - queue_task_irq_off(&hsp->tqueue, &tq_immediate); - mark_bh(IMMEDIATE_BH); -} - -static void -hscx_empty_fifo(struct HscxState *hsp, int count) -{ - byte *ptr; - struct BufHeader *ibh = hsp->rcvibh; - - if (hsp->sp->debug) - printk(KERN_DEBUG "hscx_empty_fifo\n"); - - if (hsp->rcvptr + count > BUFFER_SIZE(HSCX_RBUF_ORDER, - HSCX_RBUF_BPPS)) { - printk(KERN_WARNING - "hscx_empty_fifo: incoming packet too large\n"); - WRITEHSCX_CMDR(hsp->membase, hsp->iobase, hsp->hscx, 0x80); - return; - } - ptr = DATAPTR(ibh); - ptr += hsp->rcvptr; - - hsp->rcvptr += count; - if (hsp->membase) { - while (count--) - *ptr++ = readhscx_0(hsp->membase, hsp->hscx, 0x0); - writehscxCMDR_0(hsp->membase, hsp->hscx, 0x80); - } else { - readhscx_s(hsp->iobase, hsp->hscx, 0x3e, ptr, count); - writehscxCMDR_3(hsp->iobase, hsp->hscx, 0x80); - } -} - -static void -hscx_fill_fifo(struct HscxState *hsp) -{ - struct BufHeader *ibh; - int more, count; - byte *ptr; - - if (hsp->sp->debug) - printk(KERN_DEBUG "hscx_fill_fifo\n"); - - ibh = hsp->xmtibh; - if (!ibh) - return; - - count = ibh->datasize - hsp->sendptr; - if (count <= 0) - return; - - more = (hsp->mode == 1)?1:0; - if (count > 32) { - more = !0; - count = 32; - } - ptr = DATAPTR(ibh); - ptr += hsp->sendptr; - hsp->sendptr += count; - -#ifdef BCHAN_VERBOSE - { - int i; - printk(KERN_DEBUG "hscx_fill_fifo "); - for (i = 0; i < count; i++) - printk(" %2x", ptr[i]); - printk("\n"); - } -#endif - if (hsp->membase) { - waitforXFW_0(hsp->membase, hsp->hscx); - while (count--) - writehscx_0(hsp->membase, hsp->hscx, 0x0, *ptr++); - writehscxCMDR_0(hsp->membase, hsp->hscx, more ? 0x8 : 0xa); - } else { - waitforXFW_3(hsp->iobase, hsp->hscx); - writehscx_s(hsp->iobase, hsp->hscx, 0x3e, ptr, count); - writehscxCMDR_3(hsp->iobase, hsp->hscx, more ? 0x8 : 0xa); - } -} - -static inline void -hscx_interrupt(struct IsdnCardState *sp, byte val, byte hscx) -{ - byte r; - struct HscxState *hsp = sp->hs + hscx; - int count, err; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = READHSCX(hsp->membase, sp->iobase, hsp->hscx, HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!r & 0x80) - printk(KERN_WARNING - "Teles: HSCX invalid frame\n"); - if ((r & 0x40) && hsp->mode) - printk(KERN_WARNING "Teles: HSCX RDO mode=%d\n",hsp->mode); - if (!r & 0x20) - printk(KERN_WARNING "Teles: HSCX CRC error\n"); - if (hsp->rcvibh) - BufPoolRelease(hsp->rcvibh); - hsp->rcvibh = NULL; - WRITEHSCX_CMDR(hsp->membase, hsp->iobase, hsp->hscx, - 0x80); - goto afterRME; - } - if (!hsp->rcvibh) - if (BufPoolGet(&hsp->rcvibh, &hsp->rbufpool, - GFP_ATOMIC, (void *) 1, 1)) { - printk(KERN_WARNING - "HSCX RME out of buffers at %ld\n", - jiffies); - WRITEHSCX_CMDR(hsp->membase, hsp->iobase, - hsp->hscx, 0x80); - goto afterRME; - } else - hsp->rcvptr = 0; - - count = READHSCX(hsp->membase, sp->iobase, hsp->hscx, - HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - hsp->rcvibh->datasize = hsp->rcvptr - 1; - BufQueueLink(&hsp->rq, hsp->rcvibh); - hsp->rcvibh = NULL; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - afterRME: - if (val & 0x40) { /* RPF */ - if (!hsp->rcvibh) { - if (hsp->mode == 1) - err=BufPoolGet(&hsp->rcvibh, &hsp->smallpool, - GFP_ATOMIC, (void *)1, 2); - else - err=BufPoolGet(&hsp->rcvibh, &hsp->rbufpool, - GFP_ATOMIC, (void *)1, 2); - - if (err) { - printk(KERN_WARNING - "HSCX RPF out of buffers at %ld\n", - jiffies); - WRITEHSCX_CMDR(hsp->membase, hsp->iobase, - hsp->hscx, 0x80); - goto afterRPF; - } else - hsp->rcvptr = 0; - } - - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - hsp->rcvibh->datasize = hsp->rcvptr; - BufQueueLink(&hsp->rq, hsp->rcvibh); - hsp->rcvibh = NULL; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - - } - afterRPF: - if (val & 0x10) { /* XPR */ - if (hsp->xmtibh) - if (hsp->xmtibh->datasize > hsp->sendptr) { - hscx_fill_fifo(hsp); - goto afterXPR; - } else { - if (hsp->releasebuf) - BufPoolRelease(hsp->xmtibh); - hsp->sendptr = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->xmtibh = NULL; - } - if (!BufQueueUnlink(&hsp->xmtibh, &hsp->sq)) { - hsp->releasebuf = !0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } - afterXPR: -} - -/* - * ISAC stuff goes here - */ - -static void -isac_sched_event(struct IsdnCardState *sp, int event) -{ - sp->event |= 1 << event; - queue_task_irq_off(&sp->tqueue, &tq_immediate); - mark_bh(IMMEDIATE_BH); -} - -static void -empty_fifo(struct IsdnCardState *sp, int count) -{ - byte *ptr; - struct BufHeader *ibh = sp->rcvibh; - - if (sp->debug) - printk(KERN_DEBUG "empty_fifo\n"); - - if (sp->rcvptr >= 3072) { - printk(KERN_WARNING "empty_fifo rcvptr %d\n", sp->rcvptr); - return; - } - ptr = DATAPTR(ibh); - ptr += sp->rcvptr; - sp->rcvptr += count; - - if (sp->membase) { -#ifdef DCHAN_VERBOSE - printk(KERN_DEBUG "empty_fifo "); - while (count--) { - *ptr = readisac_0(sp->membase, 0x0); - printk("%2x ", *ptr); - ptr++; - } - printk("\n"); -#else - while (count--) - *ptr++ = readisac_0(sp->membase, 0x0); -#endif - writeisac_0(sp->membase, ISAC_CMDR, 0x80); - } else { -#ifdef DCHAN_VERBOSE - int i; - printk(KERN_DEBUG "empty_fifo "); - readisac_s(sp->iobase, 0x3e, ptr, count); - for (i = 0; i < count; i++) - printk("%2x ", ptr[i]); - printk("\n"); -#else - readisac_s(sp->iobase, 0x3e, ptr, count); -#endif - writeisac_3(sp->iobase, ISAC_CMDR, 0x80); - } -} - -static void -fill_fifo(struct IsdnCardState *sp) -{ - struct BufHeader *ibh; - int count, more; - byte *ptr; - - if (sp->debug) - printk(KERN_DEBUG "fill_fifo\n"); - - ibh = sp->xmtibh; - if (!ibh) - return; - - count = ibh->datasize - sp->sendptr; - if (count <= 0) - return; - if (count >= 3072) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - ptr = DATAPTR(ibh); - ptr += sp->sendptr; - sp->sendptr += count; - - if (sp->membase) { -#ifdef DCHAN_VERBOSE - printk(KERN_DEBUG "fill_fifo "); - while (count--) { - writeisac_0(sp->membase, 0x0, *ptr); - printk("%2x ", *ptr); - ptr++; - } - printk("\n"); -#else - while (count--) - writeisac_0(sp->membase, 0x0, *ptr++); -#endif - writeisac_0(sp->membase, ISAC_CMDR, more ? 0x8 : 0xa); - } else { -#ifdef DCHAN_VERBOSE - int i; - writeisac_s(sp->iobase, 0x3e, ptr, count); - printk(KERN_DEBUG "fill_fifo "); - for (i = 0; i < count; i++) - printk("%2x ", ptr[i]); - printk("\n"); -#else - writeisac_s(sp->iobase, 0x3e, ptr, count); -#endif - writeisac_3(sp->iobase, ISAC_CMDR, more ? 0x8 : 0xa); - } -} - -static int -act_wanted(struct IsdnCardState *sp) -{ - struct PStack *st; - - st = sp->stlist; - while (st) - if (st->l1.act_state) - return (!0); - else - st = st->next; - return (0); -} - -static void -ph_command(struct IsdnCardState *sp, unsigned int command) -{ - printk(KERN_DEBUG "ph_command %d\n", command); - WRITEISAC(sp->membase, sp->iobase, ISAC_CIX0, (command << 2) | 3); -} - -static void -isac_new_ph(struct IsdnCardState *sp) -{ - int enq; - - enq = act_wanted(sp); - - switch (sp->ph_state) { - case (0): - case (6): - if (enq) - ph_command(sp, 0); - else - ph_command(sp, 15); - break; - case (7): - if (enq) - ph_command(sp, 9); - break; - case (12): - ph_command(sp, 8); - sp->ph_active = 5; - isac_sched_event(sp, ISAC_PHCHANGE); - if (!sp->xmtibh) - if (!BufQueueUnlink(&sp->xmtibh, &sp->sq)) - sp->sendptr = 0; - if (sp->xmtibh) - fill_fifo(sp); - break; - case (13): - ph_command(sp, 9); - sp->ph_active = 5; - isac_sched_event(sp, ISAC_PHCHANGE); - if (!sp->xmtibh) - if (!BufQueueUnlink(&sp->xmtibh, &sp->sq)) - sp->sendptr = 0; - if (sp->xmtibh) - fill_fifo(sp); - break; - case (4): - case (8): - break; - default: - sp->ph_active = 0; - break; - } -} - -static void -teles_interrupt(int intno, void *dev_id, struct pt_regs *regs) -{ - byte val, r, exval; - struct IsdnCardState *sp; - unsigned int count; - struct HscxState *hsp; - - sp = (struct IsdnCardState *) irq2dev_map[intno]; - - if (!sp) { - printk(KERN_WARNING "Teles: Spurious interrupt!\n"); - return; - } - val = READHSCX(sp->membase, sp->iobase, 1, HSCX_ISTA); - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = READHSCX(sp->membase, sp->iobase, 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. - */ - hsp->sendptr = 0; - WRITEHSCX_CMDR(hsp->membase, hsp->iobase, - hsp->hscx, 0x01); - printk(KERN_DEBUG "HSCX B EXIR %x\n", exval); - } - } else - printk(KERN_WARNING "HSCX B EXIR %x\n", exval); - } - if (val & 0xf8) { - if (sp->debug) - printk(KERN_DEBUG "HSCX B interrupt %x\n", val); - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = READHSCX(sp->membase, sp->iobase, 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. - */ - hsp->sendptr = 0; - WRITEHSCX_CMDR(hsp->membase, hsp->iobase, - hsp->hscx, 0x01); - printk(KERN_DEBUG "HSCX A EXIR %x\n", exval); - } - } else - printk(KERN_WARNING "HSCX A EXIR %x\n", exval); - } - if (val & 0x04) { - val = READHSCX(sp->membase, sp->iobase, 0, HSCX_ISTA); - if (sp->debug) - printk(KERN_DEBUG "HSCX A interrupt %x\n", - val); - hscx_interrupt(sp, val, 0); - } - - val = READISAC(sp->membase, sp->iobase, ISAC_ISTA); - - if (sp->debug) - printk(KERN_DEBUG "ISAC interrupt %x\n", val); - - if (val & 0x80) { /* RME */ - - r = READISAC(sp->membase, sp->iobase, ISAC_RSTA); - if ((r & 0x70) != 0x20) { - if (r & 0x40) - printk(KERN_WARNING "Teles: ISAC RDO\n"); - if (!r & 0x20) - printk(KERN_WARNING "Teles: ISAC CRC error\n"); - if (sp->rcvibh) - BufPoolRelease(sp->rcvibh); - sp->rcvibh = NULL; - WRITEISAC(sp->membase, sp->iobase, ISAC_CMDR, 0x80); - goto afterRME; - } - if (!sp->rcvibh) - if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool), - GFP_ATOMIC, - (void *) 1, 3)) { - printk(KERN_WARNING - "ISAC RME out of buffers!\n"); - WRITEISAC(sp->membase, sp->iobase, - ISAC_CMDR, 0x80); - goto afterRME; - } else - sp->rcvptr = 0; - - count = READISAC(sp->membase, sp->iobase, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - empty_fifo(sp, count); - sp->rcvibh->datasize = sp->rcvptr; - BufQueueLink(&(sp->rq), sp->rcvibh); - sp->rcvibh = NULL; - isac_sched_event(sp, ISAC_RCVBUFREADY); - } - afterRME: - if (val & 0x40) { /* RPF */ - if (!sp->rcvibh) - if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool), - GFP_ATOMIC, - (void *) 1, 4)) { - printk(KERN_WARNING - "ISAC RME out of buffers!\n"); - WRITEISAC(sp->membase, sp->iobase, - ISAC_CMDR, 0x80); - goto afterRPF; - } else - sp->rcvptr = 0; - empty_fifo(sp, 32); - } - afterRPF: - if (val & 0x20) { - } - if (val & 0x10) { /* XPR */ - if (sp->xmtibh) - if (sp->xmtibh->datasize > sp->sendptr) { - fill_fifo(sp); - goto afterXPR; - } else { - if (sp->releasebuf) - BufPoolRelease(sp->xmtibh); - sp->xmtibh = NULL; - sp->sendptr = 0; - } - if (!BufQueueUnlink(&sp->xmtibh, &sp->sq)) { - sp->releasebuf = !0; - fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (READISAC(sp->membase, sp->iobase, ISAC_CIX0) - >> 2) & 0xf; - printk(KERN_DEBUG "l1state %d\n", sp->ph_state); - isac_new_ph(sp); - } - if (sp->membase) { - writeisac_0(sp->membase, ISAC_MASK, 0xFF); - writehscx_0(sp->membase, 0, HSCX_MASK, 0xFF); - writehscx_0(sp->membase, 1, HSCX_MASK, 0xFF); - writeisac_0(sp->membase, ISAC_MASK, 0x0); - writehscx_0(sp->membase, 0, HSCX_MASK, 0x0); - writehscx_0(sp->membase, 1, HSCX_MASK, 0x0); - } else { - writeisac_3(sp->iobase, ISAC_MASK, 0xFF); - writehscx_3(sp->iobase, 0, HSCX_MASK, 0xFF); - writehscx_3(sp->iobase, 1, HSCX_MASK, 0xFF); - writeisac_3(sp->iobase, ISAC_MASK, 0x0); - writehscx_3(sp->iobase, 0, HSCX_MASK, 0x0); - writehscx_3(sp->iobase, 1, HSCX_MASK, 0x0); - } -} - -/* - * soft interrupt - */ - -static void -act_ivated(struct IsdnCardState *sp) -{ - struct PStack *st; - - st = sp->stlist; - while (st) { - if (st->l1.act_state == 1) { - st->l1.act_state = 2; - st->l1.l1man(st, PH_ACTIVATE, NULL); - } - st = st->next; - } -} - -static void -process_new_ph(struct IsdnCardState *sp) -{ - if (sp->ph_active == 5) - act_ivated(sp); -} - -static void -process_xmt(struct IsdnCardState *sp) -{ - struct PStack *stptr; - - if (sp->xmtibh) - return; - - stptr = sp->stlist; - while (stptr != NULL) - if (stptr->l1.requestpull) { - stptr->l1.requestpull = 0; - stptr->l1.l1l2(stptr, PH_PULL_ACK, NULL); - break; - } else - stptr = stptr->next; -} - -static void -process_rcv(struct IsdnCardState *sp) -{ - struct BufHeader *ibh, *cibh; - struct PStack *stptr; - byte *ptr; - int found, broadc; - char tmp[64]; - - while (!BufQueueUnlink(&ibh, &sp->rq)) { - stptr = sp->stlist; - ptr = DATAPTR(ibh); - broadc = (ptr[1] >> 1) == 127; - - if (broadc && sp->dlogflag && (!(ptr[0] >> 2))) - dlogframe(sp, ptr + 3, ibh->datasize - 3, - "Q.931 frame network->user broadcast"); - - if (broadc) { - while (stptr != NULL) { - if ((ptr[0] >> 2) == stptr->l2.sap) - if (!BufPoolGet(&cibh, &sp->rbufpool, GFP_ATOMIC, - (void *) 1, 5)) { - memcpy(DATAPTR(cibh), DATAPTR(ibh), ibh->datasize); - cibh->datasize = ibh->datasize; - stptr->l1.l1l2(stptr, PH_DATA, cibh); - } else - printk(KERN_WARNING "isdn broadcast buffer shortage\n"); - stptr = stptr->next; - } - BufPoolRelease(ibh); - } else { - found = 0; - while (stptr != NULL) - if (((ptr[0] >> 2) == stptr->l2.sap) && - ((ptr[1] >> 1) == stptr->l2.tei)) { - stptr->l1.l1l2(stptr, PH_DATA, ibh); - found = !0; - break; - } else - stptr = stptr->next; - if (!found) { - /* BD 10.10.95 - * Print out D-Channel msg not processed - * by isdn4linux - */ - - if ((!(ptr[0] >> 2)) && (!(ptr[2] & 0x01))) { - sprintf(tmp, "Q.931 frame network->user with tei %d (not for us)", ptr[1] >> 1); - dlogframe(sp, ptr + 4, ibh->datasize - 4, tmp); - } - BufPoolRelease(ibh); - } - } - - } - -} - -static void -isac_bh(struct IsdnCardState *sp) -{ - if (!sp) - return; - - if (clear_bit(ISAC_PHCHANGE, &sp->event)) - process_new_ph(sp); - if (clear_bit(ISAC_RCVBUFREADY, &sp->event)) - process_rcv(sp); - if (clear_bit(ISAC_XMTBUFREADY, &sp->event)) - process_xmt(sp); -} - - -static void -hscx_process_xmt(struct HscxState *hsp) -{ - struct PStack *st = hsp->st; - - if (hsp->xmtibh) - return; - - if (st->l1.requestpull) { - st->l1.requestpull = 0; - st->l1.l1l2(st, PH_PULL_ACK, NULL); - } - if (!hsp->active) - if ((!hsp->xmtibh) && (!hsp->sq.head)) - modehscx(hsp, 0, 0); -} - -static void -hscx_process_rcv(struct HscxState *hsp) -{ - struct BufHeader *ibh; - -#ifdef DEBUG_MAGIC - if (hsp->magic != 301270) { - printk(KERN_DEBUG "hscx_process_rcv magic not 301270\n"); - return; - } -#endif - while (!BufQueueUnlink(&ibh, &hsp->rq)) { - hsp->st->l1.l1l2(hsp->st, PH_DATA, ibh); - } -} - -static void -hscx_bh(struct HscxState *hsp) -{ - - if (!hsp) - return; - - if (clear_bit(HSCX_RCVBUFREADY, &hsp->event)) - hscx_process_rcv(hsp); - if (clear_bit(HSCX_XMTBUFREADY, &hsp->event)) - hscx_process_xmt(hsp); - -} - -/* - * interrupt stuff ends here - */ - -static void -restart_ph(struct IsdnCardState *sp) -{ - switch (sp->ph_active) { - case (0): - if (sp->ph_state == 6) - ph_command(sp, 0); - else - ph_command(sp, 1); - sp->ph_active = 1; - break; - } -} - -static void -initisac(byte * cardmem, int iobase) -{ - if (cardmem) { - writeisac_0(cardmem, ISAC_MASK, 0xff); - writeisac_0(cardmem, ISAC_ADF2, 0x0); - writeisac_0(cardmem, ISAC_SPCR, 0xa); - writeisac_0(cardmem, ISAC_ADF1, 0x2); - writeisac_0(cardmem, ISAC_STCR, 0x70); - writeisac_0(cardmem, ISAC_MODE, 0xc9); - writeisac_0(cardmem, ISAC_CMDR, 0x41); - writeisac_0(cardmem, ISAC_CIX0, (1 << 2) | 3); - } else { - writeisac_3(iobase, ISAC_MASK, 0xff); - writeisac_3(iobase, ISAC_ADF2, 0x80); - writeisac_3(iobase, ISAC_SQXR, 0x2f); - writeisac_3(iobase, ISAC_SPCR, 0x00); - writeisac_3(iobase, ISAC_ADF1, 0x02); - writeisac_3(iobase, ISAC_STCR, 0x70); - writeisac_3(iobase, ISAC_MODE, 0xc9); - writeisac_3(iobase, ISAC_TIMR, 0x00); - writeisac_3(iobase, ISAC_ADF1, 0x00); - writeisac_3(iobase, ISAC_CMDR, 0x41); - writeisac_3(iobase, ISAC_CIX0, (1 << 2) | 3); - } -} - -static int -checkcard(int cardnr) -{ - int timout; - byte cfval, val; - struct IsdnCard *card = cards + cardnr; - - if (card->membase) - if ((unsigned long)card->membase < 0x10000) { - (unsigned long)card->membase <<= 4; - printk(KERN_INFO - "Teles membase configured DOSish, assuming 0x%lx\n", - (unsigned long)card->membase); - } - if (!card->iobase) { - if (card->membase) { - printk(KERN_NOTICE - "Teles 8 assumed, mem: %lx irq: %d proto: %s\n", - (long) card->membase, card->interrupt, - (card->protocol == ISDN_PTYPE_1TR6) ? - "1TR6" : "EDSS1"); - printk(KERN_INFO "HSCX version A:%x B:%x\n", - readhscx_0(card->membase, 0, HSCX_VSTR) & 0xf, - readhscx_0(card->membase, 1, HSCX_VSTR) & 0xf); - } - } else { - switch (card->iobase) { - case 0x180: - case 0x280: - case 0x380: - card->iobase |= 0xc00; - break; - } - if (card->membase) { /* 16.0 */ - if (check_region(card->iobase, 8)) { - printk(KERN_WARNING - "teles: ports %x-%x already in use\n", - card->iobase, - card->iobase + 8 ); - return -1; - } - } else { /* 16.3 */ - if (check_region(card->iobase, 16)) { - printk(KERN_WARNING - "teles: 16.3 ports %x-%x already in use\n", - card->iobase, - card->iobase + 16 ); - return -1; - } - if (check_region((card->iobase - 0xc00) , 32)) { - printk(KERN_WARNING - "teles: 16.3 ports %x-%x already in use\n", - card->iobase - 0xc00, - card->iobase - 0xc00 + 32); - return -1; - } - if (check_region((card->iobase - 0x800) , 32)) { - printk(KERN_WARNING - "teles: 16.3 ports %x-%x already in use\n", - card->iobase - 0x800, - card->iobase - 0x800 + 32); - return -1; - } - if (check_region((card->iobase - 0x400) , 32)) { - printk(KERN_WARNING - "teles: 16.3 ports %x-%x already in use\n", - card->iobase - 0x400, - card->iobase - 0x400 + 32); - return -1; - } - } - switch (card->interrupt) { - 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; - } - if (card->membase) { - cfval |= (((unsigned int) card->membase >> 9) & 0xF0); - } - if (bytein(card->iobase + 0) != 0x51) { - printk(KERN_INFO "XXX Byte at %x is %x\n", - card->iobase + 0, - bytein(card->iobase + 0)); - return -2; - } - if (bytein(card->iobase + 1) != 0x93) { - printk(KERN_INFO "XXX Byte at %x is %x\n", - card->iobase + 1, - bytein(card->iobase + 1)); - return -2; - } - val = bytein(card->iobase + 2); /* 0x1e=without AB - * 0x1f=with AB - * 0x1c 16.3 ??? - */ - if (val != 0x1c && val != 0x1e && val != 0x1f) { - printk(KERN_INFO "XXX Byte at %x is %x\n", - card->iobase + 2, - bytein(card->iobase + 2)); - return -2; - } - if (card->membase) { /* 16.0 */ - request_region(card->iobase, 8, "teles 16.0"); - } else { - request_region(card->iobase, 16, "teles 16.3"); - request_region(card->iobase - 0xC00, 32, "teles HSCX0"); - request_region(card->iobase - 0x800, 32, "teles HSCX1"); - request_region(card->iobase - 0x400, 32, "teles ISAC"); - } - cli(); - timout = jiffies + (HZ / 10) + 1; - byteout(card->iobase + 4, cfval); - sti(); - while (jiffies <= timout); - - cli(); - timout = jiffies + (HZ / 10) + 1; - byteout(card->iobase + 4, cfval | 1); - sti(); - while (jiffies <= timout); - - if (card->membase) - printk(KERN_NOTICE - "Teles 16.0 found, io: %x mem: %lx irq: %d proto: %s\n", - card->iobase, (long) card->membase, - card->interrupt, - (card->protocol == ISDN_PTYPE_1TR6) ? - "1TR6" : "EDSS1"); - else - printk(KERN_NOTICE - "Teles 16.3 found, io: %x irq: %d proto: %s\n", - card->iobase, card->interrupt, - (card->protocol == ISDN_PTYPE_1TR6) ? - "1TR6" : "EDSS1"); - printk(KERN_INFO "HSCX version A:%x B:%x\n", - READHSCX(card->membase, card->iobase, 0, - HSCX_VSTR) & 0xf, - READHSCX(card->membase, card->iobase, 1, - HSCX_VSTR) & 0xf); - - } - if (card->membase) { - cli(); - timout = jiffies + (HZ / 5) + 1; - writeb(0, card->membase + 0x80); - sti(); - while (jiffies <= timout); - - cli(); - writeb(1, card->membase + 0x80); - timout = jiffies + (HZ / 5) + 1; - sti(); - while (jiffies <= timout); - } - return (0); -} - -void -modehscx(struct HscxState *hs, int mode, - int ichan) -{ - struct IsdnCardState *sp = hs->sp; - int hscx = hs->hscx; - - printk(KERN_DEBUG "modehscx hscx %d mode %d ichan %d\n", - hscx, mode, ichan); - - hs->mode = mode; - if (sp->membase) { - /* What's that ??? KKeil */ - if (hscx == 0) - ichan = 1 - ichan; /* raar maar waar... */ - writehscx_0(sp->membase, hscx, HSCX_CCR1, 0x85); - writehscx_0(sp->membase, hscx, HSCX_XAD1, 0xFF); - writehscx_0(sp->membase, hscx, HSCX_XAD2, 0xFF); - writehscx_0(sp->membase, hscx, HSCX_RAH2, 0xFF); - writehscx_0(sp->membase, hscx, HSCX_XBCH, 0x0); - - switch (mode) { - case (0): - writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30); - writehscx_0(sp->membase, hscx, HSCX_TSAX, 0xff); - writehscx_0(sp->membase, hscx, HSCX_TSAR, 0xff); - writehscx_0(sp->membase, hscx, HSCX_XCCR, 7); - writehscx_0(sp->membase, hscx, HSCX_RCCR, 7); - writehscx_0(sp->membase, hscx, HSCX_MODE, 0x84); - break; - case (1): - if (ichan == 0) { - writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30); - writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x7); - writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x7); - writehscx_0(sp->membase, hscx, HSCX_XCCR, 7); - writehscx_0(sp->membase, hscx, HSCX_RCCR, 7); - } else { - writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30); - writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x3); - writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x3); - writehscx_0(sp->membase, hscx, HSCX_XCCR, 7); - writehscx_0(sp->membase, hscx, HSCX_RCCR, 7); - } - writehscx_0(sp->membase, hscx, HSCX_MODE, 0xe4); - writehscx_0(sp->membase, hscx, HSCX_CMDR, 0x41); - break; - case (2): - if (ichan == 0) { - writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30); - writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x7); - writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x7); - writehscx_0(sp->membase, hscx, HSCX_XCCR, 7); - writehscx_0(sp->membase, hscx, HSCX_RCCR, 7); - } else { - writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30); - writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x3); - writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x3); - writehscx_0(sp->membase, hscx, HSCX_XCCR, 7); - writehscx_0(sp->membase, hscx, HSCX_RCCR, 7); - } - writehscx_0(sp->membase, hscx, HSCX_MODE, 0x8c); - writehscx_0(sp->membase, hscx, HSCX_CMDR, 0x41); - break; - } - writehscx_0(sp->membase, hscx, HSCX_ISTA, 0x00); - } else { - writehscx_3(sp->iobase, hscx, HSCX_CCR1, 0x85); - writehscx_3(sp->iobase, hscx, HSCX_XAD1, 0xFF); - writehscx_3(sp->iobase, hscx, HSCX_XAD2, 0xFF); - writehscx_3(sp->iobase, hscx, HSCX_RAH2, 0xFF); - writehscx_3(sp->iobase, hscx, HSCX_XBCH, 0x00); - writehscx_3(sp->iobase, hscx, HSCX_RLCR, 0x00); - - switch (mode) { - case (0): - writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30); - writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0xff); - writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0xff); - writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7); - writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7); - writehscx_3(sp->iobase, hscx, HSCX_MODE, 0x84); - break; - case (1): - if (ichan == 0) { - writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30); - writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x2f); - writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x2f); - writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7); - writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7); - } else { - writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30); - writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x3); - writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x3); - writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7); - writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7); - } - writehscx_3(sp->iobase, hscx, HSCX_MODE, 0xe4); - writehscx_3(sp->iobase, hscx, HSCX_CMDR, 0x41); - break; - case (2): - if (ichan == 0) { - writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30); - writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x2f); - writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x2f); - writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7); - writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7); - } else { - writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30); - writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x3); - writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x3); - writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7); - writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7); - } - writehscx_3(sp->iobase, hscx, HSCX_MODE, 0x8c); - writehscx_3(sp->iobase, hscx, HSCX_CMDR, 0x41); - break; - } - writehscx_3(sp->iobase, hscx, HSCX_ISTA, 0x00); - } -} - -void -teles_addlist(struct IsdnCardState *sp, - struct PStack *st) -{ - st->next = sp->stlist; - sp->stlist = st; -} - -void -teles_rmlist(struct IsdnCardState *sp, - struct PStack *st) -{ - struct PStack *p; - - if (sp->stlist == st) - sp->stlist = st->next; - else { - p = sp->stlist; - while (p) - if (p->next == st) { - p->next = st->next; - return; - } else - p = p->next; - } -} - - -static void -teles_l2l1(struct PStack *st, int pr, - struct BufHeader *ibh) -{ - struct IsdnCardState *sp = (struct IsdnCardState *) - st->l1.hardware; - - - switch (pr) { - case (PH_DATA): - if (sp->xmtibh) - BufQueueLink(&sp->sq, ibh); - else { - sp->xmtibh = ibh; - sp->sendptr = 0; - sp->releasebuf = !0; - fill_fifo(sp); - } - break; - case (PH_DATA_PULLED): - if (sp->xmtibh) { - printk(KERN_DEBUG "teles_l2l1: this shouldn't happen\n"); - break; - } - sp->xmtibh = ibh; - sp->sendptr = 0; - sp->releasebuf = 0; - fill_fifo(sp); - break; - case (PH_REQUEST_PULL): - if (!sp->xmtibh) { - st->l1.requestpull = 0; - st->l1.l1l2(st, PH_PULL_ACK, NULL); - } else - st->l1.requestpull = !0; - break; - } -} - -static void -check_ph_act(struct IsdnCardState *sp) -{ - struct PStack *st = sp->stlist; - - while (st) { - if (st->l1.act_state) - return; - st = st->next; - } - sp->ph_active = 0; -} - -static void -teles_manl1(struct PStack *st, int pr, - void *arg) -{ - struct IsdnCardState *sp = (struct IsdnCardState *) - st->l1.hardware; - long flags; - - switch (pr) { - case (PH_ACTIVATE): - save_flags(flags); - cli(); - if (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; - check_ph_act(sp); - break; - } -} - -static void -teles_l2l1discardq(struct PStack *st, int pr, - void *heldby, int releasetoo) -{ - struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware; - -#ifdef DEBUG_MAGIC - if (sp->magic != 301271) { - printk(KERN_DEBUG "isac_discardq magic not 301271\n"); - return; - } -#endif - - BufQueueDiscard(&sp->sq, pr, heldby, releasetoo); -} - -void -setstack_teles(struct PStack *st, struct IsdnCardState *sp) -{ - st->l1.hardware = sp; - st->l1.sbufpool = &(sp->sbufpool); - st->l1.rbufpool = &(sp->rbufpool); - st->l1.smallpool = &(sp->smallpool); - st->protocol = sp->teistack->protocol; - - setstack_tei(st); - - st->l1.stlistp = &(sp->stlist); - st->l1.act_state = 0; - st->l2.l2l1 = teles_l2l1; - st->l2.l2l1discardq = teles_l2l1discardq; - st->ma.manl1 = teles_manl1; - st->l1.requestpull = 0; -} - -void -init_hscxstate(struct IsdnCardState *sp, - int hscx) -{ - struct HscxState *hsp = sp->hs + hscx; - - hsp->sp = sp; - hsp->hscx = hscx; - hsp->membase = sp->membase; - hsp->iobase = sp->iobase; - - hsp->tqueue.next = 0; - hsp->tqueue.sync = 0; - hsp->tqueue.routine = (void *) (void *) hscx_bh; - hsp->tqueue.data = hsp; - - hsp->inuse = 0; - hsp->init = 0; - hsp->active = 0; - -#ifdef DEBUG_MAGIC - hsp->magic = 301270; -#endif -} - -void -initcard(int cardnr) -{ - struct IsdnCardState *sp; - struct IsdnCard *card = cards + cardnr; - - sp = (struct IsdnCardState *) - Smalloc(sizeof(struct IsdnCardState), GFP_KERNEL, - "struct IsdnCardState"); - - sp->membase = card->membase; - sp->iobase = card->iobase; - sp->cardnr = cardnr; - - BufPoolInit(&sp->sbufpool, ISAC_SBUF_ORDER, ISAC_SBUF_BPPS, - ISAC_SBUF_MAXPAGES); - BufPoolInit(&sp->rbufpool, ISAC_RBUF_ORDER, ISAC_RBUF_BPPS, - ISAC_RBUF_MAXPAGES); - BufPoolInit(&sp->smallpool, ISAC_SMALLBUF_ORDER, ISAC_SMALLBUF_BPPS, - ISAC_SMALLBUF_MAXPAGES); - - sp->dlogspace = Smalloc(4096, GFP_KERNEL, "dlogspace"); - - initisac(card->membase, card->iobase); - - sp->rcvibh = NULL; - sp->rcvptr = 0; - sp->xmtibh = NULL; - sp->sendptr = 0; - sp->event = 0; - sp->tqueue.next = 0; - sp->tqueue.sync = 0; - sp->tqueue.routine = (void *) (void *) isac_bh; - sp->tqueue.data = sp; - - BufQueueInit(&sp->rq); - BufQueueInit(&sp->sq); - - sp->stlist = NULL; - - sp->ph_active = 0; - - sp->dlogflag = 0; - sp->debug = 0; - - sp->releasebuf = 0; -#ifdef DEBUG_MAGIC - sp->magic = 301271; -#endif - - cards[sp->cardnr].sp = sp; - - init_hscxstate(sp, 0); - init_hscxstate(sp, 1); - - modehscx(sp->hs, 0, 0); - modehscx(sp->hs + 1, 0, 0); - - WRITEISAC(sp->membase, sp->iobase, ISAC_MASK, 0x0); -} - -static int -get_irq(int cardnr) -{ - struct IsdnCard *card = cards + cardnr; - long flags; - - save_flags(flags); - cli(); - if (request_irq(card->interrupt, &teles_interrupt, - SA_INTERRUPT, "teles", NULL)) { - printk(KERN_WARNING "Teles couldn't get interrupt %d\n", - card->interrupt); - restore_flags(flags); - return (!0); - } - irq2dev_map[card->interrupt] = (void *) card->sp; - restore_flags(flags); - return (0); -} - -static void -release_irq(int cardnr) -{ - struct IsdnCard *card = cards + cardnr; - - irq2dev_map[card->interrupt] = NULL; - free_irq(card->interrupt, NULL); -} - -void -close_hscxstate(struct HscxState *hs) -{ - modehscx(hs, 0, 0); - hs->inuse = 0; - - if (hs->init) { - BufPoolFree(&hs->smallpool); - BufPoolFree(&hs->rbufpool); - BufPoolFree(&hs->sbufpool); - } - hs->init = 0; -} - -void -closecard(int cardnr) -{ - struct IsdnCardState *sp = cards[cardnr].sp; - - cards[cardnr].sp = NULL; - - Sfree(sp->dlogspace); - - BufPoolFree(&sp->smallpool); - BufPoolFree(&sp->rbufpool); - BufPoolFree(&sp->sbufpool); - - close_hscxstate(sp->hs + 1); - close_hscxstate(sp->hs); - - if (cards[cardnr].iobase) - if (cards[cardnr].membase) { /* 16.0 */ - release_region(cards[cardnr].iobase, 8); - } else { - release_region(cards[cardnr].iobase, 16); - release_region(cards[cardnr].iobase - 0xC00, 32); - release_region(cards[cardnr].iobase - 0x800, 32); - release_region(cards[cardnr].iobase - 0x400, 32); - } - - Sfree((void *) sp); -} - -void -teles_shiftcards(int idx) -{ - int i; - - for (i = idx; i < 15; i++) - memcpy(&cards[i],&cards[i+1],sizeof(cards[i])); -} - -int -teles_inithardware(void) -{ - int foundcards = 0; - int i = 0; - - while (i < nrcards) { - if (!cards[i].protocol) - break; - switch (checkcard(i)) { - case (0): - initcard(i); - if (get_irq(i)) { - closecard(i); - teles_shiftcards(i); - } else { - foundcards++; - i++; - } - break; - case (-1): - teles_shiftcards(i); - break; - case (-2): - release_region(cards[i].iobase, 8); - printk(KERN_WARNING "NO Teles card found at 0x%x!\n", cards[i].iobase); - teles_shiftcards(i); - break; - } - } - return foundcards; -} - -void -teles_closehardware(void) -{ - int i; - - for (i = 0; i < nrcards; i++) - if (cards[i].sp) { - release_irq(i); - closecard(i); - } -} - -static void -hscx_l2l1(struct PStack *st, int pr, - struct BufHeader *ibh) -{ - 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->xmtibh) { - BufQueueLink(&hsp->sq, ibh); - restore_flags(flags); - } - else { - restore_flags(flags); - hsp->xmtibh = ibh; - hsp->sendptr = 0; - hsp->releasebuf = !0; - hscx_fill_fifo(hsp); - } - break; - case (PH_DATA_PULLED): - if (hsp->xmtibh) { - printk(KERN_DEBUG "hscx_l2l1: this shouldn't happen\n"); - break; - } - hsp->xmtibh = ibh; - hsp->sendptr = 0; - hsp->releasebuf = 0; - hscx_fill_fifo(hsp); - break; - case (PH_REQUEST_PULL): - if (!hsp->xmtibh) { - st->l1.requestpull = 0; - st->l1.l1l2(st, PH_PULL_ACK, NULL); - } else - st->l1.requestpull = !0; - break; - } - -} - -extern struct IsdnBuffers *tracebuf; - -static void -hscx_l2l1discardq(struct PStack *st, int pr, void *heldby, - int releasetoo) -{ - struct IsdnCardState *sp = (struct IsdnCardState *) - st->l1.hardware; - struct HscxState *hsp = sp->hs + st->l1.hscx; - -#ifdef DEBUG_MAGIC - if (hsp->magic != 301270) { - printk(KERN_DEBUG "hscx_discardq magic not 301270\n"); - return; - } -#endif - - BufQueueDiscard(&hsp->sq, pr, heldby, releasetoo); -} - -static int -open_hscxstate(struct IsdnCardState *sp, - int hscx) -{ - struct HscxState *hsp = sp->hs + hscx; - - if (!hsp->init) { - BufPoolInit(&hsp->sbufpool, HSCX_SBUF_ORDER, HSCX_SBUF_BPPS, - HSCX_SBUF_MAXPAGES); - BufPoolInit(&hsp->rbufpool, HSCX_RBUF_ORDER, HSCX_RBUF_BPPS, - HSCX_RBUF_MAXPAGES); - BufPoolInit(&hsp->smallpool, HSCX_SMALLBUF_ORDER, HSCX_SMALLBUF_BPPS, - HSCX_SMALLBUF_MAXPAGES); - } - hsp->init = !0; - - BufQueueInit(&hsp->rq); - BufQueueInit(&hsp->sq); - - hsp->releasebuf = 0; - hsp->rcvibh = NULL; - hsp->xmtibh = NULL; - hsp->rcvptr = 0; - hsp->sendptr = 0; - hsp->event = 0; - return (0); -} - -static void -hscx_manl1(struct PStack *st, int pr, - void *arg) -{ - struct IsdnCardState *sp = (struct IsdnCardState *) - st->l1.hardware; - struct HscxState *hsp = sp->hs + st->l1.hscx; - - switch (pr) { - case (PH_ACTIVATE): - hsp->active = !0; - modehscx(hsp, st->l1.hscxmode, st->l1.hscxchannel); - st->l1.l1man(st, PH_ACTIVATE, NULL); - break; - case (PH_DEACTIVATE): - if (!hsp->xmtibh) - modehscx(hsp, 0, 0); - - hsp->active = 0; - break; - } -} - -int -setstack_hscx(struct PStack *st, struct HscxState *hs) -{ - 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.sbufpool = &hs->sbufpool; - st->l1.rbufpool = &hs->rbufpool; - st->l1.smallpool = &hs->smallpool; - st->l1.act_state = 0; - st->l1.requestpull = 0; - - hs->st = st; - return (0); -} - -void -teles_reportcard(int cardnr) -{ - printk(KERN_DEBUG "teles_reportcard\n"); -} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/config.c linux/drivers/isdn/teles/config.c --- v2.0.30/linux/drivers/isdn/teles/config.c Sun Apr 21 01:56:15 1996 +++ linux/drivers/isdn/teles/config.c Wed Dec 31 16:00:00 1969 @@ -1,48 +0,0 @@ -/* $Id: config.c,v 1.1 1996/04/13 10:23:11 fritz Exp $ - * - * $Log: config.c,v $ - * Revision 1.1 1996/04/13 10:23:11 fritz - * Initial revision - * - * - */ -#define __NO_VERSION__ -#include -#include -#include -#include "teles.h" - -/* - * This structure array contains one entry per card. An entry looks - * like this: - * - * { membase,irq,portbase,protocol,NULL } - * - * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 - * - * Cards which don't have an io port (Teles 8 bit cards for - * example) can be entered with io port 0x0 - * - * For the Teles 16.3, membase has to be set to 0. - * - */ - -struct IsdnCard cards[] = -{ - {(byte *) 0xd0000, 15, 0xd80, ISDN_PTYPE_EURO, NULL}, /* example */ - {NULL, 0, 0, 0, NULL}, - {NULL, 0, 0, 0, NULL}, - {NULL, 0, 0, 0, NULL}, - {NULL, 0, 0, 0, NULL}, - {NULL, 0, 0, 0, NULL}, - {NULL, 0, 0, 0, NULL}, - {NULL, 0, 0, 0, NULL}, - {NULL, 0, 0, 0, NULL}, - {NULL, 0, 0, 0, NULL}, - {NULL, 0, 0, 0, NULL}, - {NULL, 0, 0, 0, NULL}, - {NULL, 0, 0, 0, NULL}, - {NULL, 0, 0, 0, NULL}, - {NULL, 0, 0, 0, NULL}, - {NULL, 0, 0, 0, NULL}, -}; diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/fsm.c linux/drivers/isdn/teles/fsm.c --- v2.0.30/linux/drivers/isdn/teles/fsm.c Sun May 19 05:29:30 1996 +++ linux/drivers/isdn/teles/fsm.c Wed Dec 31 16:00:00 1969 @@ -1,159 +0,0 @@ -/* $Id: fsm.c,v 1.2 1996/04/29 22:49:57 fritz Exp $ - * - * $Log: fsm.c,v $ - * Revision 1.2 1996/04/29 22:49:57 fritz - * Removed compatibility-macros. - * - * Revision 1.1 1996/04/13 10:23:41 fritz - * Initial revision - * - * - */ -#define __NO_VERSION__ -#include "teles.h" - -void -FsmNew(struct Fsm *fsm, - struct FsmNode *fnlist, int fncount) -{ - int i; - - fsm->jumpmatrix = (int *) Smalloc(4L * fsm->state_count * fsm->event_count, - GFP_KERNEL, "Fsm jumpmatrix"); - 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; -} - -void -FsmFree(struct Fsm *fsm) -{ - Sfree((void *) fsm->jumpmatrix); -} - -int -FsmEvent(struct FsmInst *fi, int event, void *arg) -{ - void (*r) (struct FsmInst *, int, void *); - char str[80]; - - r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; - if (r) { - if (fi->debug) { - sprintf(str, "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", - fi->fsm->strState[fi->state], - fi->fsm->strEvent[event]); - fi->printdebug(fi, str); - } - return (!0); - } -} - -void -FsmChangeState(struct FsmInst *fi, int newstate) -{ - char str[80]; - - fi->state = newstate; - if (fi->debug) { - sprintf(str, "ChangeState %s", - fi->fsm->strState[newstate]); - fi->printdebug(fi, str); - } -} - -static void -FsmExpireTimer(struct FsmTimer *ft) -{ - FsmEvent(ft->fi, ft->event, ft->arg); -} - -void -FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft) -{ - ft->fi = fi; - ft->tl.function = (void *) FsmExpireTimer; - ft->tl.data = (long) ft; - init_timer(&ft->tl); -} - -void -FsmDelTimer(struct FsmTimer *ft, int where) -{ - long flags; - -#if 0 - if (ft->fi->debug) { - sprintf(str, "FsmDelTimer %lx %d", ft, where); - ft->fi->printdebug(ft->fi, str); - } -#endif - - save_flags(flags); - cli(); - if (ft->tl.next) - del_timer(&ft->tl); - restore_flags(flags); -} - -int -FsmAddTimer(struct FsmTimer *ft, - int millisec, int event, void *arg, int where) -{ - -#if 0 - if (ft->fi->debug) { - sprintf(str, "FsmAddTimer %lx %d %d", ft, millisec, where); - ft->fi->printdebug(ft->fi, str); - } -#endif - - if (ft->tl.next) { - printk(KERN_WARNING "FsmAddTimer: timer already active!\n"); - return -1; - } - init_timer(&ft->tl); - ft->event = event; - ft->arg = arg; - ft->tl.expires = jiffies + (millisec * HZ) / 1000; - add_timer(&ft->tl); - return 0; -} - -int -FsmTimerRunning(struct FsmTimer *ft) -{ - return (ft->tl.next != NULL); -} - -void -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'; -} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/isdnl2.c linux/drivers/isdn/teles/isdnl2.c --- v2.0.30/linux/drivers/isdn/teles/isdnl2.c Sun May 19 05:29:31 1996 +++ linux/drivers/isdn/teles/isdnl2.c Wed Dec 31 16:00:00 1969 @@ -1,1317 +0,0 @@ -/* $Id: isdnl2.c,v 1.2 1996/05/17 03:46:15 fritz Exp $ - * - * $Log: isdnl2.c,v $ - * Revision 1.2 1996/05/17 03:46:15 fritz - * General cleanup. - * - * Revision 1.1 1996/04/13 10:24:16 fritz - * Initial revision - * - * - */ -#define __NO_VERSION__ -#include "teles.h" - -#define TIMER_1 2000 - -static void l2m_debug(struct FsmInst *fi, char *s); - -struct Fsm l2fsm = -{NULL, 0, 0}; - -enum { - ST_L2_1, - ST_L2_3, - ST_L2_4, - ST_L2_5, - ST_L2_6, - ST_L2_7, - ST_L2_8, -}; - -#define L2_STATE_COUNT (ST_L2_8+1) - -static char *strL2State[] = -{ - "ST_L2_1", - "ST_L2_3", - "ST_L2_4", - "ST_L2_5", - "ST_L2_6", - "ST_L2_7", - "ST_L2_8", -}; - -enum { - EV_L2_UI, - EV_L2_SABMX, - EV_L2_UA, - EV_L2_DISC, - EV_L2_I, - EV_L2_RR, - EV_L2_REJ, - EV_L2_FRMR, - EV_L2_DL_DATA, - EV_L2_DL_ESTABLISH, - EV_L2_MDL_ASSIGN, - EV_L2_DL_UNIT_DATA, - EV_L2_DL_RELEASE, - EV_L2_MDL_NOTEIPROC, - EV_L2_T200, - EV_L2_ACK_PULL, - EV_L2_T203, - EV_L2_RNR, -}; - -#define L2_EVENT_COUNT (EV_L2_RNR+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_FRMR", - "EV_L2_DL_DATA", - "EV_L2_DL_ESTABLISH", - "EV_L2_MDL_ASSIGN", - "EV_L2_DL_UNIT_DATA", - "EV_L2_DL_RELEASE", - "EV_L2_MDL_NOTEIPROC", - "EV_L2_T200", - "EV_L2_ACK_PULL", - "EV_L2_T203", - "EV_L2_RNR", -}; - -int errcount = 0; - -static int l2addrsize(struct Layer2 *tsp); - -static int -cansend(struct PStack *st) -{ - int p1; - - p1 = (st->l2.va + st->l2.window) % (st->l2.extended ? 128 : 8); - return (st->l2.vs != p1); -} - -static void -discard_i_queue(struct PStack *st) -{ - struct BufHeader *ibh; - - while (!BufQueueUnlink(&ibh, &st->l2.i_queue)) - BufPoolRelease(ibh); -} - -int -l2headersize(struct Layer2 *tsp, int UI) -{ - return ((tsp->extended && (!UI) ? 2 : 1) + (tsp->laptype == LAPD ? 2 : 1)); -} - -int -l2addrsize(struct Layer2 *tsp) -{ - return (tsp->laptype == LAPD ? 2 : 1); -} - -static int -sethdraddr(struct Layer2 *tsp, - struct BufHeader *ibh, int rsp) -{ - byte *ptr = DATAPTR(ibh); - int crbit; - - if (tsp->laptype == LAPD) { - crbit = rsp; - if (!tsp->orig) - crbit = !crbit; - *ptr++ = (tsp->sap << 2) | (crbit ? 2 : 0); - *ptr++ = (tsp->tei << 1) | 1; - return (2); - } else { - crbit = rsp; - if (tsp->orig) - crbit = !crbit; - if (crbit) - *ptr++ = 1; - else - *ptr++ = 3; - return (1); - } -} - -static void -enqueue_ui(struct PStack *st, - struct BufHeader *ibh) -{ - st->l2.l2l1(st, PH_DATA, ibh); -} - -static void -enqueue_super(struct PStack *st, - struct BufHeader *ibh) -{ - st->l2.l2l1(st, PH_DATA, ibh); -} - -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 + l2->extended ? 128 : 8); - lnr = (nr >= l2->va) ? nr : (nr + l2->extended ? 128 : 8); - return (lnr <= lvs); -} - -static void -setva(struct PStack *st, int nr) -{ - struct Layer2 *l2 = &st->l2; - - if (l2->va != nr) { - while (l2->va != nr) { - l2->va = (l2->va + 1) % (l2->extended ? 128 : 8); - BufPoolRelease(l2->windowar[l2->sow]); - l2->sow = (l2->sow + 1) % l2->window; - } - if (st->l4.l2writewakeup) - st->l4.l2writewakeup(st); - } -} - -static void -l2s1(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - - st->l2.l2tei(st, MDL_ASSIGN, (void *)st->l2.ces); - FsmChangeState(fi, ST_L2_3); -} - -static void -l2s2(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct BufHeader *ibh = arg; - - byte *ptr; - int i; - - i = sethdraddr(&(st->l2), ibh, 0); - ptr = DATAPTR(ibh); - ptr += i; - *ptr = 0x3; - - enqueue_ui(st, ibh); -} - -static void -l2s3(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct BufHeader *ibh = arg; - - st->l2.l2l3(st, DL_UNIT_DATA, ibh); -} - -static void -establishlink(struct FsmInst *fi) -{ - struct PStack *st = fi->userdata; - struct BufHeader *ibh; - int i; - byte *ptr; - - FsmChangeState(fi, ST_L2_5); - st->l2.rc = 0; - - 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"); - - - if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15)) - return; - i = sethdraddr(&st->l2, ibh, 0); - ptr = DATAPTR(ibh); - ptr += i; - if (st->l2.extended) - *ptr = 0x7f; - else - *ptr = 0x3f; - ibh->datasize = i + 1; - - enqueue_super(st, ibh); -} - -static void -l2s11(struct FsmInst *fi, int event, void *arg) -{ - establishlink(fi); -} - -static void -l2s13(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; - byte *ptr; - struct BufHeader *ibh; - int i; - - FsmChangeState(fi, ST_L2_6); - - FsmDelTimer(&st->l2.t203_timer, 1); - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 2); - st->l2.t200_running = 0; - } - 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 ((chanp->impair == 2) && (st->l2.laptype == LAPB)) - goto nodisc; - - if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 9)) - return; - i = sethdraddr(&(st->l2), ibh, 0); - ptr = DATAPTR(ibh); - ptr += i; - *ptr = 0x53; - ibh->datasize = i + 1; - enqueue_super(st, ibh); - - nodisc: - discard_i_queue(st); -} - -static void -l2s12(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct BufHeader *ibh = arg; - byte *ptr; - int i; - - BufPoolRelease(ibh); - st->l2.vs = 0; - st->l2.va = 0; - st->l2.vr = 0; - st->l2.sow = 0; - FsmChangeState(fi, ST_L2_7); - 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"); - - st->l2.l2man(st, DL_ESTABLISH, NULL); - - if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 10)) - return; - i = sethdraddr(&(st->l2), ibh, 0); - ptr = DATAPTR(ibh); - ptr += i; - *ptr = 0x73; - ibh->datasize = i + 1; - enqueue_super(st, ibh); - -} - -static void -l2s14(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct BufHeader *ibh = arg; - struct Channel *chanp = st->l4.userdata; - byte *ptr; - int i, p; - - ptr = DATAPTR(ibh); - ptr += l2addrsize(&(st->l2)); - p = (*ptr) & 0x10; - BufPoolRelease(ibh); - - FsmChangeState(fi, ST_L2_4); - - FsmDelTimer(&st->l2.t203_timer, 3); - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 4); - st->l2.t200_running = 0; - } - if ((chanp->impair == 1) && (st->l2.laptype == LAPB)) - goto noresponse; - - if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 11)) - return; - i = sethdraddr(&(st->l2), ibh, 0); - ptr = DATAPTR(ibh); - ptr += i; - *ptr = 0x63 | (p ? 0x10 : 0x0); - ibh->datasize = i + 1; - enqueue_super(st, ibh); - - noresponse: - st->l2.l2man(st, DL_RELEASE, NULL); - -} - -static void -l2s5(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct BufHeader *ibh = arg; - int f; - byte *data; - - data = DATAPTR(ibh); - data += l2addrsize(&(st->l2)); - - f = *data & 0x10; - BufPoolRelease(ibh); - - if (f) { - st->l2.vs = 0; - st->l2.va = 0; - st->l2.vr = 0; - st->l2.sow = 0; - FsmChangeState(fi, ST_L2_7); - - 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"); - - - st->l2.l2man(st, DL_ESTABLISH, NULL); - } -} - -static void -l2s15(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct BufHeader *ibh = arg; - int f; - byte *data; - - data = DATAPTR(ibh); - data += l2addrsize(&st->l2); - - f = *data & 0x10; - BufPoolRelease(ibh); - - if (f) { - FsmDelTimer(&st->l2.t200_timer, 6); - FsmChangeState(fi, ST_L2_4); - st->l2.l2man(st, DL_RELEASE, NULL); - } -} - -static void -l2s6(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; - struct BufHeader *ibh = arg; - int p, i, seq, rsp; - byte *ptr; - struct Layer2 *l2; - - l2 = &st->l2; - ptr = DATAPTR(ibh); - - if (l2->laptype == LAPD) { - rsp = ptr[0] & 0x2; - if (l2->orig) - rsp = !rsp; - } else { - rsp = ptr[0] == 0x3; - if (l2->orig) - rsp = !rsp; - } - - ptr += l2addrsize(l2); - - if (l2->extended) { - p = (ptr[1] & 0x1) == 0x1; - seq = ptr[1] >> 1; - } else { - p = (ptr[0] & 0x10); - seq = (ptr[0] >> 5) & 0x7; - } - BufPoolRelease(ibh); - - if ((chanp->impair == 4) && (st->l2.laptype == LAPB)) - goto noresp; - - if ((!rsp) && p) { - if (!BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 12)) { - i = sethdraddr(l2, ibh, !0); - ptr = DATAPTR(ibh); - ptr += i; - - if (l2->extended) { - *ptr++ = 0x1; - *ptr++ = (l2->vr << 1) | (p ? 1 : 0); - i += 2; - } else { - *ptr++ = (l2->vr << 5) | 0x1 | (p ? 0x10 : 0x0); - i += 1; - } - ibh->datasize = i; - enqueue_super(st, ibh); - } - } - noresp: - if (legalnr(st, seq)) - if (seq == st->l2.vs) { - setva(st, seq); - FsmDelTimer(&st->l2.t200_timer, 7); - st->l2.t200_running = 0; - FsmDelTimer(&st->l2.t203_timer, 8); - if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 5)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 5"); - - if (st->l2.i_queue.head) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); - } else if (st->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 (st->l2.i_queue.head) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); - } -} - -static void -l2s7(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct BufHeader *ibh = arg; - int i; - byte *ptr; - struct IsdnCardState *sp = st->l1.hardware; - char str[64]; - - i = sethdraddr(&st->l2, ibh, 0); - ptr = DATAPTR(ibh); - - if (st->l2.laptype == LAPD) - if (sp->dlogflag) { - sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); - dlogframe(sp, ptr + st->l2.ihsize, ibh->datasize - st->l2.ihsize, - str); - } - BufQueueLink(&st->l2.i_queue, ibh); - - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); -} - -static void -l2s8(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; - struct BufHeader *ibh = arg; - byte *ptr; - struct BufHeader *ibh2; - struct IsdnCardState *sp = st->l1.hardware; - struct Layer2 *l2 = &(st->l2); - int i, p, seq, nr, wasok; - char str[64]; - - ptr = DATAPTR(ibh); - ptr += l2addrsize(l2); - if (l2->extended) { - p = (ptr[1] & 0x1) == 0x1; - seq = ptr[0] >> 1; - nr = (ptr[1] >> 1) & 0x7f; - } else { - p = (ptr[0] & 0x10); - seq = (ptr[0] >> 1) & 0x7; - nr = (ptr[0] >> 5) & 0x7; - } - - if (l2->vr == seq) { - wasok = !0; - - l2->vr = (l2->vr + 1) % (l2->extended ? 128 : 8); - l2->rejexp = 0; - - ptr = DATAPTR(ibh); - if (st->l2.laptype == LAPD) - if (sp->dlogflag) { - sprintf(str, "Q.931 frame network->user tei %d", st->l2.tei); - dlogframe(st->l1.hardware, ptr + l2->ihsize, - ibh->datasize - l2->ihsize, str); - } - label8_1: - if ((chanp->impair == 3) && (st->l2.laptype == LAPB)) - goto noRR; - - if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 13)) { - i = sethdraddr(&(st->l2), ibh2, p); - ptr = DATAPTR(ibh2); - ptr += i; - - if (l2->extended) { - *ptr++ = 0x1; - *ptr++ = (l2->vr << 1) | (p ? 1 : 0); - i += 2; - } else { - *ptr++ = (l2->vr << 5) | 0x1 | (p ? 0x10 : 0x0); - i += 1; - } - ibh2->datasize = i; - enqueue_super(st, ibh2); - noRR: - } - } else { - /* n(s)!=v(r) */ - wasok = 0; - BufPoolRelease(ibh); - if (st->l2.rejexp) { - if (p) - goto label8_1; - } else { - st->l2.rejexp = !0; - if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 14)) { - i = sethdraddr(&(st->l2), ibh2, p); - ptr = DATAPTR(ibh2); - ptr += i; - - if (l2->extended) { - *ptr++ = 0x9; - *ptr++ = (l2->vr << 1) | (p ? 1 : 0); - i += 2; - } else { - *ptr++ = (l2->vr << 5) | 0x9 | (p ? 0x10 : 0x0); - i += 1; - } - ibh2->datasize = i; - enqueue_super(st, ibh2); - } - } - } - - 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 (st->l2.i_queue.head) - 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 (st->l2.i_queue.head) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); - } - if (wasok) - st->l2.l2l3(st, DL_DATA, ibh); - -} - -static void -l2s17(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - - st->l2.tei = (int) arg; - establishlink(fi); -} - -static void -enquiry_response(struct PStack *st) -{ - struct BufHeader *ibh2; - int i; - byte *ptr; - struct Layer2 *l2; - - l2 = &st->l2; - if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 16)) { - i = sethdraddr(&(st->l2), ibh2, !0); - ptr = DATAPTR(ibh2); - ptr += i; - - if (l2->extended) { - *ptr++ = 0x1; - *ptr++ = (l2->vr << 1) | 0x1; - i += 2; - } else { - *ptr++ = (l2->vr << 5) | 0x1 | 0x10; - i += 1; - } - ibh2->datasize = i; - enqueue_super(st, ibh2); - } -} - -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; - - BufQueueLinkFront(&l2->i_queue, l2->windowar[p1]); - } - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); - } -} - -static void -l2s16(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct BufHeader *ibh = arg; - int p, seq, rsp; - byte *ptr; - struct Layer2 *l2; - - l2 = &(st->l2); - ptr = DATAPTR(ibh); - - if (l2->laptype == LAPD) { - rsp = ptr[0] & 0x2; - if (l2->orig) - rsp = !rsp; - } else { - rsp = ptr[0] == 0x3; - if (l2->orig) - rsp = !rsp; - } - - - ptr += l2addrsize(l2); - - if (l2->extended) { - p = (ptr[1] & 0x1) == 0x1; - seq = ptr[1] >> 1; - } else { - p = (ptr[0] & 0x10); - seq = (ptr[0] >> 5) & 0x7; - } - BufPoolRelease(ibh); - - if ((!rsp) && p) - enquiry_response(st); - - if (!legalnr(st, seq)) - return; - - setva(st, seq); - invoke_retransmission(st, seq); - -} - -static void -l2s19(struct FsmInst *fi, int event, void *arg) -{ - FsmChangeState(fi, ST_L2_4); -} - -static void -l2s20(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - int i; - struct BufHeader *ibh; - byte *ptr; - - 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, 9)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 7"); - - if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15)) - return; - - i = sethdraddr(&st->l2, ibh, 0); - ptr = DATAPTR(ibh); - ptr += i; - if (st->l2.extended) - *ptr = 0x7f; - else - *ptr = 0x3f; - ibh->datasize = i + 1; - enqueue_super(st, ibh); - } -} - -static void -l2s21(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; - int i; - struct BufHeader *ibh; - byte *ptr; - - 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)) - goto nodisc; - - if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15)) - return; - - i = sethdraddr(&st->l2, ibh, 0); - ptr = DATAPTR(ibh); - ptr += i; - *ptr = 0x53; - ibh->datasize = i + 1; - enqueue_super(st, ibh); - nodisc: - - } -} - -static void -l2s22(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct BufHeader *ibh; - struct Layer2 *l2 = &st->l2; - byte *ptr; - int p1; - - if (!cansend(st)) - return; - - if (BufQueueUnlink(&ibh, &l2->i_queue)) - return; - - - p1 = l2->vs - l2->va; - if (p1 < 0) - p1 += l2->extended ? 128 : 8; - p1 = (p1 + l2->sow) % l2->window; - l2->windowar[p1] = ibh; - - ptr = DATAPTR(ibh); - ptr += l2addrsize(l2); - - if (l2->extended) { - *ptr++ = l2->vs << 1; - *ptr++ = (l2->vr << 1) | 0x1; - l2->vs = (l2->vs + 1) % 128; - } else { - *ptr++ = (l2->vr << 5) | (l2->vs << 1) | 0x10; - l2->vs = (l2->vs + 1) % 8; - } - - st->l2.l2l1(st, PH_DATA_PULLED, ibh); - - 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; - } - if (l2->i_queue.head && cansend(st)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); - -} - -static void -transmit_enquiry(struct PStack *st) -{ - struct BufHeader *ibh; - byte *ptr; - - if (!BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 12)) { - ptr = DATAPTR(ibh); - ptr += sethdraddr(&st->l2, ibh, 0); - - if (st->l2.extended) { - *ptr++ = 0x1; - *ptr++ = (st->l2.vr << 1) | 1; - } else { - *ptr++ = (st->l2.vr << 5) | 0x11; - } - ibh->datasize = ptr - DATAPTR(ibh); - enqueue_super(st, ibh); - 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; - } -} - -static void -l2s23(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 -l2s24(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct BufHeader *ibh = arg; - int p, seq, rsp; - byte *ptr; - struct Layer2 *l2; - - l2 = &st->l2; - ptr = DATAPTR(ibh); - - if (l2->laptype == LAPD) { - rsp = ptr[0] & 0x2; - if (l2->orig) - rsp = !rsp; - } else { - rsp = ptr[0] == 0x3; - if (l2->orig) - rsp = !rsp; - } - - - ptr += l2addrsize(l2); - - if (l2->extended) { - p = (ptr[1] & 0x1) == 0x1; - seq = ptr[1] >> 1; - } else { - p = (ptr[0] & 0x10); - seq = (ptr[0] >> 5) & 0x7; - } - BufPoolRelease(ibh); - - if (rsp && p) { - if (legalnr(st, seq)) { - 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); - } - } else { - if (!rsp && p) - enquiry_response(st); - if (legalnr(st, seq)) { - setva(st, seq); - } - } -} - -static void -l2s25(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 -l2s26(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - - if (st->l2.rc == st->l2.n200) { - l2s13(fi, event, NULL); - } else { - st->l2.rc++; - transmit_enquiry(st); - } -} - -static void -l2s27(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct BufHeader *ibh = arg; - byte *ptr; - int i, p, est; - - ptr = DATAPTR(ibh); - ptr += l2addrsize(&st->l2); - - if (st->l2.extended) - p = ptr[1] & 0x1; - else - p = ptr[0] & 0x10; - - BufPoolRelease(ibh); - - if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 10)) - return; - i = sethdraddr(&st->l2, ibh, 0); - ptr = DATAPTR(ibh); - ptr += i; - *ptr = 0x63 | p; - ibh->datasize = i + 1; - enqueue_super(st, ibh); - - if (st->l2.vs != st->l2.va) { - discard_i_queue(st); - est = !0; - } else - est = 0; - - 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 12"); - - st->l2.vs = 0; - st->l2.va = 0; - st->l2.vr = 0; - st->l2.sow = 0; - - - if (est) - st->l2.l2man(st, DL_ESTABLISH, NULL); - -} - -static void -l2s28(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct BufHeader *ibh = arg; - byte *ptr; - char tmp[64]; - - ptr = DATAPTR(ibh); - ptr += l2addrsize(&st->l2); - ptr++; - - if (st->l2.l2m.debug) { - if (st->l2.extended) - sprintf(tmp, "FRMR information %2x %2x %2x %2x %2x", - ptr[0], ptr[1], ptr[2], ptr[3], ptr[4]); - else - sprintf(tmp, "FRMR information %2x %2x %2x", - ptr[0], ptr[1], ptr[2]); - - l2m_debug(&st->l2.l2m, tmp); - } - BufPoolRelease(ibh); -} - -static int -IsUI(byte * data, int ext) -{ - return ((data[0] & 0xef) == 0x3); -} - -static int -IsUA(byte * data, int ext) -{ - return ((data[0] & 0xef) == 0x63); -} - -static int -IsDISC(byte * data, int ext) -{ - return ((data[0] & 0xef) == 0x43); -} - -static int -IsRR(byte * data, int ext) -{ - if (ext) - return (data[0] == 0x1); - else - return ((data[0] & 0xf) == 1); -} - -static int -IsI(byte * data, int ext) -{ - return ((data[0] & 0x1) == 0x0); -} - -static int -IsSABMX(byte * data, int ext) -{ - return (ext ? data[0] == 0x7f : data[0] == 0x3f); -} - -static int -IsREJ(byte * data, int ext) -{ - return (ext ? data[0] == 0x9 : (data[0] & 0xf) == 0x9); -} - -static int -IsFRMR(byte * data, int ext) -{ - return ((data[0] & 0xef) == 0x87); -} - -static int -IsRNR(byte * data, int ext) -{ - if (ext) - return (data[0] == 0x5); - else - return ((data[0] & 0xf) == 5); -} - -static struct FsmNode L2FnList[] = -{ - {ST_L2_1, EV_L2_DL_ESTABLISH, l2s1}, - {ST_L2_1, EV_L2_MDL_NOTEIPROC, l2s19}, - {ST_L2_3, EV_L2_MDL_ASSIGN, l2s17}, - {ST_L2_4, EV_L2_DL_UNIT_DATA, l2s2}, - {ST_L2_4, EV_L2_DL_ESTABLISH, l2s11}, - {ST_L2_7, EV_L2_DL_UNIT_DATA, l2s2}, - {ST_L2_7, EV_L2_DL_DATA, l2s7}, - {ST_L2_7, EV_L2_DL_RELEASE, l2s13}, - {ST_L2_7, EV_L2_ACK_PULL, l2s22}, - {ST_L2_8, EV_L2_DL_RELEASE, l2s13}, - - {ST_L2_1, EV_L2_UI, l2s3}, - {ST_L2_4, EV_L2_UI, l2s3}, - {ST_L2_4, EV_L2_SABMX, l2s12}, - {ST_L2_5, EV_L2_UA, l2s5}, - {ST_L2_6, EV_L2_UA, l2s15}, - {ST_L2_7, EV_L2_UI, l2s3}, - {ST_L2_7, EV_L2_DISC, l2s14}, - {ST_L2_7, EV_L2_I, l2s8}, - {ST_L2_7, EV_L2_RR, l2s6}, - {ST_L2_7, EV_L2_REJ, l2s16}, - {ST_L2_7, EV_L2_SABMX, l2s27}, - {ST_L2_7, EV_L2_FRMR, l2s28}, - {ST_L2_8, EV_L2_RR, l2s24}, - {ST_L2_8, EV_L2_DISC, l2s14}, - {ST_L2_8, EV_L2_FRMR, l2s28}, - - {ST_L2_5, EV_L2_T200, l2s20}, - {ST_L2_6, EV_L2_T200, l2s21}, - {ST_L2_7, EV_L2_T200, l2s23}, - {ST_L2_7, EV_L2_T203, l2s25}, - {ST_L2_8, EV_L2_T200, l2s26}, -}; - -#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode)) - -static void -isdnl2_l1l2(struct PStack *st, int pr, struct BufHeader *arg) -{ - struct BufHeader *ibh; - byte *datap; - int ret = !0; - - switch (pr) { - case (PH_DATA): - - ibh = arg; - datap = DATAPTR(ibh); - datap += l2addrsize(&st->l2); - - if (IsI(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_I, ibh); - else if (IsRR(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_RR, ibh); - else if (IsUI(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_UI, ibh); - else if (IsSABMX(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_SABMX, ibh); - else if (IsUA(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_UA, ibh); - else if (IsDISC(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, ibh); - else if (IsREJ(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_REJ, ibh); - else if (IsFRMR(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, ibh); - else if (IsRNR(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_RNR, ibh); - - if (ret) - BufPoolRelease(ibh); - - break; - case (PH_PULL_ACK): - FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg); - break; - } -} - -static void -isdnl2_l3l2(struct PStack *st, int pr, - void *arg) -{ - switch (pr) { - case (DL_DATA): - if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg)) - BufPoolRelease((struct BufHeader *) arg); - break; - case (DL_UNIT_DATA): - if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg)) - BufPoolRelease((struct BufHeader *) arg); - 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); - break; - case (DL_RELEASE): - FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE, arg); - break; - case (MDL_NOTEIPROC): - FsmEvent(&st->l2.l2m, EV_L2_MDL_NOTEIPROC, NULL); - break; - } -} - -static void -isdnl2_teil2(struct PStack *st, int pr, - void *arg) -{ - switch (pr) { - case (MDL_ASSIGN): - FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg); - break; - } -} - -void -releasestack_isdnl2(struct PStack *st) -{ - FsmDelTimer(&st->l2.t200_timer, 15); - FsmDelTimer(&st->l2.t203_timer, 16); -} - -static void -l2m_debug(struct FsmInst *fi, char *s) -{ - 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); - teles_putstatus(str); -} - - -void -setstack_isdnl2(struct PStack *st, char *debug_id) -{ - 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); - BufQueueInit(&(st->l2.i_queue)); - st->l2.rejexp = 0; - st->l2.debug = 1; - - st->l2.l2m.fsm = &l2fsm; - st->l2.l2m.state = ST_L2_1; - st->l2.l2m.debug = 0; - st->l2.l2m.userdata = st; - 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; -} - -void -setstack_transl2(struct PStack *st) -{ -} - -void -releasestack_transl2(struct PStack *st) -{ -} - -void -Isdnl2New(void) -{ - l2fsm.state_count = L2_STATE_COUNT; - l2fsm.event_count = L2_EVENT_COUNT; - l2fsm.strEvent = strL2Event; - l2fsm.strState = strL2State; - FsmNew(&l2fsm, L2FnList, L2_FN_COUNT); -} - -void -Isdnl2Free(void) -{ - FsmFree(&l2fsm); -} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/isdnl3.c linux/drivers/isdn/teles/isdnl3.c --- v2.0.30/linux/drivers/isdn/teles/isdnl3.c Tue Nov 12 22:36:20 1996 +++ linux/drivers/isdn/teles/isdnl3.c Wed Dec 31 16:00:00 1969 @@ -1,663 +0,0 @@ -/* $Id: isdnl3.c,v 1.11 1996/09/29 19:41:58 fritz Exp $ - * - * $Log: isdnl3.c,v $ - * Revision 1.11 1996/09/29 19:41:58 fritz - * Bugfix: ignore unknown frames. - * - * Revision 1.10 1996/09/25 18:32:43 keil - * response for STATUS_ENQ message added - * - * Revision 1.9 1996/06/06 14:22:27 fritz - * Changed level of "non-digital call..." message, since - * with audio support, this is quite normal. - * - * Revision 1.8 1996/06/03 20:35:04 fritz - * Fixed typos. - * - * Revision 1.7 1996/06/03 20:03:39 fritz - * Fixed typos. - * - * Revision 1.6 1996/05/21 11:33:50 keil - * Adding SETUP_ACKNOWLEDGE as answer of a SETUP message. - * - * Revision 1.5 1996/05/18 01:37:16 fritz - * Added spelling corrections and some minor changes - * to stay in sync with kernel. - * - * Revision 1.4 1996/05/17 03:46:16 fritz - * General cleanup. - * - * Revision 1.3 1996/04/30 21:57:53 isdn4dev - * remove some debugging code, improve callback Karsten Keil - * - * Revision 1.2 1996/04/20 16:45:05 fritz - * Changed to report all incoming calls to Linklevel, not just those - * with Service 7. - * Misc. typos - * - * Revision 1.1 1996/04/13 10:24:45 fritz - * Initial revision - * - * - */ -#define __NO_VERSION__ -#define P_1TR6 -#include "teles.h" -#include "l3_1TR6.h" -#define DEBUG_1TR6 0 - -static void -i_down(struct PStack *st, - struct BufHeader *ibh) -{ - st->l3.l3l2(st, DL_DATA, ibh); -} - -static void -newl3state(struct PStack *st, int state) -{ - st->l3.state = state; - if (DEBUG_1TR6 > 4) - printk(KERN_INFO "isdnl3: bc:%d cr:%x new state %d\n", - st->pa->bchannel, st->pa->callref, state); - -} - -static void -l3_message(struct PStack *st, int mt) -{ - struct BufHeader *dibh; - byte *p; - int size; - - BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 18); - p = DATAPTR(dibh); - p += st->l2.ihsize; - size = st->l2.ihsize; - - *p++ = 0x8; - *p++ = 0x1; - *p++ = st->l3.callref; - *p++ = mt; - size += 4; - - dibh->datasize = size; - i_down(st, dibh); -} - -static void -l3s3(struct PStack *st, byte pr, void *arg) -{ - l3_message(st, MT_RELEASE); - newl3state(st, 19); -} - -static void -l3s4(struct PStack *st, byte pr, void *arg) -{ - struct BufHeader *ibh = arg; - - BufPoolRelease(ibh); - newl3state(st, 0); - st->l3.l3l4(st, CC_RELEASE_CNF, NULL); -} - -static void -l3s4_1(struct PStack *st, byte pr, void *arg) -{ - struct BufHeader *ibh = arg; - - BufPoolRelease(ibh); - newl3state(st, 19); - l3_message(st, MT_RELEASE); - st->l3.l3l4(st, CC_RELEASE_CNF, NULL); -} - -static void -l3s5(struct PStack *st, byte pr, - void *arg) -{ - struct BufHeader *dibh; - byte *p; - char *teln; - - st->l3.callref = st->pa->callref; - BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 19); - p = DATAPTR(dibh); - p += st->l2.ihsize; - - *p++ = 0x8; - *p++ = 0x1; - *p++ = st->l3.callref; - *p++ = MT_SETUP; - *p++ = 0xa1; - - /* - * Set Bearer Capability, Map info from 1TR6-convention to EDSS1 - */ - switch (st->pa->info) { - case 1: /* Telephony */ - *p++ = 0x4; /* BC-IE-code */ - *p++ = 0x3; /* Length */ - *p++ = 0x90; /* Coding Std. national, 3.1 kHz audio */ - *p++ = 0x90; /* Circuit-Mode 64kbps */ - *p++ = 0xa3; /* A-Law Audio */ - break; - case 5: /* Datatransmission 64k, BTX */ - case 7: /* Datatransmission 64k */ - default: - *p++ = 0x4; /* BC-IE-code */ - *p++ = 0x2; /* Length */ - *p++ = 0x88; /* Coding Std. nat., unrestr. dig. Inform. */ - *p++ = 0x90; /* Packet-Mode 64kbps */ - break; - } - /* - * What about info2? Mapping to High-Layer-Compatibility? - */ - if (st->pa->calling[0] != '\0') { - *p++ = 0x6c; - *p++ = strlen(st->pa->calling) + 1; - /* Classify as AnyPref. */ - *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */ - teln = st->pa->calling; - while (*teln) - *p++ = *teln++ & 0x7f; - } - *p++ = 0x70; - *p++ = strlen(st->pa->called) + 1; - /* Classify as AnyPref. */ - *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */ - - teln = st->pa->called; - while (*teln) - *p++ = *teln++ & 0x7f; - - - dibh->datasize = p - DATAPTR(dibh); - - newl3state(st, 1); - i_down(st, dibh); - -} - -static void -l3s6(struct PStack *st, byte pr, void *arg) -{ - byte *p; - struct BufHeader *ibh = arg; - - p = DATAPTR(ibh); - if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize, - 0x18, 0))) { - st->pa->bchannel = p[2] & 0x3; - } else - printk(KERN_WARNING "octect 3 not found\n"); - - BufPoolRelease(ibh); - newl3state(st, 3); - st->l3.l3l4(st, CC_PROCEEDING_IND, NULL); -} - -static void -l3s7(struct PStack *st, byte pr, void *arg) -{ - struct BufHeader *ibh = arg; - - BufPoolRelease(ibh); - newl3state(st, 12); - st->l3.l3l4(st, CC_DISCONNECT_IND, NULL); -} - -static void -l3s8(struct PStack *st, byte pr, void *arg) -{ - struct BufHeader *ibh = arg; - - BufPoolRelease(ibh); - st->l3.l3l4(st, CC_SETUP_CNF, NULL); - newl3state(st, 10); -} - -static void -l3s11(struct PStack *st, byte pr, void *arg) -{ - struct BufHeader *ibh = arg; - - BufPoolRelease(ibh); - newl3state(st, 4); - st->l3.l3l4(st, CC_ALERTING_IND, NULL); -} - -static void -l3s12(struct PStack *st, byte pr, void *arg) -{ - byte *p; - int bcfound = 0; - struct BufHeader *ibh = arg; - - p = DATAPTR(ibh); - p += st->l2.uihsize; - st->pa->callref = getcallref(p); - st->l3.callref = 0x80 + st->pa->callref; - - /* - * Channel Identification - */ - p = DATAPTR(ibh); - if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, - 0x18, 0))) { - st->pa->bchannel = p[2] & 0x3; - bcfound++ ; - } else - printk(KERN_WARNING "l3s12: Channel ident not found\n"); - - p = DATAPTR(ibh); - if (st->protocol == ISDN_PTYPE_1TR6) { - if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, 0x01, 6))) { - st->pa->info = p[2]; - st->pa->info2 = p[3]; - } else - printk(KERN_WARNING "l3s12(1TR6): ServiceIndicator not found\n"); - } else { - /* - * Bearer Capabilities - */ - if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, 0x04, 0))) { - switch (p[2] & 0x1f) { - case 0x00: - /* Speech */ - case 0x10: - /* 3.1 Khz audio */ - st->pa->info = 1; - break; - case 0x08: - /* Unrestricted digital information */ - st->pa->info = 7; - break; - case 0x09: - /* Restricted digital information */ - st->pa->info = 2; - break; - case 0x11: - /* Unrestr. digital information with tones/announcements */ - st->pa->info = 3; - break; - case 0x18: - /* Video */ - st->pa->info = 4; - break; - default: - st->pa->info = 0; - } - } else - printk(KERN_WARNING "l3s12: Bearer capabilities not found\n"); - } - - p = DATAPTR(ibh); - if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, - 0x70, 0))) - iecpy(st->pa->called, p, 1); - else - strcpy(st->pa->called, ""); - - p = DATAPTR(ibh); - if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, - 0x6c, 0))) { - if (st->protocol == ISDN_PTYPE_1TR6) - iecpy(st->pa->calling, p, 1); - else - iecpy(st->pa->calling, p, 2); - } else - strcpy(st->pa->calling, ""); - BufPoolRelease(ibh); - - if (bcfound) { - if (st->pa->info != 7) { - printk(KERN_DEBUG "non-digital call: %s -> %s\n", - st->pa->calling, - st->pa->called); - } - newl3state(st, 6); - st->l3.l3l4(st, CC_SETUP_IND, NULL); - } -} - -static void -l3s13(struct PStack *st, byte pr, void *arg) -{ - newl3state(st, 0); -} - -static void -l3s16(struct PStack *st, byte pr, - void *arg) -{ - st->l3.callref = 0x80 + st->pa->callref; - l3_message(st, MT_CONNECT); - newl3state(st, 8); -} - -static void -l3s17(struct PStack *st, byte pr, void *arg) -{ - struct BufHeader *ibh = arg; - - BufPoolRelease(ibh); - st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL); - newl3state(st, 10); -} - -static void -l3s18(struct PStack *st, byte pr, void *arg) -{ - struct BufHeader *dibh; - byte *p; - int size; - - BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20); - p = DATAPTR(dibh); - p += st->l2.ihsize; - size = st->l2.ihsize; - - *p++ = 0x8; - *p++ = 0x1; - *p++ = st->l3.callref; - *p++ = MT_DISCONNECT; - size += 4; - - *p++ = IE_CAUSE; - *p++ = 0x2; - *p++ = 0x80; - *p++ = 0x90; - size += 4; - - dibh->datasize = size; - i_down(st, dibh); - - newl3state(st, 11); -} - -static void -l3s19(struct PStack *st, byte pr, void *arg) -{ - struct BufHeader *ibh = arg; - - BufPoolRelease(ibh); - newl3state(st, 0); - l3_message(st, MT_RELEASE_COMPLETE); - st->l3.l3l4(st, CC_RELEASE_IND, NULL); -} - -static void -l3s20(struct PStack *st, byte pr, - void *arg) -{ - l3_message(st, MT_ALERTING); - newl3state(st, 7); -} - -static void -l3s21(struct PStack *st, byte pr, void *arg) -{ - struct BufHeader *dibh=arg; - byte *p; - int size; - - BufPoolRelease(dibh); - - BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20); - p = DATAPTR(dibh); - p += st->l2.ihsize; - size = st->l2.ihsize; - - *p++ = 0x8; - *p++ = 0x1; - *p++ = st->l3.callref; - *p++ = MT_STATUS; - size += 4; - - *p++ = IE_CAUSE; - *p++ = 0x2; - *p++ = 0x80; - *p++ = 0x9E; /* answer status enquire */ - size += 4; - - *p++ = 0x14; /* CallState */ - *p++ = 0x1; - *p++ = st->l3.state & 0x3f; /* ISO L3 CallState */ - size += 3; - - dibh->datasize = size; - i_down(st, dibh); - -} - -struct stateentry { - int state; - byte primitive; - void (*rout) (struct PStack *, byte, void *); -}; - -static struct stateentry downstatelist[] = -{ - {0,CC_SETUP_REQ,l3s5}, - {1,CC_DISCONNECT_REQ,l3s18}, - {1,CC_RELEASE_REQ,l3s3}, - {1,CC_DLRL,l3s13}, - {3,CC_DISCONNECT_REQ,l3s18}, - {3,CC_RELEASE_REQ,l3s3}, - {3,CC_DLRL,l3s13}, - {4,CC_RELEASE_REQ,l3s3}, - {4,CC_DISCONNECT_REQ,l3s18}, - {4,CC_DLRL,l3s13}, - {6,CC_RELEASE_REQ,l3s3}, - {6,CC_DISCONNECT_REQ,l3s18}, - {6,CC_ALERTING_REQ,l3s20}, - {6,CC_DLRL,l3s13}, - {7,CC_RELEASE_REQ,l3s3}, - {7,CC_SETUP_RSP,l3s16}, - {7,CC_DLRL,l3s13}, - {8,CC_RELEASE_REQ,l3s3}, - {8,CC_DISCONNECT_REQ,l3s18}, - {8,CC_DLRL,l3s13}, - {10,CC_DISCONNECT_REQ,l3s18}, - {10,CC_RELEASE_REQ,l3s3}, - {10,CC_DLRL,l3s13}, - {11,CC_RELEASE_REQ,l3s3}, - {12,CC_RELEASE_REQ,l3s3}, - {19,CC_DLRL,l3s13}, -}; - -static int downsllen = sizeof(downstatelist) / -sizeof(struct stateentry); - -static struct stateentry datastatelist[] = -{ - {0,MT_STATUS_ENQUIRY,l3s21}, - {0,MT_SETUP,l3s12}, - {1,MT_STATUS_ENQUIRY,l3s21}, - {1,MT_CALL_PROCEEDING,l3s6}, - {1,MT_SETUP_ACKNOWLEDGE,l3s6}, - {1,MT_RELEASE_COMPLETE,l3s4}, - {1,MT_RELEASE,l3s19}, - {1,MT_DISCONNECT,l3s7}, - {3,MT_STATUS_ENQUIRY,l3s21}, - {3,MT_DISCONNECT,l3s7}, - {3,MT_CONNECT,l3s8}, - {3,MT_ALERTING,l3s11}, - {3,MT_RELEASE,l3s19}, - {3,MT_RELEASE_COMPLETE,l3s4}, - {4,MT_STATUS_ENQUIRY,l3s21}, - {4,MT_CONNECT,l3s8}, - {4,MT_DISCONNECT,l3s7}, - {4,MT_RELEASE,l3s19}, - {4,MT_RELEASE_COMPLETE,l3s4}, - {8,MT_STATUS_ENQUIRY,l3s21}, - {6,MT_SETUP,l3s12}, - {8,MT_STATUS_ENQUIRY,l3s21}, - {7,MT_RELEASE,l3s19}, - {7,MT_RELEASE_COMPLETE,l3s4_1}, - {7,MT_DISCONNECT,l3s7}, - {8,MT_STATUS_ENQUIRY,l3s21}, - {8,MT_RELEASE,l3s19}, - {8,MT_CONNECT_ACKNOWLEDGE,l3s17}, - {8,MT_DISCONNECT,l3s7}, - {8,MT_RELEASE_COMPLETE,l3s4_1}, - {10,MT_STATUS_ENQUIRY,l3s21}, - {10,MT_DISCONNECT,l3s7}, - {10,MT_RELEASE,l3s19}, - {10,MT_RELEASE_COMPLETE,l3s4_1}, - {11,MT_STATUS_ENQUIRY,l3s21}, - {11,MT_RELEASE,l3s19}, - {11,MT_RELEASE_COMPLETE,l3s4}, - {19,MT_STATUS_ENQUIRY,l3s21}, - {19,MT_RELEASE_COMPLETE,l3s4}, -}; - -static int datasllen = sizeof(datastatelist) / -sizeof(struct stateentry); - -#ifdef P_1TR6 -#include "l3_1TR6.c" -#endif - -static void -l3up(struct PStack *st, - int pr, void *arg) -{ - int i, mt, size; - byte *ptr; - struct BufHeader *ibh = arg; - - if (pr == DL_DATA) { - ptr = DATAPTR(ibh); - ptr += st->l2.ihsize; - size = ibh->datasize - st->l2.ihsize; - mt = ptr[3]; - switch (ptr[0]) { -#ifdef P_1TR6 - case PROTO_DIS_N0: - BufPoolRelease(ibh); - break; - case PROTO_DIS_N1: - for (i = 0; i < datasl_1tr6t_len; i++) - if ((st->l3.state == datastatelist_1tr6t[i].state) && - (mt == datastatelist_1tr6t[i].primitive)) - break; - if (i == datasl_1tr6t_len) { - BufPoolRelease(ibh); - if (DEBUG_1TR6 > 0) - printk(KERN_INFO "isdnl3up unhandled 1tr6 state %d MT %x\n", - st->l3.state, mt); - } else - datastatelist_1tr6t[i].rout(st, pr, ibh); - break; -#endif - case PROTO_EURO: /* E-DSS1 */ - for (i = 0; i < datasllen; i++) - if ((st->l3.state == datastatelist[i].state) && - (mt == datastatelist[i].primitive)) - break; - if (i == datasllen) { - BufPoolRelease(ibh); - if (DEBUG_1TR6 > 0) - printk(KERN_INFO "isdnl3up unhandled E-DSS1 state %d MT %x\n", - st->l3.state, mt); - } else - datastatelist[i].rout(st, pr, ibh); - break; - default: - BufPoolRelease(ibh); - break; - } - } else if (pr == DL_UNIT_DATA) { - ptr = DATAPTR(ibh); - ptr += st->l2.uihsize; - size = ibh->datasize - st->l2.uihsize; - mt = ptr[3]; - switch (ptr[0]) { -#ifdef P_1TR6 - case PROTO_DIS_N0: - BufPoolRelease(ibh); - break; - case PROTO_DIS_N1: - for (i = 0; i < datasl_1tr6t_len; i++) - if ((st->l3.state == datastatelist_1tr6t[i].state) && - (mt == datastatelist_1tr6t[i].primitive)) - break; - if (i == datasl_1tr6t_len) { - if (DEBUG_1TR6 > 0) { - printk(KERN_INFO "isdnl3up unhandled 1tr6 state %d MT %x\n" - ,st->l3.state, mt); - } - BufPoolRelease(ibh); - } else - datastatelist_1tr6t[i].rout(st, pr, ibh); - break; -#endif - case PROTO_EURO: /* E-DSS1 */ - for (i = 0; i < datasllen; i++) - if ((st->l3.state == datastatelist[i].state) && - (mt == datastatelist[i].primitive)) - break; - if (i == datasllen) { - BufPoolRelease(ibh); - if (DEBUG_1TR6 > 0) - printk(KERN_INFO "isdnl3up unhandled E-DSS1 state %d MT %x\n", - st->l3.state, mt); - } else - datastatelist[i].rout(st, pr, ibh); - break; - default: - BufPoolRelease(ibh); - break; - } - } -} - -static void -l3down(struct PStack *st, - int pr, void *arg) -{ - int i; - struct BufHeader *ibh = arg; - - switch (st->protocol) { -#ifdef P_1TR6 - case ISDN_PTYPE_1TR6: - for (i = 0; i < downsl_1tr6t_len; i++) - if ((st->l3.state == downstatelist_1tr6t[i].state) && - (pr == downstatelist_1tr6t[i].primitive)) - break; - if (i == downsl_1tr6t_len) { - if (DEBUG_1TR6 > 0) { - printk(KERN_INFO "isdnl3down unhandled 1tr6 state %d primitive %x\n", st->l3.state, pr); - } - } else - downstatelist_1tr6t[i].rout(st, pr, ibh); - break; -#endif - default: - for (i = 0; i < downsllen; i++) - if ((st->l3.state == downstatelist[i].state) && - (pr == downstatelist[i].primitive)) - break; - if (i == downsllen) { - if (DEBUG_1TR6 > 0) { - printk(KERN_INFO "isdnl3down unhandled E-DSS1 state %d primitive %x\n", st->l3.state, pr); - } - } else - downstatelist[i].rout(st, pr, ibh); - } -} - -void -setstack_isdnl3(struct PStack *st) -{ - st->l4.l4l3 = l3down; - st->l2.l2l3 = l3up; - st->l3.state = 0; - st->l3.callref = 0; - st->l3.debug = 0; -} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/l3_1TR6.c linux/drivers/isdn/teles/l3_1TR6.c --- v2.0.30/linux/drivers/isdn/teles/l3_1TR6.c Tue Nov 12 22:36:20 1996 +++ linux/drivers/isdn/teles/l3_1TR6.c Wed Dec 31 16:00:00 1969 @@ -1,535 +0,0 @@ -/* $Id: l3_1TR6.c,v 1.6 1996/09/25 18:34:57 keil Exp $ - * - * $Log: l3_1TR6.c,v $ - * Revision 1.6 1996/09/25 18:34:57 keil - * missing states in 1TR6 Statemachine added - * - * Revision 1.5 1996/09/23 01:53:51 fritz - * Bugfix: discard unknown frames (non-EDSS1 and non-1TR6). - * - * Revision 1.4 1996/06/06 14:22:28 fritz - * Changed level of "non-digital call..." message, since - * with audio support, this is quite normal. - * - * Revision 1.3 1996/04/30 21:54:42 isdn4dev - * SPV, callback , remove some debugging code Karsten Keil - * - * Revision 1.2 1996/04/20 16:47:23 fritz - * Changed statemachine to allow reject of an incoming call. - * Report all incoming calls, not just those with Service = 7. - * Misc. typos - * - * Revision 1.1 1996/04/13 10:25:16 fritz - * Initial revision - * - * - */ - -#include "proto.h" - -static void -l3_1TR6_message(struct PStack *st, int mt, int pd) -{ - struct BufHeader *dibh; - byte *p; - - BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 18); - p = DATAPTR(dibh); - p += st->l2.ihsize; - - *p++ = pd; - *p++ = 0x1; - *p++ = st->l3.callref; - *p++ = mt; - - dibh->datasize = p - DATAPTR(dibh); - i_down(st, dibh); -} - -static void -l3_1tr6_setup(struct PStack *st, byte pr, void *arg) -{ - struct BufHeader *dibh; - byte *p; - char *teln; - - st->l3.callref = st->pa->callref; - BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 19); - p = DATAPTR(dibh); - p += st->l2.ihsize; - - *p++ = PROTO_DIS_N1; - *p++ = 0x1; - *p++ = st->l3.callref; - *p++ = MT_N1_SETUP; - - if ('S' == (st->pa->called[0] & 0x5f)) { /* SPV ??? */ - /* NSF SPV */ - *p++ = WE0_netSpecFac; - *p++ = 4; /* Laenge */ - *p++ = 0; - *p++ = FAC_SPV; /* SPV */ - *p++ = st->pa->info; /* 0 for all Services */ - *p++ = st->pa->info2; /* 0 for all Services */ - *p++ = WE0_netSpecFac; - *p++ = 4; /* Laenge */ - *p++ = 0; - *p++ = FAC_Activate; /* aktiviere SPV (default) */ - *p++ = st->pa->info; /* 0 for all Services */ - *p++ = st->pa->info2; /* 0 for all Services */ - } - if (st->pa->calling[0] != '\0') { - *p++ = WE0_origAddr; - *p++ = strlen(st->pa->calling) + 1; - /* Classify as AnyPref. */ - *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */ - teln = st->pa->calling; - while (*teln) - *p++ = *teln++ & 0x7f; - } - *p++ = WE0_destAddr; - teln = st->pa->called; - if ('S' != (st->pa->called[0] & 0x5f)) { /* Keine SPV ??? */ - *p++ = strlen(st->pa->called) + 1; - st->pa->spv = 0; - } else { /* SPV */ - *p++ = strlen(st->pa->called); - teln++; /* skip S */ - st->pa->spv = 1; - } - /* Classify as AnyPref. */ - *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */ - while (*teln) - *p++ = *teln++ & 0x7f; - - *p++ = WE_Shift_F6; - /* Codesatz 6 fuer Service */ - *p++ = WE6_serviceInd; - *p++ = 2; /* len=2 info,info2 */ - *p++ = st->pa->info; - *p++ = st->pa->info2; - - dibh->datasize = p - DATAPTR(dibh); - - newl3state(st, 1); - i_down(st, dibh); - -} - -static void -l3_1tr6_tu_setup(struct PStack *st, byte pr, void *arg) -{ - byte *p; - struct BufHeader *ibh = arg; - - p = DATAPTR(ibh); - p += st->l2.uihsize; - st->pa->callref = getcallref(p); - st->l3.callref = 0x80 + st->pa->callref; - - /* Channel Identification */ - p = DATAPTR(ibh); - if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, - WE0_chanID, 0))) { - st->pa->bchannel = p[2] & 0x3; - } else - printk(KERN_INFO "l3tu_setup: Channel ident not found\n"); - - p = DATAPTR(ibh); - - if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, WE6_serviceInd, 6))) { - st->pa->info = p[2]; - st->pa->info2 = p[3]; - } else - printk(KERN_INFO "l3s12(1TR6): ServiceIndicator not found\n"); - - p = DATAPTR(ibh); - if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, - WE0_destAddr, 0))) - iecpy(st->pa->called, p, 1); - else - strcpy(st->pa->called, ""); - - p = DATAPTR(ibh); - if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, - WE0_origAddr, 0))) { - iecpy(st->pa->calling, p, 1); - } else - strcpy(st->pa->calling, ""); - - p = DATAPTR(ibh); - st->pa->spv = 0; - if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, - WE0_netSpecFac, 0))) { - if ((FAC_SPV == p[3]) || (FAC_Activate == p[3])) - st->pa->spv = 1; - } - BufPoolRelease(ibh); - - /* Signal all services, linklevel takes care of Service-Indicator */ - if (st->pa->info != 7) { - printk(KERN_DEBUG "non-digital call: %s -> %s\n", - st->pa->calling, - st->pa->called); - } - newl3state(st, 6); - st->l3.l3l4(st, CC_SETUP_IND, NULL); -} - -static void -l3_1tr6_tu_setup_ack(struct PStack *st, byte pr, void *arg) -{ - byte *p; - struct BufHeader *ibh = arg; - - p = DATAPTR(ibh); - if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize, - WE0_chanID, 0))) { - st->pa->bchannel = p[2] & 0x3; - } else - printk(KERN_INFO "octect 3 not found\n"); - - - BufPoolRelease(ibh); - newl3state(st, 2); -} - -static void -l3_1tr6_tu_call_sent(struct PStack *st, byte pr, void *arg) -{ - byte *p; - struct BufHeader *ibh = arg; - - p = DATAPTR(ibh); - if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize, - WE0_chanID, 0))) { - st->pa->bchannel = p[2] & 0x3; - } else - printk(KERN_INFO "octect 3 not found\n"); - - BufPoolRelease(ibh); - newl3state(st, 3); - st->l3.l3l4(st, CC_PROCEEDING_IND, NULL); -} - -static void -l3_1tr6_tu_alert(struct PStack *st, byte pr, void *arg) -{ - byte *p; - struct BufHeader *ibh = arg; - - - p = DATAPTR(ibh); - if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize, - WE6_statusCalled, 6))) { - if (DEBUG_1TR6 > 2) - printk(KERN_INFO "status called %x\n", p[2]); - } else if (DEBUG_1TR6 > 0) - printk(KERN_INFO "statusCalled not found\n"); - - BufPoolRelease(ibh); - newl3state(st, 4); - st->l3.l3l4(st, CC_ALERTING_IND, NULL); -} - -static void -l3_1tr6_tu_info(struct PStack *st, byte pr, void *arg) -{ - byte *p; - int i,tmpcharge=0; - char a_charge[8]; - struct BufHeader *ibh = arg; - - p = DATAPTR(ibh); - if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize, - WE6_chargingInfo, 6))) { - iecpy(a_charge, p, 1); - for (i = 0; i < strlen (a_charge); i++) { - 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 (DEBUG_1TR6 > 2) - printk(KERN_INFO "chargingInfo %d\n", st->pa->chargeinfo); - } else if (DEBUG_1TR6 > 2) - printk(KERN_INFO "chargingInfo not found\n"); - - BufPoolRelease(ibh); -} - -static void -l3_1tr6_tu_info_s2(struct PStack *st, byte pr, void *arg) -{ - byte *p; - int i; - struct BufHeader *ibh = arg; - - if (DEBUG_1TR6 > 4) { - p = DATAPTR(ibh); - for (i = 0; i < ibh->datasize; i++) { - printk(KERN_INFO "Info DATA %x\n", p[i]); - } - } - BufPoolRelease(ibh); -} - -static void -l3_1tr6_tu_connect(struct PStack *st, byte pr, void *arg) -{ - struct BufHeader *ibh = arg; - - st->pa->chargeinfo=0; - BufPoolRelease(ibh); - st->l3.l3l4(st, CC_SETUP_CNF, NULL); - newl3state(st, 10); -} - -static void -l3_1tr6_tu_rel(struct PStack *st, byte pr, void *arg) -{ - struct BufHeader *ibh = arg; - - BufPoolRelease(ibh); - l3_1TR6_message(st, MT_N1_REL_ACK, PROTO_DIS_N1); - st->l3.l3l4(st, CC_RELEASE_IND, NULL); - newl3state(st, 0); -} - -static void -l3_1tr6_tu_rel_ack(struct PStack *st, byte pr, void *arg) -{ - struct BufHeader *ibh = arg; - - BufPoolRelease(ibh); - newl3state(st, 0); - st->l3.l3l4(st, CC_RELEASE_CNF, NULL); -} - -static void -l3_1tr6_tu_disc(struct PStack *st, byte pr, void *arg) -{ - struct BufHeader *ibh = arg; - byte *p; - int i,tmpcharge=0; - char a_charge[8]; - - p = DATAPTR(ibh); - if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize, - WE6_chargingInfo, 6))) { - iecpy(a_charge, p, 1); - for (i = 0; i < strlen (a_charge); i++) { - 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 (DEBUG_1TR6 > 2) - printk(KERN_INFO "chargingInfo %d\n", st->pa->chargeinfo); - } else if (DEBUG_1TR6 > 2) - printk(KERN_INFO "chargingInfo not found\n"); - - p = DATAPTR(ibh); - if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize, - WE0_cause, 0))) { - if (p[1] > 0) { - st->pa->cause = p[2]; - } else { - st->pa->cause = 0; - } - if (DEBUG_1TR6 > 1) - printk(KERN_INFO "Cause %x\n", st->pa->cause); - } else if (DEBUG_1TR6 > 0) - printk(KERN_INFO "Cause not found\n"); - - BufPoolRelease(ibh); - newl3state(st, 12); - st->l3.l3l4(st, CC_DISCONNECT_IND, NULL); -} - - -static void -l3_1tr6_tu_connect_ack(struct PStack *st, byte pr, void *arg) -{ - struct BufHeader *ibh = arg; - - BufPoolRelease(ibh); - st->pa->chargeinfo = 0; - st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL); - newl3state(st, 10); -} - -static void -l3_1tr6_alert(struct PStack *st, byte pr, - void *arg) -{ - l3_1TR6_message(st, MT_N1_ALERT, PROTO_DIS_N1); - newl3state(st, 7); -} - -static void -l3_1tr6_conn(struct PStack *st, byte pr, - void *arg) -{ - struct BufHeader *dibh; - byte *p; - - st->l3.callref = 0x80 + st->pa->callref; - - BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20); - p = DATAPTR(dibh); - p += st->l2.ihsize; - - *p++ = PROTO_DIS_N1; - *p++ = 0x1; - *p++ = st->l3.callref; - *p++ = MT_N1_CONN; - - if (st->pa->spv) { /* SPV ??? */ - /* NSF SPV */ - *p++ = WE0_netSpecFac; - *p++ = 4; /* Laenge */ - *p++ = 0; - *p++ = FAC_SPV; /* SPV */ - *p++ = st->pa->info; - *p++ = st->pa->info2; - *p++ = WE0_netSpecFac; - *p++ = 4; /* Laenge */ - *p++ = 0; - *p++ = FAC_Activate; /* aktiviere SPV */ - *p++ = st->pa->info; - *p++ = st->pa->info2; - } - dibh->datasize = p - DATAPTR(dibh); - - i_down(st, dibh); - - newl3state(st, 8); -} - -static void -l3_1tr6_reset(struct PStack *st, byte pr, void *arg) -{ - newl3state(st, 0); -} - -static void -l3_1tr6_disconn_req(struct PStack *st, byte pr, void *arg) -{ - struct BufHeader *dibh; - byte *p; - byte rejflg; - - BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 21); - p = DATAPTR(dibh); - p += st->l2.ihsize; - - *p++ = PROTO_DIS_N1; - *p++ = 0x1; - *p++ = st->l3.callref; - *p++ = MT_N1_DISC; - - if (st->l3.state == 7) { - rejflg = 1; - *p++ = WE0_cause; /* Anruf abweisen */ - *p++ = 0x01; /* Laenge = 1 */ - *p++ = CAUSE_CallRejected; - } else { - rejflg = 0; - *p++ = WE0_cause; - *p++ = 0x0; /* Laenge = 0 normales Ausloesen */ - } - - dibh->datasize = p - DATAPTR(dibh); - - i_down(st, dibh); - - newl3state(st, 11); -} - -static void -l3_1tr6_rel_req(struct PStack *st, byte pr, void *arg) -{ - l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1); - newl3state(st, 19); -} - -static struct stateentry downstatelist_1tr6t[] = -{ - {0, CC_SETUP_REQ, l3_1tr6_setup}, - {1, CC_DISCONNECT_REQ, l3_1tr6_disconn_req}, - {1, CC_RELEASE_REQ, l3_1tr6_rel_req}, - {1, CC_DLRL, l3_1tr6_reset}, - {2, CC_DISCONNECT_REQ, l3_1tr6_disconn_req}, - {2, CC_RELEASE_REQ, l3_1tr6_rel_req}, - {2, CC_DLRL, l3_1tr6_reset}, - {3, CC_DISCONNECT_REQ, l3_1tr6_disconn_req}, - {3, CC_RELEASE_REQ, l3_1tr6_rel_req}, - {3, CC_DLRL, l3_1tr6_reset}, - {4, CC_DISCONNECT_REQ, l3_1tr6_disconn_req}, - {4, CC_RELEASE_REQ, l3_1tr6_rel_req}, - {4, CC_DLRL, l3_1tr6_reset}, - {6, CC_REJECT_REQ, l3_1tr6_reset}, - {6, CC_RELEASE_REQ, l3_1tr6_rel_req}, - {6, CC_SETUP_RSP, l3_1tr6_conn}, - {6, CC_ALERTING_REQ, l3_1tr6_alert}, - {6, CC_DLRL, l3_1tr6_reset}, - {7, CC_SETUP_RSP, l3_1tr6_conn}, - {7, CC_RELEASE_REQ, l3_1tr6_rel_req}, - {7, CC_DISCONNECT_REQ, l3_1tr6_disconn_req}, - {7, CC_DLRL, l3_1tr6_reset}, - {8, CC_DISCONNECT_REQ, l3_1tr6_disconn_req}, - {8, CC_RELEASE_REQ, l3_1tr6_rel_req}, - {8, CC_DLRL, l3_1tr6_reset}, - {10, CC_DISCONNECT_REQ, l3_1tr6_disconn_req}, - {10, CC_RELEASE_REQ, l3_1tr6_rel_req}, - {10, CC_DLRL, l3_1tr6_reset}, - {12, CC_RELEASE_REQ, l3_1tr6_rel_req}, - {12, CC_DLRL, l3_1tr6_reset}, - {19, CC_DLRL, l3_1tr6_reset}, -}; - -static int downsl_1tr6t_len = sizeof(downstatelist_1tr6t) / -sizeof(struct stateentry); - -static struct stateentry datastatelist_1tr6t[] = -{ - {0, MT_N1_SETUP, l3_1tr6_tu_setup}, - {0, MT_N1_REL, l3_1tr6_tu_rel}, - {1, MT_N1_SETUP_ACK, l3_1tr6_tu_setup_ack}, - {1, MT_N1_CALL_SENT, l3_1tr6_tu_call_sent}, - {1, MT_N1_REL, l3_1tr6_tu_rel}, - {1, MT_N1_DISC, l3_1tr6_tu_disc}, - {2, MT_N1_CALL_SENT, l3_1tr6_tu_call_sent}, - {2, MT_N1_ALERT, l3_1tr6_tu_alert}, - {2, MT_N1_CONN, l3_1tr6_tu_connect}, - {2, MT_N1_REL, l3_1tr6_tu_rel}, - {2, MT_N1_DISC, l3_1tr6_tu_disc}, - {2, MT_N1_INFO, l3_1tr6_tu_info_s2}, - {3, MT_N1_ALERT, l3_1tr6_tu_alert}, - {3, MT_N1_CONN, l3_1tr6_tu_connect}, - {3, MT_N1_REL, l3_1tr6_tu_rel}, - {3, MT_N1_DISC, l3_1tr6_tu_disc}, - {4, MT_N1_ALERT, l3_1tr6_tu_alert}, - {4, MT_N1_CONN, l3_1tr6_tu_connect}, - {4, MT_N1_REL, l3_1tr6_tu_rel}, - {4, MT_N1_DISC, l3_1tr6_tu_disc}, - {7, MT_N1_REL, l3_1tr6_tu_rel}, - {7, MT_N1_DISC, l3_1tr6_tu_disc}, - {8, MT_N1_REL, l3_1tr6_tu_rel}, - {8, MT_N1_DISC, l3_1tr6_tu_disc}, - {8, MT_N1_CONN_ACK, l3_1tr6_tu_connect_ack}, - {10, MT_N1_REL, l3_1tr6_tu_rel}, - {10, MT_N1_DISC, l3_1tr6_tu_disc}, - {10, MT_N1_INFO, l3_1tr6_tu_info}, - {11, MT_N1_REL, l3_1tr6_tu_rel}, - {12, MT_N1_REL, l3_1tr6_tu_rel}, - {19, MT_N1_REL_ACK, l3_1tr6_tu_rel_ack} -}; - -static int datasl_1tr6t_len = sizeof(datastatelist_1tr6t) / -sizeof(struct stateentry); diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/l3_1TR6.h linux/drivers/isdn/teles/l3_1TR6.h --- v2.0.30/linux/drivers/isdn/teles/l3_1TR6.h Tue Nov 12 22:36:20 1996 +++ linux/drivers/isdn/teles/l3_1TR6.h Wed Dec 31 16:00:00 1969 @@ -1,160 +0,0 @@ -/* $Id: l3_1TR6.h,v 1.4 1996/09/23 01:53:52 fritz Exp $ - * - * $Log: l3_1TR6.h,v $ - * Revision 1.4 1996/09/23 01:53:52 fritz - * Bugfix: discard unknown frames (non-EDSS1 and non-1TR6). - * - * Revision 1.3 1996/04/30 21:53:48 isdn4dev - * Bugs, SPV, Logging in q931.c Karsten Keil - * - * Revision 1.1 1996/04/13 10:25:42 fritz - * Initial revision - * - * - */ -#ifndef l3_1TR6 -#define l3_1TR6 - -/* - * MsgType N0 - */ -#define MT_N0_REG_IND 0x61 -#define MT_N0_CANC_IND 0x62 -#define MT_N0_FAC_STA 0x63 -#define MT_N0_STA_ACK 0x64 -#define MT_N0_STA_REJ 0x65 -#define MT_N0_FAC_INF 0x66 -#define MT_N0_INF_ACK 0x67 -#define MT_N0_INF_REJ 0x68 -#define MT_N0_CLOSE 0x75 -#define MT_N0_CLO_ACK 0x77 - - -/* - * MsgType N1 - */ - -#define MT_N1_ESC 0x00 -#define MT_N1_ALERT 0x01 -#define MT_N1_CALL_SENT 0x02 -#define MT_N1_CONN 0x07 -#define MT_N1_CONN_ACK 0x0F -#define MT_N1_SETUP 0x05 -#define MT_N1_SETUP_ACK 0x0D -#define MT_N1_RES 0x26 -#define MT_N1_RES_ACK 0x2E -#define MT_N1_RES_REJ 0x22 -#define MT_N1_SUSP 0x25 -#define MT_N1_SUSP_ACK 0x2D -#define MT_N1_SUSP_REJ 0x21 -#define MT_N1_USER_INFO 0x20 -#define MT_N1_DET 0x40 -#define MT_N1_DISC 0x45 -#define MT_N1_REL 0x4D -#define MT_N1_REL_ACK 0x5A -#define MT_N1_CANC_ACK 0x6E -#define MT_N1_CANC_REJ 0x67 -#define MT_N1_CON_CON 0x69 -#define MT_N1_FAC 0x60 -#define MT_N1_FAC_ACK 0x68 -#define MT_N1_FAC_CAN 0x66 -#define MT_N1_FAC_REG 0x64 -#define MT_N1_FAC_REJ 0x65 -#define MT_N1_INFO 0x6D -#define MT_N1_REG_ACK 0x6C -#define MT_N1_REG_REJ 0x6F -#define MT_N1_STAT 0x63 - - - -/* - * W Elemente - */ - -#define WE_Shift_F0 0x90 -#define WE_Shift_F6 0x96 -#define WE_Shift_OF0 0x98 -#define WE_Shift_OF6 0x9E - -#define WE0_cause 0x08 -#define WE0_connAddr 0x0C -#define WE0_callID 0x10 -#define WE0_chanID 0x18 -#define WE0_netSpecFac 0x20 -#define WE0_display 0x28 -#define WE0_keypad 0x2C -#define WE0_origAddr 0x6C -#define WE0_destAddr 0x70 -#define WE0_userInfo 0x7E - -#define WE0_moreData 0xA0 -#define WE0_congestLevel 0xB0 - -#define WE6_serviceInd 0x01 -#define WE6_chargingInfo 0x02 -#define WE6_date 0x03 -#define WE6_facSelect 0x05 -#define WE6_facStatus 0x06 -#define WE6_statusCalled 0x07 -#define WE6_addTransAttr 0x08 - -/* - * FacCodes - */ -#define FAC_Sperre 0x01 -#define FAC_Sperre_All 0x02 -#define FAC_Sperre_Fern 0x03 -#define FAC_Sperre_Intl 0x04 -#define FAC_Sperre_Interk 0x05 - -#define FAC_Forward1 0x02 -#define FAC_Forward2 0x03 -#define FAC_Konferenz 0x06 -#define FAC_GrabBchan 0x0F -#define FAC_Reactivate 0x10 -#define FAC_Konferenz3 0x11 -#define FAC_Dienstwechsel1 0x12 -#define FAC_Dienstwechsel2 0x13 -#define FAC_NummernIdent 0x14 -#define FAC_GBG 0x15 -#define FAC_DisplayUebergeben 0x17 -#define FAC_DisplayUmgeleitet 0x1A -#define FAC_Unterdruecke 0x1B -#define FAC_Deactivate 0x1E -#define FAC_Activate 0x1D -#define FAC_SPV 0x1F -#define FAC_Rueckwechsel 0x23 -#define FAC_Umleitung 0x24 - -/* - * Cause codes - */ -#define CAUSE_InvCRef 0x01 -#define CAUSE_BearerNotImpl 0x03 -#define CAUSE_CIDunknown 0x07 -#define CAUSE_CIDinUse 0x08 -#define CAUSE_NoChans 0x0A -#define CAUSE_FacNotImpl 0x10 -#define CAUSE_FacNotSubscr 0x11 -#define CAUSE_OutgoingBarred 0x20 -#define CAUSE_UserAccessBusy 0x21 -#define CAUSE_NegativeGBG 0x22 -#define CAUSE_UnknownGBG 0x23 -#define CAUSE_NoSPVknown 0x25 -#define CAUSE_DestNotObtain 0x35 -#define CAUSE_NumberChanged 0x38 -#define CAUSE_OutOfOrder 0x39 -#define CAUSE_NoUserResponse 0x3A -#define CAUSE_UserBusy 0x3B -#define CAUSE_IncomingBarred 0x3D -#define CAUSE_CallRejected 0x3E -#define CAUSE_NetworkCongestion 0x59 -#define CAUSE_RemoteUser 0x5A -#define CAUSE_LocalProcErr 0x70 -#define CAUSE_RemoteProcErr 0x71 -#define CAUSE_RemoteUserSuspend 0x72 -#define CAUSE_RemoteUserResumed 0x73 -#define CAUSE_UserInfoDiscarded 0x7F - - -#endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/llglue.c linux/drivers/isdn/teles/llglue.c --- v2.0.30/linux/drivers/isdn/teles/llglue.c Tue Nov 12 22:36:20 1996 +++ linux/drivers/isdn/teles/llglue.c Wed Dec 31 16:00:00 1969 @@ -1,151 +0,0 @@ -/* $Id: llglue.c,v 1.7 1996/10/22 23:14:17 fritz Exp $ - * - * $Log: llglue.c,v $ - * Revision 1.7 1996/10/22 23:14:17 fritz - * Changes for compatibility to 2.0.X and 2.1.X kernels. - * - * Revision 1.6 1996/06/03 20:03:39 fritz - * Fixed typos. - * - * Revision 1.5 1996/05/31 00:58:47 fritz - * Errata: Reverted change from rev 1.4. - * - * Revision 1.4 1996/05/26 14:59:57 fritz - * Bugfix: maxbufsize had been set without respect to possible X.75 header. - * - * Revision 1.3 1996/05/01 14:19:57 fritz - * Added ISDN_FEATURE_L2_TRANS - * - * Revision 1.2 1996/04/29 23:01:46 fritz - * Added driverId and channel to readstatus(). - * - * Revision 1.1 1996/04/13 10:26:29 fritz - * Initial revision - * - * - */ -#define __NO_VERSION__ -#include "teles.h" -#include -#include - - -extern struct Channel *chanlist; -int drid; -char *teles_id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - -isdn_if iif; - -#define TELES_STATUS_BUFSIZE 4096 -static byte *teles_status_buf = NULL; -static byte *teles_status_read = NULL; -static byte *teles_status_write = NULL; -static byte *teles_status_end = NULL; - -int -teles_readstatus(byte * buf, int len, int user, int id, int channel) -{ - int count; - byte *p; - - for (p = buf, count = 0; count < len; p++, count++) { - if (user) - put_user(*teles_status_read++, p); - else - *p++ = *teles_status_read++; - if (teles_status_read > teles_status_end) - teles_status_read = teles_status_buf; - } - return count; -} - -void -teles_putstatus(char *buf) -{ - long flags; - int len, count, i; - byte *p; - isdn_ctrl ic; - - save_flags(flags); - cli(); - count = 0; - len = strlen(buf); - for (p = buf, i = len; i > 0; i--, p++) { - *teles_status_write++ = *p; - if (teles_status_write > teles_status_end) - teles_status_write = teles_status_buf; - count++; - } - restore_flags(flags); - if (count) { - ic.command = ISDN_STAT_STAVAIL; - ic.driver = drid; - ic.arg = count; - iif.statcallb(&ic); - } -} - - -int -ll_init(void) -{ - isdn_ctrl ic; - - teles_status_buf = Smalloc(TELES_STATUS_BUFSIZE, - GFP_KERNEL, "teles_status_buf"); - if (!teles_status_buf) { - printk(KERN_ERR "teles: Could not allocate status-buffer\n"); - return (-EIO); - } else { - teles_status_read = teles_status_buf; - teles_status_write = teles_status_buf; - teles_status_end = teles_status_buf + TELES_STATUS_BUFSIZE - 1; - } - - iif.channels = CallcNewChan(); - iif.maxbufsize = BUFFER_SIZE(HSCX_SBUF_ORDER, HSCX_SBUF_BPPS); - iif.features = - ISDN_FEATURE_L2_X75I | - ISDN_FEATURE_L2_HDLC | - ISDN_FEATURE_L2_TRANS | - ISDN_FEATURE_L3_TRANS | - ISDN_FEATURE_P_1TR6 | - ISDN_FEATURE_P_EURO; - - iif.command = teles_command; - iif.writebuf = teles_writebuf; - iif.writecmd = NULL; - iif.readstat = teles_readstatus; - strncpy(iif.id, teles_id, sizeof(iif.id) - 1); - - register_isdn(&iif); - drid = iif.channels; - - ic.driver = drid; - ic.command = ISDN_STAT_RUN; - iif.statcallb(&ic); - return 0; -} - -void -ll_stop(void) -{ - isdn_ctrl ic; - - ic.command = ISDN_STAT_STOP; - ic.driver = drid; - iif.statcallb(&ic); - - CallcFreeChan(); -} - -void -ll_unload(void) -{ - isdn_ctrl ic; - - ic.command = ISDN_STAT_UNLOAD; - ic.driver = drid; - iif.statcallb(&ic); -} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/mod.c linux/drivers/isdn/teles/mod.c --- v2.0.30/linux/drivers/isdn/teles/mod.c Sun Apr 21 01:56:15 1996 +++ linux/drivers/isdn/teles/mod.c Wed Dec 31 16:00:00 1969 @@ -1,143 +0,0 @@ -/* $Id: mod.c,v 1.1 1996/04/13 10:27:02 fritz Exp $ - * - * $Log: mod.c,v $ - * Revision 1.1 1996/04/13 10:27:02 fritz - * Initial revision - * - * - */ -#include "teles.h" - -extern struct IsdnCard cards[]; -extern char *teles_id; - -int nrcards; - -typedef struct { - byte *membase; - int interrupt; - unsigned int iobase; - unsigned int protocol; -} io_type; - -io_type io[] = -{ - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, -}; - -void -teles_mod_dec_use_count(void) -{ - MOD_DEC_USE_COUNT; -} - -void -teles_mod_inc_use_count(void) -{ - MOD_INC_USE_COUNT; -} - -#ifdef MODULE -#define teles_init init_module -#else -void teles_setup(char *str, int *ints) -{ - int i, j, argc; - static char sid[20]; - - argc = ints[0]; - i = 0; - j = 1; - while (argc && (i<16)) { - if (argc) { - io[i].iobase = ints[j]; - j++; argc--; - } - if (argc) { - io[i].interrupt = ints[j]; - j++; argc--; - } - if (argc) { - io[i].membase = (byte *)ints[j]; - j++; argc--; - } - if (argc) { - io[i].protocol = ints[j]; - j++; argc--; - } - i++; - } - if (strlen(str)) { - strcpy(sid,str); - teles_id = sid; - } -} -#endif - -int -teles_init(void) -{ - int i; - - nrcards = 0; - for (i = 0; i < 16; i++) { - if (io[i].protocol) { - cards[i].membase = io[i].membase; - cards[i].interrupt = io[i].interrupt; - cards[i].iobase = io[i].iobase; - cards[i].protocol = io[i].protocol; - } - } - for (i = 0; i < 16; i++) - if (cards[i].protocol) - nrcards++; - printk(KERN_DEBUG "teles: Total %d card%s defined\n", - nrcards, (nrcards > 1) ? "s" : ""); - if (teles_inithardware()) { - /* Install only, if at least one card found */ - Isdnl2New(); - TeiNew(); - CallcNew(); - ll_init(); - - /* No symbols to export, hide all symbols */ - register_symtab(NULL); - -#ifdef MODULE - printk(KERN_NOTICE "Teles module installed\n"); -#endif - return (0); - } else - return -EIO; -} - -#ifdef MODULE -void -cleanup_module(void) -{ - - ll_stop(); - TeiFree(); - Isdnl2Free(); - CallcFree(); - teles_closehardware(); - ll_unload(); - printk(KERN_NOTICE "Teles module removed\n"); - -} -#endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/proto.h linux/drivers/isdn/teles/proto.h --- v2.0.30/linux/drivers/isdn/teles/proto.h Fri Nov 29 01:10:13 1996 +++ linux/drivers/isdn/teles/proto.h Wed Dec 31 16:00:00 1969 @@ -1,18 +0,0 @@ -/* $Id: proto.h,v 1.1 1996/09/23 01:53:52 fritz Exp $ - * - * not much now - just the l3 proto discriminator - * - * $Log: proto.h,v $ - * Revision 1.1 1996/09/23 01:53:52 fritz - * Bugfix: discard unknown frames (non-EDSS1 and non-1TR6). - * - */ - -#ifndef PROTO_H -#define PROTO_H - -#define PROTO_EURO 0x08 -#define PROTO_DIS_N0 0x40 -#define PROTO_DIS_N1 0x41 - -#endif diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/q931.c linux/drivers/isdn/teles/q931.c --- v2.0.30/linux/drivers/isdn/teles/q931.c Tue Nov 12 22:36:20 1996 +++ linux/drivers/isdn/teles/q931.c Wed Dec 31 16:00:00 1969 @@ -1,1155 +0,0 @@ -/* $Id: q931.c,v 1.6 1996/09/23 01:53:53 fritz Exp $ - * - * q931.c code to decode ITU Q.931 call control messages - * - * Author Jan den Ouden - * - * Changelog - * - * Pauline Middelink general improvements - * - * Beat Doebeli cause texts, display information element - * - * Karsten Keil cause texts, display information element for 1TR6 - * - * - * $Log: q931.c,v $ - * Revision 1.6 1996/09/23 01:53:53 fritz - * Bugfix: discard unknown frames (non-EDSS1 and non-1TR6). - * - * Revision 1.5 1996/06/03 20:03:40 fritz - * Fixed typos. - * - * Revision 1.4 1996/05/17 03:46:17 fritz - * General cleanup. - * - * Revision 1.3 1996/04/30 22:06:50 isdn4dev - * logging 1TR6 messages correctly Karsten Keil - * - * Revision 1.2 1996/04/20 16:48:19 fritz - * Misc. typos - * - * Revision 1.1 1996/04/13 10:27:49 fritz - * Initial revision - * - * - */ - - -#define __NO_VERSION__ -#include "teles.h" -#include "proto.h" -#include "l3_1TR6.h" - -byte * -findie(byte * p, int size, byte ie, int wanted_set) -{ - int l, codeset, maincodeset; - byte *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(byte * dest, byte * iestart, int ieoffset) -{ - byte *p; - int l; - - p = iestart + ieoffset + 2; - l = iestart[1] - ieoffset; - while (l--) - *dest++ = *p++; - *dest++ = '\0'; -} - -int -getcallref(byte * p) -{ - p++; /* prot discr */ - p++; /* callref length */ - return (*p); /* assuming one-byte callref */ -} - -/* - * According to Table 4-2/Q.931 - */ -static -struct MessageType { - byte nr; - char *descr; -} mtlist[] = { - - { - 0x1, "ALERTING" - }, - { - 0x2, "CALL PROCEEDING" - }, - { - 0x7, "CONNECT" - }, - { - 0xf, "CONNECT ACKNOWLEDGE" - }, - { - 0x3, "PROGRESS" - }, - { - 0x5, "SETUP" - }, - { - 0xd, "SETUP ACKNOWLEDGE" - }, - { - 0x26, "RESUME" - }, - { - 0x2e, "RESUME ACKNOWLEDGE" - }, - { - 0x22, "RESUME REJECT" - }, - { - 0x25, "SUSPEND" - }, - { - 0x2d, "SUSPEND ACKNOWLEDGE" - }, - { - 0x21, "SUSPEND REJECT" - }, - { - 0x20, "USER INFORMATION" - }, - { - 0x45, "DISCONNECT" - }, - { - 0x4d, "RELEASE" - }, - { - 0x5a, "RELEASE COMPLETE" - }, - { - 0x46, "RESTART" - }, - { - 0x4e, "RESTART ACKNOWLEDGE" - }, - { - 0x60, "SEGMENT" - }, - { - 0x79, "CONGESTION CONTROL" - }, - { - 0x7b, "INFORMATION" - }, - { - 0x62, "FACILITY" - }, - { - 0x6e, "NOTIFY" - }, - { - 0x7d, "STATUS" - }, - { - 0x75, "STATUS ENQUIRY" - } -}; - -#define MTSIZE sizeof(mtlist)/sizeof(struct MessageType) - -static -struct MessageType mt_n0[] = -{ - {MT_N0_REG_IND, "REGister INDication"}, - {MT_N0_CANC_IND, "CANCel INDication"}, - {MT_N0_FAC_STA, "FACility STAtus"}, - {MT_N0_STA_ACK, "STAtus ACKnowledge"}, - {MT_N0_STA_REJ, "STAtus REJect"}, - {MT_N0_FAC_INF, "FACility INFormation"}, - {MT_N0_INF_ACK, "INFormation ACKnowledge"}, - {MT_N0_INF_REJ, "INFormation REJect"}, - {MT_N0_CLOSE, "CLOSE"}, - {MT_N0_CLO_ACK, "CLOse ACKnowledge"} -}; - -int mt_n0_len = (sizeof(mt_n0) / sizeof(struct MessageType)); - -static -struct MessageType mt_n1[] = -{ - {MT_N1_ESC, "ESCape"}, - {MT_N1_ALERT, "ALERT"}, - {MT_N1_CALL_SENT, "CALL SENT"}, - {MT_N1_CONN, "CONNect"}, - {MT_N1_CONN_ACK, "CONNect ACKnowledge"}, - {MT_N1_SETUP, "SETUP"}, - {MT_N1_SETUP_ACK, "SETUP ACKnowledge"}, - {MT_N1_RES, "RESume"}, - {MT_N1_RES_ACK, "RESume ACKnowledge"}, - {MT_N1_RES_REJ, "RESume REJect"}, - {MT_N1_SUSP, "SUSPend"}, - {MT_N1_SUSP_ACK, "SUSPend ACKnowledge"}, - {MT_N1_SUSP_REJ, "SUSPend REJect"}, - {MT_N1_USER_INFO, "USER INFO"}, - {MT_N1_DET, "DETach"}, - {MT_N1_DISC, "DISConnect"}, - {MT_N1_REL, "RELease"}, - {MT_N1_REL_ACK, "RELease ACKnowledge"}, - {MT_N1_CANC_ACK, "CANCel ACKnowledge"}, - {MT_N1_CANC_REJ, "CANCel REJect"}, - {MT_N1_CON_CON, "CONgestion CONtrol"}, - {MT_N1_FAC, "FACility"}, - {MT_N1_FAC_ACK, "FACility ACKnowledge"}, - {MT_N1_FAC_CAN, "FACility CANcel"}, - {MT_N1_FAC_REG, "FACility REGister"}, - {MT_N1_FAC_REJ, "FACility REJect"}, - {MT_N1_INFO, "INFOrmation"}, - {MT_N1_REG_ACK, "REGister ACKnowledge"}, - {MT_N1_REG_REJ, "REGister REJect"}, - {MT_N1_STAT, "STATus"} -}; - -int mt_n1_len = (sizeof(mt_n1) / sizeof(struct MessageType)); - -static struct MessageType fac_1tr6[] = -{ - {FAC_Sperre, "Sperre"}, - {FAC_Forward1, "Forward 1"}, - {FAC_Forward2, "Forward 2"}, - {FAC_Konferenz, "Konferenz"}, - {FAC_GrabBchan, "Grab Bchannel"}, - {FAC_Reactivate, "Reactivate"}, - {FAC_Konferenz3, "Dreier Konferenz"}, - {FAC_Dienstwechsel1, "Einseitiger Dienstwechsel"}, - {FAC_Dienstwechsel2, "Zweiseitiger Dienstwechsel"}, - {FAC_NummernIdent, "Rufnummer-Identifizierung"}, - {FAC_GBG, "GBG"}, - {FAC_DisplayUebergeben, "Display Uebergeben"}, - {FAC_DisplayUmgeleitet, "Display Umgeleitet"}, - {FAC_Unterdruecke, "Unterdruecke Rufnummer"}, - {FAC_Deactivate, "Deactivate"}, - {FAC_Activate, "Activate"}, - {FAC_SPV, "SPV"}, - {FAC_Rueckwechsel, "Rueckwechsel"}, - {FAC_Umleitung, "Umleitung"} -}; -int fac_1tr6_len = (sizeof(fac_1tr6) / sizeof(struct MessageType)); - - - -static int -prbits(char *dest, byte b, int start, int len) -{ - char *dp = dest; - - b = b << (8 - start); - while (len--) { - if (b & 0x80) - *dp++ = '1'; - else - *dp++ = '0'; - b = b << 1; - } - return (dp - dest); -} - -static -byte * -skipext(byte * p) -{ - while (!(*p++ & 0x80)); - return (p); -} - -/* - * Cause Values According to Q.850 - * edescr: English description - * ddescr: German description used by Swissnet II (Swiss Telecom - * not yet written... - */ - -static -struct CauseValue { - byte nr; - char *edescr; - char *ddescr; -} cvlist[] = { - - { - 0x01, "Unallocated (unassigned) number", "Nummer nicht zugeteilt" - }, - { - 0x02, "No route to specified transit network", "" - }, - { - 0x03, "No route to destination", "" - }, - { - 0x04, "Send special information tone", "" - }, - { - 0x05, "Misdialled trunk prefix", "" - }, - { - 0x06, "Channel unacceptable", "Kanal nicht akzeptierbar" - }, - { - 0x07, "Channel awarded and being delivered in an established channel", "" - }, - { - 0x08, "Preemption", "" - }, - { - 0x09, "Preemption - circuit reserved for reuse", "" - }, - { - 0x10, "Normal call clearing", "Normale Ausloesung" - }, - { - 0x11, "User busy", "TNB besetzt" - }, - { - 0x12, "No user responding", "" - }, - { - 0x13, "No answer from user (user alerted)", "" - }, - { - 0x14, "Subscriber absent", "" - }, - { - 0x15, "Call rejected", "" - }, - { - 0x16, "Number changed", "" - }, - { - 0x1a, "non-selected user clearing", "" - }, - { - 0x1b, "Destination out of order", "" - }, - { - 0x1c, "Invalid number format (address incomplete)", "" - }, - { - 0x1d, "Facility rejected", "" - }, - { - 0x1e, "Response to Status enquiry", "" - }, - { - 0x1f, "Normal, unspecified", "" - }, - { - 0x22, "No circuit/channel available", "" - }, - { - 0x26, "Network out of order", "" - }, - { - 0x27, "Permanent frame mode connection out-of-service", "" - }, - { - 0x28, "Permanent frame mode connection operational", "" - }, - { - 0x29, "Temporary failure", "" - }, - { - 0x2a, "Switching equipment congestion", "" - }, - { - 0x2b, "Access information discarded", "" - }, - { - 0x2c, "Requested circuit/channel not available", "" - }, - { - 0x2e, "Precedence call blocked", "" - }, - { - 0x2f, "Resource unavailable, unspecified", "" - }, - { - 0x31, "Quality of service unavailable", "" - }, - { - 0x32, "Requested facility not subscribed", "" - }, - { - 0x35, "Outgoing calls barred within CUG", "" - }, - { - 0x37, "Incoming calls barred within CUG", "" - }, - { - 0x39, "Bearer capability not authorized", "" - }, - { - 0x3a, "Bearer capability not presently available", "" - }, - { - 0x3e, "Inconsistency in designated outgoing access information and subscriber class ", " " - }, - { - 0x3f, "Service or option not available, unspecified", "" - }, - { - 0x41, "Bearer capability not implemented", "" - }, - { - 0x42, "Channel type not implemented", "" - }, - { - 0x43, "Requested facility not implemented", "" - }, - { - 0x44, "Only restricted digital information bearer capability is available", "" - }, - { - 0x4f, "Service or option not implemented", "" - }, - { - 0x51, "Invalid call reference value", "" - }, - { - 0x52, "Identified channel does not exist", "" - }, - { - 0x53, "A suspended call exists, but this call identity does not", "" - }, - { - 0x54, "Call identity in use", "" - }, - { - 0x55, "No call suspended", "" - }, - { - 0x56, "Call having the requested call identity has been cleared", "" - }, - { - 0x57, "User not member of CUG", "" - }, - { - 0x58, "Incompatible destination", "" - }, - { - 0x5a, "Non-existent CUG", "" - }, - { - 0x5b, "Invalid transit network selection", "" - }, - { - 0x5f, "Invalid message, unspecified", "" - }, - { - 0x60, "Mandatory information element is missing", "" - }, - { - 0x61, "Message type non-existent or not implemented", "" - }, - { - 0x62, "Message not compatible with call state or message type non-existent or not implemented ", " " - }, - { - 0x63, "Information element/parameter non-existent or not implemented", "" - }, - { - 0x64, "Invalid information element contents", "" - }, - { - 0x65, "Message not compatible with call state", "" - }, - { - 0x66, "Recovery on timer expiry", "" - }, - { - 0x67, "Parameter non-existent or not implemented - passed on", "" - }, - { - 0x6e, "Message with unrecognized parameter discarded", "" - }, - { - 0x6f, "Protocol error, unspecified", "" - }, - { - 0x7f, "Interworking, unspecified", "" - }, -}; - -#define CVSIZE sizeof(cvlist)/sizeof(struct CauseValue) - -static -int -prcause(char *dest, byte * p) -{ - byte *end; - char *dp = dest; - int i, cause; - - end = p + p[1] + 1; - p += 2; - dp += sprintf(dp, " coding "); - dp += prbits(dp, *p, 7, 2); - dp += sprintf(dp, " location "); - dp += prbits(dp, *p, 4, 4); - *dp++ = '\n'; - p = skipext(p); - - cause = 0x7f & *p++; - - /* locate cause value */ - for (i = 0; i < CVSIZE; i++) - if (cvlist[i].nr == cause) - break; - - /* display cause value if it exists */ - if (i == CVSIZE) - dp += sprintf(dp, "Unknown cause type %x!\n", cause); - else - dp += sprintf(dp, " cause value %x : %s \n", cause, cvlist[i].edescr); - - while (!0) { - if (p > end) - break; - dp += sprintf(dp, " diag attribute %d ", *p++ & 0x7f); - dp += sprintf(dp, " rej %d ", *p & 0x7f); - if (*p & 0x80) { - *dp++ = '\n'; - break; - } else - dp += sprintf(dp, " av %d\n", (*++p) & 0x7f); - } - return (dp - dest); - -} - -static -struct MessageType cause_1tr6[] = -{ - {CAUSE_InvCRef, "Invalid Call Reference"}, - {CAUSE_BearerNotImpl, "Bearer Service Not Implemented"}, - {CAUSE_CIDunknown, "Caller Identity unknown"}, - {CAUSE_CIDinUse, "Caller Identity in Use"}, - {CAUSE_NoChans, "No Channels available"}, - {CAUSE_FacNotImpl, "Facility Not Implemented"}, - {CAUSE_FacNotSubscr, "Facility Not Subscribed"}, - {CAUSE_OutgoingBarred, "Outgoing calls barred"}, - {CAUSE_UserAccessBusy, "User Access Busy"}, - {CAUSE_NegativeGBG, "Negative GBG"}, - {CAUSE_UnknownGBG, "Unknown GBG"}, - {CAUSE_NoSPVknown, "No SPV known"}, - {CAUSE_DestNotObtain, "Destination not obtainable"}, - {CAUSE_NumberChanged, "Number changed"}, - {CAUSE_OutOfOrder, "Out Of Order"}, - {CAUSE_NoUserResponse, "No User Response"}, - {CAUSE_UserBusy, "User Busy"}, - {CAUSE_IncomingBarred, "Incoming Barred"}, - {CAUSE_CallRejected, "Call Rejected"}, - {CAUSE_NetworkCongestion, "Network Congestion"}, - {CAUSE_RemoteUser, "Remote User initiated"}, - {CAUSE_LocalProcErr, "Local Procedure Error"}, - {CAUSE_RemoteProcErr, "Remote Procedure Error"}, - {CAUSE_RemoteUserSuspend, "Remote User Suspend"}, - {CAUSE_RemoteUserResumed, "Remote User Resumed"}, - {CAUSE_UserInfoDiscarded, "User Info Discarded"} -}; - -int cause_1tr6_len = (sizeof(cause_1tr6) / sizeof(struct MessageType)); - -static int -prcause_1tr6(char *dest, byte * p) -{ - char *dp = dest; - int i, cause; - - p++; - if (0 == *p) { - dp += sprintf(dp, " OK (cause length=0)\n"); - return (dp - dest); - } else if (*p > 1) { - dp += sprintf(dp, " coding "); - dp += prbits(dp, p[2], 7, 2); - dp += sprintf(dp, " location "); - dp += prbits(dp, p[2], 4, 4); - *dp++ = '\n'; - } - p++; - cause = 0x7f & *p; - - /* locate cause value */ - for (i = 0; i < cause_1tr6_len; i++) - if (cause_1tr6[i].nr == cause) - break; - - /* display cause value if it exists */ - if (i == cause_1tr6_len) - dp += sprintf(dp, "Unknown cause type %x!\n", cause); - else - dp += sprintf(dp, " cause value %x : %s \n", cause, cause_1tr6[i].descr); - - return (dp - dest); - -} - -static int -prchident(char *dest, byte * p) { - char *dp = dest; - - p += 2; - dp += sprintf(dp, " octet 3 "); - dp += prbits(dp, *p, 8, 8); - *dp++ = '\n'; - return (dp - dest); -} - -static int -prcalled(char *dest, byte * p) { - int l; - char *dp = dest; - - p++; - l = *p++ - 1; - dp += sprintf(dp, " octet 3 "); - dp += prbits(dp, *p++, 8, 8); - *dp++ = '\n'; - dp += sprintf(dp, " number digits "); - while (l--) - *dp++ = *p++; - *dp++ = '\n'; - return (dp - dest); -} -static int -prcalling(char *dest, byte * p) { - int l; - char *dp = dest; - - p++; - l = *p++ - 1; - dp += sprintf(dp, " octet 3 "); - dp += prbits(dp, *p, 8, 8); - *dp++ = '\n'; - if (!(*p & 0x80)) { - dp += sprintf(dp, " octet 3a "); - dp += prbits(dp, *++p, 8, 8); - *dp++ = '\n'; - l--; - }; - p++; - - dp += sprintf(dp, " number digits "); - while (l--) - *dp++ = *p++; - *dp++ = '\n'; - return (dp - dest); -} - -static -int -prbearer(char *dest, byte * p) -{ - char *dp = dest, ch; - - p += 2; - dp += sprintf(dp, " octet 3 "); - dp += prbits(dp, *p++, 8, 8); - *dp++ = '\n'; - dp += sprintf(dp, " octet 4 "); - dp += prbits(dp, *p, 8, 8); - *dp++ = '\n'; - if ((*p++ & 0x1f) == 0x18) { - dp += sprintf(dp, " octet 4.1 "); - dp += prbits(dp, *p++, 8, 8); - *dp++ = '\n'; - } - /* check for user information layer 1 */ - if ((*p & 0x60) == 0x20) { - ch = ' '; - do { - dp += sprintf(dp, " octet 5%c ", ch); - dp += prbits(dp, *p, 8, 8); - *dp++ = '\n'; - if (ch == ' ') - ch = 'a'; - else - ch++; - } - while (!(*p++ & 0x80)); - } - /* check for user information layer 2 */ - if ((*p & 0x60) == 0x40) { - dp += sprintf(dp, " octet 6 "); - dp += prbits(dp, *p++, 8, 8); - *dp++ = '\n'; - } - /* check for user information layer 3 */ - if ((*p & 0x60) == 0x60) { - dp += sprintf(dp, " octet 7 "); - dp += prbits(dp, *p++, 8, 8); - *dp++ = '\n'; - } - return (dp - dest); -} - -static int -general(char *dest, byte * p) { - char *dp = dest; - char ch = ' '; - int l, octet = 3; - - p++; - l = *p++; - /* Iterate over all octets in the information element */ - while (l--) { - dp += sprintf(dp, " octet %d%c ", octet, ch); - dp += prbits(dp, *p++, 8, 8); - *dp++ = '\n'; - - /* last octet in group? */ - if (*p & 0x80) { - octet++; - ch = ' '; - } else if (ch == ' ') - ch = 'a'; - else - ch++; - } - return (dp - dest); -} - -static int -prcharge(char *dest, byte * p) { - char *dp = dest; - int l; - - p++; - l = *p++ - 1; - dp += sprintf(dp, " GEA "); - dp += prbits(dp, *p++, 8, 8); - dp += sprintf(dp, " Anzahl: "); - /* Iterate over all octets in the * information element */ - while (l--) - *dp++ = *p++; - *dp++ = '\n'; - return (dp - dest); -} -static int -prtext(char *dest, byte * p) { - char *dp = dest; - int l; - - p++; - l = *p++; - dp += sprintf(dp, " "); - /* Iterate over all octets in the * information element */ - while (l--) - *dp++ = *p++; - *dp++ = '\n'; - return (dp - dest); -} -static int -display(char *dest, byte * p) { - char *dp = dest; - char ch = ' '; - int l, octet = 3; - - p++; - l = *p++; - /* Iterate over all octets in the * display-information element */ - dp += sprintf(dp, " \""); - while (l--) { - dp += sprintf(dp, "%c", *p++); - - /* last octet in group? */ - if (*p & 0x80) { - octet++; - ch = ' '; - } else if (ch == ' ') - ch = 'a'; - - else - ch++; - } - *dp++ = '\"'; - *dp++ = '\n'; - return (dp - dest); -} - -int -prfacility(char *dest, byte * p) -{ - char *dp = dest; - int l, l2; - - p++; - l = *p++; - dp += sprintf(dp, " octet 3 "); - dp += prbits(dp, *p++, 8, 8); - dp += sprintf(dp, "\n"); - l -= 1; - - while (l > 0) { - dp += sprintf(dp, " octet 4 "); - dp += prbits(dp, *p++, 8, 8); - dp += sprintf(dp, "\n"); - dp += sprintf(dp, " octet 5 %d\n", l2 = *p++ & 0x7f); - l -= 2; - dp += sprintf(dp, " contents "); - while (l2--) { - dp += sprintf(dp, "%2x ", *p++); - l--; - } - dp += sprintf(dp, "\n"); - } - - return (dp - dest); -} - -static -struct InformationElement { - byte nr; - char *descr; - int (*f) (char *, byte *); -} ielist[] = { - - { - 0x00, "Segmented message", general - }, - { - 0x04, "Bearer capability", prbearer - }, - { - 0x08, "Cause", prcause - }, - { - 0x10, "Call identity", general - }, - { - 0x14, "Call state", general - }, - { - 0x18, "Channel identification", prchident - }, - { - 0x1c, "Facility", prfacility - }, - { - 0x1e, "Progress indicator", general - }, - { - 0x20, "Network-specific facilities", general - }, - { - 0x27, "Notification indicator", general - }, - { - 0x28, "Display", display - }, - { - 0x29, "Date/Time", general - }, - { - 0x2c, "Keypad facility", general - }, - { - 0x34, "Signal", general - }, - { - 0x40, "Information rate", general - }, - { - 0x42, "End-to-end delay", general - }, - { - 0x43, "Transit delay selection and indication", general - }, - { - 0x44, "Packet layer binary parameters", general - }, - { - 0x45, "Packet layer window size", general - }, - { - 0x46, "Packet size", general - }, - { - 0x47, "Closed user group", general - }, - { - 0x4a, "Reverse charge indication", general - }, - { - 0x6c, "Calling party number", prcalling - }, - { - 0x6d, "Calling party subaddress", general - }, - { - 0x70, "Called party number", prcalled - }, - { - 0x71, "Called party subaddress", general - }, - { - 0x74, "Redirecting number", general - }, - { - 0x78, "Transit network selection", general - }, - { - 0x79, "Restart indicator", general - }, - { - 0x7c, "Low layer compatibility", general - }, - { - 0x7d, "High layer compatibility", general - }, - { - 0x7e, "User-user", general - }, - { - 0x7f, "Escape for extension", general - }, -}; - - -#define IESIZE sizeof(ielist)/sizeof(struct InformationElement) - -static struct InformationElement we_0[] = -{ - {WE0_cause, "Cause", prcause_1tr6}, - {WE0_connAddr, "Connecting Address", prcalled}, - {WE0_callID, "Call IDentity", general}, - {WE0_chanID, "Channel IDentity", general}, - {WE0_netSpecFac, "Network Specific Facility", general}, - {WE0_display, "Display", general}, - {WE0_keypad, "Keypad", general}, - {WE0_origAddr, "Origination Address", prcalled}, - {WE0_destAddr, "Destination Address", prcalled}, - {WE0_userInfo, "User Info", general} -}; - -static int we_0_len = (sizeof(we_0) / sizeof(struct InformationElement)); - -static struct InformationElement we_6[] = -{ - {WE6_serviceInd, "Service Indicator", general}, - {WE6_chargingInfo, "Charging Information", prcharge}, - {WE6_date, "Date", prtext}, - {WE6_facSelect, "Facility Select", general}, - {WE6_facStatus, "Facility Status", general}, - {WE6_statusCalled, "Status Called", general}, - {WE6_addTransAttr, "Additional Transmission Attributes", general} -}; -static int we_6_len = (sizeof(we_6) / sizeof(struct InformationElement)); - -void -dlogframe(struct IsdnCardState *sp, byte * buf, int size, char *comment) { - byte *bend = buf + size; - char *dp; - int i, cs = 0, cs_old = 0, cs_fest = 0; - - /* display header */ - dp = sp->dlogspace; - dp += sprintf(dp, "%s\n", comment); - - { - byte *p = buf; - dp += sprintf(dp, "hex: "); - while (p < bend) - dp += sprintf(dp, "%02x ", *p++); - dp += sprintf(dp, "\n"); - teles_putstatus(sp->dlogspace); - dp = sp->dlogspace; - } - if ((0xfe & buf[0]) == PROTO_DIS_N0) { /* 1TR6 */ - /* locate message type */ - if (buf[0] == PROTO_DIS_N0) { /* N0 */ - for (i = 0; i < mt_n0_len; i++) - if (mt_n0[i].nr == buf[3]) - break; - /* display message type iff it exists */ - if (i == mt_n0_len) - dp += sprintf(dp, "Unknown message type N0 %x!\n", buf[3]); - else - dp += sprintf(dp, "call reference %d size %d message type %s\n", - buf[2], size, mt_n0[i].descr); - } else { /* N1 */ - for (i = 0; i < mt_n1_len; i++) - if (mt_n1[i].nr == buf[3]) - break; - /* display message type iff it exists */ - if (i == mt_n1_len) - dp += sprintf(dp, "Unknown message type N1 %x!\n", buf[3]); - else - dp += sprintf(dp, "call reference %d size %d message type %s\n", - buf[2], size, mt_n1[i].descr); - } - - /* display each information element */ - buf += 4; - while (buf < bend) { - /* Is it a single octet information element? */ - if (*buf & 0x80) { - switch ((*buf >> 4) & 7) { - case 1: - dp += sprintf(dp, " Shift %x\n", *buf & 0xf); - cs_old = cs; - cs = *buf & 7; - cs_fest = *buf & 8; - break; - case 3: - dp += sprintf(dp, " Congestion level %x\n", *buf & 0xf); - break; - case 2: - if (*buf == 0xa0) { - dp += sprintf(dp, " More data\n"); - break; - } - if (*buf == 0xa1) { - dp += sprintf(dp, " Sending complete\n"); - } - break; - /* fall through */ - default: - dp += sprintf(dp, " Reserved %x\n", *buf); - break; - } - buf++; - continue; - } - /* No, locate it in the table */ - if (cs == 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) { - 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++) - if (*buf == we_6[i].nr) - break; - - /* When found, give appropriate msg */ - 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]); - } else - dp += sprintf(dp, " Unknown Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]); - /* Skip to next element */ - if (cs_fest == 8) { - cs = cs_old; - cs_old = 0; - cs_fest = 0; - } - buf += buf[1] + 2; - } - } else if (buf[0]==PROTO_EURO) { /* EURO */ - /* locate message type */ - for (i = 0; i < MTSIZE; i++) - if (mtlist[i].nr == buf[3]) - break; - - /* display message type iff it exists */ - if (i == MTSIZE) - dp += sprintf(dp, "Unknown message type %x!\n", buf[3]); - else - dp += sprintf(dp, "call reference %d size %d message type %s\n", - buf[2], size, mtlist[i].descr); - - /* display each information element */ - buf += 4; - while (buf < bend) { - /* Is it a single octet information element? */ - if (*buf & 0x80) { - switch ((*buf >> 4) & 7) { - case 1: - dp += sprintf(dp, " Shift %x\n", *buf & 0xf); - break; - case 3: - dp += sprintf(dp, " Congestion level %x\n", *buf & 0xf); - break; - case 5: - dp += sprintf(dp, " Repeat indicator %x\n", *buf & 0xf); - break; - case 2: - if (*buf == 0xa0) { - dp += sprintf(dp, " More data\n"); - break; - } - if (*buf == 0xa1) { - dp += sprintf(dp, " Sending complete\n"); - } - break; - /* fall through */ - default: - dp += sprintf(dp, " Reserved %x\n", *buf); - break; - } - buf++; - continue; - } - /* No, locate it in the table */ - for (i = 0; i < IESIZE; i++) - if (*buf == ielist[i].nr) - break; - - /* When not found, give appropriate msg */ - if (i != IESIZE) { - dp += sprintf(dp, " %s\n", ielist[i].descr); - dp += ielist[i].f(dp, buf); - } else - dp += sprintf(dp, " attribute %x attribute size %d\n", *buf, buf[1]); - - /* Skip to next element */ - buf += buf[1] + 2; - } - } - else dp += sprintf(dp,"Unnown frame type %.2x, ignored\n",buf[0]); - - dp += sprintf(dp, "\n"); - teles_putstatus(sp->dlogspace); -} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/tei.c linux/drivers/isdn/teles/tei.c --- v2.0.30/linux/drivers/isdn/teles/tei.c Sun Apr 21 01:56:15 1996 +++ linux/drivers/isdn/teles/tei.c Wed Dec 31 16:00:00 1969 @@ -1,248 +0,0 @@ -/* $Id: tei.c,v 1.1 1996/04/13 10:28:25 fritz Exp $ - * - * $Log: tei.c,v $ - * Revision 1.1 1996/04/13 10:28:25 fritz - * Initial revision - * - * - */ -#define __NO_VERSION__ -#include "teles.h" - -extern struct IsdnCard cards[]; -extern int nrcards; - -static struct PStack * -findces(struct PStack *st, int ces) -{ - struct PStack *ptr = *(st->l1.stlistp); - - while (ptr) - if (ptr->l2.ces == ces) - return (ptr); - else - ptr = ptr->next; - return (NULL); -} - -static struct PStack * -findtei(struct PStack *st, int tei) -{ - struct PStack *ptr = *(st->l1.stlistp); - - if (tei == 127) - return (NULL); - - while (ptr) - if (ptr->l2.tei == tei) - return (ptr); - else - ptr = ptr->next; - return (NULL); -} - -void -tei_handler(struct PStack *st, - byte pr, struct BufHeader *ibh) -{ - byte *bp; - unsigned int tces; - struct PStack *otsp, *ptr; - unsigned int data; - - if (st->l2.debug) - printk(KERN_DEBUG "teihandler %d\n", pr); - - switch (pr) { - case (MDL_ASSIGN): - data = (unsigned int) ibh; - BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 6); - if (!ibh) - return; - bp = DATAPTR(ibh); - bp += st->l2.uihsize; - bp[0] = 0xf; - bp[1] = data >> 8; - bp[2] = data & 0xff; - bp[3] = 0x1; - bp[4] = 0xff; - ibh->datasize = 8; - st->l3.l3l2(st, DL_UNIT_DATA, ibh); - break; - case (DL_UNIT_DATA): - bp = DATAPTR(ibh); - bp += 3; - if (bp[0] != 0xf) - break; - switch (bp[3]) { - case (2): - tces = (bp[1] << 8) | bp[2]; - BufPoolRelease(ibh); - if (st->l3.debug) - printk(KERN_DEBUG "tei identity assigned for %d=%d\n", tces, - bp[4] >> 1); - if ((otsp = findces(st, tces))) - otsp->ma.teil2(otsp, MDL_ASSIGN, - (void *)(bp[4] >> 1)); - break; - case (4): - if (st->l3.debug) - printk(KERN_DEBUG "checking identity for %d\n", bp[4] >> 1); - if (bp[4] >> 1 == 0x7f) { - BufPoolRelease(ibh); - ptr = *(st->l1.stlistp); - while (ptr) { - if ((ptr->l2.tei & 0x7f) != 0x7f) { - if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 7)) - break; - bp = DATAPTR(ibh); - bp += 3; - bp[0] = 0xf; - bp[1] = ptr->l2.ces >> 8; - bp[2] = ptr->l2.ces & 0xff; - bp[3] = 0x5; - bp[4] = (ptr->l2.tei << 1) | 1; - ibh->datasize = 8; - st->l3.l3l2(st, DL_UNIT_DATA, ibh); - } - ptr = ptr->next; - } - } else { - otsp = findtei(st, bp[4] >> 1); - BufPoolRelease(ibh); - if (!otsp) - break; - if (st->l3.debug) - printk(KERN_DEBUG "ces is %d\n", otsp->l2.ces); - if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 7)) - break; - bp = DATAPTR(ibh); - bp += 3; - bp[0] = 0xf; - bp[1] = otsp->l2.ces >> 8; - bp[2] = otsp->l2.ces & 0xff; - bp[3] = 0x5; - bp[4] = (otsp->l2.tei << 1) | 1; - ibh->datasize = 8; - st->l3.l3l2(st, DL_UNIT_DATA, ibh); - } - break; - default: - BufPoolRelease(ibh); - if (st->l3.debug) - printk(KERN_DEBUG "tei message unknown %d ai %d\n", bp[3], bp[4] >> 1); - } - break; - default: - printk(KERN_WARNING "tei handler unknown primitive %d\n", pr); - break; - } -} - -unsigned int -randomces(void) -{ - int x = jiffies & 0xffff; - - return (x); -} - -static void -tei_man(struct PStack *sp, int i, void *v) -{ - printk(KERN_DEBUG "tei_man\n"); -} - -static void -tei_l2tei(struct PStack *st, int pr, void *arg) -{ - struct IsdnCardState *sp = st->l1.hardware; - - tei_handler(sp->teistack, pr, arg); -} - -void -setstack_tei(struct PStack *st) -{ - st->l2.l2tei = tei_l2tei; -} - -static void -init_tei(struct IsdnCardState *sp, int protocol) -{ - struct PStack *st; - char tmp[128]; - -#define DIRTY_HACK_AGAINST_SIGSEGV - - st = (struct PStack *) Smalloc(sizeof(struct PStack), GFP_KERNEL, - "struct PStack"); - -#ifdef DIRTY_HACK_AGAINST_SIGSEGV - sp->teistack = st; /* struct is not initialized yet */ - sp->teistack->protocol = protocol; /* struct is not initialized yet */ -#endif /* DIRTY_HACK_AGAINST_SIGSEGV */ - - - setstack_teles(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; - - sprintf(tmp, "Card %d tei ", sp->cardnr); - 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; - - teles_addlist(sp, st); - sp->teistack = st; -} - -static void -release_tei(struct IsdnCardState *sp) -{ - struct PStack *st = sp->teistack; - - teles_rmlist(sp, st); - Sfree((void *) st); -} - -void -TeiNew(void) -{ - int i; - - for (i = 0; i < nrcards; i++) - if (cards[i].sp) - init_tei(cards[i].sp, cards[i].protocol); -} - -void -TeiFree(void) -{ - int i; - - for (i = 0; i < nrcards; i++) - if (cards[i].sp) - release_tei(cards[i].sp); -} diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/teles/teles.h linux/drivers/isdn/teles/teles.h --- v2.0.30/linux/drivers/isdn/teles/teles.h Sun Dec 1 10:01:37 1996 +++ linux/drivers/isdn/teles/teles.h Wed Dec 31 16:00:00 1969 @@ -1,487 +0,0 @@ -/* $Id: teles.h,v 1.2 1996/04/30 21:52:04 isdn4dev Exp $ - * - * $Log: teles.h,v $ - * Revision 1.2 1996/04/30 21:52:04 isdn4dev - * SPV for 1TR6 - Karsten - * - * Revision 1.1 1996/04/13 10:29:00 fritz - * Initial revision - * - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 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 - -/* - * 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 - -struct HscxIoctlArg { - int channel; - int mode; - int transbufsize; -}; - -#ifdef __KERNEL__ - -#undef DEBUG_MAGIC - -#define HSCX_SBUF_ORDER 1 -#define HSCX_SBUF_BPPS 2 -#define HSCX_SBUF_MAXPAGES 3 - -#define HSCX_RBUF_ORDER 1 -#define HSCX_RBUF_BPPS 2 -#define HSCX_RBUF_MAXPAGES 3 - -#define HSCX_SMALLBUF_ORDER 0 -#define HSCX_SMALLBUF_BPPS 40 -#define HSCX_SMALLBUF_MAXPAGES 1 - -#define ISAC_SBUF_ORDER 0 -#define ISAC_SBUF_BPPS 16 -#define ISAC_SBUF_MAXPAGES 1 - -#define ISAC_RBUF_ORDER 0 -#define ISAC_RBUF_BPPS 16 -#define ISAC_RBUF_MAXPAGES 1 - -#define ISAC_SMALLBUF_ORDER 0 -#define ISAC_SMALLBUF_BPPS 40 -#define ISAC_SMALLBUF_MAXPAGES 1 - -#define byte unsigned char - -#define MAX_WINDOW 8 - -byte *Smalloc(int size, int pr, char *why); -void Sfree(byte * ptr); - -/* - * Statemachine - */ -struct Fsm { - int *jumpmatrix; - int state_count, event_count; - char **strEvent, **strState; -}; - -struct FsmInst { - struct Fsm *fsm; - int state; - int debug; - void *userdata; - int userint; - void (*printdebug) (struct FsmInst *, char *); -}; - -struct FsmNode { - int state, event; - void (*routine) (struct FsmInst *, int, void *); -}; - -struct FsmTimer { - struct FsmInst *fi; - struct timer_list tl; - int event; - void *arg; -}; - -struct BufHeader { -#ifdef DEBUG_MAGIC - int magic; -#endif - struct BufHeader *next; - struct BufPool *bp; - int datasize; - byte primitive, where; - void *heldby; -}; - -struct Pages { - struct Pages *next; -}; - -struct BufPool { -#ifdef DEBUG_MAGIC - int magic; -#endif - struct BufHeader *freelist; - struct Pages *pageslist; - int pageorder; - int pagescount; - int bpps; - int bufsize; - int maxpages; -}; - -struct BufQueue { -#ifdef DEBUG_MAGIC - int magic; -#endif - struct BufHeader *head, *tail; -}; - -struct Layer1 { - void *hardware; - int hscx; - struct BufPool *sbufpool, *rbufpool, *smallpool; - struct PStack **stlistp; - int act_state; - void (*l1l2) (struct PStack *, int, struct BufHeader *); - void (*l1man) (struct PStack *, int, void *); - int hscxmode, hscxchannel, requestpull; -}; - -struct Layer2 { - int sap, tei, ces; - int extended, laptype; - int uihsize, ihsize; - int vs, va, vr; - struct BufQueue i_queue; - int window, orig; - int rejexp; - int debug; - struct BufHeader *windowar[MAX_WINDOW]; - int sow; - struct FsmInst l2m; - void (*l2l1) (struct PStack *, int, struct BufHeader *); - 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 Layer3 { - void (*l3l4) (struct PStack *, int, struct BufHeader *); - void (*l3l2) (struct PStack *, int, void *); - int state, callref; - int debug; -}; - -struct Layer4 { - void (*l4l3) (struct PStack *, int, void *); - void *userdata; - void (*l1writewakeup) (struct PStack *); - void (*l2writewakeup) (struct PStack *); -}; - -struct Management { - void (*manl1) (struct PStack *, int, void *); - void (*manl2) (struct PStack *, int, void *); - void (*teil2) (struct PStack *, int, void *); -}; - -struct Param { - int cause; - int bchannel; - int callref; /* TEI-Number */ - int itc; - int info; /* Service-Indicator */ - int info2; /* Service-Indicator, second octet */ - char calling[40]; /* Called Id */ - char called[40]; /* Caller Id */ - int chargeinfo; /* Charge Info - only for 1tr6 in - * the moment - */ - int spv; /* SPV Flag */ -}; - -struct PStack { - struct PStack *next; - struct Layer1 l1; - struct Layer2 l2; - struct Layer3 l3; - struct Layer4 l4; - struct Management ma; - struct Param *pa; - int protocol; /* EDSS1 or 1TR6 */ -}; - -struct HscxState { - byte *membase; - int iobase; - int inuse, init, active; - struct BufPool sbufpool, rbufpool, smallpool; - struct IsdnCardState *sp; - int hscx, mode; - int transbufsize, receive; - struct BufHeader *rcvibh, *xmtibh; - int rcvptr, sendptr; - struct PStack *st; - struct tq_struct tqueue; - int event; - struct BufQueue rq, sq; - int releasebuf; -#ifdef DEBUG_MAGIC - int magic; /* 301270 */ -#endif -}; - -struct IsdnCardState { -#ifdef DEBUG_MAGIC - int magic; -#endif - byte *membase; - int iobase; - struct BufPool sbufpool, rbufpool, smallpool; - struct PStack *stlist; - struct BufHeader *xmtibh, *rcvibh; - int rcvptr, sendptr; - int event; - struct tq_struct tqueue; - int ph_active; - struct BufQueue rq, sq; - - int cardnr, ph_state; - struct PStack *teistack; - struct HscxState hs[2]; - - int dlogflag; - char *dlogspace; - int debug; - int releasebuf; -}; - -struct IsdnCard { - byte *membase; - int interrupt; - unsigned int iobase; - int protocol; /* EDSS1 or 1TR6 */ - struct IsdnCardState *sp; -}; - -#define DATAPTR(x) ((byte *)x+sizeof(struct BufHeader)) - -#define LAPD 0 -#define LAPB 1 - -void BufPoolInit(struct BufPool *bp, int order, int bpps, - int maxpages); -int BufPoolAdd(struct BufPool *bp, int priority); -void BufPoolFree(struct BufPool *bp); -int BufPoolGet(struct BufHeader **bh, - struct BufPool *bp, int priority, void *heldby, int where); -void BufPoolRelease(struct BufHeader *bh); -void BufQueueLink(struct BufQueue *bq, - struct BufHeader *bh); -int BufQueueUnlink(struct BufHeader **bh, struct BufQueue *bq); -void BufQueueInit(struct BufQueue *bq); -void BufQueueRelease(struct BufQueue *bq); -void BufQueueDiscard(struct BufQueue *q, int pr, void *heldby, - int releasetoo); -int BufQueueLength(struct BufQueue *bq); -void BufQueueLinkFront(struct BufQueue *bq, - struct BufHeader *bh); - -void l2down(struct PStack *st, - byte pr, struct BufHeader *ibh); -void l2up(struct PStack *st, - byte pr, struct BufHeader *ibh); -void acceptph(struct PStack *st, - struct BufHeader *ibh); -void setstack_isdnl2(struct PStack *st, char *debug_id); -int teles_inithardware(void); -void teles_closehardware(void); - -void setstack_teles(struct PStack *st, struct IsdnCardState *sp); -unsigned int randomces(void); -void setstack_isdnl3(struct PStack *st); -void teles_addlist(struct IsdnCardState *sp, - struct PStack *st); -void releasestack_isdnl2(struct PStack *st); -void teles_rmlist(struct IsdnCardState *sp, - struct PStack *st); -void newcallref(struct PStack *st); - -int ll_init(void); -void ll_stop(void), ll_unload(void); -int setstack_hscx(struct PStack *st, struct HscxState *hs); -void modehscx(struct HscxState *hs, int mode, int ichan); -byte *findie(byte * p, int size, byte ie, int wanted_set); -int getcallref(byte * p); - -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); -void FsmDelTimer(struct FsmTimer *ft, int where); -int FsmTimerRunning(struct FsmTimer *ft); -void jiftime(char *s, long mark); - -void CallcNew(void); -void CallcFree(void); -int CallcNewChan(void); -void CallcFreeChan(void); -int teles_command(isdn_ctrl * ic); -int teles_writebuf(int id, int chan, const u_char * buf, int count, int user); -void teles_putstatus(char *buf); -void teles_reportcard(int cardnr); -int ListLength(struct BufHeader *ibh); -void dlogframe(struct IsdnCardState *sp, byte * p, int size, char *comment); -void iecpy(byte * dest, byte * 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); - -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]; -}; - -struct Channel { - struct PStack ds, is; - struct IsdnCardState *sp; - int hscx; - int chan; - int incoming; - struct FsmInst fi; - struct LcFsm lc_d, lc_b; - struct Param para; - int debug; -#ifdef DEBUG_MAGIC - int magic; /* 301272 */ -#endif - int l2_protocol, l2_active_protocol; - int l2_primitive, l2_headersize; - int data_open; - int outcallref; - int impair; -}; - -#define PART_SIZE(order,bpps) (( (PAGE_SIZE< @@ -64,7 +72,7 @@ /* To minimize the size of the driver source I only define operating constants if they are used several times. You'll need the manual - if you want to understand driver details. */ + anyway if you want to understand driver details. */ /* Offsets from base I/O address. */ #define EL3_DATA 0x00 #define EL3_CMD 0x0e @@ -115,11 +123,13 @@ struct el3_private { struct enet_statistics stats; + struct device *next_dev; /* skb send-queue */ int head, size; struct sk_buff *queue[SKB_QUEUE_SIZE]; }; static int id_port = 0x100; +static struct device *el3_root_dev = NULL; static ushort id_read_eeprom(int index); static ushort read_eeprom(short ioaddr, int index); @@ -130,9 +140,7 @@ static struct enet_statistics *el3_get_stats(struct device *dev); static int el3_rx(struct device *dev); static int el3_close(struct device *dev); -#ifdef HAVE_MULTICAST static void set_multicast_list(struct device *dev); -#endif @@ -140,7 +148,7 @@ { short lrs_state = 0xff, i; ushort ioaddr, irq, if_port; - short *phys_addr = (short *)dev->dev_addr; + short phys_addr[3]; static int current_tag = 0; /* First check all slots of the EISA bus. The next slot address to @@ -195,6 +203,8 @@ 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) { + if (check_region(id_port, 1)) + continue; outb(0x00, id_port); outb(0xff, id_port); if (inb(id_port) & 0x01) @@ -210,13 +220,6 @@ on cards as they are found. Cards with their tag set will not respond to subsequent ID sequences. */ - if (check_region(id_port,1)) { - static int once = 1; - if (once) printk("3c509: Somebody has reserved 0x%x, can't do ID_PORT lookup, nor card auto-probing\n",id_port); - once = 0; - return -ENODEV; - } - outb(0x00, id_port); outb(0x00, id_port); for(i = 0; i < 255; i++) { @@ -247,13 +250,13 @@ if_port = iobase >> 14; ioaddr = 0x200 + ((iobase & 0x1f) << 4); } - if (dev->irq > 1 && dev->irq < 16) + if (dev && dev->irq > 1 && dev->irq < 16) irq = dev->irq; else irq = id_read_eeprom(9) >> 12; - if (dev->base_addr != 0 - && dev->base_addr != (unsigned short)ioaddr) { + if (dev && dev->base_addr != 0 + && dev->base_addr != (unsigned short)ioaddr) { return -ENODEV; } @@ -270,9 +273,16 @@ /* Free the interrupt so that some other card can use it. */ outw(0x0f00, ioaddr + WN0_IRQ); found: + if (dev == NULL) { + dev = init_etherdev(dev, sizeof(struct el3_private)); + } + memcpy(dev->dev_addr, phys_addr, sizeof(phys_addr)); dev->base_addr = ioaddr; dev->irq = irq; - dev->if_port = if_port; + if (dev->mem_start) + dev->if_port = dev->mem_start & 3; + else + dev->if_port = if_port; request_region(dev->base_addr, EL3_IO_EXTENT, "3c509"); { @@ -287,11 +297,15 @@ printk(", IRQ %d.\n", dev->irq); /* Make up a EL3-specific-data structure. */ - dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL); + if (dev->priv == NULL) + dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL); if (dev->priv == NULL) return -ENOMEM; memset(dev->priv, 0, sizeof(struct el3_private)); + ((struct el3_private *)dev->priv)->next_dev = el3_root_dev; + el3_root_dev = dev; + if (el3_debug > 0) printk(version); @@ -300,9 +314,7 @@ dev->hard_start_xmit = &el3_start_xmit; dev->stop = &el3_close; dev->get_stats = &el3_get_stats; -#ifdef HAVE_MULTICAST - dev->set_multicast_list = &set_multicast_list; -#endif + dev->set_multicast_list = &set_multicast_list; /* Fill in the generic fields of the device structure. */ ether_setup(dev); @@ -354,7 +366,6 @@ outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); if (request_irq(dev->irq, &el3_interrupt, 0, "3c509", dev)) { - irq2dev_map[dev->irq] = NULL; return -EAGAIN; } @@ -430,7 +441,7 @@ /* Transmitter timeout, serious problems. */ if (dev->tbusy) { int tickssofar = jiffies - dev->trans_start; - if (tickssofar < 40*HZ/100) + if (tickssofar < TX_TIMEOUT) return 1; printk("%s: transmit timed out, Tx_status %2.2x status %4.4x " "Tx FIFO room %d.\n", @@ -444,14 +455,6 @@ dev->tbusy = 0; } - if (skb == NULL) { - dev_tint(dev); - return 0; - } - - if (skb->len <= 0) - return 0; - if (el3_debug > 4) { printk("%s: el3_start_xmit(length = %ld) called, status %4.4x.\n", dev->name, skb->len, inw(ioaddr + EL3_STATUS)); @@ -483,7 +486,11 @@ outw(skb->len, ioaddr + TX_FIFO); outw(0x00, ioaddr + TX_FIFO); /* ... and the packet rounded to a doubleword. */ +#ifdef __powerpc__ + outsl_unswapped(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); +#else outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); +#endif dev->trans_start = jiffies; if (inw(ioaddr + TX_FREE) > 1536) { @@ -516,7 +523,7 @@ { struct device *dev = (struct device *)dev_id; int ioaddr, status; - int i = 0; + int i = INTR_WORK; if (dev == NULL) { printk ("el3_interrupt(): irq %d for unknown device.\n", irq); @@ -568,7 +575,7 @@ } } - if (++i > 10) { + if (--i < 0) { printk("%s: Infinite loop in interrupt, status %4.4x.\n", dev->name, status); /* Clear all interrupts. */ @@ -667,13 +674,18 @@ pkt_len, rx_status); if (skb != NULL) { skb->dev = dev; - skb_reserve(skb,2); /* Align IP on 16 byte */ + skb_reserve(skb, 2); /* Align IP on 16 byte */ /* 'skb->data' points to the start of sk_buff data area. */ - insl(ioaddr+RX_FIFO, skb_put(skb,pkt_len), - (pkt_len + 3) >> 2); +#ifdef __powerpc__ + insl_unswapped(ioaddr+RX_FIFO, skb_put(skb,pkt_len), + (pkt_len + 3) >> 2); +#else + insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len), + (pkt_len + 3) >> 2); +#endif - skb->protocol=eth_type_trans(skb,dev); + skb->protocol = eth_type_trans(skb,dev); netif_rx(skb); outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ lp->stats.rx_packets++; @@ -682,8 +694,9 @@ 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); + lp->stats.rx_dropped++; + SLOW_DOWN_IO; while (inw(ioaddr + EL3_STATUS) & 0x1000) printk(" Waiting for 3c509 to discard packet, status %x.\n", inw(ioaddr + EL3_STATUS) ); @@ -692,7 +705,6 @@ return 0; } -#ifdef HAVE_MULTICAST /* * Set or clear the multicast filter for this adaptor. */ @@ -715,9 +727,8 @@ outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD); } else - outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); + outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); } -#endif static int el3_close(struct device *dev) @@ -758,43 +769,50 @@ } #ifdef MODULE -static char devicename[9] = { 0, }; -static struct device dev_3c509 = { - devicename, /* device name is inserted by linux/drivers/net/net_init.c */ - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, el3_probe }; - -static int io = 0; -static int irq = 0; +/* Parameter 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}; int init_module(void) { - dev_3c509.base_addr = io; - dev_3c509.irq = irq; - if (!EISA_bus && !io) { - printk("3c509: WARNING! Module load-time probing works reliably only for EISA bus!!\n"); + int el3_cards = 0; + + if (debug >= 0) + el3_debug = debug; + + el3_root_dev = NULL; + while (el3_probe(0) == 0) { + if (irq[el3_cards] > 1) + el3_root_dev->irq = irq[el3_cards]; + if (xcvr[el3_cards] >= 0) + el3_root_dev->if_port = xcvr[el3_cards]; + el3_cards++; } - if (register_netdev(&dev_3c509) != 0) - return -EIO; - return 0; + + return el3_cards ? 0 : -ENODEV; } void cleanup_module(void) { - unregister_netdev(&dev_3c509); - kfree_s(dev_3c509.priv,sizeof(struct el3_private)); - dev_3c509.priv=NULL; - /* If we don't do this, we can't re-insmod it later. */ - release_region(dev_3c509.base_addr, EL3_IO_EXTENT); + struct device *next_dev; + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (el3_root_dev) { + next_dev = ((struct el3_private *)el3_root_dev->priv)->next_dev; + unregister_netdev(el3_root_dev); + release_region(el3_root_dev->base_addr, EL3_IO_EXTENT); + kfree(el3_root_dev); + el3_root_dev = next_dev; + } } #endif /* MODULE */ /* * Local variables: - * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c509.c" + * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c509.c" * version-control: t * kept-new-versions: 5 * tab-width: 4 diff -u --recursive --new-file v2.0.30/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c --- v2.0.30/linux/drivers/net/3c59x.c Tue Apr 8 08:47:45 1997 +++ linux/drivers/net/3c59x.c Tue Aug 12 16:04:36 1997 @@ -1,30 +1,46 @@ -/* 3c59x.c: A 3Com 3c590/3c595 "Vortex" ethernet driver for linux. */ +/* 3c900.c: A 3Com EtherLink III XL "Boomerang" ethernet driver for linux. */ /* - Written 1995 by Donald Becker. + Written 1996-1997 by Donald Becker. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. - This driver is for the 3Com "Vortex" series ethercards. Members of - the series include the 3c590 PCI EtherLink III and 3c595-Tx PCI Fast - EtherLink. + This driver is for the 3Com "Vortex" and "Boomerang" series ethercards. + Members of the series include Fast EtherLink 3c590/3c592/3c595/3c597 + and the EtherLink XL 3c900 and 3c905 cards. The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O Center of Excellence in Space Data and Information Sciences Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 */ -static char *version = "3c59x.c:v0.25 5/17/96 becker@cesdis.gsfc.nasa.gov\n"; +static char *version = +"3c59x.c/3c900.c:v0.42 7/15/97 Donald Becker linux-vortex@cesdis.gsfc.nasa.gov\n"; /* "Knobs" that turn on special features. */ -/* Enable the experimental automatic media selection code. */ +/* Enable the automatic media selection code. */ #define AUTOMEDIA 1 -/* Allow the use of bus master transfers instead of programmed-I/O for the - Tx process. Bus master transfers are always disabled by default, but - iff this is set they may be turned on using 'options'. */ +/* Allow the use of fragment bus master transfers instead of only + programmed-I/O for Vortex cards. Full-bus-master transfers are always + enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined, + the feature may be turned on using 'options'. */ #define VORTEX_BUS_MASTER +/* A few values that may be tweaked. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT ((400*HZ)/1000) + +/* Keep the ring sizes a power of two for efficiency. */ +#define TX_RING_SIZE 16 +#define RX_RING_SIZE 32 +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. + Setting to > 1512 effectively disables this feature. */ +static const rx_copybreak = 200; + +#include #include #include @@ -39,6 +55,7 @@ #include #include #include +#include /* For NR_IRQS only. */ #include #include @@ -46,27 +63,64 @@ #include #include +/* Kernel compatibility defines, common to David Hind's PCMCIA package. + This is only in the support-all-kernels source code. */ +#ifndef LINUX_VERSION_CODE +#include /* Redundant above, here for easy clean-up. */ +#endif +#if LINUX_VERSION_CODE < 0x10300 +#define RUN_AT(x) (x) /* What to put in timer->expires. */ +#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC) +#if defined(__alpha) +#error "The Alpha architecture is only support with kernel version 2.0." +#endif +#define virt_to_bus(addr) ((unsigned long)addr) +#define bus_to_virt(addr) ((void*)addr) +#else /* 1.3.0 and later */ #define RUN_AT(x) (jiffies + (x)) #define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2) +#endif +#ifdef SA_SHIRQ #define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev) #define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance) #define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs) +#else +#define FREE_IRQ(irqnum, dev) free_irq(irqnum) +#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n) +#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs) +#endif + +#if (LINUX_VERSION_CODE >= 0x10344) +#define NEW_MULTICAST +#include +#else +#define udelay(microsec) do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) +#endif + +#if (LINUX_VERSION_CODE < 0x20123) +#define test_and_set_bit(val, addr) set_bit(val, addr) +#endif /* "Knobs" for adjusting internal parameters. */ /* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */ -#define VORTEX_DEBUG 2 +#define VORTEX_DEBUG 1 +/* Some values here only for performance evaluation and path-coverage + debugging. */ +static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0; /* Number of times to check to see if the Tx FIFO has space, used in some limited cases. */ #define WAIT_TX_AVAIL 200 /* Operational parameter that usually are not changed. */ -#define TX_TIMEOUT 40 /* Time in jiffies before concluding Tx hung */ -/* The total size is twice that of the original EtherLinkIII series: the - runtime register window, window 1, is now always mapped in. */ +/* The Vortex size is twice that of the original EtherLinkIII series: the + runtime register window, window 1, is now always mapped in. + The Boomerang size is twice as large as the Vortex -- it has additional + bus master control registers. */ #define VORTEX_TOTAL_SIZE 0x20 +#define BOOMERANG_TOTAL_SIZE 0x40 #ifdef HAVE_DEVLIST struct netdev_entry tc59x_drv = @@ -79,24 +133,37 @@ int vortex_debug = 1; #endif -static int product_ids[] = {0x5900, 0x5950, 0x5951, 0x5952, 0, 0}; +/* Caution! These entries must be consistent, with the EISA ones last. */ +static int product_ids[] = {0x5900, 0x5950, 0x5951, 0x5952, 0x9000, 0x9001, + 0x9050, 0x9051, 0, 0}; static const char *product_names[] = { "3c590 Vortex 10Mbps", "3c595 Vortex 100baseTX", "3c595 Vortex 100baseT4", "3c595 Vortex 100base-MII", - "EISA Vortex 3c597", + "3c900 Boomerang 10baseT", + "3c900 Boomerang 10Mbps/Combo", + "3c905 Boomerang 100baseTx", + "3c905 Boomerang 100baseT4", + "3c592 EISA 10mbps Demon/Vortex", + "3c597 EISA Fast Demon/Vortex", }; -#define DEMON_INDEX 5 /* Caution! Must be consistent with above! */ +#define DEMON10_INDEX 8 +#define DEMON100_INDEX 9 /* Theory of Operation I. Board Compatibility -This device driver is designed for the 3Com FastEtherLink, 3Com's PCI to -10/100baseT adapter. It also works with the 3c590, a similar product -with only a 10Mbs interface. +This device driver is designed for the 3Com FastEtherLink and FastEtherLink +XL, 3Com's PCI to 10/100baseT adapters. It also works with the 10Mbs +versions of the FastEtherLink cards. The supported product IDs are + 3c590, 3c592, 3c595, 3c597, 3c900, 3c905 + +The ISA 3c515 is supported with a seperate driver, 3c515.c, included with +the kernel source or available from + cesdis.gsfc.nasa.gov:/pub/linux/drivers/3c515.html II. Board-specific settings @@ -112,12 +179,33 @@ series. The primary interface is two programmed-I/O FIFOs, with an alternate single-contiguous-region bus-master transfer (see next). +The 3c900 "Boomerang" series uses a full-bus-master interface with seperate +lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet, +DEC Tulip and Intel Speedo3. The first chip version retains a compatible +programmed-I/O interface that will be removed in the 'B' and subsequent +revisions. + One extension that is advertised in a very large font is that the adapters -are capable of being bus masters. Unfortunately this capability is only for -a single contiguous region making it less useful than the list of transfer -regions available with the DEC Tulip or AMD PCnet. Given the significant -performance impact of taking an extra interrupt for each transfer, using -DMA transfers is a win only with large blocks. +are capable of being bus masters. On the Vortex chip this capability was +only for a single contiguous region making it far less useful than the full +bus master capability. There is a significant performance impact of taking +an extra interrupt or polling for the completion of each transfer, as well +as difficulty sharing the single transfer engine between the transmit and +receive threads. Using DMA transfers is a win only with large blocks or +with the flawed versions of the Intel Orion motherboard PCI controller. + +The Boomerang chip's full-bus-master interface is useful, and has the +currently-unused advantages over other similar chips that queued transmit +packets may be reordered and receive buffer groups are associated with a +single frame. + +With full-bus-master support, this driver uses a "RX_COPYBREAK" scheme. +Tather than a fixed intermediate receive buffer, this scheme allocates +full-sized skbuffs as receive buffers. The value RX_COPYBREAK is used as +the copying breakpoint: it is chosen to trade-off the memory wasted by +passing the full-sized skbuff to the queue layer for all frames vs. the +copying cost of copying a frame to a correctly-sized skbuff. + IIIC. Synchronization The driver runs as two independent, single-threaded flows of control. One @@ -127,8 +215,8 @@ IV. Notes -Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing both -3c590 and 3c595 boards. +Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing development +3c590, 3c595, and 3c900 boards. The name "Vortex" is the internal 3Com project name for the PCI ASIC, and the EISA version is called "Demon". According to Terry these names come from rides at the local amusement park. @@ -159,8 +247,10 @@ enum vortex_cmd { TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, - RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11, - TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, + RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, + UpStall = 6<<11, UpUnstall = (6<<11)+1, + DownStall = (6<<11)+2, DownUnstall = (6<<11)+3, + RxDiscard = 8<<11, TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, SetTxThreshold = 18<<11, SetTxStart = 19<<11, @@ -175,7 +265,8 @@ enum vortex_status { IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, - IntReq = 0x0040, StatsFull = 0x0080, DMADone = 1<<8, + IntReq = 0x0040, StatsFull = 0x0080, + DMADone = 1<<8, DownComplete = 1<<9, UpComplete = 1<<10, DMAInProgress = 1<<11, /* DMA controller is still busy.*/ CmdInProgress = 1<<12, /* EL3_CMD is still busy.*/ }; @@ -212,12 +303,12 @@ unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2; int pad8:8; unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1; - int pad24:8; + int pad24:7; } u; }; enum Window4 { - Wn4_Media = 0x0A, /* Window 4: Various transcvr/media bits. */ + Wn4_NetDiag = 6, Wn4_Media = 10, /* Window 4: Xcvr/media bits. */ }; enum Win4_Media_bits { Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */ @@ -228,11 +319,43 @@ enum Window7 { /* Window 7: Bus Master control. */ Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12, }; +/* Boomerang bus master control registers. */ +enum MasterCtrl { + PktStatus = 0x20, DownListPtr = 0x24, FragAddr = 0x28, FragLen = 0x2c, + TxFreeThreshold = 0x2f, UpPktStatus = 0x30, UpListPtr = 0x38, +}; + +/* The Rx and Tx descriptor lists. + Caution Alpha hackers: these types are 32 bits! Note also the 8 byte + alignment contraint on tx_ring[] and rx_ring[]. */ +struct boom_rx_desc { + u32 next; + s32 status; + u32 addr; + s32 length; +}; +/* Values for the Rx status entry. */ +#define RX_COMPLETE 0x00008000 + +struct boom_tx_desc { + u32 next; + s32 status; + u32 addr; + s32 length; +}; struct vortex_private { char devname[8]; /* "ethN" string, also for kernel debug. */ const char *product_name; struct device *next_module; + /* The Rx and Tx rings are here to keep them quad-word-aligned. */ + struct boom_rx_desc rx_ring[RX_RING_SIZE]; + struct boom_tx_desc tx_ring[TX_RING_SIZE]; + /* The addresses of transmit- and receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ struct enet_statistics stats; struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */ struct timer_list timer; /* Media selection timer. */ @@ -241,7 +364,12 @@ unsigned int available_media:8, /* From Wn3_Options */ media_override:3, /* Passed-in media type. */ default_media:3, /* Read from the EEPROM. */ - full_duplex:1, bus_master:1, autoselect:1; + full_duplex:1, autoselect:1, + bus_master:1, /* Vortex can only do a fragment bus-m. */ + full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang */ + tx_full:1; + u16 capabilities; /* Adapter capabilities word. */ + u16 info1, info2; /* Software information information. */ }; /* The action to take with a media selection timer tick. @@ -256,28 +384,34 @@ } media_tbl[] = { { "10baseT", Media_10TP,0x08, 3 /* 10baseT->10base2 */, (14*HZ)/10}, { "10Mbs AUI", Media_SQE, 0x20, 8 /* AUI->default */, (1*HZ)/10}, - { "undefined", 0, 0x80, 0 /* Undefined */, 0}, + { "undefined", 0, 0x80, 0 /* Undefined */, 10000}, { "10base2", 0, 0x10, 1 /* 10base2->AUI. */, (1*HZ)/10}, { "100baseTX", Media_Lnk, 0x02, 5 /* 100baseTX->100baseFX */, (14*HZ)/10}, { "100baseFX", Media_Lnk, 0x04, 6 /* 100baseFX->MII */, (14*HZ)/10}, { "MII", 0, 0x40, 0 /* MII->10baseT */, (14*HZ)/10}, - { "undefined", 0, 0x01, 0 /* Undefined/100baseT4 */, 0}, - { "Default", 0, 0xFF, 0 /* Use default */, 0}, + { "undefined", 0, 0x01, 0 /* Undefined/100baseT4 */, 10000}, + { "Default", 0, 0xFF, 0 /* Use default */, 10000}, }; static int vortex_scan(struct device *dev); -static int vortex_found_device(struct device *dev, int ioaddr, int irq, - int product_index, int options); +static struct device *vortex_found_device(struct device *dev, int ioaddr, + int irq, int product_index, + int options); static int vortex_probe1(struct device *dev); static int vortex_open(struct device *dev); static void vortex_timer(unsigned long arg); static int vortex_start_xmit(struct sk_buff *skb, struct device *dev); +static int boomerang_start_xmit(struct sk_buff *skb, struct device *dev); static int vortex_rx(struct device *dev); +static int boomerang_rx(struct device *dev); static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs); static int vortex_close(struct device *dev); static void update_stats(int addr, struct device *dev); static struct enet_statistics *vortex_get_stats(struct device *dev); static void set_rx_mode(struct device *dev); +#ifndef NEW_MULTICAST +static void set_multicast_list(struct device *dev, int num_addrs, void *addrs); +#endif /* Unlike the other PCI cards the 59x cards don't need a large contiguous @@ -297,6 +431,8 @@ /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */ /* Note: this is the only limit on the number of cards supported!! */ static int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,}; +/* Variables to work-around the Compaq PCI BIOS32 problem. */ +static int compaq_ioaddr = 0, compaq_irq = 0, compaq_prod_id = 0; #ifdef MODULE static int debug = -1; @@ -336,94 +472,141 @@ { int cards_found = 0; +#ifndef NO_PCI /* Allow an EISA-only driver. */ + /* Ideally we would detect all cards in slot order. That would + be best done a central PCI probe dispatch, which wouldn't work + well with the current structure. So instead we detect 3Com cards + in slot order. */ if (pcibios_present()) { static int pci_index = 0; - static int board_index = 0; - for (; product_ids[board_index]; board_index++, pci_index = 0) { - for (; pci_index < 16; pci_index++) { - unsigned char pci_bus, pci_device_fn, pci_irq_line; - unsigned char pci_latency; - unsigned int pci_ioaddr; - unsigned short pci_command; - - if (pcibios_find_device(TCOM_VENDOR_ID, - product_ids[board_index], pci_index, - &pci_bus, &pci_device_fn)) + unsigned char pci_bus, pci_device_fn; + + for (;pci_index < 0xff; pci_index++) { + unsigned char pci_irq_line, pci_latency; + unsigned short pci_command, vendor, device; + unsigned int pci_ioaddr; + + int board_index = 0; + if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, + pci_index, &pci_bus, &pci_device_fn) + != PCIBIOS_SUCCESSFUL) + break; + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_DEVICE_ID, &device); + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + /* Remove I/O space marker in bit 0. */ + pci_ioaddr &= ~3; + + if (vendor != TCOM_VENDOR_ID) + continue; + + for (board_index = 0; product_ids[board_index]; board_index++) { + if (device == product_ids[board_index]) break; - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_INTERRUPT_LINE, &pci_irq_line); - pcibios_read_config_dword(pci_bus, pci_device_fn, - PCI_BASE_ADDRESS_0, &pci_ioaddr); - /* Remove I/O space marker in bit 0. */ - pci_ioaddr &= ~3; + } + if (product_ids[board_index] == 0) { + printk("Unknown 3Com PCI ethernet adapter type %4.4x detected:" + " not configured.\n", device); + continue; + } + if (check_region(pci_ioaddr, VORTEX_TOTAL_SIZE)) + continue; -#ifdef VORTEX_BUS_MASTER + dev = vortex_found_device(dev, pci_ioaddr, pci_irq_line, + board_index, dev && dev->mem_start + ? dev->mem_start : options[cards_found]); + + if (dev) { /* Get and check the bus-master and latency values. Some PCI BIOSes fail to set the master-enable bit, and the latency timer must be set to the maximum value to avoid data corruption that occurs when the timer expires during - a transfer. Yes, it's a bug. */ + a transfer -- a bug in the Vortex chip. */ pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, &pci_command); if ( ! (pci_command & PCI_COMMAND_MASTER)) { - printk(" PCI Master Bit has not been set! Setting...\n"); + printk("%s: PCI Master Bit has not been set! " + " Setting...\n", dev->name); pci_command |= PCI_COMMAND_MASTER; pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, pci_command); } pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &pci_latency); - if (pci_latency != 255) { - printk(" Overriding PCI latency timer (CFLT) setting of" - " %d, new value is 255.\n", pci_latency); + if (pci_latency != 248) { + printk("%s: Overriding PCI latency" + " timer (CFLT) setting of %d, new value is 248.\n", + dev->name, pci_latency); pcibios_write_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, 255); + PCI_LATENCY_TIMER, 248); } -#endif /* VORTEX_BUS_MASTER */ - vortex_found_device(dev, pci_ioaddr, pci_irq_line, board_index, - dev && dev->mem_start ? dev->mem_start - : options[cards_found]); dev = 0; cards_found++; } } } +#endif /* NO_PCI */ /* Now check all slots of the EISA bus. */ if (EISA_bus) { static int ioaddr = 0x1000; for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) { + int product_id, product_index; + if (check_region(ioaddr, VORTEX_TOTAL_SIZE)) + continue; /* Check the standard EISA ID register for an encoded '3Com'. */ if (inw(ioaddr + 0xC80) != 0x6d50) continue; /* Check for a product that we support, 3c59{2,7} any rev. */ - if ((inw(ioaddr + 0xC82) & 0xF0FF) != 0x7059 /* 597 */ - && (inw(ioaddr + 0xC82) & 0xF0FF) != 0x2059) /* 592 */ + product_id = inw(ioaddr + 0xC82) & 0xF0FF; + if (product_id == 0x7059) /* 597 */ + product_index = DEMON100_INDEX; + else if (product_id == 0x2059) /* 592 */ + product_index = DEMON10_INDEX; + else continue; vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12, - DEMON_INDEX, dev && dev->mem_start + product_index, dev && dev->mem_start ? dev->mem_start : options[cards_found]); dev = 0; cards_found++; } } + /* Special code to work-around the Compaq PCI BIOS32 problem. */ + if (compaq_ioaddr) { + vortex_found_device(dev, compaq_ioaddr, compaq_irq, compaq_prod_id, + dev && dev->mem_start ? dev->mem_start : options[cards_found]); + cards_found++; + dev = 0; + } + + /* Finally check for a 3c515 on the ISA bus. */ + /* (3c515 support omitted on this version.) */ + return cards_found; } -static int vortex_found_device(struct device *dev, int ioaddr, int irq, - int product_index, int options) +static struct device * +vortex_found_device(struct device *dev, int ioaddr, int irq, + int product_index, int options) { struct vortex_private *vp; #ifdef MODULE /* Allocate and fill new device structure. */ int dev_size = sizeof(struct device) + - sizeof(struct vortex_private); - + sizeof(struct vortex_private) + 15; /* Pad for alignment */ + dev = (struct device *) kmalloc(dev_size, GFP_KERNEL); memset(dev, 0, dev_size); - dev->priv = ((void *)dev) + sizeof(struct device); + /* Align the Rx and Tx ring entries. */ + dev->priv = (void *)(((long)dev + sizeof(struct device) + 15) & ~15); vp = (struct vortex_private *)dev->priv; dev->name = vp->devname; /* An empty string. */ dev->base_addr = ioaddr; @@ -444,9 +627,10 @@ vp->next_module = root_vortex_dev; root_vortex_dev = dev; if (register_netdev(dev) != 0) - return -EIO; + return 0; #else /* not a MODULE */ if (dev) { + /* Caution: quad-word alignment required for rings! */ dev->priv = kmalloc(sizeof (struct vortex_private), GFP_KERNEL); memset(dev->priv, 0, sizeof (struct vortex_private)); } @@ -468,13 +652,14 @@ vortex_probe1(dev); #endif /* MODULE */ - return 0; + return dev; } static int vortex_probe1(struct device *dev) { int ioaddr = dev->base_addr; struct vortex_private *vp = (struct vortex_private *)dev->priv; + unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */ int i; printk("%s: 3Com %s at %#3x,", dev->name, @@ -482,27 +667,33 @@ /* Read the station address from the EEPROM. */ EL3WINDOW(0); - for (i = 0; i < 3; i++) { + for (i = 0; i < 0x18; i++) { short *phys_addr = (short *)dev->dev_addr; int timer; - outw(EEPROM_Read + PhysAddr01 + i, ioaddr + Wn0EepromCmd); + outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd); /* Pause for at least 162 us. for the read to take place. */ - for (timer = 162*4 + 400; timer >= 0; timer--) { - SLOW_DOWN_IO; + for (timer = 4; timer >= 0; timer--) { + udelay(162); if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0) break; } - phys_addr[i] = htons(inw(ioaddr + Wn0EepromData)); + eeprom[i] = inw(ioaddr + Wn0EepromData); + checksum ^= eeprom[i]; + if (i >= 10 && i < 13) + phys_addr[i - 10] = htons(inw(ioaddr + Wn0EepromData)); } + checksum = (checksum ^ (checksum >> 8)) & 0xff; + if (checksum != 0x00) + printk(" ***INVALID CHECKSUM %4.4x*** ", checksum); for (i = 0; i < 6; i++) printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]); printk(", IRQ %d\n", dev->irq); /* Tell them about an invalid IRQ. */ - if (vortex_debug && (dev->irq <= 0 || dev->irq > 15)) - printk(" *** Warning: this IRQ is unlikely to work!\n"); + if (vortex_debug && (dev->irq <= 0 || dev->irq >= NR_IRQS)) + printk(" *** Warning: this IRQ is unlikely to work! ***\n"); { - char *ram_split[] = {"5:3", "3:1", "1:1", "invalid"}; + char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; union wn3_config config; EL3WINDOW(3); vp->available_media = inw(ioaddr + Wn3_Options); @@ -520,8 +711,23 @@ vp->default_media = config.u.xcvr; vp->autoselect = config.u.autoselect; } + if (vp->media_override != 7) { + printk(" Media override to transceiver type %d (%s).\n", + vp->media_override, media_tbl[vp->media_override].name); + dev->if_port = vp->media_override; + } - /* We do a request_region() only to register /proc/ioports info. */ + vp->info1 = eeprom[13]; + vp->info2 = eeprom[15]; + vp->capabilities = eeprom[16]; + if (vp->capabilities & 0x20) { + vp->full_bus_master_tx = 0; /* TX bugs, force bus_master_tx to 0? */ + printk(" Enabling bus-master transmits and %s receives.\n", + (vp->info2 & 1) ? "early" : "whole-frame" ); + vp->full_bus_master_rx = (vp->info2 & 1) ? 1 : 2; + } + + /* We do a request_region() to register /proc/ioports info. */ request_region(ioaddr, VORTEX_TOTAL_SIZE, vp->product_name); /* The 3c59x-specific entries in the device structure. */ @@ -529,7 +735,11 @@ dev->hard_start_xmit = &vortex_start_xmit; dev->stop = &vortex_close; dev->get_stats = &vortex_get_stats; +#ifdef NEW_MULTICAST dev->set_multicast_list = &set_rx_mode; +#else + dev->set_multicast_list = &set_multicast_list; +#endif return 0; } @@ -545,8 +755,6 @@ /* Before initializing select the active media port. */ EL3WINDOW(3); - if (vp->full_duplex) - outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */ config.i = inl(ioaddr + Wn3_Config); if (vp->media_override != 7) { @@ -576,6 +784,10 @@ config.u.xcvr = dev->if_port; outl(config.i, ioaddr + Wn3_Config); + /* Set the full-duplex bit. */ + outb(((vp->info1 & 0x8000) || vp->full_duplex ? 0x20 : 0) | + (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl); + if (vortex_debug > 1) { printk("%s: vortex_open() InternalConfig %8.8x.\n", dev->name, config.i); @@ -583,22 +795,32 @@ outw(TxReset, ioaddr + EL3_CMD); for (i = 20; i >= 0 ; i--) - if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; outw(RxReset, ioaddr + EL3_CMD); /* Wait a few ticks for the RxReset command to complete. */ for (i = 20; i >= 0 ; i--) - if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); +#ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */ if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ, vp->product_name, dev)) { return -EAGAIN; } +#else + if (dev->irq == 0 || irq2dev_map[dev->irq] != NULL) + return -EAGAIN; + irq2dev_map[dev->irq] = dev; + if (request_irq(dev->irq, &vortex_interrupt, 0, vp->product_name)) { + irq2dev_map[dev->irq] = NULL; + return -EAGAIN; + } +#endif if (vortex_debug > 1) { EL3WINDOW(4); @@ -623,18 +845,52 @@ /* Switch to the stats window, and clear all stats by reading. */ outw(StatsDisable, ioaddr + EL3_CMD); EL3WINDOW(6); - for (i = 0; i < 10; i++) + for (i = 0; i < 10; i++) inb(ioaddr + i); inw(ioaddr + 10); inw(ioaddr + 12); /* New: On the Vortex we must also clear the BadSSD counter. */ EL3WINDOW(4); inb(ioaddr + 12); + /* ..and on the Boomerang we enable the extra statistics bits. */ + outw(0x0040, ioaddr + Wn4_NetDiag); /* Switch to register set 7 for normal use. */ EL3WINDOW(7); - /* Set receiver mode: presumably accept b-case and phys addr only. */ + if (vp->full_bus_master_rx) { /* Boomerang bus master. */ + vp->cur_rx = vp->dirty_rx = 0; + /* Initialize the RxEarly register as recommended. */ + outw(SetRxThreshold + (1536>>2), ioaddr + EL3_CMD); + outl(0x0020, ioaddr + PktStatus); + if (vortex_debug > 2) + printk("%s: Filling in the Rx ring.\n", dev->name); + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb; + vp->rx_ring[i].next = virt_to_bus(&vp->rx_ring[i+1]); + vp->rx_ring[i].status = 0; /* Clear complete bit. */ + vp->rx_ring[i].length = PKT_BUF_SZ | 0x80000000; + skb = dev_alloc_skb(PKT_BUF_SZ); + vp->rx_skbuff[i] = skb; + if (skb == NULL) + break; /* Bad news! */ + skb->dev = dev; /* Mark as being used by this device. */ + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ + vp->rx_ring[i].addr = virt_to_bus(skb->tail); + } + vp->rx_ring[i-1].next = virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */ + outl(virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr); + } + if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */ + dev->hard_start_xmit = &boomerang_start_xmit; + vp->cur_tx = vp->dirty_tx = 0; + outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); /* Room for a packet. */ + /* Clear the Tx ring. */ + for (i = 0; i < TX_RING_SIZE; i++) + vp->tx_skbuff[i] = 0; + outl(0, ioaddr + DownListPtr); + } + /* Set reciever mode: presumably accept b-case and phys addr only. */ set_rx_mode(dev); outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ @@ -645,12 +901,17 @@ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ /* Allow status bits to be seen. */ - outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); + outw(SetStatusEnb | AdapterFailure|IntReq|StatsFull | + (vp->full_bus_master_tx ? DownComplete : TxAvailable) | + (vp->full_bus_master_rx ? UpComplete : RxComplete) | + (vp->bus_master ? DMADone : 0), + ioaddr + EL3_CMD); /* Ack all pending events, and set active indicator mask. */ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, ioaddr + EL3_CMD); outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull - | DMADone, ioaddr + EL3_CMD); + | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete, + ioaddr + EL3_CMD); MOD_INC_USE_COUNT; @@ -685,7 +946,7 @@ } else if (vortex_debug > 1) printk("%s: Media %s is has no link beat, %x.\n", dev->name, media_tbl[dev->if_port].name, media_status); - + break; default: /* Other media types handled by Tx timeouts. */ if (vortex_debug > 1) @@ -731,72 +992,113 @@ return; } -static int -vortex_start_xmit(struct sk_buff *skb, struct device *dev) +static void vortex_tx_timeout(struct device *dev) { struct vortex_private *vp = (struct vortex_private *)dev->priv; int ioaddr = dev->base_addr; + int i; - /* Part of the following code is inspired by code from Giuseppe Ciaccio, - ciaccio@disi.unige.it. - It works around a ?bug? in the 8K Vortex that only occurs on some - systems: the TxAvailable interrupt seems to be lost. - The ugly work-around is to busy-wait for room available in the Tx - buffer before deciding the transmitter is actually hung. - This busy-wait should never really occur, since the problem is that - there actually *is* room in the Tx FIFO. - - This pointed out an optimization -- we can ignore dev->tbusy if - we actually have room for this packet. - */ - -#if 0 - /* unstable optimization */ - if (inw(ioaddr + TxFree) > skb->len) /* We actually have free room. */ - dev->tbusy = 0; /* Fake out the check below. */ - else -#endif - if (dev->tbusy) { - /* Transmitter timeout, serious problems. */ - int tickssofar = jiffies - dev->trans_start; - int i; - - if (tickssofar < 2) /* We probably aren't empty. */ - return 1; - /* Wait a while to see if there really is room. */ - for (i = WAIT_TX_AVAIL; i >= 0; i--) - if (inw(ioaddr + TxFree) > skb->len) - break; - if ( i < 0) { - if (tickssofar < TX_TIMEOUT) - return 1; - printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n", - dev->name, inb(ioaddr + TxStatus), inw(ioaddr + EL3_STATUS)); - /* Issue TX_RESET and TX_START commands. */ - outw(TxReset, ioaddr + EL3_CMD); - for (i = 20; i >= 0 ; i--) - if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) - break; - outw(TxEnable, ioaddr + EL3_CMD); - dev->trans_start = jiffies; - dev->tbusy = 0; - vp->stats.tx_errors++; - vp->stats.tx_dropped++; - dev_kfree_skb(skb, FREE_WRITE); - return 0; /* Yes, silently *drop* the packet! */ + printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n", + dev->name, inb(ioaddr + TxStatus), + inw(ioaddr + EL3_STATUS)); + /* Slight code bloat to be user friendly. */ + if ((inb(ioaddr + TxStatus) & 0x88) == 0x88) + printk("%s: Transmitter encountered 16 collisions -- network" + " network cable problem?\n", dev->name); +#ifndef final_version + printk(" Flags; bus-master %d, full %d; dirty %d current %d.\n", + vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx, vp->cur_tx); + printk(" Down list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr), + &vp->tx_ring[0]); + for (i = 0; i < TX_RING_SIZE; i++) { + printk(" %d: %p length %8.8x status %8.8x\n", i, + &vp->tx_ring[i], + vp->tx_ring[i].length, + vp->tx_ring[i].status); + } + if (vp->full_bus_master_rx) { + printk(" Switching to non-bus-master receives.\n"); + outw(SetStatusEnb | AdapterFailure|IntReq|StatsFull | + (vp->full_bus_master_tx ? DownComplete : TxAvailable) | + RxComplete | (vp->bus_master ? DMADone : 0), + ioaddr + EL3_CMD); + } +#endif + /* Issue TX_RESET and TX_START commands. */ + outw(TxReset, ioaddr + EL3_CMD); + for (i = 20; i >= 0 ; i--) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + if (vp->full_bus_master_tx) { + /* Change 6/25/97 Michael Sievers sieversm@mail.desy.de + The card has been resetted, but the Tx Ring is still full. + Since the card won't know where to resume, 'update' the + Tx Ring. Probably, we'll lose 16 packets this way, these + will be accounted for as 'dropped'. The code to update the + Tx Ring is taken from the Interrupt handler. */ + + unsigned int dirty_tx = vp->dirty_tx; + + while (vp->cur_tx - dirty_tx > 0) { + int entry = dirty_tx % TX_RING_SIZE; + if (inl(ioaddr + DownListPtr) == + virt_to_bus(&vp->tx_ring[entry])) + break; /* It still hasn't been processed. */ + if (vp->tx_skbuff[entry]) { + if (vortex_debug > 0) + printk("%s: Freeing Tx ring entry %d\n",dev->name,entry); + dev_kfree_skb(vp->tx_skbuff[entry], FREE_WRITE); + vp->tx_skbuff[entry] = 0; } + vp->stats.tx_errors++; + vp->stats.tx_dropped++; + dirty_tx++; + } + vp->dirty_tx = dirty_tx; + vp->tx_full= 0; + } else { /* not bus-master, no Tx ring to clear */ + vp->stats.tx_errors++; + vp->stats.tx_dropped++; } + + /* Issue Tx Enable */ + outw(TxEnable, ioaddr + EL3_CMD); + dev->trans_start = jiffies; + + /* Switch to register set 7 for normal use. */ + EL3WINDOW(7); + + /* The TxFreeThreshold has to be set again after a reset! */ + if (vp->full_bus_master_tx) { + outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); + /* This is to be sure that all bus-master Tx features + are correctly re-initialized after a reset, although + the DownListPtr should already be 0 at this point. */ + outl(0, ioaddr + DownListPtr); + } + /* finally, allow new Transmits */ + dev->tbusy = 0; + /* End of Michael Sievers changes. */ +} - if(skb == NULL) { +static int +vortex_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + int ioaddr = dev->base_addr; + +#ifndef final_version + if (skb == NULL || skb->len <= 0) { + printk("%s: Obsolete driver layer request made: skbuff==NULL.\n", + dev->name); dev_tint(dev); - return NULL; + return 0; } +#endif - /* Block a timer-based transmit from overlapping. This could better be - done with atomic_swap(1, dev->tbusy), but set_bit() works as well. - If this ever occurs the queue layer is doing something evil! */ - if (set_bit(0, (void*)&dev->tbusy) != 0) { - printk("%s: Transmitter access conflict.\n", dev->name); + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + if (jiffies - dev->trans_start >= TX_TIMEOUT) + vortex_tx_timeout(dev); return 1; } @@ -849,7 +1151,7 @@ int j; outw(TxReset, ioaddr + EL3_CMD); for (j = 20; j >= 0 ; j--) - if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; } outw(TxEnable, ioaddr + EL3_CMD); @@ -860,12 +1162,84 @@ return 0; } +static int +boomerang_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + int ioaddr = dev->base_addr; + +#ifndef final_version + if (skb == NULL || skb->len <= 0) { + printk("%s: Obsolete driver layer request made: skbuff==NULL.\n", + dev->name); + dev_tint(dev); + return 0; + } +#endif + + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + if (jiffies - dev->trans_start >= TX_TIMEOUT) + vortex_tx_timeout(dev); + return 1; + } else { + /* Calculate the next Tx descriptor entry. */ + int entry = vp->cur_tx % TX_RING_SIZE; + struct boom_tx_desc *prev_entry = + &vp->tx_ring[(vp->cur_tx-1) % TX_RING_SIZE]; + unsigned long flags, i; + + if (vortex_debug > 3) + printk("%s: Trying to send a packet, Tx index %d.\n", + dev->name, vp->cur_tx); + if (vp->tx_full) { + if (vortex_debug >0) + printk("%s: Tx Ring full, refusing to send buffer.\n", + dev->name); + return 1; + } + /* end change 06/25/97 M. Sievers */ + vp->tx_skbuff[entry] = skb; + vp->tx_ring[entry].next = 0; + vp->tx_ring[entry].addr = virt_to_bus(skb->data); + vp->tx_ring[entry].length = skb->len | 0x80000000; + vp->tx_ring[entry].status = skb->len | 0x80000000; + + save_flags(flags); + cli(); + outw(DownStall, ioaddr + EL3_CMD); + /* Wait for the stall to complete. */ + for (i = 20; i >= 0 ; i--) + if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0) + break; + prev_entry->next = virt_to_bus(&vp->tx_ring[entry]); + if (inl(ioaddr + DownListPtr) == 0) { + outl(virt_to_bus(&vp->tx_ring[entry]), ioaddr + DownListPtr); + queued_packet++; + } + outw(DownUnstall, ioaddr + EL3_CMD); + restore_flags(flags); + + vp->cur_tx++; + if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1) + vp->tx_full = 1; + else { /* Clear previous interrupt enable. */ + prev_entry->status &= ~0x80000000; + dev->tbusy = 0; + } + dev->trans_start = jiffies; + return 0; + } +} + /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs) { - /* Use the now-standard shared IRQ implementation. */ +#ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */ struct device *dev = dev_id; +#else + struct device *dev = (struct device *)(irq2dev_map[irq]); +#endif struct vortex_private *lp; int ioaddr, status; int latency; @@ -911,13 +1285,41 @@ dev->tbusy = 0; mark_bh(NET_BH); } + if (status & DownComplete) { + unsigned int dirty_tx = lp->dirty_tx; + + while (lp->cur_tx - dirty_tx > 0) { + int entry = dirty_tx % TX_RING_SIZE; + if (inl(ioaddr + DownListPtr) == + virt_to_bus(&lp->tx_ring[entry])) + break; /* It still hasn't been processed. */ + if (lp->tx_skbuff[entry]) { + dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); + lp->tx_skbuff[entry] = 0; + } + lp->stats.tx_packets++; + dirty_tx++; + } + outw(AckIntr | DownComplete, ioaddr + EL3_CMD); + if (lp->tx_full) { + lp->tx_full= 0; + dev->tbusy = 0; + mark_bh(NET_BH); + } + lp->dirty_tx = dirty_tx; + } #ifdef VORTEX_BUS_MASTER if (status & DMADone) { outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */ dev->tbusy = 0; + dev_kfree_skb (lp->tx_skb, FREE_WRITE); /* Release the transfered buffer */ mark_bh(NET_BH); } #endif + if (status & UpComplete) { + boomerang_rx(dev); + outw(AckIntr | UpComplete, ioaddr + EL3_CMD); + } if (status & (AdapterFailure | RxEarly | StatsFull)) { /* Handle all uncommon interrupts at once. */ if (status & RxEarly) { /* Rx early is unused. */ @@ -943,7 +1345,8 @@ printk(" %2.2x", inb(ioaddr+reg)); } EL3WINDOW(7); - outw(SetIntrEnb | 0x18, ioaddr + EL3_CMD); + outw(SetIntrEnb | TxAvailable | RxComplete + | UpComplete | DownComplete, ioaddr + EL3_CMD); DoneDidThat++; } } @@ -960,10 +1363,10 @@ if (++i > 10) { printk("%s: Infinite loop in interrupt, status %4.4x. " "Disabling functions (%4.4x).\n", - dev->name, status, SetStatusEnb | ((~status) & 0xFE)); + dev->name, status, SetStatusEnb | ((~status) & 0x7FE)); /* Disable all pending interrupts. */ - outw(SetStatusEnb | ((~status) & 0xFE), ioaddr + EL3_CMD); - outw(AckIntr | 0xFF, ioaddr + EL3_CMD); + outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD); + outw(AckIntr | 0x7FF, ioaddr + EL3_CMD); break; } /* Acknowledge the IRQ. */ @@ -992,7 +1395,7 @@ while ((rx_status = inw(ioaddr + RxStatus)) > 0) { if (rx_status & 0x4000) { /* Error, update stats. */ unsigned char rx_error = inb(ioaddr + RxErrors); - if (vortex_debug > 4) + if (vortex_debug > 2) printk(" Rx error: status %2.2x.\n", rx_error); vp->stats.rx_errors++; if (rx_error & 0x01) vp->stats.rx_over_errors++; @@ -1011,28 +1414,36 @@ pkt_len, rx_status); if (skb != NULL) { skb->dev = dev; +#if LINUX_VERSION_CODE >= 0x10300 skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len), (pkt_len + 3) >> 2); + outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); +#else + skb->len = pkt_len; + /* 'skb->data' points to the start of sk_buff data area. */ + insl(ioaddr + RX_FIFO, skb->data, (pkt_len + 3) >> 2); outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ +#endif /* KERNEL_1_3_0 */ + netif_rx(skb); + dev->last_rx = jiffies; + vp->stats.rx_packets++; /* Wait a limited time to go to next packet. */ for (i = 200; i >= 0; i--) - if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; - vp->stats.rx_packets++; continue; } else if (vortex_debug) printk("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len); } - vp->stats.rx_dropped++; outw(RxDiscard, ioaddr + EL3_CMD); + vp->stats.rx_dropped++; /* Wait a limited time to skip this packet. */ for (i = 200; i >= 0; i--) - if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; } @@ -1040,17 +1451,111 @@ } static int +boomerang_rx(struct device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + int entry = vp->cur_rx % RX_RING_SIZE; + int ioaddr = dev->base_addr; + int rx_status; + + if (vortex_debug > 5) + printk(" In boomerang_rx(), status %4.4x, rx_status %4.4x.\n", + inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus)); + while ((rx_status = vp->rx_ring[entry].status) & RX_COMPLETE) { + if (rx_status & 0x4000) { /* Error, update stats. */ + unsigned char rx_error = rx_status >> 16; + if (vortex_debug > 4) + printk(" Rx error: status %2.2x.\n", rx_error); + vp->stats.rx_errors++; + if (rx_error & 0x01) vp->stats.rx_over_errors++; + if (rx_error & 0x02) vp->stats.rx_length_errors++; + if (rx_error & 0x04) vp->stats.rx_frame_errors++; + if (rx_error & 0x08) vp->stats.rx_crc_errors++; + if (rx_error & 0x10) vp->stats.rx_length_errors++; + } else { + /* The packet length: up to 4.5K!. */ + short pkt_len = rx_status & 0x1fff; + struct sk_buff *skb; + + if (vortex_debug > 4) + printk("Receiving packet size %d status %4.4x.\n", + pkt_len, rx_status); + + /* Check if the packet is long enough to just accept without + copying to a properly sized skbuff. */ + if (pkt_len < rx_copybreak + && (skb = DEV_ALLOC_SKB(pkt_len + 2)) != 0) { + skb->dev = dev; + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ + /* 'skb_put()' points to the start of sk_buff data area. */ + memcpy(skb_put(skb, pkt_len), + bus_to_virt(vp->rx_ring[entry].addr), + pkt_len); + rx_copy++; + } else{ + void *temp; + /* Pass up the skbuff already on the Rx ring. */ + skb = vp->rx_skbuff[entry]; + vp->rx_skbuff[entry] = NULL; + temp = skb_put(skb, pkt_len); + /* Remove this checking code for final release. */ + if (bus_to_virt(vp->rx_ring[entry].addr) != temp) + printk("%s: Warning -- the skbuff addresses do not match" + " in boomerang_rx: %p vs. %p / %p.\n", dev->name, + bus_to_virt(vp->rx_ring[entry].addr), + skb->head, temp); + rx_nocopy++; + } +#if LINUX_VERSION_CODE > 0x10300 + skb->protocol = eth_type_trans(skb, dev); +#else + skb->len = pkt_len; +#endif + netif_rx(skb); + dev->last_rx = jiffies; + vp->stats.rx_packets++; + } + entry = (++vp->cur_rx) % RX_RING_SIZE; + } + /* Refill the Rx ring buffers. */ + for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) { + struct sk_buff *skb; + entry = vp->dirty_rx % RX_RING_SIZE; + if (vp->rx_skbuff[entry] == NULL) { + skb = dev_alloc_skb(PKT_BUF_SZ); + if (skb == NULL) + break; /* Bad news! */ + skb->dev = dev; /* Mark as being used by this device. */ +#if LINUX_VERSION_CODE > 0x10300 + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ + vp->rx_ring[entry].addr = virt_to_bus(skb->tail); +#else + vp->rx_ring[entry].addr = virt_to_bus(skb->data); +#endif + vp->rx_skbuff[entry] = skb; + } + vp->rx_ring[entry].status = 0; /* Clear complete bit. */ + } + return 0; +} + +static int vortex_close(struct device *dev) { struct vortex_private *vp = (struct vortex_private *)dev->priv; int ioaddr = dev->base_addr; + int i; dev->start = 0; dev->tbusy = 1; - if (vortex_debug > 1) + if (vortex_debug > 1) { printk("%s: vortex_close() status %4.4x, Tx status %2.2x.\n", dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus)); + printk("%s: vortex close stats: rx_nocopy %d rx_copy %d" + " tx_queued %d.\n", + dev->name, rx_nocopy, rx_copy, queued_packet); + } del_timer(&vp->timer); @@ -1065,9 +1570,36 @@ /* Turn off thinnet power. Green! */ outw(StopCoax, ioaddr + EL3_CMD); - FREE_IRQ(dev->irq, dev); +#ifdef SA_SHIRQ + free_irq(dev->irq, dev); +#else + free_irq(dev->irq); + irq2dev_map[dev->irq] = 0; +#endif + + outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); update_stats(ioaddr, dev); + if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */ + outl(0, ioaddr + UpListPtr); + for (i = 0; i < RX_RING_SIZE; i++) + if (vp->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 + vp->rx_skbuff[i]->free = 1; +#endif + dev_kfree_skb (vp->rx_skbuff[i], FREE_WRITE); + vp->rx_skbuff[i] = 0; + } + } + if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */ + outl(0, ioaddr + DownListPtr); + for (i = 0; i < TX_RING_SIZE; i++) + if (vp->tx_skbuff[i]) { + dev_kfree_skb(vp->tx_skbuff[i], FREE_WRITE); + vp->tx_skbuff[i] = 0; + } + } + MOD_DEC_USE_COUNT; return 0; @@ -1079,10 +1611,12 @@ struct vortex_private *vp = (struct vortex_private *)dev->priv; unsigned long flags; - save_flags(flags); - cli(); - update_stats(dev->base_addr, dev); - restore_flags(flags); + if (dev->start) { + save_flags(flags); + cli(); + update_stats(dev->base_addr, dev); + restore_flags(flags); + } return &vp->stats; } @@ -1140,11 +1674,19 @@ new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm; } else if ((dev->mc_list) || (dev->flags & IFF_ALLMULTI)) { new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast; - } else + } else new_mode = SetRxFilter | RxStation | RxBroadcast; outw(new_mode, ioaddr + EL3_CMD); } +#ifndef NEW_MULTICAST +/* The old interface to set the Rx mode. */ +static void +set_multicast_list(struct device *dev, int num_addrs, void *addrs) +{ + set_rx_mode(dev); +} +#endif #ifdef MODULE void @@ -1166,7 +1708,7 @@ /* * Local variables: - * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c59x.c -o ../../modules/3c59x.o" + * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c" * c-indent-level: 4 * tab-width: 4 * End: diff -u --recursive --new-file v2.0.30/linux/drivers/net/Config.in linux/drivers/net/Config.in --- v2.0.30/linux/drivers/net/Config.in Thu Mar 6 10:03:51 1997 +++ linux/drivers/net/Config.in Tue Aug 12 20:29:26 1997 @@ -50,7 +50,7 @@ tristate '3c507 support' CONFIG_EL16 fi tristate '3c509/3c579 support' CONFIG_EL3 - tristate '3c590 series (592/595/597) "Vortex" support' CONFIG_VORTEX + tristate '3c590/3c900 series (592/595/597/900/905) "Vortex/Boomerang" support' CONFIG_VORTEX fi bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE if [ "$CONFIG_LANCE" = "y" ]; then @@ -60,6 +60,7 @@ if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then tristate 'WD80*3 support' CONFIG_WD80x3 tristate 'SMC Ultra support' CONFIG_ULTRA + tristate 'SMC Ultra32 support' CONFIG_ULTRA32 tristate 'SMC 9194 support' CONFIG_SMC9194 fi bool 'Other ISA cards' CONFIG_NET_ISA @@ -97,6 +98,7 @@ tristate 'Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200 fi tristate 'Apricot Xen-II on board ethernet' CONFIG_APRICOT + tristate 'Intel EtherExpress/Pro 100B support' CONFIG_EEXPRESS_PRO100B tristate 'DE425, DE434, DE435, DE450, DE500 support' CONFIG_DE4X5 tristate 'DECchip Tulip (dc21x4x) PCI support' CONFIG_DEC_ELCP tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS diff -u --recursive --new-file v2.0.30/linux/drivers/net/Makefile linux/drivers/net/Makefile --- v2.0.30/linux/drivers/net/Makefile Tue Oct 29 17:42:40 1996 +++ linux/drivers/net/Makefile Tue Aug 12 20:29:26 1997 @@ -144,6 +144,16 @@ endif endif +ifeq ($(CONFIG_ULTRA32),y) +L_OBJS += smc-ultra32.o +CONFIG_8390_BUILTIN = y +else + ifeq ($(CONFIG_ULTRA),m) + CONFIG_8390_MODULE = y + M_OBJS += smc-ultra32.o + endif +endif + ifeq ($(CONFIG_E2100),y) L_OBJS += e2100.o CONFIG_8390_BUILTIN = y @@ -309,6 +319,15 @@ M_OBJS += eepro.o endif endif + +ifeq ($(CONFIG_EEXPRESS_PRO100B),y) +L_OBJS += eepro100.o +else + ifeq ($(CONFIG_EEXPRESS_PRO100B),m) + M_OBJS += eepro100.o + endif +endif + ifeq ($(CONFIG_WAVELAN),y) L_OBJS += wavelan.o diff -u --recursive --new-file v2.0.30/linux/drivers/net/Space.c linux/drivers/net/Space.c --- v2.0.30/linux/drivers/net/Space.c Tue Apr 8 08:47:45 1997 +++ linux/drivers/net/Space.c Tue Aug 12 20:29:26 1997 @@ -41,6 +41,7 @@ extern int tulip_probe(struct device *dev); extern int hp100_probe(struct device *dev); extern int ultra_probe(struct device *dev); +extern int ultra32_probe(struct device *dev); extern int wd_probe(struct device *dev); extern int el2_probe(struct device *dev); extern int ne_probe(struct device *dev); @@ -49,6 +50,7 @@ extern int znet_probe(struct device *); extern int express_probe(struct device *); extern int eepro_probe(struct device *); +extern int eepro100_probe(struct device *); extern int el3_probe(struct device *); extern int at1500_probe(struct device *); extern int at1700_probe(struct device *); @@ -111,6 +113,9 @@ #if defined(CONFIG_ULTRA) && ultra_probe(dev) #endif +#if defined(CONFIG_ULTRA32) + && ultra32_probe(dev) +#endif #if defined(CONFIG_SMC9194) && smc_init(dev) #endif @@ -158,6 +163,9 @@ #endif #ifdef CONFIG_EEXPRESS_PRO /* Intel EtherExpress Pro/10 */ && eepro_probe(dev) +#endif +#ifdef CONFIG_EEXPRESS_PRO100B /* Intel EtherExpress Pro100B */ + && eepro100_probe(dev) #endif #ifdef CONFIG_DEPCA /* DEC DEPCA */ && depca_probe(dev) diff -u --recursive --new-file v2.0.30/linux/drivers/net/atp.c linux/drivers/net/atp.c --- v2.0.30/linux/drivers/net/atp.c Thu Feb 29 21:50:43 1996 +++ linux/drivers/net/atp.c Tue Aug 12 13:21:12 1997 @@ -1,9 +1,9 @@ /* atp.c: Attached (pocket) ethernet adapter driver for linux. */ /* - This is a driver for a commonly OEMed pocket (parallel port) - ethernet adapter. + This is a driver for commonly OEMed pocket (parallel port) + ethernet adapters based on the Realtek RTL8002 and RTL8012 chips. - Written 1993,1994,1995 by Donald Becker. + Written 1993-95,1997 by Donald Becker. Copyright 1993 United States Government as represented by the Director, National Security Agency. @@ -19,7 +19,11 @@ */ static const char *version = - "atp.c:v1.01 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + "atp.c:v1.08 4/1/97 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + +/* Operational parameters that may be safely changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT ((400*HZ)/1000) /* This file is a device driver for the RealTek (aka AT-Lan-Tec) pocket @@ -33,6 +37,10 @@ description is written based on guesses and writing lots of special-purpose code to test my theorized operation. + In 1997 Realtek made available the documentation for the second generation + RTL8012 chip, which has lead to several driver improvements. + http://www.realtek.com.tw/cn/cn.html + Theory of Operation The RTL8002 adapter seems to be built around a custom spin of the SEEQ @@ -58,6 +66,10 @@ timing and control bits. The data is then read from status port or written to the data port. + Correction: the controller has two banks of 16 registers. The second + bank contains only the multicast filter table (now used) and the EEPROM + access registers. + Since the bulk data transfer of the actual packets through the slow parallel port dominates the driver's running time, four distinct data (non-register) transfer modes are provided by the adapter, two in each @@ -80,6 +92,14 @@ generate reasonable object code. This header file also documents my interpretations of the device registers. */ +#include +#ifdef MODULE +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif #include #include @@ -103,6 +123,49 @@ #include "atp.h" +/* Kernel compatibility defines, common to David Hind's PCMCIA package. + This is only in the support-all-kernels source code. */ +#include /* Evil, but neccessary */ + +#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300 +#define RUN_AT(x) (x) /* What to put in timer->expires. */ +#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC) +#define virt_to_bus(addr) ((unsigned long)addr) +#define bus_to_virt(addr) ((void*)addr) + +#else /* 1.3.0 and later */ +#define RUN_AT(x) (jiffies + (x)) +#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2) +#endif +#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338 +#ifdef MODULE +#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__) +char kernel_version[] = UTS_RELEASE; +#endif +#else +#undef MOD_INC_USE_COUNT +#define MOD_INC_USE_COUNT +#undef MOD_DEC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif +#endif /* 1.3.38 */ + +#if (LINUX_VERSION_CODE >= 0x10344) +#define NEW_MULTICAST +#include +#endif + +#ifdef SA_SHIRQ +#define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev) +#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance) +#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs) +#else +#define FREE_IRQ(irqnum, dev) free_irq(irqnum) +#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n) +#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs) +#endif +/* End of kernel compatibility defines. */ + /* use 0 for production, 1 for verification, >2 for debug */ #ifndef NET_DEBUG #define NET_DEBUG 1 @@ -112,6 +175,9 @@ /* The number of low I/O ports used by the ethercard. */ #define ETHERCARD_TOTAL_SIZE 3 +/* Sequence to switch an 8012 from printer mux to ethernet mode. */ +static char mux_8012[] = { 0xff, 0xf7, 0xff, 0xfb, 0xf3, 0xfb, 0xff, 0xf7,}; + /* This code, written by wwc@super.org, resets the adapter every TIMED_CHECKER ticks. This recovers from an unknown error which hangs the device. */ @@ -119,13 +185,11 @@ #ifdef TIMED_CHECKER #include static void atp_timed_checker(unsigned long ignored); -static struct device *atp_timed_dev; -static struct timer_list atp_timer = {NULL, NULL, 0, 0, atp_timed_checker}; #endif /* Index to functions, as function prototypes. */ -extern int atp_probe(struct device *dev); +extern int atp_init(struct device *dev); static int atp_probe1(struct device *dev, short ioaddr); static void get_node_ID(struct device *dev); @@ -135,14 +199,23 @@ static void write_packet(short ioaddr, int length, unsigned char *packet, int mode); static void trigger_send(short ioaddr, int length); static int net_send_packet(struct sk_buff *skb, struct device *dev); -static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void net_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs); static void net_rx(struct device *dev); static void read_block(short ioaddr, int length, unsigned char *buffer, int data_mode); static int net_close(struct device *dev); static struct enet_statistics *net_get_stats(struct device *dev); -static void set_multicast_list(struct device *dev); +#ifdef NEW_MULTICAST +static void set_rx_mode_8002(struct device *dev); +static void set_rx_mode_8012(struct device *dev); +#else +static void set_rx_mode_8002(struct device *dev, int num_addrs, void *addrs); +static void set_rx_mode_8012(struct device *dev, int num_addrs, void *addrs); +#endif +/* A list of all installed ATP devices, for removing the driver module. */ +static struct device *root_atp_dev = NULL; + /* Check for a network adapter 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. @@ -153,7 +226,7 @@ atp_init(struct device *dev) { int *port, ports[] = {0x378, 0x278, 0x3bc, 0}; - int base_addr = dev->base_addr; + int base_addr = dev ? dev->base_addr : 0; if (base_addr > 0x1ff) /* Check a single specified location. */ return atp_probe1(dev, base_addr); @@ -174,7 +247,8 @@ static int atp_probe1(struct device *dev, short ioaddr) { - int saved_ctrl_reg, status; + struct net_local *lp; + int saved_ctrl_reg, status, i; outb(0xff, ioaddr + PAR_DATA); /* Save the original value of the Control register, in case we guessed @@ -182,6 +256,9 @@ saved_ctrl_reg = inb(ioaddr + PAR_CONTROL); /* IRQEN=0, SLCTB=high INITB=high, AUTOFDB=high, STBB=high. */ outb(0x04, ioaddr + PAR_CONTROL); + /* Turn off the printer multiplexer on the 8012. */ + for (i = 0; i < 8; i++) + outb(mux_8012[i], ioaddr + PAR_DATA); write_reg_high(ioaddr, CMR1, CMR1h_RESET); eeprom_delay(2048); status = read_nibble(ioaddr, CMR1); @@ -196,6 +273,9 @@ outb(saved_ctrl_reg, ioaddr + PAR_CONTROL); return 1; } + + dev = init_etherdev(dev, sizeof(struct net_local)); + /* Find the IRQ used by triggering an interrupt. */ write_reg_byte(ioaddr, CMR2, 0x01); /* No accept mode, IRQ out. */ write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE); /* Enable Tx and Rx. */ @@ -218,27 +298,29 @@ dev->irq, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); - /* Leave the hardware in a reset state. */ - write_reg_high(ioaddr, CMR1, CMR1h_RESET); + /* Reset the ethernet hardware and activate the printer pass-through. */ + write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX); if (net_debug) printk(version); /* Initialize the device structure. */ ether_setup(dev); - dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); + if (dev->priv == NULL) + dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); if (dev->priv == NULL) return -ENOMEM; memset(dev->priv, 0, sizeof(struct net_local)); + lp = (struct net_local *)dev->priv; + lp->chip_type = RTL8002; + lp->addr_mode = CMR2h_Normal; - { - struct net_local *lp = (struct net_local *)dev->priv; - lp->addr_mode = CMR2h_Normal; - } + lp->next_module = root_atp_dev; + root_atp_dev = dev; /* For the ATP adapter the "if_port" is really the data transfer mode. */ - dev->if_port = (dev->mem_start & 0xf) ? dev->mem_start & 0x7 : 4; + dev->if_port = (dev->mem_start & 0xf) ? (dev->mem_start & 0x7) : 4; if (dev->mem_end & 0xf) net_debug = dev->mem_end & 7; @@ -246,14 +328,9 @@ dev->stop = net_close; dev->hard_start_xmit = net_send_packet; dev->get_stats = net_get_stats; - dev->set_multicast_list = &set_multicast_list; + dev->set_multicast_list = + lp->chip_type == RTL8002 ? &set_rx_mode_8002 : &set_rx_mode_8012; -#ifdef TIMED_CHECKER - del_timer(&atp_timer); - atp_timer.expires = jiffies + TIMED_CHECKER; - atp_timed_dev = dev; - add_timer(&atp_timer); -#endif return 0; } @@ -322,18 +399,32 @@ */ static int net_open(struct device *dev) { + struct net_local *lp = (struct net_local *)dev->priv; /* The interrupt line is turned off (tri-stated) when the device isn't in use. That's especially important for "attached" interfaces where the port or interrupt may be shared. */ +#ifndef SA_SHIRQ if (irq2dev_map[dev->irq] != 0 || (irq2dev_map[dev->irq] = dev) == 0 - || request_irq(dev->irq, &net_interrupt, 0, "ATP", NULL)) { + || REQUEST_IRQ(dev->irq, &net_interrupt, 0, "ATP", dev)) { return -EAGAIN; } +#else + if (request_irq(dev->irq, &net_interrupt, 0, "ATP Ethernet", dev)) + return -EAGAIN; +#endif + MOD_INC_USE_COUNT; hardware_init(dev); dev->start = 1; + + init_timer(&lp->timer); + lp->timer.expires = RUN_AT(TIMED_CHECKER); + lp->timer.data = (unsigned long)dev; + lp->timer.function = &atp_timed_checker; /* timer handler */ + add_timer(&lp->timer); + return 0; } @@ -345,6 +436,9 @@ int ioaddr = dev->base_addr; int i; + /* Turn off the printer multiplexer on the 8012. */ + for (i = 0; i < 8; i++) + outb(mux_8012[i], ioaddr + PAR_DATA); write_reg_high(ioaddr, CMR1, CMR1h_RESET); for (i = 0; i < 6; i++) @@ -418,11 +512,18 @@ struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; - if (dev->tbusy) { - /* If we get here, some higher level has decided we are broken. - There should really be a "kick me" function call instead. */ - int tickssofar = jiffies - dev->trans_start; - if (tickssofar < 5) +#ifndef final_version + if (skb == NULL || skb->len <= 0) { + printk("%s: Obsolete driver layer request made: skbuff==NULL.\n", + dev->name); + dev_tint(dev); + return 0; + } +#endif + + /* Use transmit-while-tbusy as a crude error timer. */ + if (set_bit(0, (void*)&dev->tbusy) != 0) { + if (jiffies - dev->trans_start < TX_TIMEOUT) return 1; printk("%s: transmit timed out, %s?\n", dev->name, inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem" @@ -432,21 +533,8 @@ hardware_init(dev); 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 transmit from overlapping. This could better be - done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ - if (set_bit(0, (void*)&dev->tbusy) != 0) - printk("%s: Transmitter access conflict.\n", dev->name); - else { + return 1; + } else { short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; unsigned char *buf = skb->data; int flags; @@ -484,9 +572,13 @@ /* The typical workload of the driver: Handle the network interface interrupts. */ static void -net_interrupt(int irq, void *dev_id, struct pt_regs * regs) +net_interrupt IRQ(int irq, void *dev_instance, struct pt_regs * regs) { +#ifdef SA_SHIRQ + struct device *dev = (struct device *)dev_instance; +#else struct device *dev = (struct device *)(irq2dev_map[irq]); +#endif struct net_local *lp; int ioaddr, status, boguscount = 20; static int num_tx_since_rx = 0; @@ -585,11 +677,6 @@ int i; for (i = 0; i < 6; i++) write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]); -#ifdef TIMED_CHECKER - del_timer(&atp_timer); - atp_timer.expires = jiffies + TIMED_CHECKER; - add_timer(&atp_timer); -#endif } /* Tell the adapter that it can go back to using the output line as IRQ. */ @@ -610,17 +697,23 @@ #ifdef TIMED_CHECKER /* This following code fixes a rare (and very difficult to track down) problem where the adapter forgets its ethernet address. */ -static void atp_timed_checker(unsigned long ignored) +static void atp_timed_checker(unsigned long data) { - int i; - int ioaddr = atp_timed_dev->base_addr; + struct device *dev = (struct device *)data; + int ioaddr = dev->base_addr; + struct net_local *lp = (struct net_local *)dev->priv; + int tickssofar = jiffies - lp->last_rx_time; + int i; - if (!atp_timed_dev->interrupt) - { - for (i = 0; i < 6; i++) -#if 0 - if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i]) - { + if (tickssofar > 2*HZ && dev->interrupt == 0) { +#if 1 + for (i = 0; i < 6; i++) + write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]); + lp->last_rx_time = jiffies; +#else + for (i = 0; i < 6; i++) + if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i]) + { struct net_local *lp = (struct net_local *)atp_timed_dev->priv; write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]); if (i == 2) @@ -632,13 +725,10 @@ else lp->stats.rx_errors++; } -#else - write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]); #endif } - del_timer(&atp_timer); - atp_timer.expires = jiffies + TIMED_CHECKER; - add_timer(&atp_timer); + lp->timer.expires = RUN_AT(TIMED_CHECKER); + add_timer(&lp->timer); } #endif @@ -647,11 +737,7 @@ { struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; -#ifdef notdef - ushort header[4]; -#else struct rx_header rx_head; -#endif /* Process the received packet. */ outb(EOC+MAR, ioaddr + PAR_DATA); @@ -661,18 +747,23 @@ rx_head.rx_count, rx_head.rx_status, rx_head.cur_addr); if ((rx_head.rx_status & 0x77) != 0x01) { lp->stats.rx_errors++; - /* Ackkk! I don't have any documentation on what the error bits mean! - The best I can do is slap the device around a bit. */ + if (rx_head.rx_status & 0x0004) lp->stats.rx_frame_errors++; + else if (rx_head.rx_status & 0x0002) lp->stats.rx_crc_errors++; if (net_debug > 3) printk("%s: Unknown ATP Rx error %04x.\n", dev->name, rx_head.rx_status); - hardware_init(dev); + if (rx_head.rx_status & 0x0020) { + lp->stats.rx_fifo_errors++; + write_reg_high(ioaddr, CMR1, CMR1h_TxENABLE); + write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE); + } else if (rx_head.rx_status & 0x0050) + hardware_init(dev); return; } else { - /* Malloc up new buffer. */ - int pkt_len = (rx_head.rx_count & 0x7ff) - 4; /* The "-4" is omits the FCS (CRC). */ + /* Malloc up new buffer. The "-4" is omits the FCS (CRC). */ + int pkt_len = (rx_head.rx_count & 0x7ff) - 4; struct sk_buff *skb; - skb = dev_alloc_skb(pkt_len); + skb = DEV_ALLOC_SKB(pkt_len + 2); if (skb == NULL) { printk("%s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; @@ -680,23 +771,20 @@ } skb->dev = dev; +#if LINUX_VERSION_CODE >= 0x10300 + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port); - - if (net_debug > 6) { - unsigned char *data = skb->data; - printk(" data %02x%02x%02x %02x%02x%02x %02x%02x%02x" - "%02x%02x%02x %02x%02x..", - data[0], data[1], data[2], data[3], data[4], data[5], - data[6], data[7], data[8], data[9], data[10], data[11], - data[12], data[13]); - } - - skb->protocol=eth_type_trans(skb,dev); + skb->protocol = eth_type_trans(skb, dev); +#else + read_block(ioaddr, pkt_len, skb->data, dev->if_port); + skb->len = pkt_len; +#endif netif_rx(skb); lp->stats.rx_packets++; } done: write_reg(ioaddr, CMR1, CMR1_NextPkt); + lp->last_rx_time = jiffies; return; } @@ -730,17 +818,23 @@ dev->tbusy = 1; dev->start = 0; + del_timer(&lp->timer); + /* Flush the Tx and disable Rx here. */ lp->addr_mode = CMR2h_OFF; write_reg_high(ioaddr, CMR2, CMR2h_OFF); /* Free the IRQ line. */ outb(0x00, ioaddr + PAR_CONTROL); - free_irq(dev->irq, NULL); + FREE_IRQ(dev->irq, dev); +#ifndef SA_SHIRQ irq2dev_map[dev->irq] = 0; +#endif + + /* Reset the ethernet hardware and activate the printer pass-through. */ + write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX); - /* Leave the hardware in a reset state. */ - write_reg_high(ioaddr, CMR1, CMR1h_RESET); + MOD_DEC_USE_COUNT; return 0; } @@ -757,29 +851,125 @@ /* * Set or clear the multicast filter for this adapter. */ - -static void set_multicast_list(struct device *dev) + +/* The little-endian AUTODIN32 ethernet CRC calculation. + This is common code and should be moved to net/core/crc.c */ +static unsigned const ethernet_polynomial_le = 0xedb88320U; +static inline unsigned ether_crc_le(int length, unsigned char *data) +{ + unsigned int crc = 0xffffffff; /* Initial value. */ + while(--length >= 0) { + unsigned char current_octet = *data++; + int bit; + for (bit = 8; --bit >= 0; current_octet >>= 1) { + if ((crc ^ current_octet) & 1) { + crc >>= 1; + crc ^= ethernet_polynomial_le; + } else + crc >>= 1; + } + } + return crc; +} + +static void +#ifdef NEW_MULTICAST +set_rx_mode_8002(struct device *dev) +#else +static void set_rx_mode_8002(struct device *dev, int num_addrs, void *addrs); +#endif { struct net_local *lp = (struct net_local *)dev->priv; short ioaddr = dev->base_addr; - int num_addrs=dev->mc_list; - - if(dev->flags&(IFF_ALLMULTI|IFF_PROMISC)) - num_addrs=1; - /* - * We must make the kernel realise we had to move - * into promisc mode or we start all out war on - * the cable. - AC - */ - if(num_addrs) - dev->flags|=IFF_PROMISC; - lp->addr_mode = num_addrs ? CMR2h_PROMISC : CMR2h_Normal; + + if ( dev->mc_count > 0 || (dev->flags & (IFF_ALLMULTI|IFF_PROMISC))) { + /* We must make the kernel realise we had to move + * into promisc mode or we start all out war on + * the cable. - AC + */ + dev->flags|=IFF_PROMISC; + lp->addr_mode = CMR2h_PROMISC; + } else + lp->addr_mode = CMR2h_Normal; write_reg_high(ioaddr, CMR2, lp->addr_mode); } + +static void +#ifdef NEW_MULTICAST +set_rx_mode_8012(struct device *dev) +#else +static void set_rx_mode_8012(struct device *dev, int num_addrs, void *addrs); +#endif +{ + struct net_local *lp = (struct net_local *)dev->priv; + short ioaddr = dev->base_addr; + unsigned char new_mode, mc_filter[8]; /* Multicast hash filter */ + int i; + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + new_mode = CMR2h_PROMISC; + } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter perfectly -- accept all multicasts. */ + memset(mc_filter, 0xff, sizeof(mc_filter)); + new_mode = CMR2h_Normal; + } else { + struct dev_mc_list *mclist; + + 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_le(ETH_ALEN, mclist->dmi_addr) & 0x3f, + mc_filter); + new_mode = CMR2h_Normal; + } + lp->addr_mode = new_mode; + write_reg(ioaddr, CMR2, CMR2_IRQOUT | 0x04); /* Switch to page 1. */ + for (i = 0; i < 8; i++) + write_reg_byte(ioaddr, i, mc_filter[i]); + if (net_debug > 2 || 1) { + lp->addr_mode = 1; + printk("%s: Mode %d, setting multicast filter to", + dev->name, lp->addr_mode); + for (i = 0; i < 8; i++) + printk(" %2.2x", mc_filter[i]); + printk(".\n"); + } + + write_reg_high(ioaddr, CMR2, lp->addr_mode); + write_reg(ioaddr, CMR2, CMR2_IRQOUT); /* Switch back to page 0 */ +} + +#ifdef MODULE +static int debug = 1; +int +init_module(void) +{ + net_debug = debug; + root_atp_dev = NULL; + atp_init(0); + return 0; +} + +void +cleanup_module(void) +{ + struct device *next_dev; + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + /* No need to release_region(), since we never snarf it. */ + while (root_atp_dev) { + next_dev = ((struct net_local *)root_atp_dev->priv)->next_module; + unregister_netdev(root_atp_dev); + kfree(root_atp_dev); + root_atp_dev = next_dev; + } +} +#endif /* MODULE */ + /* * Local variables: - * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c atp.c" + * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c atp.c" * version-control: t * kept-new-versions: 5 * tab-width: 4 diff -u --recursive --new-file v2.0.30/linux/drivers/net/atp.h linux/drivers/net/atp.h --- v2.0.30/linux/drivers/net/atp.h Tue Oct 10 06:03:24 1995 +++ linux/drivers/net/atp.h Tue Aug 12 13:21:12 1997 @@ -1,3 +1,6 @@ +/* Linux header file for the ATP pocket ethernet adapter. */ +/* v1.04 4/1/97 becker@cesdis.gsfc.nasa.gov. */ + #include #include #include @@ -6,16 +9,19 @@ #ifdef __KERNEL__ struct enet_statistics stats; #endif + struct device *next_module; + struct timer_list timer; /* Media selection timer. */ + long last_rx_time; /* Last Rx, in jiffies, to handle Rx hang. */ ushort saved_tx_size; - unsigned char - re_tx, /* Number of packet retransmissions. */ - tx_unit_busy, - addr_mode, /* Current Rx filter e.g. promiscuous, etc. */ - pac_cnt_in_tx_buf; + unsigned tx_unit_busy:1; + unsigned char re_tx, /* Number of packet retransmissions. */ + addr_mode, /* Current Rx filter e.g. promiscuous, etc. */ + pac_cnt_in_tx_buf, + chip_type; }; struct rx_header { - ushort pad; /* The first read is always corrupted. */ + ushort pad; /* Pad. */ ushort rx_count; ushort rx_status; /* Unknown bit assignments :-<. */ ushort cur_addr; /* Apparently the current buffer address(?) */ @@ -25,6 +31,8 @@ #define PAR_STATUS 1 #define PAR_CONTROL 2 +enum chip_type { RTL8002, RTL8012 }; + #define Ctrl_LNibRead 0x08 /* LP_PSELECP */ #define Ctrl_HNibRead 0 #define Ctrl_LNibWrite 0x08 /* LP_PSELECP */ @@ -47,7 +55,8 @@ ISR = 10, IMR = 11, /* Interrupt status and mask. */ CMR1 = 12, /* Command register 1. */ CMR2 = 13, /* Command register 2. */ - MAR = 14, /* Memory address register. */ + MODSEL = 14, /* Mode select register. */ + MAR = 14, /* Memory address register (?). */ CMR2_h = 0x1d, }; enum eepage_regs @@ -59,6 +68,7 @@ #define ISR_TxErr 0x02 #define ISRh_RxErr 0x11 /* ISR, high nibble */ +#define CMR1h_MUX 0x08 /* Select printer multiplexor on 8012. */ #define CMR1h_RESET 0x04 /* Reset. */ #define CMR1h_RxENABLE 0x02 /* Rx unit enable. */ #define CMR1h_TxENABLE 0x01 /* Tx unit enable. */ diff -u --recursive --new-file v2.0.30/linux/drivers/net/auto_irq.c linux/drivers/net/auto_irq.c --- v2.0.30/linux/drivers/net/auto_irq.c Thu Feb 29 21:50:43 1996 +++ linux/drivers/net/auto_irq.c Mon Aug 4 12:10:18 1997 @@ -54,17 +54,23 @@ { irq_number = irq; set_bit(irq, (void *)&irq_bitmap); /* irq_bitmap |= 1 << irq; */ - disable_irq(irq); + /* This code used to disable the irq. However, the interrupt stub + * would then re-enable the interrupt with (potentially) disastrous + * consequences + */ + free_irq(irq, dev_id); return; } int autoirq_setup(int waittime) { - int i, mask; + int i; int timeout = jiffies + waittime; int boguscount = (waittime*loops_per_sec) / 100; irq_handled = 0; + irq_bitmap = 0; + for (i = 0; i < 16; i++) { if (test_bit(i, &irqs_busy) == 0 && request_irq(i, autoirq_probe, SA_INTERRUPT, "irq probe", NULL) == 0) @@ -72,22 +78,15 @@ } /* Update our USED lists. */ irqs_used |= ~irq_handled; - irq_number = 0; - irq_bitmap = 0; /* Hang out at least jiffies waiting for bogus IRQ hits. */ while (timeout > jiffies && --boguscount > 0) ; - for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) { - if (irq_bitmap & irq_handled & mask) { - irq_handled &= ~mask; -#ifdef notdef - printk(" Spurious interrupt on IRQ %d\n", i); -#endif - free_irq(i, NULL); - } - } + irq_handled &= ~irq_bitmap; + + irq_number = 0; /* We are interested in new interrupts from now on */ + return irq_handled; } @@ -102,6 +101,8 @@ while (timeout > jiffies && --boguscount > 0) if (irq_number) break; + + irq_handled &= ~irq_bitmap; /* This eliminates the already reset handlers */ /* Retract the irq handlers that we installed. */ for (i = 0; i < 16; i++) { diff -u --recursive --new-file v2.0.30/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c --- v2.0.30/linux/drivers/net/de4x5.c Sat Nov 9 09:31:38 1996 +++ linux/drivers/net/de4x5.c Tue Aug 12 13:21:12 1997 @@ -1,12 +1,35 @@ -/* de4x5.c: A DIGITAL DE425/DE434/DE435/DE450/DE500 ethernet driver for Linux. +/* de4x5.c: A DIGITAL DC21x4x DECchip and DE425/DE434/DE435/DE450/DE500 + ethernet driver for Linux. Copyright 1994, 1995 Digital Equipment Corporation. - This software may be used and distributed according to the terms of - the GNU Public License, incorporated herein by reference. + Testing resources for this driver have been made available + in part by NASA Ames Research Center (mjacob@nas.nasa.gov). - This driver is written for the Digital Equipment Corporation series - of EtherWORKS ethernet cards: + The author may be reached at davies@maniac.ultranet.com. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 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. + + Originally, this driver was written for the Digital Equipment + Corporation series of EtherWORKS ethernet cards: DE425 TP/COAX EISA DE434 TP PCI @@ -14,6 +37,25 @@ DE450 TP/COAX/AUI PCI DE500 10/100 PCI Fasternet + but it will now attempt to support all cards which conform to the + Digital Semiconductor SROM Specification. The driver currently + recognises the following chips: + + DC21040 (no SROM) + DC21041[A] + DC21140[A] + + I plan to add DC2114[23] support ASAP, time permitting. So far the + driver is known to work with the following cards: + + KINGSTON + Linksys + ZNYX342 + SMC8432 + SMC9332 (w/new SROM) + ZNYX31[45] + ZNYX346 10/100 4 port (can act as a 10/100 bridge!) + The driver has been tested on a relatively busy network using the DE425, DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred 16M of data to a DECstation 5000/200 as follows: @@ -29,12 +71,12 @@ measurement. Their error is +/-20k on a quiet (private) network and also depend on what load the CPU has. - The author may be reached at davies@maniac.ultranet.com. - ========================================================================= - This driver has been written substantially from scratch, although its + This driver has been written substantially from scratch, although its inheritance of style and stack interface from 'ewrk3.c' and in turn from - Donald Becker's 'lance.c' should be obvious. + Donald Becker's 'lance.c' should be obvious. With the module autoload of + every usable DECchip board, I pinched Donald's 'next_module' field to + link my modules together. Upto 15 EISA cards can be supported under this driver, limited primarily by the available IRQ lines. I have checked different configurations of @@ -46,43 +88,37 @@ to the differences in the EISA and PCI CSR address offsets from the base address. - The ability to load this driver as a loadable module has been included - and used extensively during the driver development (to save those long + The ability to load this driver as a loadable module has been included + and used extensively during the driver development (to save those long reboot sequences). Loadable module support under PCI and EISA has been achieved by letting the driver autoprobe as if it were compiled into the - kernel, except that there is no autoprobing of the IRQ lines. This is of - no great consequence except do make sure you're not sharing interrupts - with anything that cannot accommodate interrupt sharing! The existing - register_netdevice() code will only allow one device to be registered at - a time. - - ************************************************************************ - For now, please only use the 'io=??' assignment (see 2. below, ?? != 0) - when loading a module. - ************************************************************************ - - Essentially, the I/O address and IRQ information are ignored and filled - in later by the PCI BIOS during the PCI probe. Note that the board - should be in the system at boot time so that its I/O address and IRQ are - allocated by the PCI BIOS automatically. + kernel. Do make sure you're not sharing interrupts with anything that + cannot accommodate interrupt sharing! To utilise this ability, you have to do 8 things: 0) have a copy of the loadable modules code installed on your system. 1) copy de4x5.c from the /linux/drivers/net directory to your favourite temporary directory. - 2) edit the source code near line 3779 to reflect the I/O address you're - using (only if you want to manually load the module), or assign these - when loading by: + 2) for fixed autoprobes (not recommended), edit the source code near + line 5005 to reflect the I/O address you're using, or assign these when + loading by: - insmod de4x5.o io=0xghh where g = bus number + insmod de4x5 io=0xghh where g = bus number hh = device number + NB: autoprobing for modules is now supported by default. You may just + use: + + insmod de4x5 + + to load all available boards. For a specific board, still use + the 'io=?' above. 3) compile de4x5.c, but include -DMODULE in the command line to ensure that the correct bits are compiled (see end of source code). 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a kernel with the de4x5 configuration turned off and reboot. - 5) insmod de4x5.o [io=0xghh] + 5) insmod de4x5 [io=0xghh] 6) run the net startup bits for your new eth?? interface(s) manually (usually /etc/rc.inet[12] at boot time). 7) enjoy! @@ -98,13 +134,7 @@ By default, the driver will now autodetect any DECchip based card. Should you have a need to restrict the driver to DIGITAL only cards, you can compile with a DEC_ONLY define, or if loading as a module, use the - 'dec_only=1' parameter. However, this "feature" is in no way supported - nor tested in this driver and the user may use it at his/her sole - discretion. I have had 2 conflicting reports that my driver will or - won't work with Znyx. Try Donald Becker's 'tulip.c' if this driver - doesn't work for you. I will not be supporting Znyx and SMC cards since - I have no information on them and can't test them in a system (this - applies most particularly to the DC21140 based cards). + 'dec_only=1' parameter. I've changed the timing routines to use the kernel timer and scheduling functions so that the hangs and other assorted problems that occurred @@ -125,6 +155,30 @@ aligned DMA transfers and the Alphas get alignment traps with non longword aligned data copies (which makes them really slow). No comment. + I have added SROM decoding routines to make this driver work with any + card that supports the Digital Semiconductor SROM spec. This will help + all cards running the dc2114x series chips in particular. Cards using + the dc2104x chips should run correctly with the basic driver. I'm in + debt to for the testing and feedback that helped get + this feature working. So far we have tested KINGSTON, SMC8432, SMC9332 + (with the latest SROM complying with the SROM spec V3: their first was + broken), ZNYX342 and LinkSys. ZYNX314 (dual 21041 MAC) and ZNYX 315 + (quad 21041 MAC) cards also appear to work despite their incorrectly + wired IRQs. + + I have added a temporary fix for interrupt problems when some SCSI cards + share the same interrupt as the DECchip based cards. The problem occurs + because the SCSI card wants to grab the interrupt as a fast interrupt + (runs the service routine with interrupts turned off) vs. this card + which really needs to run the service routine with interrupts turned on. + This driver will now add the interrupt service routine as a fast + interrupt if it is bounced from the slow interrupt. THIS IS NOT A + RECOMMENDED WAY TO RUN THE DRIVER and has been done for a limited time + until people sort out their compatibility issues and the kernel + interrupt service code is fixed. YOU SHOULD SEPARATE OUT THE FAST + INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not + run on the same interrupt. PCMCIA/CardBus is another can of worms... + TO DO: ------ @@ -208,16 +262,49 @@ with 0.44 13-Aug-96 Fix RX overflow bug in 2114[023] chips. Fix EISA probe bugs reported by - and + and . 0.441 9-Sep-96 Change dc21041_autoconf() to probe quiet BNC media with a loopback packet. 0.442 9-Sep-96 Include AUI in dc21041 media printout. Bug reported by + 0.45 8-Dec-96 Include endian functions for PPC use, from work + by and . + 0.451 28-Dec-96 Added fix to allow autoprobe for modules after + suggestion from . + 0.5 30-Jan-97 Added SROM decoding functions. + Updated debug flags. + Fix sleep/wakeup calls for PCI cards, bug reported + by . + Added multi-MAC, one SROM feature from discussion + with . + Added full module autoprobe capability. + Added attempt to use an SMC9332 with broken SROM. + Added fix for ZYNX multi-mac cards that didn't + get their IRQs wired correctly. + 0.51 13-Feb-97 Added endian fixes for the SROM accesses from + + Fix init_connection() to remove extra device reset. + Fix MAC/PHY reset ordering in dc21140m_autoconf(). + Fix initialisation problem with lp->timeout in + typeX_infoblock() from . + Fix MII PHY reset problem from work done by + . + 0.52 26-Apr-97 Some changes may not credit the right people - + a disk crash meant I lost some mail. + Change RX interrupt routine to drop rather than + defer packets to avoid hang reported by + . + Fix srom_exec() to return for COMPACT and type 1 + infoblocks. + Added DC21142 and DC21143 functions. + Added byte counters from + Added SA_INTERRUPT temporary fix from + . ========================================================================= */ -static const char *version = "de4x5.c:v0.442 96/11/7 davies@maniac.ultranet.com\n"; +static const char *version = "de4x5.c:V0.52 1997/4/26 davies@maniac.ultranet.com\n"; #include @@ -235,7 +322,8 @@ #include #include #include -#include +#include +#include #include #include @@ -250,14 +338,37 @@ #define c_char const char +#include +#if LINUX_VERSION_CODE < ((2 << 16) | (1 << 8)) +#define net_device_stats enet_statistics +#define copy_to_user(a,b,c) memcpy_tofs(a,b,c) +#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c) +#define le16_to_cpu(a) cpu_to_le16(a) +#define le32_to_cpu(a) cpu_to_le32(a) +#ifdef __powerpc__ +#define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8)) +#define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\ + (((a) & 0x0000ff00U) << 8) |\ + (((a) & 0x00ff0000U) >> 8) |\ + (((a) & 0xff000000U) >> 24)) +#else +#define cpu_to_le16(a) (a) +#define cpu_to_le32(a) (a) +#endif /* __powerpc__ */ +#include +#else +#include +#endif /* LINUX_VERSION_CODE */ +#define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a))) + /* ** MII Information */ struct phy_table { - int reset; /* Hard reset required? */ - int id; /* IEEE OUI */ + int reset; /* Hard reset required? */ + int id; /* IEEE OUI */ int ta; /* One cycle TA time - 802.3u is confusing here */ - struct { /* Non autonegotiation (parallel) speed det. */ + struct { /* Non autonegotiation (parallel) speed det. */ int reg; int mask; int value; @@ -265,19 +376,35 @@ }; struct mii_phy { - int reset; /* Hard reset required? */ - int id; /* IEEE OUI */ - int ta; /* One cycle TA time */ + int reset; /* Hard reset required? */ + int id; /* IEEE OUI */ + int ta; /* One cycle TA time */ struct { /* Non autonegotiation (parallel) speed det. */ int reg; int mask; int value; } spd; - int addr; /* MII address for the PHY */ + int addr; /* MII address for the PHY */ + u_char *gep; /* Start of GEP sequence block in SROM */ + u_char *rst; /* Start of reset sequence in SROM */ + u_int mc; /* Media Capabilities */ + u_int ana; /* NWay Advertisement */ + u_int fdx; /* Full DupleX capabilites for each media */ + u_int ttm; /* Transmit Threshold Mode for each media */ }; #define DE4X5_MAX_PHY 8 /* Allow upto 8 attached PHY devices per board */ +struct sia_phy { + u_char mc; /* Media Code */ + u_char ext; /* csr13-15 valid when set */ + int csr13; /* SIA Connectivity Register */ + int csr14; /* SIA TX/RX Register */ + int csr15; /* SIA General Register */ + int gepc; /* SIA GEP Control Information */ + int gep; /* SIA GEP Data */ +}; + /* ** Define the know universe of PHY devices that can be ** recognised by this driver @@ -285,8 +412,8 @@ static struct phy_table phy_info[] = { {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}}, /* National TX */ {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}}, /* Broadcom T4 */ - {0, SEEQ_T4 , 1, {0x12, 0x10, 0x10}}, /* SEEQ T4 */ - {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}} /* Cypress T4 */ + {0, SEEQ_T4 , 1, {0x12, 0x10, 0x10}}, /* SEEQ T4 */ + {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}} /* Cypress T4 */ }; /* @@ -300,11 +427,23 @@ #define SMC 1 #define ACCTON 2 +/* +** SROM Repair definitions. If a broken SROM is detected a card may +** use this information to help figure out what to do. This is a +** "stab in the dark" and so far for SMC9332's only. +*/ +static c_char srom_repair_info[][100] = { + {0x00,0x1e,0x00,0x00,0x00,0x08, /* SMC9332 */ + 0x1f,0x01,0x8f,0x01,0x00,0x01,0x00,0x02, + 0x01,0x00,0x00,0x78,0xe0,0x01,0x00,0x50, + 0x00,0x18,} +}; + #ifdef DE4X5_DEBUG static int de4x5_debug = DE4X5_DEBUG; #else -static int de4x5_debug = 1; +static int de4x5_debug = (0); #endif #ifdef DE4X5_AUTOSENSE /* Should be done on a per adapter basis */ @@ -449,7 +588,7 @@ char id_block_crc; char reserved2; char version; - char num_adapters; + char num_controllers; char ieee_addr[6]; char info[100]; short chksum; @@ -494,7 +633,7 @@ int tx_new, tx_old; /* TX descriptor ring pointers */ char setup_frame[SETUP_FRAME_LEN]; /* Holds MCA and PA info. */ char frame[64]; /* Min sized packet for loopback*/ - struct enet_statistics stats; /* Public stats */ + struct net_device_stats stats; /* Public stats */ struct { u_int bins[DE4X5_PKT_STAT_SZ]; /* Private stats counters */ u_int unicast; @@ -512,19 +651,21 @@ char txRingSize; int bus; /* EISA or PCI */ int bus_num; /* PCI Bus number */ + int device; /* Device number on PCI bus */ int state; /* Adapter OPENED or CLOSED */ int chipset; /* DC21040, DC21041 or DC21140 */ s32 irq_mask; /* Interrupt Mask (Enable) bits */ s32 irq_en; /* Summary interrupt bits */ int media; /* Media (eg TP), mode (eg 100B)*/ int c_media; /* Remember the last media conn */ + int fdx; /* media full duplex flag */ int linkOK; /* Link is OK */ int autosense; /* Allow/disallow autosensing */ int tx_enable; /* Enable descriptor polling */ - int lostMedia; /* Possibly lost media */ int setup_f; /* Setup frame filtering type */ int local_state; /* State within a 'media' state */ struct mii_phy phy[DE4X5_MAX_PHY]; /* List of attached PHY devices */ + struct sia_phy sia; /* SIA PHY Information */ int active; /* Index to active PHY device */ int mii_cnt; /* Number of attached PHY's */ int timeout; /* Scheduling counter */ @@ -537,13 +678,31 @@ s32 csr0; /* Saved Bus Mode Register */ s32 csr6; /* Saved Operating Mode Reg. */ s32 csr7; /* Saved IRQ Mask Register */ + s32 gep; /* Saved General Purpose Reg. */ + s32 gepc; /* Control info for GEP */ s32 csr13; /* Saved SIA Connectivity Reg. */ s32 csr14; /* Saved SIA TX/RX Register */ s32 csr15; /* Saved SIA General Register */ int save_cnt; /* Flag if state already saved */ struct sk_buff *skb; /* Save the (re-ordered) skb's */ } cache; + struct de4x5_srom srom; /* A copy of the SROM */ + struct device *next_module; /* Link to the next module */ int rx_ovf; /* Check for 'RX overflow' tag */ + int useSROM; /* For non-DEC card use SROM */ + int useMII; /* Infoblock using the MII */ + int asBitValid; /* Autosense bits in GEP? */ + int asPolarity; /* 0 => asserted high */ + int asBit; /* Autosense bit number in GEP */ + int defMedium; /* SROM default medium */ + int tcount; /* Last infoblock number */ + int infoblock_init; /* Initialised this infoblock? */ + int infoleaf_offset; /* SROM infoleaf for controller */ + s32 infoblock_csr6; /* csr6 value in SROM infoblock */ + int infoblock_media; /* infoblock media */ + int (*infoleaf_fn)(struct device *); /* Pointer to infoleaf function */ + u_char *rst; /* Pointer to Type 5 reset info */ + u_char ibn; /* Infoblock number */ }; /* @@ -558,9 +717,29 @@ int chipset; struct de4x5_srom srom; int autosense; + int useSROM; } bus; /* +** To get around certain poxy cards that don't provide an SROM +** for the second and more DECchip, I have to key off the first +** chip's address. I'll assume there's not a bad SROM iff: +** +** o the chipset is the same +** o the bus number is the same and > 0 +** o the sum of all the returned hw address bytes is 0 or 0x5fa +** +** Also have to save the irq for those cards whose hardware designers +** can't follow the PCI to PCI Bridge Architecture spec. +*/ +static struct { + int chipset; + int bus; + int irq; + u_char addr[ETH_ALEN]; +} last = {0,}; + +/* ** The transmit ring full condition is described by the tx_old and tx_new ** pointers by: ** tx_old = tx_new Empty ring @@ -580,7 +759,7 @@ static int de4x5_queue_pkt(struct sk_buff *skb, struct device *dev); static void de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs); static int de4x5_close(struct device *dev); -static struct enet_statistics *de4x5_get_stats(struct device *dev); +static struct net_device_stats *de4x5_get_stats(struct device *dev); static void de4x5_local_stats(struct device *dev, char *buf, int pkt_len); static void set_multicast_list(struct device *dev); static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd); @@ -605,6 +784,7 @@ static int dc21040_autoconf(struct device *dev); static int dc21041_autoconf(struct device *dev); static int dc21140m_autoconf(struct device *dev); +static int srom_autoconf(struct device *dev); static int de4x5_suspect_state(struct device *dev, int timeout, int prev_state, int (*fn)(struct device *, int), int (*asfn)(struct device *)); static int dc21040_state(struct device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn)(struct device *, int)); static int test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec); @@ -642,6 +822,10 @@ /*static void srom_busy(u_int command, u_long address);*/ static void sendto_srom(u_int command, u_long addr); static int getfrom_srom(u_long addr); +static void srom_map_media(struct device *dev); +static int srom_infoleaf_info(struct device *dev); +static void srom_init(struct device *dev); +static void srom_exec(struct device *dev, u_char *p); static int mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr); static void mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr); static int mii_rdata(u_long ioaddr); @@ -655,6 +839,8 @@ static int mii_get_phy(struct device *dev); static void SetMulticastFilter(struct device *dev); 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); static void eisa_probe(struct device *dev, u_long iobase); static void pci_probe(struct device *dev, u_long iobase); @@ -664,20 +850,33 @@ static char *build_setup_frame(struct device *dev, int mode); static void disable_ast(struct device *dev); static void enable_ast(struct device *dev, u32 time_out); -static long de4x5_switch_to_srl(struct device *dev); -static long de4x5_switch_to_mii(struct device *dev); +static long de4x5_switch_mac_port(struct device *dev); static void timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec); +static void yawn(struct device *dev, int state); static int de4x5_dev_index(char *s); +static void link_modules(struct device *dev, struct device *tmp); static void de4x5_dbg_open(struct device *dev); static void de4x5_dbg_mii(struct device *dev, int k); static void de4x5_dbg_media(struct device *dev); static void de4x5_dbg_srom(struct de4x5_srom *p); static void de4x5_dbg_rx(struct sk_buff *skb, int len); static int de4x5_strncmp(char *a, char *b, int n); +static int dc21041_infoleaf(struct device *dev); +static int dc21140_infoleaf(struct device *dev); +static int dc21142_infoleaf(struct device *dev); +static int dc21143_infoleaf(struct device *dev); +static int type0_infoblock(struct device *dev, u_char count, u_char *p); +static int type1_infoblock(struct device *dev, u_char count, u_char *p); +static int type2_infoblock(struct device *dev, u_char count, u_char *p); +static int type3_infoblock(struct device *dev, u_char count, u_char *p); +static int type4_infoblock(struct device *dev, u_char count, u_char *p); +static int type5_infoblock(struct device *dev, u_char count, u_char *p); +static int compact_infoblock(struct device *dev, u_char count, u_char *p); #ifdef MODULE int init_module(void); void cleanup_module(void); +static struct device *unlink_modules(struct device *p); static int autoprobed = 0, loading_module = 1; # else static int autoprobed = 0, loading_module = 0; @@ -686,7 +885,37 @@ static char name[DE4X5_NAME_LENGTH + 1]; static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST; static int num_de4x5s = 0, num_eth = 0; -static int cfrv = 0; +static int cfrv = 0, useSROM = 0; + +/* +** List the SROM infoleaf functions and chipsets +*/ +struct InfoLeaf { + int chipset; + int (*fn)(struct device *); +}; +static struct InfoLeaf infoleaf_array[] = { + {DC21041, dc21041_infoleaf}, + {DC21140, dc21140_infoleaf}, + {DC21142, dc21142_infoleaf}, + {DC21143, dc21143_infoleaf} +}; +#define INFOLEAF_SIZE (sizeof(infoleaf_array)/(sizeof(int)+sizeof(int *))) + +/* +** List the SROM info block functions +*/ +static int (*dc_infoblock[])(struct device *dev, u_char, u_char *) = { + type0_infoblock, + type1_infoblock, + type2_infoblock, + type3_infoblock, + type4_infoblock, + type5_infoblock, + compact_infoblock +}; + +#define COMPACT (sizeof(dc_infoblock)/sizeof(int *) - 1) /* ** Miscellaneous defines... @@ -703,6 +932,13 @@ de4x5_ms_delay(1);\ } +#define PHY_HARD_RESET {\ + outl(GEP_HRST, DE4X5_GEP); /* Hard RESET the PHY dev. */\ + udelay(1000); /* Assert for 1ms */\ + outl(0x00, DE4X5_GEP);\ + udelay(2000); /* Wait for 2ms */\ +} + /* ** Autoprobing in modules is allowed here. See the top of the file for @@ -712,17 +948,12 @@ int de4x5_probe(struct device *dev) { - int tmp = num_de4x5s, status = -ENODEV; + int status = -ENODEV; u_long iobase = dev->base_addr; eisa_probe(dev, iobase); pci_probe(dev, iobase); - if ((tmp == num_de4x5s) && (iobase != 0) && loading_module) { - printk("%s: de4x5_probe() cannot find device at 0x%04lx.\n", dev->name, - iobase); - } - /* ** Walk the device list to check that at least one device ** initialised OK @@ -731,7 +962,7 @@ if (dev->priv) status = 0; if (iobase == 0) autoprobed = 1; - + return status; } @@ -739,16 +970,18 @@ de4x5_hw_init(struct device *dev, u_long iobase) { struct bus_type *lp = &bus; - int tmpbus, tmpchs, status=0; - int i, media = *((char *)&(lp->srom) + *((char *)&(lp->srom) + 19) * 3); + int i, status=0; char *tmp; /* Ensure we're not sleeping */ - if (lp->chipset == DC21041) { - outl(0, PCI_CFDA); - de4x5_ms_delay(10); + if (lp->bus == EISA) { + outb(WAKEUP, PCI_CFPM); + } else { + pcibios_write_config_byte(lp->bus_num, lp->device << 3, + PCI_CFDA_PSM, WAKEUP); } - + de4x5_ms_delay(10); + RESET_DE4X5; if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) != 0) { @@ -758,6 +991,7 @@ /* ** Now find out what kind of DC21040/DC21041/DC21140 board we have. */ + useSROM = FALSE; if (lp->bus == PCI) { PCI_signature(name, lp); } else { @@ -784,9 +1018,6 @@ } printk("%2.2x,\n", dev->dev_addr[i]); - tmpbus = lp->bus; - tmpchs = lp->chipset; - if (status != 0) { printk(" which has an Ethernet PROM CRC error.\n"); return -ENXIO; @@ -810,26 +1041,23 @@ dev->priv = (void *)(((u_long)dev->priv + ALIGN) & ~ALIGN); lp = (struct de4x5_private *)dev->priv; memset(dev->priv, 0, sizeof(struct de4x5_private)); - lp->bus = tmpbus; - lp->chipset = tmpchs; + lp->bus = bus.bus; + lp->bus_num = bus.bus_num; + lp->device = bus.device; + lp->chipset = bus.chipset; lp->cache.priv = tmp; + lp->cache.gepc = GEP_INIT; + lp->asBit = GEP_SLNK; + lp->asPolarity = GEP_SLNK; + lp->asBitValid = TRUE; + lp->timeout = -1; + lp->useSROM = useSROM; + memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom)); /* - ** Check for an MII interface - */ - if (media & MEDIA_MII) { /* MII interface? */ - if (!mii_get_phy(dev)) { - printk("%s: MII search failed, no device found when one was expected\n", dev->name); - return -ENXIO; - } - } else { - mii_get_phy(dev); /* Search the MII anyway! */ - } - - /* ** Choose correct autosensing in case someone messed up */ - if (de4x5_autosense & AUTO) { + if ((de4x5_autosense & AUTO) || lp->useSROM) { lp->autosense = AUTO; } else { if (lp->chipset != DC21140) { @@ -844,14 +1072,14 @@ lp->autosense = de4x5_autosense & 0x00c0; } } - + lp->fdx = de4x5_full_duplex; sprintf(lp->adapter_name,"%s (%s)", name, dev->name); /* ** Set up the RX descriptor ring (Intels) ** Allocate contiguous receive buffers, long word aligned (Alphas) */ -#if !defined(__alpha__) && !defined(DE4X5_DO_MEMCPY) +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY) for (i=0; irx_ring[i].status = 0; lp->rx_ring[i].des1 = RX_BUFF_SZ; @@ -871,8 +1099,8 @@ tmp = (char *)(((u_long) tmp + ALIGN) & ~ALIGN); for (i=0; irx_ring[i].status = 0; - lp->rx_ring[i].des1 = RX_BUFF_SZ; - lp->rx_ring[i].buf = virt_to_bus(tmp + i * RX_BUFF_SZ); + lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); + lp->rx_ring[i].buf = cpu_to_le32(virt_to_bus(tmp+i*RX_BUFF_SZ)); lp->rx_ring[i].next = 0; lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */ } @@ -888,8 +1116,8 @@ lp->txRingSize = NUM_TX_DESC; /* Write the end of list marker to the descriptor lists */ - lp->rx_ring[lp->rxRingSize - 1].des1 |= RD_RER; - lp->tx_ring[lp->txRingSize - 1].des1 |= TD_TER; + lp->rx_ring[lp->rxRingSize - 1].des1 |= cpu_to_le32(RD_RER); + lp->tx_ring[lp->txRingSize - 1].des1 |= cpu_to_le32(TD_TER); /* Tell the adapter where the TX/RX rings are located. */ outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA); @@ -903,20 +1131,35 @@ create_packet(dev, lp->frame, sizeof(lp->frame)); /* Check if the RX overflow bug needs testing for */ - tmpchs = cfrv & 0x000000fe; - if ((lp->chipset == DC21140) && (tmpchs == 0x20)) { + i = cfrv & 0x000000fe; + if ((lp->chipset == DC21140) && (i == 0x20)) { lp->rx_ovf = 1; } - /* Initialise the adapter state */ + /* Initialise the SROM pointers if possible */ + if (lp->useSROM) { + lp->state = INITIALISED; + de4x5_dbg_srom((struct de4x5_srom *)&lp->srom); + if (srom_infoleaf_info(dev)) { + return -ENXIO; + } + srom_init(dev); + } + lp->state = CLOSED; + /* + ** Check for an MII interface + */ + if ((lp->chipset != DC21040) && (lp->chipset != DC21041)) { + mii_get_phy(dev); + } + printk(" and requires IRQ%d (provided by %s).\n", dev->irq, ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG")); - } - if (de4x5_debug > 1) { + if (de4x5_debug & DEBUG_VERSION) { printk(version); } @@ -930,14 +1173,11 @@ dev->mem_start = 0; - /* Fill in the generic field of the device structure. */ + /* Fill in the generic fields of the device structure. */ ether_setup(dev); /* Let the adapter sleep to save power */ - if (lp->chipset == DC21041) { - outl(0, DE4X5_SICR); - outl(CFDA_PSM, PCI_CFDA); - } + yawn(dev, SLEEP); return status; } @@ -962,10 +1202,7 @@ /* ** Wake up the adapter */ - if (lp->chipset == DC21041) { - outl(0, PCI_CFDA); - de4x5_ms_delay(10); - } + yawn(dev, WAKEUP); /* ** Re-initialize the DE4X5... @@ -977,20 +1214,31 @@ if (request_irq(dev->irq, (void *)de4x5_interrupt, SA_SHIRQ, lp->adapter_name, dev)) { - printk("de4x5_open(): Requested IRQ%d is busy\n",dev->irq); - status = -EAGAIN; - } else { - dev->tbusy = 0; - dev->start = 1; - dev->interrupt = UNMASK_INTERRUPTS; - dev->trans_start = jiffies; - - START_DE4X5; - - de4x5_setup_intr(dev); + printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq); + if (request_irq(dev->irq, de4x5_interrupt, SA_INTERRUPT | SA_SHIRQ, + lp->adapter_name, dev)) { + printk("\n Cannot get IRQ- reconfigure your hardware.\n"); + disable_ast(dev); + de4x5_free_rx_buffs(dev); + de4x5_free_tx_buffs(dev); + yawn(dev, SLEEP); + lp->state = CLOSED; + return -EAGAIN; + } else { + printk("\n Succeeded, but you should reconfigure your hardware to avoid this.\n"); + printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n"); + } } + dev->tbusy = 0; + dev->start = 1; + dev->interrupt = UNMASK_INTERRUPTS; + dev->trans_start = jiffies; + + START_DE4X5; + + de4x5_setup_intr(dev); - if (de4x5_debug > 1) { + if (de4x5_debug & DEBUG_OPEN) { printk("\tsts: 0x%08x\n", inl(DE4X5_STS)); printk("\tbmr: 0x%08x\n", inl(DE4X5_BMR)); printk("\timr: 0x%08x\n", inl(DE4X5_IMR)); @@ -1037,12 +1285,15 @@ s32 bmr, omr; /* Select the MII or SRL port now and RESET the MAC */ - if (lp->phy[lp->active].id == 0) { - de4x5_switch_to_srl(dev); - } else { - de4x5_switch_to_mii(dev); + if (!lp->useSROM) { + if (lp->phy[lp->active].id != 0) { + lp->infoblock_csr6 = OMR_PS | OMR_HBD; + } else { + lp->infoblock_csr6 = OMR_TTM; + } + de4x5_switch_mac_port(dev); } - + /* ** Set the programmable burst length to 8 longwords for all the DC21140 ** Fasternet chips and 4 longwords for all others: DMA errors result @@ -1063,11 +1314,11 @@ lp->tx_new = lp->tx_old = 0; for (i = 0; i < lp->rxRingSize; i++) { - lp->rx_ring[i].status = R_OWN; + lp->rx_ring[i].status = cpu_to_le32(R_OWN); } for (i = 0; i < lp->txRingSize; i++) { - lp->tx_ring[i].status = 0; + lp->tx_ring[i].status = cpu_to_le32(0); } barrier(); @@ -1082,7 +1333,7 @@ sti(); /* Ensure timer interrupts */ for (j=0, i=0;(i<500) && (j==0);i++) { /* Upto 500ms delay */ udelay(1000); - if (lp->tx_ring[lp->tx_new].status >= 0) j=1; + if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1; } outl(omr, DE4X5_OMR); /* Stop everything! */ @@ -1137,8 +1388,8 @@ } else { de4x5_put_cache(dev, skb); } - if (de4x5_debug > 1) { - printk("%s: transmit busy, lost media or stale skb found:\n STS:%08x\n tbusy:%ld\n lostMedia:%d\n IMR:%08x\n OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), dev->tbusy, lp->lostMedia, inl(DE4X5_IMR), inl(DE4X5_OMR), (lp->tx_skb[lp->tx_new] ? "YES" : "NO")); + if (de4x5_debug & DEBUG_TX) { + printk("%s: transmit busy, lost media or stale skb found:\n STS:%08x\n tbusy:%ld\n IMR:%08x\n OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), dev->tbusy, inl(DE4X5_IMR), inl(DE4X5_OMR), (lp->tx_skb[lp->tx_new] ? "YES" : "NO")); } } else if (skb->len > 0) { /* If we already have stuff queued locally, use that first */ @@ -1151,6 +1402,9 @@ cli(); set_bit(0, (void*)&dev->tbusy); load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb); +#if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8)) + lp->stats.tx_bytes += skb->len; +#endif outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */ lp->tx_new = (++lp->tx_new) % lp->txRingSize; @@ -1200,6 +1454,9 @@ printk("%s: Re-entering the interrupt handler.\n", dev->name); DISABLE_IRQs; /* Ensure non re-entrancy */ +#if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8)) + synchronize_irq(); +#endif dev->interrupt = MASK_INTERRUPTS; for (limit=0; limit<8; limit++) { @@ -1215,7 +1472,6 @@ de4x5_tx(dev); if (sts & STS_LNF) { /* TP Link has failed */ - lp->lostMedia = LOST_MEDIA_THRESHOLD + 1; lp->irq_mask &= ~IMR_LFM; } @@ -1253,8 +1509,9 @@ int entry; s32 status; - for (entry=lp->rx_new; lp->rx_ring[entry].status>=0;entry=lp->rx_new) { - status = lp->rx_ring[entry].status; + for (entry=lp->rx_new; (s32)le32_to_cpu(lp->rx_ring[entry].status)>=0; + entry=lp->rx_new) { + status = (s32)le32_to_cpu(lp->rx_ring[entry].status); if (lp->rx_ovf) { if (inl(DE4X5_MFC) & MFC_FOCM) { @@ -1268,7 +1525,7 @@ } if (status & RD_LS) { /* Valid frame status */ - lp->linkOK++; + if (lp->tx_enable) lp->linkOK++; if (status & RD_ES) { /* There was an error. */ lp->stats.rx_errors++; /* Update the error stats. */ if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++; @@ -1281,31 +1538,35 @@ if (status & RD_OF) lp->pktStats.rx_overflow++; } else { /* A valid frame received */ struct sk_buff *skb; - short pkt_len = (short)(lp->rx_ring[entry].status >> 16) - 4; + short pkt_len = (short)(le32_to_cpu(lp->rx_ring[entry].status) + >> 16) - 4; if ((skb = de4x5_alloc_rx_buff(dev, entry, pkt_len)) == NULL) { printk("%s: Insufficient memory; nuking packet.\n", dev->name); - lp->stats.rx_dropped++; /* Really, deferred. */ - break; - } - de4x5_dbg_rx(skb, pkt_len); + lp->stats.rx_dropped++; + } else { + de4x5_dbg_rx(skb, pkt_len); - /* Push up the protocol stack */ - skb->protocol=eth_type_trans(skb,dev); - netif_rx(skb); + /* Push up the protocol stack */ + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); - /* Update stats */ - lp->stats.rx_packets++; - de4x5_local_stats(dev, skb->data, pkt_len); + /* Update stats */ + lp->stats.rx_packets++; +#if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8)) + lp->stats.rx_bytes += pkt_len; +#endif + de4x5_local_stats(dev, skb->data, pkt_len); + } } /* Change buffer ownership for this frame, back to the adapter */ for (;lp->rx_old!=entry;lp->rx_old=(++lp->rx_old)%lp->rxRingSize) { - lp->rx_ring[lp->rx_old].status = R_OWN; + lp->rx_ring[lp->rx_old].status = cpu_to_le32(R_OWN); barrier(); } - lp->rx_ring[entry].status = R_OWN; + lp->rx_ring[entry].status = cpu_to_le32(R_OWN); barrier(); } @@ -1330,7 +1591,7 @@ s32 status; for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) { - status = lp->tx_ring[entry].status; + status = (s32)le32_to_cpu(lp->tx_ring[entry].status); if (status < 0) { /* Buffer not sent yet */ break; } else if (status != 0x7fffffff) { /* Not setup frame */ @@ -1342,16 +1603,12 @@ if (status & TD_EC) lp->pktStats.excessive_collisions++; if (status & TD_DE) lp->stats.tx_aborted_errors++; - if (status & (TD_LO | TD_NC | TD_EC | TD_LF)) { - lp->lostMedia++; - } if (TX_PKT_PENDING) { outl(POLL_DEMAND, DE4X5_TPD);/* Restart a stalled TX */ } } else { /* Packet sent */ lp->stats.tx_packets++; - lp->lostMedia = 0; /* Remove transient problem */ - lp->linkOK++; + if (lp->tx_enable) lp->linkOK++; } /* Update the collision counter */ lp->stats.collisions += ((status & TD_EC) ? 16 : @@ -1384,7 +1641,9 @@ disable_ast(dev); - if (lp->chipset == DC21140) { + if (lp->useSROM) { + next_tick = srom_autoconf(dev); + } else if (lp->chipset == DC21140) { next_tick = dc21140m_autoconf(dev); } else if (lp->chipset == DC21041) { next_tick = dc21041_autoconf(dev); @@ -1431,8 +1690,8 @@ outl(omr & ~OMR_SR, DE4X5_OMR); while (inl(DE4X5_STS) & STS_RS); - for (; lp->rx_ring[lp->rx_new].status>=0;) { - lp->rx_ring[lp->rx_new].status = R_OWN; + for (; (s32)le32_to_cpu(lp->rx_ring[lp->rx_new].status)>=0;) { + lp->rx_ring[lp->rx_new].status = cpu_to_le32(R_OWN); lp->rx_new = (++lp->rx_new % lp->rxRingSize); } @@ -1452,7 +1711,7 @@ dev->start = 0; dev->tbusy = 1; - if (de4x5_debug > 1) { + if (de4x5_debug & DEBUG_CLOSE) { printk("%s: Shutting down ethercard, status was %8.8x.\n", dev->name, inl(DE4X5_STS)); } @@ -1474,15 +1733,12 @@ MOD_DEC_USE_COUNT; /* Put the adapter to sleep to save power */ - if (lp->chipset == DC21041) { - outl(0, DE4X5_SICR); - outl(CFDA_PSM, PCI_CFDA); - } + yawn(dev, SLEEP); return 0; } -static struct enet_statistics * +static struct net_device_stats * de4x5_get_stats(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; @@ -1529,12 +1785,12 @@ { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - lp->tx_ring[lp->tx_new].buf = virt_to_bus(buf); - lp->tx_ring[lp->tx_new].des1 &= TD_TER; - lp->tx_ring[lp->tx_new].des1 |= flags; + lp->tx_ring[lp->tx_new].buf = cpu_to_le32(virt_to_bus(buf)); + lp->tx_ring[lp->tx_new].des1 &= cpu_to_le32(TD_TER); + lp->tx_ring[lp->tx_new].des1 |= cpu_to_le32(flags); lp->tx_skb[lp->tx_new] = skb; barrier(); - lp->tx_ring[lp->tx_new].status = T_OWN; + lp->tx_ring[lp->tx_new].status = cpu_to_le32(T_OWN); barrier(); return; @@ -1548,7 +1804,7 @@ { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - + /* First, double check that the adapter is open */ if (lp->state == OPEN) { if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ @@ -1586,7 +1842,7 @@ u32 omr, crc, poly = CRC_POLYNOMIAL_LE; char *pa; unsigned char *addrs; - + omr = inl(DE4X5_OMR); omr &= ~(OMR_PR | OMR_PM); pa = build_setup_frame(dev, ALL); /* Build the basic frame */ @@ -1639,15 +1895,15 @@ static void eisa_probe(struct device *dev, u_long ioaddr) { - int i, maxSlots, status; - u_short vendor, device; - s32 cfid; + int i, maxSlots, status, device; + u_short vendor; + u32 cfid; u_long iobase; struct bus_type *lp = &bus; char name[DE4X5_STRLEN]; struct device *tmp; - if (!ioaddr && autoprobed) return; /* Been here before ! */ + if (autoprobed) return; /* Been here before ! */ lp->bus = EISA; @@ -1661,17 +1917,18 @@ maxSlots = i + 1; } - for (status = -ENODEV; (i> 16); + cfid = (u32) inl(PCI_CFID); + cfrv = (u_short) inl(PCI_CFRV); + device = (cfid >> 8) & 0x00ffff00; vendor = (u_short) cfid; /* Read the EISA Configuration Registers */ dev->irq = inb(EISA_REG0); dev->irq = de4x5_irq[(dev->irq >> 1) & 0x03]; + if (is_DC2114x) device |= (cfrv & 0x00f0); lp->chipset = device; DevicePresent(DE4X5_APROM); /* Write the PCI Configuration Registers */ @@ -1683,6 +1940,9 @@ if ((tmp = alloc_device(dev, iobase)) != NULL) { if ((status = de4x5_hw_init(tmp, iobase)) == 0) { num_de4x5s++; + if (loading_module) link_modules(dev, tmp); + } else if (loading_module && (tmp != dev)) { + kfree(tmp); } } } else if (autoprobed) { @@ -1714,13 +1974,13 @@ { u_char irq; u_char pb, pbus, dev_num, dnum, dev_fn; - u_short vendor, device, index, status; + u_short dev_id, vendor, index, status; u_int class = DE4X5_CLASS_CODE; - u_int iobase; + u_int device, iobase; struct bus_type *lp = &bus; struct device *tmp; - if ((!ioaddr || !loading_module) && autoprobed) return; + if (autoprobed) return; if (!pcibios_present()) return; /* No PCI bus in this machine! */ @@ -1738,21 +1998,32 @@ (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND); index++) { dev_num = PCI_SLOT(dev_fn); - + if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) { + device = 0; pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor); - pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &device); - if (!(is_DC21040 || is_DC21041 || is_DC21140)) continue; + pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id); + device = dev_id; + device <<= 8; + if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) { + continue; + } + + /* Get the chip configuration revision register */ + pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv); /* Set the device number information */ lp->device = dev_num; lp->bus_num = pb; /* Set the chipset information */ + if (is_DC2114x) device |= (cfrv & 0x00f0); lp->chipset = device; - - /* Get the chip configuration revision register */ - pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv); + + if (is_DC21142 || is_DC21143) { + printk("de4x5: Detected a %s chip. Currently this is unsupported in this driver.\nPlease email the author to request its inclusion!\n", (is_DC21142?"DC21142":"DC21143")); + continue; + } /* Get the board I/O address */ pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase); @@ -1778,6 +2049,9 @@ tmp->irq = irq; if ((status = de4x5_hw_init(tmp, iobase)) == 0) { num_de4x5s++; + if (loading_module) link_modules(dev, tmp); + } else if (loading_module && (tmp != dev)) { + kfree(tmp); } } } else if (autoprobed) { @@ -1802,9 +2076,17 @@ struct device *adev = NULL; int fixed = 0, new_dev = 0; + if (!dev) return dev; num_eth = de4x5_dev_index(dev->name); - if (loading_module) return dev; - + + if (loading_module) { + if (dev->priv) { + dev = insert_device(dev, iobase, de4x5_probe); + } + num_eth++; + return dev; + } + while (1) { if (((dev->base_addr == DE4X5_NDA) || (dev->base_addr==0)) && !adev) { adev=dev; @@ -1851,20 +2133,22 @@ printk("eth%d: Device not initialised, insufficient memory\n",num_eth); return NULL; } else { - new->next = dev->next; - dev->next = new; - dev = dev->next; /* point to the new device */ - dev->name = (char *)(dev + 1); - if (num_eth > 9999) { - sprintf(dev->name,"eth????");/* New device name */ - } else { - sprintf(dev->name,"eth%d", num_eth);/* New device name */ + memset((char *)new, 0, sizeof(struct device)+8); + new->name = (char *)(new + 1); + new->base_addr = iobase; /* assign the io address */ + new->init = init; /* initialisation routine */ + if (!loading_module) { + new->next = dev->next; + dev->next = new; + if (num_eth > 9999) { + sprintf(new->name,"eth????");/* New device name */ + } else { + sprintf(new->name,"eth%d", num_eth);/* New device name */ + } } - dev->base_addr = iobase; /* assign the io address */ - dev->init = init; /* initialisation routine */ } - return dev; + return new; } static int @@ -1882,6 +2166,26 @@ return i; } +static void +link_modules(struct device *dev, struct device *tmp) +{ + struct device *p=dev; + + if (p) { + while (((struct de4x5_private *)(p->priv))->next_module) { + p = ((struct de4x5_private *)(p->priv))->next_module; + } + + if (dev != tmp) { + ((struct de4x5_private *)(p->priv))->next_module = tmp; + } else { + ((struct de4x5_private *)(p->priv))->next_module = NULL; + } + } + + return; +} + /* ** Auto configure the media here rather than setting the port at compile ** time. This routine is called by de4x5_init() and when a loss of media is @@ -1901,13 +2205,16 @@ disable_ast(dev); inl(DE4X5_MFC); /* Zero the lost frames counter */ lp->media = INIT; - if (lp->chipset == DC21040) { + if (lp->useSROM) { + next_tick = srom_autoconf(dev); + } else if (lp->chipset == DC21040) { next_tick = dc21040_autoconf(dev); } else if (lp->chipset == DC21041) { next_tick = dc21041_autoconf(dev); } else if (lp->chipset == DC21140) { next_tick = dc21140m_autoconf(dev); } + enable_ast(dev, next_tick); return (lp->media); @@ -2046,7 +2353,7 @@ switch (lp->local_state) { case 1: - if (lp->linkOK && !LOST_MEDIA) { + if (lp->linkOK) { lp->media = prev_state; } else { lp->local_state++; @@ -2063,6 +2370,7 @@ lp->media = prev_state; } else { lp->media = INIT; + lp->tcount++; } } @@ -2110,7 +2418,7 @@ case TP_NW: if (lp->timeout < 0) { omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */ - outl(omr | OMR_FD, DE4X5_OMR); + outl(omr | OMR_FDX, DE4X5_OMR); } irqs = STS_LNF | STS_LNP; irq_mask = IMR_LFM | IMR_LPM; @@ -2157,7 +2465,7 @@ if (!lp->tx_enable) { if (lp->timeout < 0) { omr = inl(DE4X5_OMR); /* Set up half duplex for TP */ - outl(omr & ~OMR_FD, DE4X5_OMR); + outl(omr & ~OMR_FDX, DE4X5_OMR); } irqs = STS_LNF | STS_LNP; irq_mask = IMR_LFM | IMR_LPM; @@ -2191,7 +2499,7 @@ if (!lp->tx_enable) { if (lp->timeout < 0) { omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ - outl(omr & ~OMR_FD, DE4X5_OMR); + outl(omr & ~OMR_FDX, DE4X5_OMR); } irqs = 0; irq_mask = 0; @@ -2222,7 +2530,7 @@ case 0: if (lp->timeout < 0) { omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ - outl(omr & ~OMR_FD, DE4X5_OMR); + outl(omr & ~OMR_FDX, DE4X5_OMR); } irqs = 0; irq_mask = 0; @@ -2261,7 +2569,7 @@ case NC: omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */ - outl(omr | OMR_FD, DE4X5_OMR); + outl(omr | OMR_FDX, DE4X5_OMR); reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */ if (lp->media != lp->c_media) { de4x5_dbg_media(dev); @@ -2287,34 +2595,45 @@ int ana, anlpa, cap, cr, slnk, sr, iobase = dev->base_addr; int next_tick = DE4X5_AUTOSENSE_MS; u_long imr, omr; - + switch(lp->media) { case INIT: - DISABLE_IRQs; - lp->tx_enable = FALSE; - lp->timeout = -1; + if (lp->timeout < 0) { + DISABLE_IRQs; + lp->tx_enable = FALSE; + lp->linkOK = 0; + de4x5_save_skbs(dev); /* Save non transmitted skb's */ + } if ((next_tick = de4x5_reset_phy(dev)) < 0) { next_tick &= ~TIMER_CB; } else { - de4x5_save_skbs(dev); /* Save non transmitted skb's */ - lp->tmp = MII_SR_ASSC; /* Fake out the MII speed set */ - SET_10Mb; - if (lp->autosense == _100Mb) { - lp->media = _100Mb; - } else if (lp->autosense == _10Mb) { - lp->media = _10Mb; - } else if ((lp->autosense == AUTO) && - ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { - ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); - ana &= (de4x5_full_duplex ? ~0 : ~MII_ANA_FDAM); - mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); - lp->media = ANS; - } else if (lp->autosense == AUTO) { - lp->media = SPD_DET; - } else if (is_spd_100(dev) && is_100_up(dev)) { - lp->media = _100Mb; + if (lp->useSROM) { + srom_map_media(dev); + srom_exec(dev, lp->phy[lp->active].gep); + if (lp->infoblock_media == ANS) { + ana = lp->phy[lp->active].ana | MII_ANA_CSMA; + mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + } } else { - lp->media = NC; + lp->tmp = MII_SR_ASSC; /* Fake out the MII speed set */ + SET_10Mb; + if (lp->autosense == _100Mb) { + lp->media = _100Mb; + } else if (lp->autosense == _10Mb) { + lp->media = _10Mb; + } else if ((lp->autosense == AUTO) && + ((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; + } else if (lp->autosense == AUTO) { + lp->media = SPD_DET; + } else if (is_spd_100(dev) && is_100_up(dev)) { + lp->media = _100Mb; + } else { + lp->media = NC; + } } lp->local_state = 0; next_tick = dc21140m_autoconf(dev); @@ -2354,10 +2673,10 @@ if (!(anlpa & MII_ANLPA_RF) && (cap = anlpa & MII_ANLPA_TAF & ana)) { if (cap & MII_ANA_100M) { - de4x5_full_duplex = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE); + lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE); lp->media = _100Mb; } else if (cap & MII_ANA_10M) { - de4x5_full_duplex = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE); + lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE); lp->media = _10Mb; } @@ -2390,7 +2709,7 @@ break; case _100Mb: /* Set 100Mb/s */ - next_tick = 3000; + next_tick = 3000; if (!lp->tx_enable) { SET_100Mb; de4x5_init_connection(dev); @@ -2398,6 +2717,7 @@ if (!lp->linkOK && (lp->autosense == AUTO)) { if (!(is_spd_100(dev) && is_100_up(dev))) { lp->media = INIT; + lp->tcount++; next_tick = DE4X5_AUTOSENSE_MS; } } @@ -2405,7 +2725,7 @@ break; case _10Mb: /* Set 10Mb/s */ - next_tick = 3000; + next_tick = 3000; if (!lp->tx_enable) { SET_10Mb; de4x5_init_connection(dev); @@ -2413,6 +2733,7 @@ if (!lp->linkOK && (lp->autosense == AUTO)) { if (!(!is_spd_100(dev) && is_10_up(dev))) { lp->media = INIT; + lp->tcount++; next_tick = DE4X5_AUTOSENSE_MS; } } @@ -2432,6 +2753,72 @@ return next_tick; } +static int +srom_autoconf(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + return lp->infoleaf_fn(dev); +} + +/* +** This mapping keeps the original media codes and FDX flag unchanged. +** While it isn't strictly necessary, it helps me for the moment... +*/ +static void +srom_map_media(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + lp->fdx = 0; + switch(lp->infoblock_media) { + case SROM_10BASETF: + lp->fdx = TRUE; + case SROM_10BASET: + if (lp->chipset == DC21140) { + lp->media = _10Mb; + } else { + lp->media = TP; + } + break; + + case SROM_10BASE2: + lp->media = BNC; + break; + + case SROM_10BASE5: + lp->media = AUI; + break; + + case SROM_100BASETF: + lp->fdx = TRUE; + case SROM_100BASET: + lp->media = _100Mb; + break; + + case SROM_100BASET4: + lp->media = _100Mb; + break; + + case SROM_100BASEFF: + lp->fdx = TRUE; + case SROM_100BASEF: + lp->media = _100Mb; + break; + + case ANS: + lp->media = ANS; + break; + + default: + printk("%s: Bad media code [%d] detected in SROM!\n", dev->name, + lp->infoblock_media); + break; + } + + return; +} + static void de4x5_init_connection(struct device *dev) { @@ -2442,11 +2829,10 @@ de4x5_dbg_media(dev); lp->c_media = lp->media; /* Stop scrolling media messages */ } - de4x5_restore_skbs(dev); + cli(); - de4x5_rx(dev); + de4x5_restore_skbs(dev); de4x5_setup_intr(dev); - lp->lostMedia = 0; lp->tx_enable = YES; dev->tbusy = 0; sti(); @@ -2456,6 +2842,11 @@ return; } +/* +** General PHY reset function. Some MII devices don't reset correctly +** since their MII address pins can float at voltages that are dependent +** on the signal pin use. Do a double reset to ensure a reset. +*/ static int de4x5_reset_phy(struct device *dev) { @@ -2463,17 +2854,30 @@ u_long iobase = dev->base_addr; int next_tick = 0; - if (lp->phy[lp->active].id) { + if ((lp->useSROM) || (lp->phy[lp->active].id)) { if (lp->timeout < 0) { - outl(GEP_HRST, DE4X5_GEP); /* Hard RESET the PHY dev. */ - udelay(1000); /* Assert for 1ms */ - outl(0x00, DE4X5_GEP); - udelay(2000); /* Wait for 2ms */ - mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); + if (lp->useSROM) { + if (lp->phy[lp->active].rst) { /* MII device specific reset */ + srom_exec(dev, lp->phy[lp->active].rst); + srom_exec(dev, lp->phy[lp->active].rst); + } else if (lp->rst) { /* Type 5 infoblock reset */ + srom_exec(dev, lp->rst); + srom_exec(dev, lp->rst); + } + } else { + PHY_HARD_RESET; + } + if (lp->useMII) { + mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); + } + } + if (lp->useMII) { + next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500); } - next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500); + } else if (lp->chipset == DC21140) { + PHY_HARD_RESET; } - + return next_tick; } @@ -2546,7 +2950,7 @@ lp->timeout = msec/100; } - if (lp->phy[lp->active].id) { + if (lp->phy[lp->active].id || lp->useSROM) { gep = ((is_100_up(dev) && is_spd_100(dev)) ? GEP_SLNK : 0); } else { gep = (~inl(DE4X5_GEP) & (GEP_SLNK | GEP_LNP)); @@ -2594,7 +2998,11 @@ u_long iobase = dev->base_addr; int spd; - if (lp->phy[lp->active].id) { + if (lp->useSROM && !lp->useMII) { + spd = (lp->asBitValid & + (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) | + (lp->linkOK & ~lp->asBitValid); + } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII); spd = ~(spd ^ lp->phy[lp->active].spd.value); spd &= lp->phy[lp->active].spd.mask; @@ -2611,7 +3019,11 @@ struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - if (lp->phy[lp->active].id) { + if (lp->useSROM && !lp->useMII) { + return ((lp->asBitValid & + (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) | + (lp->linkOK & ~lp->asBitValid)); + } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { /* Double read for sticky bits & temporary drops */ mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); @@ -2626,7 +3038,11 @@ struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - if (lp->phy[lp->active].id) { + if (lp->useSROM && !lp->useMII) { + return ((lp->asBitValid & + (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) | + (lp->linkOK & ~lp->asBitValid)); + } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { /* Double read for sticky bits & temporary drops */ mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); @@ -2641,7 +3057,7 @@ struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - if (lp->phy[lp->active].id) { + if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII)); } else { return 0; @@ -2670,11 +3086,14 @@ sisr = inl(DE4X5_SISR); - if ((!(sisr & SISR_NCR)) && (lp->tx_ring[lp->tmp].status < 0) && (--lp->timeout)) { + if ((!(sisr & SISR_NCR)) && + ((s32)le32_to_cpu(lp->tx_ring[lp->tmp].status) < 0) && + (--lp->timeout)) { sisr = 100 | TIMER_CB; } else { if ((!(sisr & SISR_NCR)) && - !(lp->tx_ring[lp->tmp].status & (T_OWN | TD_ES)) && lp->timeout) { + !(le32_to_cpu(lp->tx_ring[lp->tmp].status) & (T_OWN | TD_ES)) && + lp->timeout) { sisr = 0; } else { sisr = 1; @@ -2696,7 +3115,7 @@ struct de4x5_private *lp = (struct de4x5_private *)dev->priv; struct sk_buff *p; -#if !defined(__alpha__) && !defined(DE4X5_DO_MEMCPY) +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY) struct sk_buff *ret; u_long i=0, tmp; @@ -2728,10 +3147,13 @@ skb_reserve(p, 2); /* Align */ if (index < lp->rx_old) { /* Wrapped buffer */ short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ; - memcpy(skb_put(p,tlen), bus_to_virt(lp->rx_ring[lp->rx_old].buf),tlen); - memcpy(skb_put(p,len-tlen), bus_to_virt(lp->rx_ring[0].buf), len-tlen); + memcpy(skb_put(p,tlen), + bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)),tlen); + memcpy(skb_put(p,len-tlen), + bus_to_virt(le32_to_cpu(lp->rx_ring[0].buf)), len-tlen); } else { /* Linear buffer */ - memcpy(skb_put(p,len), bus_to_virt(lp->rx_ring[lp->rx_old].buf),len); + memcpy(skb_put(p,len), + bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)),len); } return p; @@ -2810,13 +3232,26 @@ { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; + int i; s32 omr; if (lp->cache.save_cnt) { STOP_DE4X5; - de4x5_cache_state(dev, DE4X5_SAVE_STATE); - de4x5_sw_reset(dev); - de4x5_cache_state(dev, DE4X5_RESTORE_STATE); + outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA); + outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA); + + lp->rx_new = lp->rx_old = 0; + lp->tx_new = lp->tx_old = 0; + + for (i = 0; i < lp->rxRingSize; i++) { + lp->rx_ring[i].status = cpu_to_le32(R_OWN); + } + + for (i = 0; i < lp->txRingSize; i++) { + lp->tx_ring[i].status = cpu_to_le32(0); + } + + barrier(); lp->cache.save_cnt--; START_DE4X5; } @@ -2829,7 +3264,6 @@ { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - s32 gep; switch(flag) { case DE4X5_SAVE_STATE: @@ -2848,12 +3282,8 @@ outl(lp->cache.csr6, DE4X5_OMR); outl(lp->cache.csr7, DE4X5_IMR); if (lp->chipset == DC21140) { - outl(GEP_INIT, DE4X5_GEP); - gep = (lp->media == _100Mb ? GEP_MODE : 0); - if (!lp->phy[lp->active].id && !de4x5_full_duplex) { - gep |= GEP_FDXD; - } - outl(gep, DE4X5_GEP); + outl(lp->cache.gepc, DE4X5_GEP); + outl(lp->cache.gep, DE4X5_GEP); } else { reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, lp->cache.csr15); @@ -2975,7 +3405,7 @@ } /* -** Create a loopback ethernet packet with an invalid CRC +** Create a loopback ethernet packet */ static void create_packet(struct device *dev, char *frame, int len) @@ -3069,13 +3499,10 @@ if (lp->chipset == DC21040) { strcpy(name, "DE434/5"); - } else { + return status; + } else { /* Search for a DEC name in the SROM */ int i = *((char *)&lp->srom + 19) * 3; - if (lp->chipset == DC21041) { - strncpy(name, (char *)&lp->srom + 26 + i, 8); - } else if (lp->chipset == DC21140) { - strncpy(name, (char *)&lp->srom + 26 + i, 8); - } + strncpy(name, (char *)&lp->srom + 26 + i, 8); } name[8] = '\0'; for (i=0; ichipset == DC21040) ? "DC21040" : ((lp->chipset == DC21041) ? "DC21041" : - ((lp->chipset == DC21140) ? "DC21140" : "UNKNOWN" - ))))); + ((lp->chipset == DC21140) ? "DC21140" : + ((lp->chipset == DC21142) ? "DC21142" : + ((lp->chipset == DC21143) ? "DC21143" : "UNKNOWN" + ))))))); + } + if (lp->chipset != DC21041) { + useSROM = TRUE; /* card is not recognisably DEC */ } } @@ -3108,9 +3540,10 @@ if (lp->chipset == DC21040) { outl(0, aprom_addr); /* Reset Ethernet Address ROM Pointer */ } else { /* Read new srom */ - short *p = (short *)&lp->srom; + u_short tmp, *p = (short *)&lp->srom; for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) { - *p++ = srom_rd(aprom_addr, i); + tmp = srom_rd(aprom_addr, i); + *p++ = le16_to_cpu(tmp); } de4x5_dbg_srom((struct de4x5_srom *)&lp->srom); } @@ -3118,6 +3551,12 @@ return; } +/* +** For the bad status case and no SROM, then add one to the previous +** address. However, need to add one backwards in case we have 0xff +** as one or more of the bytes. Only the last 3 bytes should be checked +** as the first three are invariant - assigned to an organisation. +*/ static int get_hw_addr(struct device *dev) { @@ -3171,6 +3610,12 @@ if ((k != chksum) && (dec_only)) status = -1; } + /* If possible, try to fix a broken card - SMC only so far */ + srom_repair(dev, broken); + + /* Test for a bad enet address */ + status = test_bad_enet(dev, status); + return status; } @@ -3210,6 +3655,55 @@ return ret; } +static void +srom_repair(struct device *dev, int card) +{ + struct bus_type *lp = &bus; + + switch(card) { + case SMC: + memset((char *)&bus.srom, 0, sizeof(struct de4x5_srom)); + memcpy(lp->srom.ieee_addr, (char *)dev->dev_addr, ETH_ALEN); + memcpy(lp->srom.info, (char *)&srom_repair_info[SMC-1], 100); + useSROM = TRUE; + break; + } + + return; +} + +static int +test_bad_enet(struct device *dev, int status) +{ + struct bus_type *lp = &bus; + int i, tmp; + + for (tmp=0,i=0; idev_addr[i]; + if ((tmp == 0) || (tmp == 0x5fa)) { + if ((lp->chipset == last.chipset) && + (lp->bus_num == last.bus) && (lp->bus_num > 0)) { + for (i=0; idev_addr[i] = last.addr[i]; + for (i=ETH_ALEN-1; i>2; --i) { + dev->dev_addr[i] += 1; + if (dev->dev_addr[i] != 0) break; + } + for (i=0; idev_addr[i]; + if (((*((int *)dev->dev_addr) & 0x00ffffff) == 0x95c000) && + (lp->chipset == DC21040)) { + dev->irq = last.irq; + } + status = 0; + } + } else if (!status) { + last.chipset = lp->chipset; + last.bus = lp->bus_num; + last.irq = dev->irq; + for (i=0; idev_addr[i]; + } + + return status; +} + /* ** SROM Read */ @@ -3321,16 +3815,401 @@ return tmp; } -/* -** MII Read/Write -*/ - static int -mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr) +srom_infoleaf_info(struct device *dev) { - mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */ - mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */ - mii_wdata(MII_STRD, 4, ioaddr); /* SFD and Read operation */ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int i, count; + u_char *p; + + /* Find the infoleaf decoder function that matches this chipset */ + for (i=0; ichipset == infoleaf_array[i].chipset) break; + } + if (i == INFOLEAF_SIZE) { + lp->useSROM = FALSE; + printk("%s: Cannot find correct chipset for SROM decoding!\n", + dev->name); + return -ENXIO; + } + + lp->infoleaf_fn = infoleaf_array[i].fn; + + /* Find the information offset that this function should use */ + count = *((u_char *)&lp->srom + 19); + p = (u_char *)&lp->srom + 26; + + if (count > 1) { + for (i=count; i; --i, p+=3) { + if (lp->device == *p) break; + } + if (i == 0) { + lp->useSROM = FALSE; + printk("%s: Cannot find correct PCI device [%d] for SROM decoding!\n", + dev->name, lp->device); + return -ENXIO; + } + } + + lp->infoleaf_offset = TWIDDLE(p+1); + + return 0; +} + +/* +** This routine loads any type 1 or 3 MII info into the mii device +** struct and executes any type 5 code to reset PHY devices for this +** controller. +** The info for the MII devices will be valid since the index used +** will follow the discovery process from MII address 1-31 then 0. +*/ +static void +srom_init(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + u_char count; + + p+=2; + if (lp->chipset == DC21140) { + lp->cache.gepc = (*p++ | GEP_CTRL); + outl(lp->cache.gepc, DE4X5_GEP); + } + + /* Block count */ + count = *p++; + + /* Jump the infoblocks to find types */ + for (;count; --count) { + if (*p < 128) { + p += COMPACT_LEN; + } else if (*(p+1) == 5) { + type5_infoblock(dev, 1, p); + p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 3) { + type3_infoblock(dev, 1, p); + p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 1) { + type1_infoblock(dev, 1, p); + p += ((*p & BLOCK_LEN) + 1); + } else { + p += ((*p & BLOCK_LEN) + 1); + } + } + + return; +} + +static void +srom_exec(struct device *dev, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + u_char count = (p ? *p++ : 0); + + while (count--) { + if (lp->chipset == DC21140) { + outl(*p++, DE4X5_GEP); + } + udelay(2000); /* 2ms per action */ + } + + return; +} + +/* +** Basically this function is a NOP since it will never be called, +** unless I implement the DC21041 SROM functions. There's no need +** since the existing code will be satisfactory for all boards. +*/ +static int +dc21041_infoleaf(struct device *dev) +{ + return DE4X5_AUTOSENSE_MS; +} + +static int +dc21140_infoleaf(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char count = 0; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + int next_tick = DE4X5_AUTOSENSE_MS; + + /* Read the connection type */ + p+=2; + + /* GEP control */ + lp->cache.gepc = (*p++ | GEP_CTRL); + + /* Block count */ + count = *p++; + + /* Recursively figure out the info blocks */ + if (*p < 128) { + next_tick = dc_infoblock[COMPACT](dev, count, p); + } else { + next_tick = dc_infoblock[*(p+1)](dev, count, p); + } + + if (lp->tcount == count) { + lp->media = NC; + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tcount = 0; + lp->tx_enable = FALSE; + } + + return next_tick & ~TIMER_CB; +} + +static int +dc21142_infoleaf(struct device *dev) +{ +printk("dc21142_infoleaf()\n"); + return DE4X5_AUTOSENSE_MS; +} + +static int +dc21143_infoleaf(struct device *dev) +{ +printk("dc21143_infoleaf()\n"); + return DE4X5_AUTOSENSE_MS; +} + +/* +** The compact infoblock is only designed for DC21140[A] chips, so +** we'll reuse the dc21140m_autoconf function. Non MII media only. +*/ +static int +compact_infoblock(struct device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + u_char flags, csr6; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+COMPACT_LEN) < 128) { + return dc_infoblock[COMPACT](dev, count, p+COMPACT_LEN); + } else { + return dc_infoblock[*(p+COMPACT_LEN+1)](dev, count, p+COMPACT_LEN); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + outl(lp->cache.gepc, DE4X5_GEP); + lp->infoblock_media = (*p++) & COMPACT_MC; + lp->cache.gep = *p++; + csr6 = *p++; + flags = *p++; + + lp->asBitValid = (flags & 0x80) ? 0 : -1; + lp->defMedium = (flags & 0x40) ? -1 : 0; + lp->asBit = 1 << ((csr6 >> 1) & 0x07); + lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; + lp->infoblock_csr6 = (csr6 & 0x71) << 18; + lp->useMII = FALSE; + + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +/* +** This block describes non MII media for the DC21140[A] only. + */ +static int +type0_infoblock(struct device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + u_char flags, csr6, len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + outl(lp->cache.gepc, DE4X5_GEP); + p+=2; + lp->infoblock_media = (*p++) & BLOCK0_MC; + lp->cache.gep = *p++; + csr6 = *p++; + flags = *p++; + + lp->asBitValid = (flags & 0x80) ? 0 : -1; + lp->defMedium = (flags & 0x40) ? -1 : 0; + lp->asBit = 1 << ((csr6 >> 1) & 0x07); + lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; + lp->infoblock_csr6 = (csr6 & 0x71) << 18; + lp->useMII = FALSE; + + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +/* These functions are under construction! */ + +static int +type1_infoblock(struct device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + 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); + lp->phy[lp->active].mc = TWIDDLE(p); p += 2; + lp->phy[lp->active].ana = TWIDDLE(p); p += 2; + lp->phy[lp->active].fdx = TWIDDLE(p); p += 2; + lp->phy[lp->active].ttm = TWIDDLE(p); + return 0; + } else if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 1; + lp->active = *p; + lp->infoblock_csr6 = OMR_PS | OMR_HBD; + lp->useMII = TRUE; + lp->infoblock_media = ANS; + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +static int +type2_infoblock(struct device *dev, u_char count, u_char *p) +{ + return DE4X5_AUTOSENSE_MS; +} + +static int +type3_infoblock(struct device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char flags, csr6, len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if (lp->state == INITIALISED) { + lp->ibn = 3; p += 2; + 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); + lp->phy[lp->active].mc = TWIDDLE(p); p += 2; + lp->phy[lp->active].ana = TWIDDLE(p); p += 2; + lp->phy[lp->active].fdx = TWIDDLE(p); p += 2; + lp->phy[lp->active].ttm = TWIDDLE(p); + return 0; + } else if (lp->media == INIT) { + p+=2; + lp->infoblock_media = (*p++) & BLOCK0_MC; + lp->cache.gep = *p++; + csr6 = *p++; + flags = *p++; + + lp->asBitValid = (flags & 0x80) ? 0 : -1; + lp->defMedium = (flags & 0x40) ? -1 : 0; + lp->asBit = 1 << ((csr6 >> 1) & 0x07); + lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; + lp->infoblock_csr6 = (csr6 & 0x71) << 18; + lp->useMII = TRUE; + + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +static int +type4_infoblock(struct device *dev, u_char count, u_char *p) +{ + return DE4X5_AUTOSENSE_MS; +} + +/* +** This block type provides information for resetting external devices +** (chips) through the General Purpose Register. +*/ +static int +type5_infoblock(struct device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + u_char i, j, len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + /* Must be initializing to run this code */ + if ((lp->state == INITIALISED) || (lp->media == INIT)) { + p+=2; + lp->rst = p; + i = *p++; + for (j=0;i;--i) { + if (lp->chipset == DC21140) { + if (!j) { + outl(*p++ | GEP_CTRL, DE4X5_GEP); + j++; + } + outl(*p++, DE4X5_GEP); + } else if (lp->chipset == DC21142) { + } else if (lp->chipset == DC21143) { + } + } + + } + + return DE4X5_AUTOSENSE_MS; +} + +/* +** MII Read/Write +*/ + +static int +mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr) +{ + mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */ + mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */ + mii_wdata(MII_STRD, 4, ioaddr); /* SFD and Read operation */ mii_address(phyaddr, ioaddr); /* PHY address to be accessed */ mii_address(phyreg, ioaddr); /* PHY Register to read */ mii_ta(MII_STRD, ioaddr); /* Turn around time - 2 MDC */ @@ -3494,23 +4373,25 @@ return r2; /* (I did it) My way */ } +/* +** The SROM spec forces us to search addresses [1-31 0]. Bummer. +*/ static int mii_get_phy(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; int iobase = dev->base_addr; - int i, j, k, limit=sizeof(phy_info)/sizeof(struct phy_table); + int i, j, k, n, limit=sizeof(phy_info)/sizeof(struct phy_table); int id; - /* Issue a hard PHY reset - Broadcom is screwed up otherwise */ - outl(GEP_HRST, DE4X5_GEP); - udelay(1000); /* Assert for 1ms */ - outl(0x00, DE4X5_GEP); - udelay(2000); /* Wait for 2ms */ - - /* Search the MII address space for possible PHY devices */ lp->active = 0; - for (lp->mii_cnt=0, i=1; iuseMII = TRUE; + + /* Search the MII address space for possible PHY devices */ + for (n=0, lp->mii_cnt=0, i=1; !((i==1) && (n==1)); i=(++i)%DE4X5_MAX_MII) { + lp->phy[lp->active].addr = i; + if (i==0) n++; /* Count cycles */ + while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */ id = mii_get_oui(i, DE4X5_MII); if ((id == 0) || (id == -1)) continue; /* Valid ID? */ for (j=0; jphy[k].addr = i; lp->mii_cnt++; + lp->active++; } else { i = DE4X5_MAX_MII; /* Stop the search */ j = limit; } } } - if (lp->phy[lp->active].id) { /* Reset the PHY devices */ + lp->active = 0; + if (lp->phy[0].id) { /* Reset the PHY devices */ for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY*/ mii_wr(MII_CR_RST, MII_CR, lp->phy[k].addr, DE4X5_MII); while (mii_rd(MII_CR, lp->phy[k].addr, DE4X5_MII) & MII_CR_RST); @@ -3590,15 +4473,17 @@ } static long -de4x5_switch_to_mii(struct device *dev) +de4x5_switch_mac_port(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; int iobase = dev->base_addr; - long omr; - + s32 omr; + /* Assert the OMR_PS bit in CSR6 */ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR)); - omr |= (OMR_PS | OMR_HBD); + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | + OMR_FDX)); + omr |= lp->infoblock_csr6; + if (omr & OMR_PS) omr |= OMR_HBD; outl(omr, DE4X5_OMR); /* Soft Reset */ @@ -3606,42 +4491,13 @@ /* Restore the GEP */ if (lp->chipset == DC21140) { - outl(GEP_INIT, DE4X5_GEP); - outl(0, DE4X5_GEP); + outl(lp->cache.gepc, DE4X5_GEP); + outl(lp->cache.gep, DE4X5_GEP); } - - /* Restore CSR6 */ - outl(omr, DE4X5_OMR); - /* Reset CSR8 */ - inl(DE4X5_MFC); - - return omr; -} - -static long -de4x5_switch_to_srl(struct device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; - long omr; - - /* Deassert the OMR_PS bit in CSR6 */ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR)); - outl(omr, DE4X5_OMR); - - /* Soft Reset */ - RESET_DE4X5; - - /* Restore the GEP */ - if (lp->chipset == DC21140) { - outl(GEP_INIT, DE4X5_GEP); - outl(0, DE4X5_GEP); - } - /* Restore CSR6 */ outl(omr, DE4X5_OMR); - + /* Reset CSR8 */ inl(DE4X5_MFC); @@ -3671,12 +4527,60 @@ } static void +yawn(struct device *dev, int state) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int iobase = dev->base_addr; + + if ((lp->chipset == DC21040) || (lp->chipset == DC21140)) return; + + if(lp->bus == EISA) { + switch(state) { + case WAKEUP: + outb(WAKEUP, PCI_CFPM); + de4x5_ms_delay(10); + break; + + case SNOOZE: + outb(SNOOZE, PCI_CFPM); + break; + + case SLEEP: + outl(0, DE4X5_SICR); + outb(SLEEP, PCI_CFPM); + break; + } + } else { + switch(state) { + case WAKEUP: + pcibios_write_config_byte(lp->bus_num, lp->device << 3, + PCI_CFDA_PSM, WAKEUP); + de4x5_ms_delay(10); + break; + + case SNOOZE: + pcibios_write_config_byte(lp->bus_num, lp->device << 3, + PCI_CFDA_PSM, SNOOZE); + break; + + case SLEEP: + outl(0, DE4X5_SICR); + pcibios_write_config_byte(lp->bus_num, lp->device << 3, + PCI_CFDA_PSM, SLEEP); + break; + } + } + + return; +} + +static void de4x5_dbg_open(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; int i; - if (de4x5_debug > 1) { + if (de4x5_debug & DEBUG_OPEN) { printk("%s: de4x5 opening with irq %d\n",dev->name,dev->irq); printk("\tphysical address: "); for (i=0;i<6;i++) { @@ -3702,17 +4606,17 @@ printk("Descriptor buffers:\nRX: "); for (i=0;irxRingSize-1;i++){ if (i < 3) { - printk("0x%8.8x ",lp->rx_ring[i].buf); + printk("0x%8.8x ",le32_to_cpu(lp->rx_ring[i].buf)); } } - printk("...0x%8.8x\n",lp->rx_ring[i].buf); + printk("...0x%8.8x\n",le32_to_cpu(lp->rx_ring[i].buf)); printk("TX: "); for (i=0;itxRingSize-1;i++){ if (i < 3) { - printk("0x%8.8x ", lp->tx_ring[i].buf); + printk("0x%8.8x ", le32_to_cpu(lp->tx_ring[i].buf)); } } - printk("...0x%8.8x\n", lp->tx_ring[i].buf); + printk("...0x%8.8x\n", le32_to_cpu(lp->tx_ring[i].buf)); printk("Ring size: \nRX: %d\nTX: %d\n", (short)lp->rxRingSize, (short)lp->txRingSize); @@ -3727,7 +4631,7 @@ struct de4x5_private *lp = (struct de4x5_private *)dev->priv; int iobase = dev->base_addr; - if (de4x5_debug > 2) { + if (de4x5_debug & DEBUG_MII) { printk("\nMII CR: %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII)); printk("MII SR: %x\n",mii_rd(MII_SR,lp->phy[k].addr,DE4X5_MII)); printk("MII ID0: %x\n",mii_rd(MII_ID0,lp->phy[k].addr,DE4X5_MII)); @@ -3754,7 +4658,7 @@ struct de4x5_private *lp = (struct de4x5_private *)dev->priv; if (lp->media != lp->c_media) { - if (de4x5_debug > 0) { + if (de4x5_debug & DEBUG_MEDIA) { if (lp->chipset != DC21140) { printk("%s: media is %s\n", dev->name, (lp->media == NC ? "unconnected!" : @@ -3786,10 +4690,12 @@ { int i; - if (de4x5_debug > 1) { - printk("Sub-system Vendor ID: %04x\n", (u_short)*(p->sub_vendor_id)); - printk("Sub-system ID: %04x\n", (u_short)*(p->sub_system_id)); + if (de4x5_debug & DEBUG_SROM) { + printk("Sub-system Vendor ID: %04x\n", *((u_short *)p->sub_vendor_id)); + printk("Sub-system ID: %04x\n", *((u_short *)p->sub_system_id)); printk("ID Block CRC: %02x\n", (u_char)(p->id_block_crc)); + printk("SROM version: %02x\n", (u_char)(p->version)); + printk("# controllers: %02x\n", (u_char)(p->num_controllers)); printk("Hardware Address: "); for (i=0;i 2) { + if (de4x5_debug & DEBUG_RX) { printk("R: %02x:%02x:%02x:%02x:%02x:%02x <- %02x:%02x:%02x:%02x:%02x:%02x len/SAP:%02x%02x [%d]\n", (u_char)skb->data[0], (u_char)skb->data[1], @@ -3827,7 +4733,7 @@ (u_char)skb->data[12], (u_char)skb->data[13], len); - if (de4x5_debug > 3) { + if (de4x5_debug & DEBUG_RX) { for (j=0; len>0;j+=16, len-=16) { printk(" %03x: ",j); for (i=0; i<16 && i> 1]; - u32 lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2]; + u8 addr[144]; + u16 sval[72]; + u32 lval[36]; } tmp; switch(ioc->cmd) { @@ -3868,7 +4774,7 @@ for (i=0; idev_addr[i]; } - memcpy_tofs(ioc->data, tmp.addr, ioc->len); + copy_to_user(ioc->data, tmp.addr, ioc->len); break; case DE4X5_SET_HWADDR: /* Set the hardware address */ @@ -3879,7 +4785,7 @@ if (!suser()) break; status = 0; - memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN); + copy_from_user(tmp.addr, ioc->data, ETH_ALEN); for (i=0; idev_addr[i] = tmp.addr[i]; } @@ -3918,39 +4824,12 @@ break; case DE4X5_GET_MCA: /* Get the multicast address table */ - ioc->len = (HASH_TABLE_LEN >> 3); - status = verify_area(VERIFY_WRITE, ioc->data, ioc->len); - if (!status) { - memcpy_tofs(ioc->data, lp->setup_frame, ioc->len); - } - break; case DE4X5_SET_MCA: /* Set a multicast address */ - if (suser()) { - /******* FIX ME! ********/ - if (ioc->len != HASH_TABLE_LEN) { /* MCA changes */ - if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN * ioc->len))) { - memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len); - set_multicast_list(dev); - } - } else { - set_multicast_list(dev); - } - } else { - status = -EPERM; - } - break; case DE4X5_CLR_MCA: /* Clear all multicast addresses */ - if (suser()) { - /******* FIX ME! ********/ - set_multicast_list(dev); - } else { - status = -EPERM; - } - break; - case DE4X5_MCA_EN: /* Enable pass all multicast addressing */ + case DE4X5_MCA_EN: /* Enable pass all multicast addresses*/ if (suser()) { omr = inl(DE4X5_OMR); omr |= OMR_PM; @@ -3967,7 +4846,7 @@ break; cli(); - memcpy_tofs(ioc->data, &lp->pktStats, ioc->len); + copy_to_user(ioc->data, &lp->pktStats, ioc->len); sti(); break; @@ -3984,14 +4863,14 @@ 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))) { - memcpy_tofs(ioc->data, tmp.addr, 1); + copy_to_user(ioc->data, tmp.addr, 1); } break; case DE4X5_SET_OMR: /* Set the OMR Register contents */ if (suser()) { if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, 1))) { - memcpy_fromfs(tmp.addr, ioc->data, 1); + copy_from_user(tmp.addr, ioc->data, 1); outl(tmp.addr[0], DE4X5_OMR); } } else { @@ -4011,11 +4890,12 @@ tmp.lval[7] = inl(DE4X5_SIGR); j+=4; ioc->len = j; if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) { - memcpy_tofs(ioc->data, tmp.addr, ioc->len); + copy_to_user(ioc->data, tmp.addr, ioc->len); } break; -#define DE4X5_DUMP 0x0f /* Dump the DE4X5 Status */ +/* +#define DE4X5_DUMP 0x0f / * Dump the DE4X5 Status * / case DE4X5_DUMP: j = 0; @@ -4042,22 +4922,22 @@ for (i=0;irxRingSize-1;i++){ if (i < 3) { - tmp.lval[j>>2] = (s32)lp->rx_ring[i].buf; j+=4; + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4; } } - tmp.lval[j>>2] = (s32)lp->rx_ring[i].buf; j+=4; + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4; for (i=0;itxRingSize-1;i++){ if (i < 3) { - tmp.lval[j>>2] = (s32)lp->tx_ring[i].buf; j+=4; + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4; } } - tmp.lval[j>>2] = (s32)lp->tx_ring[i].buf; j+=4; + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4; for (i=0;irxRingSize;i++){ - tmp.lval[j>>2] = lp->rx_ring[i].status; j+=4; + tmp.lval[j>>2] = le32_to_cpu(lp->rx_ring[i].status); j+=4; } for (i=0;itxRingSize;i++){ - tmp.lval[j>>2] = lp->tx_ring[i].status; j+=4; + tmp.lval[j>>2] = le32_to_cpu(lp->tx_ring[i].status); j+=4; } tmp.lval[j>>2] = inl(DE4X5_BMR); j+=4; @@ -4078,7 +4958,7 @@ tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4; } tmp.lval[j>>2] = lp->phy[lp->active].id; j+=4; - if (lp->phy[lp->active].id) { + if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { tmp.lval[j>>2] = lp->active; j+=4; tmp.lval[j>>2]=mii_rd(MII_CR,lp->phy[lp->active].addr,DE4X5_MII); j+=4; tmp.lval[j>>2]=mii_rd(MII_SR,lp->phy[lp->active].addr,DE4X5_MII); j+=4; @@ -4102,10 +4982,11 @@ ioc->len = j; if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) { - memcpy_tofs(ioc->data, tmp.addr, ioc->len); + copy_to_user(ioc->data, tmp.addr, ioc->len); } break; +*/ default: status = -EOPNOTSUPP; } @@ -4118,85 +4999,60 @@ ** Note now that module autoprobing is allowed under EISA and PCI. The ** IRQ lines will not be auto-detected; instead I'll rely on the BIOSes ** to "do the right thing". -** -** NB: Current register_netdevice() code does not permit assigning io=0 as -** this driver will autoprobe all instances of acceptable DECchips. The -** cleanup_module() code needs work still....(just to unload modules owned -** by this driver). -*/ -static char devicename[9] = { 0, }; -static struct device thisDE4X5 = { - devicename, /* device name inserted by /linux/drivers/net/net_init.c */ - 0, 0, 0, 0, - 0, 0, /* I/O address, IRQ */ - 0, 0, 0, NULL, de4x5_probe }; - -static int io=0x0b; /* EDIT THESE LINES FOR YOUR CONFIGURATION */ +*/ +#define LP(a) ((struct de4x5_private *)(a)) +static struct device *mdev = NULL; +static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED */ int init_module(void) { - struct device *p = (struct device *)&thisDE4X5; - - thisDE4X5.base_addr = io; /* Now autoprobe the module */ - thisDE4X5.irq = 0; - - for (; p!=NULL; p=p->next) { + struct device *p; + + if ((mdev = insert_device(NULL, io, de4x5_probe)) == NULL) + return -ENOMEM; + + for (p = mdev; p != NULL; p = LP(p->priv)->next_module) { if (register_netdev(p) != 0) return -EIO; } - io=0; + return 0; } void cleanup_module(void) { - struct de4x5_private *lp = (struct de4x5_private *) thisDE4X5.priv; - struct device *p = (struct device *)&thisDE4X5; - int keep_loaded = 0; - - for (; p!=NULL; p=p->next) { - keep_loaded += (p->flags & IFF_UP); /* Is an interface up? */ - } - - if (keep_loaded) { - printk("de4x5: Cannot unload modules - %d interface%s%s still active.\n", - keep_loaded, (keep_loaded>1 ? "s ": " "), - (keep_loaded>1 ? "are": "is")); - return; + while (mdev != NULL) { + mdev = unlink_modules(mdev); } - - for (p=thisDE4X5.next; p!=NULL; p=p->next) { - if (p->priv) { /* Private area allocated? */ - struct de4x5_private *lp = (struct de4x5_private *)p->priv; - if (lp->cache.buf) { /* MAC buffers allocated? */ - kfree(lp->cache.buf); /* Free the MAC buffers */ - } - release_region(p->base_addr, (lp->bus == PCI ? - DE4X5_PCI_TOTAL_SIZE : - DE4X5_EISA_TOTAL_SIZE)); - kfree(lp->cache.priv); /* Free the private area */ - } - unregister_netdev(p); - kfree(p); /* Free the device structure */ - } - - if (thisDE4X5.priv) { - if (lp->cache.buf) { /* Are MAC buffers allocated */ - kfree(lp->cache.buf); - } - release_region(thisDE4X5.base_addr, - (lp->bus == PCI ? - DE4X5_PCI_TOTAL_SIZE : - DE4X5_EISA_TOTAL_SIZE)); - kfree(lp->cache.priv); - thisDE4X5.priv = NULL; - } - unregister_netdev(&thisDE4X5); return; } + +static struct device * +unlink_modules(struct device *p) +{ + struct device *next = NULL; + + if (p->priv) { /* Private areas allocated? */ + struct de4x5_private *lp = (struct de4x5_private *)p->priv; + + next = lp->next_module; + if (lp->cache.buf) { /* MAC buffers allocated? */ + kfree(lp->cache.buf); /* Free the MAC buffers */ + } + kfree(lp->cache.priv); /* Free the private area */ + release_region(p->base_addr, (lp->bus == PCI ? + DE4X5_PCI_TOTAL_SIZE : + DE4X5_EISA_TOTAL_SIZE)); + } + unregister_netdev(p); + kfree(p); /* Free the device structure */ + + return next; +} + #endif /* MODULE */ diff -u --recursive --new-file v2.0.30/linux/drivers/net/de4x5.h linux/drivers/net/de4x5.h --- v2.0.30/linux/drivers/net/de4x5.h Tue Aug 20 23:18:08 1996 +++ linux/drivers/net/de4x5.h Tue Aug 12 13:21:12 1997 @@ -62,6 +62,8 @@ #define PCI_CBER iobase+0x0030 /* PCI Expansion ROM Base Address Reg. */ #define PCI_CFIT iobase+0x003c /* PCI Configuration Interrupt Register */ #define PCI_CFDA iobase+0x0040 /* PCI Driver Area Register */ +#define PCI_CFDD iobase+0x0041 /* PCI Driver Dependent Area Register */ +#define PCI_CFPM iobase+0x0043 /* PCI Power Management Area Register */ /* ** EISA Configuration Register 0 bit definitions @@ -95,16 +97,20 @@ #define ER3_LSR 0x02 /* Local Software Reset */ /* -** PCI Configuration ID Register (PCI_CFID) +** PCI Configuration ID Register (PCI_CFID). The Device IDs are left +** shifted 8 bits to allow detection of DC21142 and DC21143 variants with +** the configuration revision register step number. */ #define CFID_DID 0xff00 /* Device ID */ #define CFID_VID 0x00ff /* Vendor ID */ -#define DC21040_DID 0x0002 /* Unique Device ID # */ +#define DC21040_DID 0x0200 /* Unique Device ID # */ #define DC21040_VID 0x1011 /* DC21040 Manufacturer */ -#define DC21041_DID 0x0014 /* Unique Device ID # */ +#define DC21041_DID 0x1400 /* Unique Device ID # */ #define DC21041_VID 0x1011 /* DC21041 Manufacturer */ -#define DC21140_DID 0x0009 /* Unique Device ID # */ +#define DC21140_DID 0x0900 /* Unique Device ID # */ #define DC21140_VID 0x1011 /* DC21140 Manufacturer */ +#define DC2114x_DID 0x1900 /* Unique Device ID # */ +#define DC2114x_VID 0x1011 /* DC2114[23] Manufacturer */ /* ** Chipset defines @@ -112,10 +118,16 @@ #define DC21040 DC21040_DID #define DC21041 DC21041_DID #define DC21140 DC21140_DID +#define DC2114x DC2114x_DID +#define DC21142 (DC2114x_DID | 0x0010) +#define DC21143 (DC2114x_DID | 0x0020) #define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID)) #define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID)) #define is_DC21140 ((vendor == DC21140_VID) && (device == DC21140_DID)) +#define is_DC2114x ((vendor == DC2114x_VID) && (device == DC2114x_DID)) +#define is_DC21142 ((vendor == DC2114x_VID) && (device == DC21142)) +#define is_DC21143 ((vendor == DC2114x_VID) && (device == DC21143)) /* ** PCI Configuration Command/Status Register (PCI_CFCS) @@ -127,7 +139,7 @@ #define CFCS_DST 0x06000000 /* DEVSEL Timing (S) */ #define CFCS_DPR 0x01000000 /* Data Parity Report (S) */ #define CFCS_FBB 0x00800000 /* Fast Back-To-Back (S) */ -#define CFCS_SLE 0x00000100 /* System Error Enable (C) */ +#define CFCS_SEE 0x00000100 /* System Error Enable (C) */ #define CFCS_PER 0x00000040 /* Parity Error Response (C) */ #define CFCS_MO 0x00000004 /* Master Operation (C) */ #define CFCS_MSA 0x00000002 /* Memory Space Access (C) */ @@ -138,8 +150,8 @@ */ #define CFRV_BC 0xff000000 /* Base Class */ #define CFRV_SC 0x00ff0000 /* Subclass */ -#define CFRV_SN 0x000000f0 /* Step Number */ -#define CFRV_RN 0x0000000f /* Revision Number */ +#define CFRV_RN 0x000000f0 /* Revision Number */ +#define CFRV_SN 0x0000000f /* Step Number */ #define BASE_CLASS 0x02000000 /* Indicates Network Controller */ #define SUB_CLASS 0x00000000 /* Indicates Ethernet Controller */ #define STEP_NUMBER 0x00000020 /* Increments for future chips */ @@ -158,19 +170,46 @@ #define CBIO_IOSI 0x00000001 /* I/O Space Indicator (RO, value is 1) */ /* +** PCI Configuration Card Information Structure Register (PCI_CCIS) +*/ +#define CCIS_ROMI 0xf0000000 /* ROM Image */ +#define CCIS_ASO 0x0ffffff8 /* Address Space Offset */ +#define CCIS_ASI 0x00000007 /* Address Space Indicator */ + +/* +** PCI Configuration Subsystem ID Register (PCI_SSID) +*/ +#define SSID_SSID 0xffff0000 /* Subsystem ID */ +#define SSID_SVID 0x0000ffff /* Subsystem Vendor ID */ + +/* ** PCI Configuration Expansion ROM Base Address Register (PCI_CBER) */ #define CBER_MASK 0xfffffc00 /* Expansion ROM Base Address Mask */ #define CBER_ROME 0x00000001 /* ROM Enable */ /* -** PCI Configuration Driver Area Register (PCI_CFDA) +** PCI Configuration Interrupt Register (PCI_CFIT) */ -#define CFDA_PSM 0x80000000 /* Power Saving Mode */ +#define CFIT_MXLT 0xff000000 /* MAX_LAT Value (0.25us periods) */ +#define CFIT_MNGT 0x00ff0000 /* MIN_GNT Value (0.25us periods) */ +#define CFIT_IRQP 0x0000ff00 /* Interrupt Pin */ +#define CFIT_IRQL 0x000000ff /* Interrupt Line */ + +/* +** PCI Configuration Power Management Area Register (PCI_CFPM) +*/ +#define SLEEP 0x80 /* Power Saving Sleep Mode */ +#define SNOOZE 0x40 /* Power Saving Snooze Mode */ +#define WAKEUP 0x00 /* Power Saving Wakeup */ + +#define PCI_CFDA_DSU 0x41 /* 8 bit Configuration Space Address */ +#define PCI_CFDA_PSM 0x43 /* 8 bit Configuration Space Address */ /* ** DC21040 Bus Mode Register (DE4X5_BMR) */ +#define BMR_RML 0x00200000 /* [Memory] Read Multiple */ #define BMR_DBO 0x00100000 /* Descriptor Byte Ordering (Endian) */ #define BMR_TAP 0x000e0000 /* Transmit Automatic Polling */ #define BMR_DAS 0x00010000 /* Diagnostic Address Space */ @@ -181,6 +220,7 @@ #define BMR_BAR 0x00000002 /* Bus ARbitration */ #define BMR_SWR 0x00000001 /* Software Reset */ + /* Timings here are for 10BASE-T/AUI only*/ #define TAP_NOPOLL 0x00000000 /* No automatic polling */ #define TAP_200US 0x00020000 /* TX automatic polling every 200us */ #define TAP_800US 0x00040000 /* TX automatic polling every 800us */ @@ -232,18 +272,21 @@ #define TRBA 0xfffffffc /* TX Descriptor List Start Address */ /* -** DC21040 Status Register (DE4X5_STS) +** Status Register (DE4X5_STS) */ +#define STS_GPI 0x04000000 /* General Purpose Port Interrupt */ #define STS_BE 0x03800000 /* Bus Error Bits */ #define STS_TS 0x00700000 /* Transmit Process State */ #define STS_RS 0x000e0000 /* Receive Process State */ #define STS_NIS 0x00010000 /* Normal Interrupt Summary */ #define STS_AIS 0x00008000 /* Abnormal Interrupt Summary */ #define STS_ER 0x00004000 /* Early Receive */ +#define STS_FBE 0x00002000 /* Fatal Bus Error */ #define STS_SE 0x00002000 /* System Error */ #define STS_LNF 0x00001000 /* Link Fail */ #define STS_FD 0x00000800 /* Full-Duplex Short Frame Received */ #define STS_TM 0x00000800 /* Timer Expired (DC21041) */ +#define STS_ETI 0x00000400 /* Early Transmit Interupt */ #define STS_AT 0x00000400 /* AUI/TP Pin */ #define STS_RWT 0x00000200 /* Receive Watchdog Time-Out */ #define STS_RPS 0x00000100 /* Receive Process Stopped */ @@ -251,6 +294,7 @@ #define STS_RI 0x00000040 /* Receive Interrupt */ #define STS_UNF 0x00000020 /* Transmit Underflow */ #define STS_LNP 0x00000010 /* Link Pass */ +#define STS_ANC 0x00000010 /* Autonegotiation Complete */ #define STS_TJT 0x00000008 /* Transmit Jabber Time-Out */ #define STS_TU 0x00000004 /* Transmit Buffer Unavailable */ #define STS_TPS 0x00000002 /* Transmit Process Stopped */ @@ -283,8 +327,10 @@ #define INT_CANCEL 0x0001ffff /* For zeroing all interrupt sources */ /* -** DC21040 Operation Mode Register (DE4X5_OMR) +** Operation Mode Register (DE4X5_OMR) */ +#define OMR_SC 0x80000000 /* Special Capture Effect Enable */ +#define OMR_RA 0x40000000 /* Receive All */ #define OMR_SDP 0x02000000 /* SD Polarity - MUST BE ASSERTED */ #define OMR_SCR 0x01000000 /* Scrambler Mode */ #define OMR_PCS 0x00800000 /* PCS Function */ @@ -298,7 +344,7 @@ #define OMR_ST 0x00002000 /* Start/Stop Transmission Command */ #define OMR_FC 0x00001000 /* Force Collision Mode */ #define OMR_OM 0x00000c00 /* Operating Mode */ -#define OMR_FD 0x00000200 /* Full Duplex Mode */ +#define OMR_FDX 0x00000200 /* Full Duplex Mode */ #define OMR_FKD 0x00000100 /* Flaky Oscillator Disable */ #define OMR_PM 0x00000080 /* Pass All Multicast */ #define OMR_PR 0x00000040 /* Promiscuous Mode */ @@ -309,27 +355,31 @@ #define OMR_SR 0x00000002 /* Start/Stop Receive */ #define OMR_HP 0x00000001 /* Hash/Perfect Receive Filtering Mode */ -#define TR_72 0x00000000 /* Threshold set to 72 bytes */ -#define TR_96 0x00004000 /* Threshold set to 96 bytes */ -#define TR_128 0x00008000 /* Threshold set to 128 bytes */ -#define TR_160 0x0000c000 /* Threshold set to 160 bytes */ +#define TR_72 0x00000000 /* Threshold set to 72 (128) bytes */ +#define TR_96 0x00004000 /* Threshold set to 96 (256) bytes */ +#define TR_128 0x00008000 /* Threshold set to 128 (512) bytes */ +#define TR_160 0x0000c000 /* Threshold set to 160 (1024) bytes */ /* ** DC21040 Interrupt Mask Register (DE4X5_IMR) */ +#define IMR_GPM 0x04000000 /* General Purpose Port Mask */ #define IMR_NIM 0x00010000 /* Normal Interrupt Summary Mask */ #define IMR_AIM 0x00008000 /* Abnormal Interrupt Summary Mask */ #define IMR_ERM 0x00004000 /* Early Receive Mask */ +#define IMR_FBM 0x00002000 /* Fatal Bus Error Mask */ #define IMR_SEM 0x00002000 /* System Error Mask */ #define IMR_LFM 0x00001000 /* Link Fail Mask */ #define IMR_FDM 0x00000800 /* Full-Duplex (Short Frame) Mask */ #define IMR_TMM 0x00000800 /* Timer Expired Mask (DC21041) */ +#define IMR_ETM 0x00000400 /* Early Transmit Interrupt Mask */ #define IMR_ATM 0x00000400 /* AUI/TP Switch Mask */ #define IMR_RWM 0x00000200 /* Receive Watchdog Time-Out Mask */ #define IMR_RSM 0x00000100 /* Receive Stopped Mask */ #define IMR_RUM 0x00000080 /* Receive Buffer Unavailable Mask */ #define IMR_RIM 0x00000040 /* Receive Interrupt Mask */ #define IMR_UNM 0x00000020 /* Underflow Interrupt Mask */ +#define IMR_ANM 0x00000010 /* Autonegotiation Complete Mask */ #define IMR_LPM 0x00000010 /* Link Pass */ #define IMR_TJM 0x00000008 /* Transmit Time-Out Jabber Mask */ #define IMR_TUM 0x00000004 /* Transmit Buffer Unavailable Mask */ @@ -337,13 +387,7 @@ #define IMR_TIM 0x00000001 /* Transmit Interrupt Mask */ /* -** DC21040 Missed Frames Counter (DE4X5_MFC) -*/ -#define MFC_OVFL 0x00010000 /* Missed Frames Counter Overflow Bit */ -#define MFC_CNTR 0x0000ffff /* Missed Frames Counter Bits */ - -/* -** DC21140 Missed Frames and FIFO Overflow Counters (DE4X5_MFC) +** Missed Frames and FIFO Overflow Counters (DE4X5_MFC) */ #define MFC_FOCO 0x10000000 /* FIFO Overflow Counter Overflow Bit */ #define MFC_FOC 0x0ffe0000 /* FIFO Overflow Counter Bits */ @@ -444,7 +488,7 @@ ** MII Management Auto Negotiation Advertisement Register */ #define MII_ANA_TAF 0x03e0 /* Technology Ability Field */ -#define MII_ANA_T4AM 0x0400 /* T4 Technology Ability Mask */ +#define MII_ANA_T4AM 0x0200 /* T4 Technology Ability Mask */ #define MII_ANA_TXAM 0x0180 /* TX Technology Ability Mask */ #define MII_ANA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ #define MII_ANA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ @@ -459,7 +503,7 @@ #define MII_ANLPA_ACK 0x4000 /* Remote Acknowledge */ #define MII_ANLPA_RF 0x2000 /* Remote Fault */ #define MII_ANLPA_TAF 0x03e0 /* Technology Ability Field */ -#define MII_ANLPA_T4AM 0x0400 /* T4 Technology Ability Mask */ +#define MII_ANLPA_T4AM 0x0200 /* T4 Technology Ability Mask */ #define MII_ANLPA_TXAM 0x0180 /* TX Technology Ability Mask */ #define MII_ANLPA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ #define MII_ANLPA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ @@ -478,6 +522,76 @@ #define MEDIA_BNC 0x0001 /* BNC Media present */ /* +** SROM Definitions (Digital Semiconductor Format) +*/ +#define SROM_SSVID 0x0000 /* Sub-system Vendor ID offset */ +#define SROM_SSID 0x0002 /* Sub-system ID offset */ +#define SROM_CISPL 0x0004 /* CardBus CIS Pointer low offset */ +#define SROM_CISPH 0x0006 /* CardBus CIS Pointer high offset */ +#define SROM_IDCRC 0x0010 /* ID Block CRC offset*/ +#define SROM_RSVD2 0x0011 /* ID Reserved 2 offset */ +#define SROM_SFV 0x0012 /* SROM Format Version offset */ +#define SROM_CCNT 0x0013 /* Controller Count offset */ +#define SROM_HWADD 0x0014 /* Hardware Address offset */ +#define SROM_MRSVD 0x007c /* Manufacturer Reserved offset*/ +#define SROM_CRC 0x007e /* SROM CRC offset */ + +/* +** SROM Media Connection Definitions +*/ +#define SROM_10BT 0x0000 /* 10BASE-T half duplex */ +#define SROM_10BTN 0x0100 /* 10BASE-T with Nway */ +#define SROM_10BTF 0x0204 /* 10BASE-T full duplex */ +#define SROM_10BTNLP 0x0400 /* 10BASE-T without Link Pass test */ +#define SROM_10B2 0x0001 /* 10BASE-2 (BNC) */ +#define SROM_10B5 0x0002 /* 10BASE-5 (AUI) */ +#define SROM_100BTH 0x0003 /* 100BASE-T half duplex */ +#define SROM_100BTF 0x0205 /* 100BASE-T full duplex */ +#define SROM_100BT4 0x0006 /* 100BASE-T4 */ +#define SROM_100BFX 0x0007 /* 100BASE-FX half duplex (Fiber) */ +#define SROM_M10BT 0x0009 /* MII 10BASE-T half duplex */ +#define SROM_M10BTF 0x020a /* MII 10BASE-T full duplex */ +#define SROM_M100BT 0x000d /* MII 100BASE-T half duplex */ +#define SROM_M100BTF 0x020e /* MII 100BASE-T full duplex */ +#define SROM_M100BT4 0x000f /* MII 100BASE-T4 */ +#define SROM_M100BF 0x0010 /* MII 100BASE-FX half duplex */ +#define SROM_M100BFF 0x0211 /* MII 100BASE-FX full duplex */ +#define SROM_PDA 0x0800 /* Powerup & Dynamic Autosense */ +#define SROM_PAO 0x8800 /* Powerup Autosense Only */ +#define SROM_NSMI 0xffff /* No Selected Media Information */ + +/* +** SROM Media Definitions +*/ +#define SROM_10BASET 0x0000 /* 10BASE-T half duplex */ +#define SROM_10BASE2 0x0001 /* 10BASE-2 (BNC) */ +#define SROM_10BASE5 0x0002 /* 10BASE-5 (AUI) */ +#define SROM_100BASET 0x0003 /* 100BASE-T half duplex */ +#define SROM_10BASETF 0x0004 /* 10BASE-T full duplex */ +#define SROM_100BASETF 0x0005 /* 100BASE-T full duplex */ +#define SROM_100BASET4 0x0006 /* 100BASE-T4 */ +#define SROM_100BASEF 0x0007 /* 100BASE-FX half duplex */ +#define SROM_100BASEFF 0x0008 /* 100BASE-FX full duplex */ + +#define BLOCK_LEN 0x7f /* Extended blocks length mask */ +#define EXT_FIELD 0x40 /* Extended blocks extension field bit */ +#define MEDIA_CODE 0x3f /* Extended blocks media code mask */ + +/* +** SROM Compact Format Block Masks +*/ +#define COMPACT_FI 0x80 /* Format Indicator */ +#define COMPACT_LEN 0x04 /* Length */ +#define COMPACT_MC 0x3f /* Media Code */ + +/* +** SROM Extended Format Block Type 0 Masks +*/ +#define BLOCK0_FI 0x80 /* Format Indicator */ +#define BLOCK0_MCS 0x80 /* Media Code byte Sign */ +#define BLOCK0_MC 0x3f /* Media Code */ + +/* ** DC21040 Full Duplex Register (DE4X5_FDR) */ #define FDR_FDACV 0x0000ffff /* Full Duplex Auto Configuration Value */ @@ -501,17 +615,21 @@ #define GEP_FLED 0x00000002 /* Force Activity LED on (output) */ #define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */ #define GEP_INIT 0x0000011f /* Setup inputs (0) and outputs (1) */ - +#define GEP_CTRL 0x00000100 /* GEP control bit */ /* -** DC21040 SIA Status Register (DE4X5_SISR) +** SIA Status Register (DE4X5_SISR) */ #define SISR_LPC 0xffff0000 /* Link Partner's Code Word */ #define SISR_LPN 0x00008000 /* Link Partner Negotiable */ #define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */ -#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected */ +#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected (DC21041) */ +#define SISR_TRF 0x00000800 /* Transmit Remote Fault */ +#define SISR_NSND 0x00000400 /* Non Stable NLPs Detected (DC21142) */ #define SISR_ANR_FDS 0x00000400 /* Auto Negotiate Restart/Full Duplex Sel.*/ +#define SISR_TRA 0x00000200 /* 10BASE-T Receive Port Activity */ #define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */ +#define SISR_ARA 0x00000100 /* AUI Receive Port Activity */ #define SISR_SRA 0x00000100 /* Selected Port Receive Activity */ #define SISR_DAO 0x00000080 /* PLL All One */ #define SISR_DAZ 0x00000040 /* PLL All Zero */ @@ -521,7 +639,7 @@ #define SISR_LKF 0x00000004 /* Link Fail Status */ #define SISR_NCR 0x00000002 /* Network Connection Error */ #define SISR_PAUI 0x00000001 /* AUI_TP Indication */ -#define SIA_RESET 0x00000000 /* SIA Reset */ +#define SISR_MRA 0x00000001 /* MII Receive Port Activity */ #define ANS_NDIS 0x00000000 /* Nway disable */ #define ANS_TDIS 0x00001000 /* Transmit Disable */ @@ -532,7 +650,7 @@ #define ANS_LCHK 0x00006000 /* Link Check */ /* -** DC21040 SIA Connectivity Register (DE4X5_SICR) +** SIA Connectivity Register (DE4X5_SICR) */ #define SICR_SDM 0xffff0000 /* SIA Diagnostics Mode */ #define SICR_OE57 0x00008000 /* Output Enable 5 6 7 */ @@ -551,14 +669,14 @@ #define SICR_SIM 0x00000040 /* Serial Interface Input Multiplexer */ #define SICR_ENI 0x00000020 /* Encoder Input Multiplexer */ #define SICR_EDP 0x00000010 /* SIA PLL External Input Enable */ -#define SICR_AUI 0x00000008 /* 10Base-T or AUI */ +#define SICR_AUI 0x00000008 /* 10Base-T (0) or AUI (1) */ #define SICR_CAC 0x00000004 /* CSR Auto Configuration */ #define SICR_PS 0x00000002 /* Pin AUI/TP Selection */ #define SICR_SRL 0x00000001 /* SIA Reset */ -#define SICR_RESET 0xffff0000 /* Reset value for SICR */ +#define SIA_RESET 0x00000000 /* SIA Reset Value */ /* -** DC21040 SIA Transmit and Receive Register (DE4X5_STRR) +** SIA Transmit and Receive Register (DE4X5_STRR) */ #define STRR_TAS 0x00008000 /* 10Base-T/AUI Autosensing Enable */ #define STRR_SPP 0x00004000 /* Set Polarity Plus */ @@ -578,8 +696,20 @@ #define STRR_RESET 0xffffffff /* Reset value for STRR */ /* -** DC21040 SIA General Register (DE4X5_SIGR) +** SIA General Register (DE4X5_SIGR) */ +#define SIGR_RMI 0x40000000 /* Receive Match Interrupt */ +#define SIGR_GI1 0x20000000 /* General Port Interrupt 1 */ +#define SIGR_GI0 0x10000000 /* General Port Interrupt 0 */ +#define SIGR_CWE 0x08000000 /* Control Write Enable */ +#define SIGR_RME 0x04000000 /* Receive Match Enable */ +#define SIGR_GEI1 0x02000000 /* GEP Interrupt Enable on Port 1 */ +#define SIGR_GEI0 0x01000000 /* GEP Interrupt Enable on Port 0 */ +#define SIGR_LGS3 0x00800000 /* LED/GEP3 Select */ +#define SIGR_LGS2 0x00400000 /* LED/GEP2 Select */ +#define SIGR_LGS1 0x00200000 /* LED/GEP1 Select */ +#define SIGR_LGS0 0x00100000 /* LED/GEP0 Select */ +#define SIGR_MD 0x000f0000 /* General Purpose Mode and Data */ #define SIGR_LV2 0x00008000 /* General Purpose LED2 value */ #define SIGR_LE2 0x00004000 /* General Purpose LED2 enable */ #define SIGR_FRL 0x00002000 /* Force Receiver Low */ @@ -602,7 +732,8 @@ ** Receive Descriptor Bit Summary */ #define R_OWN 0x80000000 /* Own Bit */ -#define RD_FL 0x7fff0000 /* Frame Length */ +#define RD_FF 0x40000000 /* Filtering Fail */ +#define RD_FL 0x3fff0000 /* Frame Length */ #define RD_ES 0x00008000 /* Error Summary */ #define RD_LE 0x00004000 /* Length Error */ #define RD_DT 0x00003000 /* Data Type */ @@ -614,6 +745,7 @@ #define RD_CS 0x00000040 /* Collision Seen */ #define RD_FT 0x00000020 /* Frame Type */ #define RD_RJ 0x00000010 /* Receive Watchdog */ +#define RD_RE 0x00000008 /* Report on MII Error */ #define RD_DB 0x00000004 /* Dribbling Bit */ #define RD_CE 0x00000002 /* CRC Error */ #define RD_OF 0x00000001 /* Overflow */ @@ -649,40 +781,53 @@ #define TD_TCH 0x01000000 /* Second Address Chained */ #define TD_DPD 0x00800000 /* Disabled Padding */ #define TD_FT0 0x00400000 /* Filtering Type */ -#define TD_RBS2 0x003ff800 /* Buffer 2 Size */ -#define TD_RBS1 0x000007ff /* Buffer 1 Size */ +#define TD_TBS2 0x003ff800 /* Buffer 2 Size */ +#define TD_TBS1 0x000007ff /* Buffer 1 Size */ #define PERFECT_F 0x00000000 #define HASH_F TD_FT0 #define INVERSE_F TD_FT1 -#define HASH_O_F TD_FT1| TD_F0 +#define HASH_O_F (TD_FT1 | TD_F0) /* ** Media / mode state machine definitions */ -#define NC 0x0000 /* No Connection */ -#define TP 0x0001 /* 10Base-T */ -#define TP_NW 0x0002 /* 10Base-T with Nway */ -#define BNC 0x0004 /* Thinwire */ -#define AUI 0x0008 /* Thickwire */ +#define NC 0x0000 /* No Connection */ +#define TP 0x0001 /* 10Base-T */ +#define TP_NW 0x0002 /* 10Base-T with Nway */ +#define BNC 0x0004 /* Thinwire */ +#define AUI 0x0008 /* Thickwire */ #define BNC_AUI 0x0010 /* BNC/AUI on DC21040 indistinguishable */ -#define ANS 0x0020 /* Intermediate AutoNegotiation State */ -#define ANS_1 0x0021 /* Intermediate AutoNegotiation State */ - -#define _10Mb 0x0040 /* 10Mb/s Ethernet */ -#define _100Mb 0x0080 /* 100Mb/s Ethernet */ -#define SPD_DET 0x0100 /* Parallel speed detection */ -#define INIT 0x0200 /* Initial state */ -#define EXT_SIA 0x0400 /* External SIA for motherboard chip */ -#define ANS_SUSPECT 0x0802 /* Suspect the ANS (TP) port is down */ -#define TP_SUSPECT 0x0803 /* Suspect the TP port is down */ -#define BNC_AUI_SUSPECT 0x0804 /* Suspect the BNC or AUI port is down */ -#define EXT_SIA_SUSPECT 0x0805 /* Suspect the EXT SIA port is down */ -#define BNC_SUSPECT 0x0806 /* Suspect the BNC port is down */ -#define AUI_SUSPECT 0x0807 /* Suspect the AUI port is down */ - -#define AUTO 0x4000 /* Auto sense the media or speed */ -#define TIMER_CB 0x80000000 /* Timer callback detection */ +#define ANS 0x0020 /* Intermediate AutoNegotiation State */ +#define _10Mb 0x0040 /* 10Mb/s Ethernet */ +#define _100Mb 0x0080 /* 100Mb/s Ethernet */ +#define SPD_DET 0x0100 /* Parallel speed detection */ +#define INIT 0x0200 /* Initial state */ +#define EXT_SIA 0x0400 /* External SIA for motherboard chip */ +#define ANS_SUSPECT 0x0802 /* Suspect the ANS (TP) port is down */ +#define TP_SUSPECT 0x0803 /* Suspect the TP port is down */ +#define BNC_AUI_SUSPECT 0x0804 /* Suspect the BNC or AUI port is down */ +#define EXT_SIA_SUSPECT 0x0805 /* Suspect the EXT SIA port is down */ +#define BNC_SUSPECT 0x0806 /* Suspect the BNC port is down */ +#define AUI_SUSPECT 0x0807 /* Suspect the AUI port is down */ +#define MII 0x1000 /* MII on the 21143 */ + +#define AUTO 0x4000 /* Auto sense the media or speed */ +#define TIMER_CB 0x80000000 /* Timer callback detection */ + +/* +** DE4X5 DEBUG Options +*/ +#define DEBUG_NONE 0x0000 /* No DEBUG messages */ +#define DEBUG_VERSION 0x0001 /* Print version message */ +#define DEBUG_MEDIA 0x0002 /* Print media messages */ +#define DEBUG_TX 0x0004 /* Print TX (queue_pkt) messages */ +#define DEBUG_RX 0x0008 /* Print RX (de4x5_rx) messages */ +#define DEBUG_SROM 0x0010 /* Print SROM messages */ +#define DEBUG_MII 0x0020 /* Print MII messages */ +#define DEBUG_OPEN 0x0040 /* Print de4x5_open() messages */ +#define DEBUG_CLOSE 0x0080 /* Print de4x5_close() messages */ +#define DEBUG_PCICFG 0x0100 /* ** Miscellaneous @@ -699,7 +844,6 @@ #define POLL_DEMAND 1 #define LOST_MEDIA_THRESHOLD 3 -#define LOST_MEDIA (lp->lostMedia > LOST_MEDIA_THRESHOLD) #define MASK_INTERRUPTS 1 #define UNMASK_INTERRUPTS 0 @@ -728,11 +872,16 @@ */ #define NO 0 #define FALSE 0 -#define CLOSED 0 #define YES ~0 #define TRUE ~0 -#define OPEN ~0 + +/* +** Adapter state +*/ +#define INITIALISED 0 /* After h/w initialised and mem alloc'd */ +#define CLOSED 1 /* Ready for opening */ +#define OPEN 2 /* Running */ /* ** IEEE OUIs for various PHY vendor/chip combos - Reg 2 values only. Since @@ -748,55 +897,67 @@ ** Speed Selection stuff */ #define SET_10Mb {\ - if (lp->phy[lp->active].id) {\ - omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD);\ + if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ + omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\ if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\ - mii_wr(MII_CR_10|(de4x5_full_duplex?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ + mii_wr(MII_CR_10|(lp->fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ }\ - omr |= ((de4x5_full_duplex ? OMR_FD : 0) | OMR_TTM);\ + omr |= ((lp->fdx ? OMR_FDX : 0) | OMR_TTM);\ outl(omr, DE4X5_OMR);\ - outl(0, DE4X5_GEP);\ + if (!lp->useSROM) lp->cache.gep = 0;\ + } else if (lp->useSROM && !lp->useMII) {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ + outl(omr | lp->infoblock_csr6, DE4X5_OMR);\ } else {\ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD));\ - omr |= (de4x5_full_duplex ? OMR_FD : 0);\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ outl(omr | OMR_TTM, DE4X5_OMR);\ - outl((de4x5_full_duplex ? 0 : GEP_FDXD), DE4X5_GEP);\ + lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD);\ }\ } #define SET_100Mb {\ - if (lp->phy[lp->active].id) {\ + if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ int fdx=0;\ if (lp->phy[lp->active].id == NATIONAL_TX) {\ mii_wr(mii_rd(0x18, lp->phy[lp->active].addr, DE4X5_MII) & ~0x2000,\ 0x18, lp->phy[lp->active].addr, DE4X5_MII);\ }\ - omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD);\ + omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\ sr = mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);\ - if (!(sr & MII_ANA_T4AM) && de4x5_full_duplex) fdx=1;\ + if (!(sr & MII_ANA_T4AM) && lp->fdx) fdx=1;\ if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\ mii_wr(MII_CR_100|(fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ }\ - if (fdx) omr |= OMR_FD;\ + if (fdx) omr |= OMR_FDX;\ outl(omr, DE4X5_OMR);\ + if (!lp->useSROM) lp->cache.gep = 0;\ + } else if (lp->useSROM && !lp->useMII) {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ + outl(omr | lp->infoblock_csr6 | OMR_HBD, DE4X5_OMR);\ } else {\ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD));\ - omr |= (de4x5_full_duplex ? OMR_FD : 0);\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ outl(omr | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\ - outl((de4x5_full_duplex ? 0 : GEP_FDXD) | GEP_MODE, DE4X5_GEP);\ + lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD) | GEP_MODE;\ }\ } /* FIX ME so I don't jam 10Mb networks */ #define SET_100Mb_PDET {\ - if (lp->phy[lp->active].id) {\ + if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ mii_wr(MII_CR_100|MII_CR_ASSE, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ - omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD));\ + omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + outl(omr, DE4X5_OMR);\ + } else if (lp->useSROM && !lp->useMII) {\ + omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ outl(omr, DE4X5_OMR);\ } else {\ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD));\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ outl(omr | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\ - outl(GEP_FDXD | GEP_MODE, DE4X5_GEP);\ + lp->cache.gep = (GEP_FDXD | GEP_MODE);\ }\ } @@ -817,7 +978,7 @@ ** Recognised commands for the driver */ #define DE4X5_GET_HWADDR 0x01 /* Get the hardware address */ -#define DE4X5_SET_HWADDR 0x02 /* Get the hardware address */ +#define DE4X5_SET_HWADDR 0x02 /* Set the hardware address */ #define DE4X5_SET_PROM 0x03 /* Set Promiscuous Mode */ #define DE4X5_CLR_PROM 0x04 /* Clear Promiscuous Mode */ #define DE4X5_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */ diff -u --recursive --new-file v2.0.30/linux/drivers/net/dgrs.c linux/drivers/net/dgrs.c --- v2.0.30/linux/drivers/net/dgrs.c Sat Dec 21 07:22:33 1996 +++ linux/drivers/net/dgrs.c Tue Aug 12 16:05:23 1997 @@ -97,7 +97,6 @@ #include #include -#include /* for CONFIG_PCI */ /* * API changed at linux version 2.1.0 diff -u --recursive --new-file v2.0.30/linux/drivers/net/eepro100.c linux/drivers/net/eepro100.c --- v2.0.30/linux/drivers/net/eepro100.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/eepro100.c Tue Aug 12 20:29:26 1997 @@ -0,0 +1,1731 @@ +/* drivers/net/eepro100.c: An Intel i82557 ethernet driver for linux. */ +/* + NOTICE: this version tested with kernels 1.3.72 and later only! + Written 1996 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 Intel EtherExpress Pro 100B boards. + It should work with other i82557 boards (if any others exist). + To use a built-in driver, install as drivers/net/eepro100.c. + To use as a module, use the compile-command at the end of the file. + + The author may be reached as becker@CESDIS.usra.edu, or C/O + Center of Excellence in Space Data and Information Sciences + Code 930.5, NASA Goddard Space Flight Center, Greenbelt MD 20771 + For updates see + +*/ + +static const char *version = +"eepro100.c:v0.32 4/8/97 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n"; + +/* A few user-configurable values that apply to all boards. + First set are undocumented and spelled per Intel recommendations. */ + +static int congenb = 0; /* Enable congestion control in the DP83840. */ +static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */ +static int rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */ +static int txdmacount = 0; /* Tx DMA burst length, 0-127, default 0. */ +static int rxdmacount = 0; /* Rx DMA length, 0 means no preemption. */ + +/* If defined use the copy-only-tiny-buffer scheme for higher performance. + The value sets the copy breakpoint. Lower uses more memory, but is + faster. */ +#define SKBUFF_RX_COPYBREAK 256 + +#include +#include +#ifdef MODULE +#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 +#include + +#include +#include +#include + +/* A nominally proper method to handle version dependencies is to use + LINUX_VERSION_CODE in version.h, but that triggers recompiles w/'make'. */ +#define VERSION(v,p,s) (((v)<<16)+(p<<8)+s) +#ifdef MODULE +#if (LINUX_VERSION_CODE < VERSION(1,3,0)) +#define KERNEL_1_2 +#else /* 1.3.0 */ +#if (LINUX_VERSION_CODE >= VERSION(1,3,44)) +#define NEW_MULTICAST +#define LINUX_1_4 +#else +#warning "This driver is tested for 1.3.44 and later development kernels only." +#endif /* 1.3.44 */ +#endif +#else + +#if (LINUX_VERSION_CODE >= 0x10344) +#define NEW_MULTICAST +#include +#endif + +#ifdef HAVE_HEADER_CACHE +#define LINUX_1_4 +#define NEW_MULTICAST +#else +#ifdef ETH_P_DDCMP /* Warning: Bogus! This means IS_LINUX_1_3. */ +#define KERNEL_1_3 +#else +#define KERNEL_1_2 +#endif +#endif + +#endif +/* This should be in a header file. */ +#if (LINUX_VERSION_CODE < VERSION(1,3,44)) +struct device *init_etherdev(struct device *dev, int sizeof_priv, + unsigned long *mem_startp); +#endif +#if LINUX_VERSION_CODE < 0x10300 +#define RUN_AT(x) (x) /* What to put in timer->expires. */ +#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC) +#define virt_to_bus(addr) ((unsigned long)addr) +#define bus_to_virt(addr) ((void*)addr) +#else /* 1.3.0 and later */ +#define RUN_AT(x) (jiffies + (x)) +#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2) +#endif + +/* The total I/O port extent of the board. Nominally 0x18, but rounded up + for PCI allocation. */ +#define SPEEDO3_TOTAL_SIZE 0x20 + +#ifdef HAVE_DEVLIST +struct netdev_entry eepro100_drv = +{"EEPro-100", eepro100_init, SPEEDO3_TOTAL_SIZE, NULL}; +#endif + +#ifdef SPEEDO3_DEBUG +int speedo_debug = SPEEDO3_DEBUG; +#else +int speedo_debug = 3; +#endif + +/* + Theory of Operation + +I. Board Compatibility + +This device driver is designed for the Intel i82557 "Speedo3" chip, Intel's +single-chip fast ethernet controller for PCI, as used on the Intel +EtherExpress Pro 100 adapter. + +II. Board-specific settings + +PCI bus devices are configured by the system at boot time, so no jumpers +need to be set on the board. The system BIOS should be set to assign the +PCI INTA signal to an otherwise unused system IRQ line. While it's +possible to share PCI interrupt lines, it negatively impacts performance and +only recent kernels support it. + +III. Driver operation + +IIIA. General +The Speedo3 is very similar to other Intel network chips, that is to say +"apparently designed on a different planet". This chips retains the complex +Rx and Tx descriptors and multiple buffers pointers as previous chips, but +also has simplified Tx and Rx buffer modes. This driver uses the "flexible" +Tx mode, but in a simplified lower-overhead manner: it associates only a +single buffer descriptor with each frame descriptor. + +Despite the extra space overhead in each recieve skbuff, the driver must use +the simplified Rx buffer mode to assure that only a single data buffer is +associated with each RxFD. The driver implements this by reserving space +for the Rx descriptor at the head of each Rx skbuff + +The Speedo-3 has receive and command unit base addresses that are added to +almost all descriptor pointers. The driver sets these to zero, so that all +pointer fields are absolute addresses. + +The System Control Block (SCB) of some previous Intel chips exists on the +chip in both PCI I/O and memory space. This driver uses the I/O space +registers, but might switch to memory mapped mode to better support non-x86 +processors. + +IIIB. Transmit structure + +The driver must use the complex Tx command+descriptor mode in order to +have a indirect pointer to the skbuff data section. Each Tx command block +(TxCB) is associated with a single, immediately appended Tx buffer descriptor +(TxBD). A fixed ring of these TxCB+TxBD pairs are kept as part of the +speedo_private data structure for each adapter instance. + +This ring structure is used for all normal transmit packets, but the +transmit packet descriptors aren't long enough for most non-Tx commands such +as CmdConfigure. This is complicated by the possibility that the chip has +already loaded the link address in the previous descriptor. So for these +commands we convert the next free descriptor on the ring to a NoOp, and point +that descriptor's link to the complex command. + +An additional complexity of these non-transmit commands are that they may be +added asynchronous to the normal transmit queue, so we disable interrupts +whenever the Tx descriptor ring is manipulated. + +A notable aspect of the these special configure commands is that they do +work with the normal Tx ring entry scavenge method. The Tx ring scavenge +is done at interrupt time using the 'dirty_tx' index, and checking for the +command-complete bit. While the setup frames may have the NoOp command on the +Tx ring marked as complete, but not have completed the setup command, this +is not a problem. The tx_ring entry can be still safely reused, as the +tx_skbuff[] entry is always empty for config_cmd and mc_setup frames. + +Commands may have bits set e.g. CmdSuspend in the command word to either +suspend or stop the transmit/command unit. This driver always flags the last +command with CmdSuspend, erases the CmdSuspend in the previous command, and +then issues a CU_RESUME. +Note: Watch out for the potential race condition here: imagine + erasing the previous suspend + the chip processes the previous command + the chip processes the final command, and suspends + 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. + +Note: In previous generation Intel chips, restarting the command unit was a +notoriously slow process. This is presumably no longer true. + +IIIC. Receive structure + +Because of the bus-master support on the Speedo3 this driver uses the new +SKBUFF_RX_COPYBREAK scheme, rather than a fixed intermediate receive buffer. +This scheme allocates full-sized skbuffs as receive buffers. The value +SKBUFF_RX_COPYBREAK is used as the copying breakpoint: it is chosen to +trade-off the memory wasted by passing the full-sized skbuff to the queue +layer for all frames vs. the copying cost of copying a frame to a +correctly-sized skbuff. + +For small frames the copying cost is negligible (esp. considering that we +are pre-loading the cache with immediately useful header information), so we +allocate a new, minimally-sized skbuff. For large frames the copying cost +is non-trivial, and the larger copy might flush the cache of useful data, so +we pass up the skbuff the packet was received into. + +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 other 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 'sp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so +we can't avoid the interrupt overhead by having the Tx routine reap the Tx +stats.) After reaping the stats, it marks the queue entry as empty by setting +the 'base' to zero. Iff the 'sp->tx_full' flag is set, it clears both the +tx_full and tbusy flags. + +IV. Notes + +Thanks to Steve Williams of Intel for arranging the non-disclosure agreement +that stated that I could disclose the information. But I still resent +having to sign an Intel NDA when I'm helping Intel sell their own product! + +*/ + +/* A few values that may be tweaked. */ +/* The ring sizes should be a power of two for efficiency. */ +#define TX_RING_SIZE 16 /* Effectively 2 entries fewer. */ +#define RX_RING_SIZE 16 +/* Size of an pre-allocated Rx buffer: + slack.*/ +#define PKT_BUF_SZ 1536 + +/* 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 16 + +/* How to wait for the command unit to accept a command. + Typically this takes 0 ticks. */ +static inline void wait_for_cmd_done(int cmd_ioaddr) +{ + short wait = 100; + do ; + while(inb(cmd_ioaddr) && --wait >= 0); +} + +/* Operational parameter that usually are not changed. */ + +#ifndef PCI_VENDOR_ID_INTEL /* Now defined in linux/pci.h */ +#define PCI_VENDOR_ID_INTEL 0x8086 /* Hmmmm, how did they pick that? */ +#endif +#ifndef PCI_DEVICE_ID_INTEL_82557 +#define PCI_DEVICE_ID_INTEL_82557 0x1229 +#endif + +/* The rest of these values should never change. */ + +/* Offsets to the various registers. + All accesses need not be longword aligned. */ +enum speedo_offsets { + SCBStatus = 0, SCBCmd = 2, /* Rx/Command Unit command and status. */ + SCBPointer = 4, /* General purpose pointer. */ + SCBPort = 8, /* Misc. commands and operands. */ + SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */ + SCBCtrlMDI = 16, /* MDI interface control. */ + SCBEarlyRx = 20, /* Early receive byte count. */ +}; +/* Commands that can be put in a command list entry. */ +enum commands { + CmdNOp = 0, CmdIASetup = 1, CmdConfigure = 2, CmdMulticastList = 3, + CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7, + CmdSuspend = 0x4000, /* Suspend after completion. */ + CmdIntr = 0x2000, /* Interrupt after completion. */ + CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */ +}; + +/* The SCB accepts the following controls for the Tx and Rx units: */ +#define CU_START 0x0010 +#define CU_RESUME 0x0020 +#define CU_STATSADDR 0x0040 +#define CU_SHOWSTATS 0x0050 /* Dump statistics counters. */ +#define CU_CMD_BASE 0x0060 /* Base address to add to add CU commands. */ +#define CU_DUMPSTATS 0x0070 /* Dump then reset stats counters. */ + +#define RX_START 0x0001 +#define RX_RESUME 0x0002 +#define RX_ABORT 0x0004 +#define RX_ADDR_LOAD 0x0006 +#define RX_RESUMENR 0x0007 +#define INT_MASK 0x0100 +#define DRVR_INT 0x0200 /* Driver generated interrupt. */ + +/* The Speedo3 Rx and Tx frame/buffer descriptors. */ +struct descriptor { /* A generic descriptor. */ + s16 status; /* Offset 0. */ + s16 command; /* Offset 2. */ + u32 link; /* struct descriptor * */ + unsigned char params[0]; +}; + +/* The Speedo3 Rx and Tx buffer descriptors. */ +struct RxFD { /* Receive frame descriptor. */ + s32 status; + u32 link; /* struct RxFD * */ + u32 rx_buf_addr; /* void * */ + u16 count; + u16 size; +}; + +/* Elements of the RxFD.status word. */ +#define RX_COMPLETE 0x8000 + +struct TxFD { /* Transmit frame descriptor set. */ + s32 status; + u32 link; /* void * */ + u32 tx_desc_addr; /* Always points to the tx_buf_addr element. */ + s32 count; /* # of TBD (=1), Tx start thresh., etc. */ + /* This constitutes a single "TBD" entry -- we only use one. */ + u32 tx_buf_addr; /* void *, frame to be transmitted. */ + s32 tx_buf_size; /* Length of Tx frame. */ +}; + +/* Elements of the dump_statistics block. This block must be lword aligned. */ +struct speedo_stats { + u32 tx_good_frames; + u32 tx_coll16_errs; + u32 tx_late_colls; + u32 tx_underruns; + u32 tx_lost_carrier; + u32 tx_deferred; + u32 tx_one_colls; + u32 tx_multi_colls; + u32 tx_total_colls; + u32 rx_good_frames; + u32 rx_crc_errs; + u32 rx_align_errs; + u32 rx_resource_errs; + u32 rx_overrun_errs; + u32 rx_colls_errs; + u32 rx_runt_errs; + u32 done_marker; +}; + +struct speedo_private { + char devname[8]; /* Used only for kernel debugging. */ + const char *product_name; + struct device *next_module; + struct TxFD tx_ring[TX_RING_SIZE]; /* Commands (usually CmdTxPacket). */ + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + struct descriptor *last_cmd; /* Last command sent. */ + /* Rx descriptor ring & addresses of receive-in-place skbuffs. */ + struct RxFD *rx_ringp[RX_RING_SIZE]; + struct sk_buff* rx_skbuff[RX_RING_SIZE]; +#if (LINUX_VERSION_CODE < 0x10300) /* Kernel v1.2.*. */ + struct RxFD saved_skhead[RX_RING_SIZE]; /* Saved skbuff header chunk. */ +#endif + struct RxFD *last_rxf; /* Last command sent. */ + struct enet_statistics stats; + struct speedo_stats lstats; + struct timer_list timer; /* Media selection timer. */ + long last_rx_time; /* Last Rx, in jiffies, to handle Rx hang. */ + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + struct descriptor config_cmd; /* A configure command, with header... */ + u8 config_cmd_data[22]; /* .. and setup parameters. */ + int mc_setup_frm_len; /* The length of an allocated.. */ + struct descriptor *mc_setup_frm; /* ..multicast setup frame. */ + char rx_mode; /* Current PROMISC/ALLMULTI setting. */ + unsigned int tx_full:1; /* The Tx queue is full. */ + unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int default_port:1; /* Last dev->if_port value. */ + unsigned int rx_bug:1; /* Work around receiver hang errata. */ + unsigned int rx_bug10:1; /* Receiver might hang at 10mbps. */ + unsigned int rx_bug100:1; /* Receiver might hang at 100mbps. */ + unsigned short phy[2]; /* PHY media interfaces available. */ +}; + +/* The parameters for a CmdConfigure operation. + There are so many options that it would be difficult to document each bit. + We mostly use the default or recommended settings. */ +const char basic_config_cmd[22] = { + 22, 0x08, 0, 0, 0, 0x80, 0x32, 0x03, 1, /* 1=Use MII 0=Use AUI */ + 0, 0x2E, 0, 0x60, 0, + 0xf2, 0x48, 0, 0x40, 0xf2, 0x80, /* 0x40=Force full-duplex */ + 0x3f, 0x05, }; + +/* PHY media interface chips. */ +static const char *phys[] = { + "None", "i82553-A/B", "i82553-C", "i82503", + "DP83840", "80c240", "80c24", "unknown-7", + "unknown-8", "unknown-9", "DP83840A", "unknown-11", + "unknown-12", "unknown-13", "unknown-14", "unknown-15", }; +enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240, + S80C24, PhyUndefined, DP83840A=10, }; +static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 }; + +static void speedo_found1(struct device *dev, int ioaddr, int irq, int options); + +static int read_eeprom(int ioaddr, int location); +static int mdio_read(int ioaddr, int phy_id, int location); +static int mdio_write(int ioaddr, int phy_id, int location, int value); +static int speedo_open(struct device *dev); +static void speedo_timer(unsigned long data); +static void speedo_init_rx_ring(struct device *dev); +static int speedo_start_xmit(struct sk_buff *skb, struct device *dev); +static int speedo_rx(struct device *dev); +#ifdef SA_SHIRQ +static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +#else +static void speedo_interrupt(int irq, struct pt_regs *regs); +#endif +static int speedo_close(struct device *dev); +static struct enet_statistics *speedo_get_stats(struct device *dev); +static void set_rx_mode(struct device *dev); + + + +#ifdef MODULE +/* The parameters that may be passed in... */ +/* 'options' is used to pass a transceiver override or full-duplex flag + e.g. "options=16" for FD, "options=32" for 100mbps-only. */ +static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int debug = -1; /* The debug level */ + +/* A list of all installed Speedo devices, for removing the driver module. */ +static struct device *root_speedo_dev = NULL; +#endif + +int eepro100_init(struct device *dev) +{ + int cards_found = 0; + + if (pcibios_present()) { + int pci_index; + for (pci_index = 0; pci_index < 8; pci_index++) { + unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency; +#if (LINUX_VERSION_CODE >= VERSION(1,3,44)) + int pci_ioaddr; +#else + long pci_ioaddr; +#endif + unsigned short pci_command; + + if (pcibios_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82557, + pci_index, &pci_bus, + &pci_device_fn)) + break; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + /* Note: BASE_ADDRESS_0 is for memory-mapping the registers. */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr); + /* Remove I/O space marker in bit 0. */ + pci_ioaddr &= ~3; + if (speedo_debug > 2) + printk("Found Intel i82557 PCI Speedo at I/O %#x, IRQ %d.\n", + (int)pci_ioaddr, pci_irq_line); + + /* Get and check the bus-master and latency values. */ + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + if ( ! (pci_command & PCI_COMMAND_MASTER)) { + printk(" PCI Master Bit has not been set! Setting...\n"); + pci_command |= PCI_COMMAND_MASTER; + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, pci_command); + } + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < 10) { + printk(" PCI latency timer (CFLT) is unreasonably low at %d." + " Setting to 255 clocks.\n", pci_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, 255); + } else if (speedo_debug > 1) + printk(" PCI latency timer (CFLT) is %#x.\n", pci_latency); + +#ifdef MODULE + speedo_found1(dev, pci_ioaddr, pci_irq_line, options[cards_found]); +#else + speedo_found1(dev, pci_ioaddr, pci_irq_line, + dev ? dev->mem_start : 0); +#endif + cards_found++; + } + } + + return cards_found; +} + +static void speedo_found1(struct device *dev, int ioaddr, int irq, int options) +{ + static int did_version = 0; /* Already printed version info. */ + struct speedo_private *sp; + int i; + u16 eeprom[0x40]; + + if (speedo_debug > 0 && did_version++ == 0) + printk(version); + +#if (LINUX_VERSION_CODE >= VERSION(1,3,44)) + dev = init_etherdev(dev, sizeof(struct speedo_private)); +#else + dev = init_etherdev(dev, sizeof(struct speedo_private), 0); +#endif + + /* Read the station address EEPROM before doing the reset. + Perhaps this should even be done before accepting the device, + then we wouldn't have a device name with which to report the error. */ + { + u16 sum = 0; + int j; + for (j = 0, i = 0; i < 0x40; i++) { + unsigned short value = read_eeprom(ioaddr, i); + eeprom[i] = value; + sum += value; + if (i < 3) { + dev->dev_addr[j++] = value; + dev->dev_addr[j++] = value >> 8; + } + } + if (sum != 0xBABA) + printk(KERN_WARNING "%s: Invalid EEPROM checksum %#4.4x, " + "check settings before activating this device!\n", + dev->name, sum); + /* Don't unregister_netdev(dev); as the EEPro may actually be + usable, especially if the MAC address is set later. */ + } + + /* Reset the chip: stop Tx and Rx processes and clear counters. + This takes less than 10usec and will easily finish before the next + action. */ + outl(0, ioaddr + SCBPort); + + printk(KERN_INFO "%s: Intel EtherExpress Pro 10/100 at %#3x, ", + dev->name, ioaddr); + for (i = 0; i < 5; i++) + printk("%2.2X:", dev->dev_addr[i]); + printk("%2.2X, IRQ %d.\n", dev->dev_addr[i], irq); + +#ifndef kernel_bloat + /* OK, this is pure kernel bloat. I don't like it when other drivers + waste non-pageable kernel space to emit similar messages, but I need + them for bug reports. */ + { + const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"}; + /* The self-test results must be paragraph aligned. */ + int str[6], *volatile self_test_results; + int boguscnt = 16000; /* Timeout for set-test. */ + if (eeprom[3] & 0x03) + printk(KERN_INFO " Receiver lock-up bug exists -- enabling" + " work-around.\n"); + printk(KERN_INFO " Board assembly %4.4x%2.2x-%3.3d, Physical" + " connectors present:", + eeprom[8], eeprom[9]>>8, eeprom[9] & 0xff); + for (i = 0; i < 4; i++) + if (eeprom[5] & (1<>8)&15], eeprom[6] & 0x1f); + if (eeprom[7] & 0x0700) + printk(KERN_INFO " Secondary interface chip %s.\n", + phys[(eeprom[7]>>8)&7]); +#if defined(notdef) + /* ToDo: Read and set PHY registers through MDIO port. */ + for (i = 0; i < 2; i++) + printk(" MDIO register %d is %4.4x.\n", + i, mdio_read(ioaddr, eeprom[6] & 0x1f, i)); + for (i = 5; i < 7; i++) + printk(" MDIO register %d is %4.4x.\n", + i, mdio_read(ioaddr, eeprom[6] & 0x1f, i)); + printk(" MDIO register %d is %4.4x.\n", + 25, mdio_read(ioaddr, eeprom[6] & 0x1f, 25)); +#endif + if (((eeprom[6]>>8) & 0x3f) == DP83840 + || ((eeprom[6]>>8) & 0x3f) == DP83840A) { + int mdi_reg23 = mdio_read(ioaddr, eeprom[6] & 0x1f, 23) | 0x0422; + if (congenb) + mdi_reg23 |= 0x0100; + printk(" DP83840 specific setup, setting register 23 to %4.4x.\n", + mdi_reg23); + mdio_write(ioaddr, eeprom[6] & 0x1f, 23, mdi_reg23); + } + if ((options >= 0) && (options & 0x60)) { + printk(KERN_INFO " Forcing %dMbs %s-duplex operation.\n", + (options & 0x20 ? 100 : 10), + (options & 0x10 ? "full" : "half")); + mdio_write(ioaddr, eeprom[6] & 0x1f, 0, + ((options & 0x20) ? 0x2000 : 0) | /* 100mbps? */ + ((options & 0x10) ? 0x0100 : 0)); /* Full duplex? */ + } + + /* Perform a system self-test. */ + self_test_results = (int*) ((((int) str) + 15) & ~0xf); + self_test_results[0] = 0; + self_test_results[1] = -1; + outl(virt_to_bus(self_test_results) | 1, ioaddr + SCBPort); + do { +#ifdef _LINUX_DELAY_H + udelay(10); +#else + SLOW_DOWN_IO; +#endif + } while (self_test_results[1] == -1 && --boguscnt >= 0); + + if (boguscnt < 0) { /* Test optimized out. */ + printk(KERN_ERR "Self test failed, status %8.8x:\n" + KERN_ERR " Failure to initialize the i82557.\n" + KERN_ERR " Verify that the card is a bus-master" + " capable slot.\n", + self_test_results[1]); + } else + printk(KERN_INFO " General self-test: %s.\n" + KERN_INFO " Serial sub-system self-test: %s.\n" + KERN_INFO " Internal registers self-test: %s.\n" + KERN_INFO " ROM checksum self-test: %s (%#8.8x).\n", + self_test_results[1] & 0x1000 ? "failed" : "passed", + self_test_results[1] & 0x0020 ? "failed" : "passed", + self_test_results[1] & 0x0008 ? "failed" : "passed", + self_test_results[1] & 0x0004 ? "failed" : "passed", + self_test_results[0]); + } +#endif /* kernel_bloat */ + + /* We do a request_region() only to register /proc/ioports info. */ + request_region(ioaddr, SPEEDO3_TOTAL_SIZE, "Intel Speedo3 Ethernet"); + + dev->base_addr = ioaddr; + dev->irq = irq; + + if (dev->priv == NULL) + dev->priv = kmalloc(sizeof(*sp), GFP_KERNEL); + sp = dev->priv; + memset(sp, 0, sizeof(*sp)); +#ifdef MODULE + sp->next_module = root_speedo_dev; + root_speedo_dev = dev; +#endif + + sp->full_duplex = options >= 0 && (options & 0x10) ? 1 : 0; + sp->default_port = options >= 0 ? (options & 0x0f) : 0; + + sp->phy[0] = eeprom[6]; + sp->phy[1] = eeprom[7]; + sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1; + + printk(KERN_INFO " Operating in %s duplex mode.\n", + sp->full_duplex ? "full" : "half"); + if (sp->rx_bug) + printk(KERN_INFO " Reciever lock-up workaround activated.\n"); + + /* The Speedo-specific entries in the device structure. */ + dev->open = &speedo_open; + dev->hard_start_xmit = &speedo_start_xmit; + dev->stop = &speedo_close; + dev->get_stats = &speedo_get_stats; +#ifdef NEW_MULTICAST + dev->set_multicast_list = &set_rx_mode; +#endif + + return; +} + +/* Serial EEPROM section. + A "bit" grungy, but we work our way through bit-by-bit :->. */ +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */ +#define EE_CS 0x02 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x01 +#define EE_WRITE_1 0x05 +#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define EE_ENB (0x4800 | EE_CS) + +/* Delay between EEPROM clock transitions. + This is a "nasty" timing loop, but PC compatible machines are defined + to delay an ISA compatible period for the SLOW_DOWN_IO macro. */ +#define eeprom_delay(nanosec) do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5 << 6) +#define EE_READ_CMD (6 << 6) +#define EE_ERASE_CMD (7 << 6) + +static int read_eeprom(int ioaddr, int location) +{ + int i; + unsigned short retval = 0; + int ee_addr = ioaddr + SCBeeprom; + int read_cmd = location | EE_READ_CMD; + + outw(EE_ENB & ~EE_CS, ee_addr); + outw(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; + outw(EE_ENB | dataval, ee_addr); + eeprom_delay(100); + outw(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(150); + outw(EE_ENB | dataval, ee_addr); /* Finish EEPROM a clock tick. */ + eeprom_delay(250); + } + outw(EE_ENB, ee_addr); + + for (i = 15; i >= 0; i--) { + outw(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(100); + retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0); + outw(EE_ENB, ee_addr); + eeprom_delay(100); + } + + /* Terminate the EEPROM access. */ + outw(EE_ENB & ~EE_CS, ee_addr); + return retval; +} + +static int mdio_read(int ioaddr, int phy_id, int location) +{ + int val, boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */ + outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI); + do { +#ifdef _LINUX_DELAY_H + udelay(16); +#else + SLOW_DOWN_IO; +#endif + val = inl(ioaddr + SCBCtrlMDI); + if (--boguscnt < 0) { + printk(KERN_ERR " mdio_read() timed out with val = %8.8x.\n", val); + } + } while (! (val & 0x10000000)); + return val & 0xffff; +} + +static int mdio_write(int ioaddr, int phy_id, int location, int value) +{ + int val, boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */ + outl(0x04000000 | (location<<16) | (phy_id<<21) | value, + ioaddr + SCBCtrlMDI); + do { +#ifdef _LINUX_DELAY_H + udelay(16); +#else + SLOW_DOWN_IO; +#endif + val = inl(ioaddr + SCBCtrlMDI); + if (--boguscnt < 0) { + printk(KERN_ERR" mdio_write() timed out with val = %8.8x.\n", val); + } + } while (! (val & 0x10000000)); + return val & 0xffff; +} + + +static int +speedo_open(struct device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int ioaddr = dev->base_addr; + +#ifdef notdef + /* We could reset the chip, but should not need to. */ + outl(0, ioaddr + SCBPort); + for (i = 40; i >= 0; i--) + SLOW_DOWN_IO; /* At least 250ns */ +#endif + +#ifdef SA_SHIRQ + if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ, + "Intel EtherExpress Pro 10/100 Ethernet", dev)) { + return -EAGAIN; + } +#else +#ifdef USE_SHARED_IRQ + if (request_shared_irq(dev->irq, &speedo_interrupt, dev, + "Intel EtherExpress Pro 10/100 Ethernet")) + return -EAGAIN; +#else + if (dev->irq < 2 || dev->irq > 15 || irq2dev_map[dev->irq] != NULL) + return -EAGAIN; + irq2dev_map[dev->irq] = dev; + if (request_irq(dev->irq, &speedo_interrupt, 0, "Intel EtherExpress Pro 10/100 Ethernet")) { + irq2dev_map[dev->irq] = NULL; + return -EAGAIN; + } +#endif +#endif + + if (speedo_debug > 1) + printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq); + + MOD_INC_USE_COUNT; + + /* Load the statistics block address. */ + outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer); + outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd); + sp->lstats.done_marker = 0; + + speedo_init_rx_ring(dev); + outl(0, ioaddr + SCBPointer); + outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd); + + /* Todo: verify that we must wait for previous command completion. */ + wait_for_cmd_done(ioaddr + SCBCmd); + outl(virt_to_bus(sp->rx_ringp[0]), ioaddr + SCBPointer); + outw(INT_MASK | RX_START, ioaddr + SCBCmd); + + /* Fill the first command with our physical address. */ + { + unsigned short *eaddrs = (unsigned short *)dev->dev_addr; + unsigned short *setup_frm = (short *)&(sp->tx_ring[0].tx_desc_addr); + + /* Avoid a bug(?!) here by marking the command already completed. */ + sp->tx_ring[0].status = ((CmdSuspend | CmdIASetup) << 16) | 0xa000; + sp->tx_ring[0].link = virt_to_bus(&(sp->tx_ring[1])); + *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; + } + sp->last_cmd = (struct descriptor *)&sp->tx_ring[0]; + sp->cur_tx = 1; + sp->dirty_tx = 0; + sp->tx_full = 0; + + outl(0, ioaddr + SCBPointer); + outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd); + + dev->if_port = sp->default_port; + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + /* Start the chip's Tx process and unmask interrupts. */ + /* Todo: verify that we must wait for previous command completion. */ + wait_for_cmd_done(ioaddr + SCBCmd); + outl(virt_to_bus(&sp->tx_ring[0]), ioaddr + SCBPointer); + outw(CU_START, ioaddr + SCBCmd); + + /* Setup the chip and configure the multicast list. */ + sp->mc_setup_frm = NULL; + sp->mc_setup_frm_len = 0; + sp->rx_mode = -1; /* Invalid -> always reset the mode. */ + set_rx_mode(dev); + + if (speedo_debug > 2) { + printk(KERN_DEBUG "%s: Done speedo_open(), status %8.8x.\n", + dev->name, inw(ioaddr + SCBStatus)); + } + /* Set the timer. The timer serves a dual purpose: + 1) to monitor the media interface (e.g. link beat) and perhaps switch + to an alternate media type + 2) to monitor Rx activity, and restart the Rx process if the receiver + hangs. */ + init_timer(&sp->timer); + sp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ + sp->timer.data = (unsigned long)dev; + sp->timer.function = &speedo_timer; /* timer handler */ + add_timer(&sp->timer); + + outw(CU_DUMPSTATS, ioaddr + SCBCmd); + return 0; +} + +/* Media monitoring and control. */ +static void speedo_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int tickssofar = jiffies - sp->last_rx_time; + + if (speedo_debug > 3) { + int ioaddr = dev->base_addr; + printk(KERN_DEBUG "%s: Media selection tick, status %4.4x.\n", + dev->name, inw(ioaddr + SCBStatus)); + } + if (sp->rx_bug) { + if (tickssofar > 2*HZ || sp->rx_mode < 0) { + /* We haven't received a packet in a Long Time. We might have been + bitten by the receiver hang bug. This can be cleared by sending + a set multicast list command. */ + set_rx_mode(dev); + } + /* We must continue to monitor the media. */ + sp->timer.expires = RUN_AT(2*HZ); /* 2.0 sec. */ + add_timer(&sp->timer); + } +} + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void +speedo_init_rx_ring(struct device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + struct RxFD *rxf, *last_rxf = NULL; + int i; + + sp->cur_rx = 0; + sp->dirty_rx = RX_RING_SIZE - 1; + + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb; +#ifndef KERNEL_1_2 + skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD)); +#else + skb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC); +#endif + sp->rx_skbuff[i] = skb; + if (skb == NULL) + break; /* Bad news! */ + skb->dev = dev; /* Mark as being used by this device. */ + +#if LINUX_VERSION_CODE >= 0x10300 + rxf = (struct RxFD *)skb->tail; + skb_reserve(skb, sizeof(struct RxFD)); +#else + /* Save the data in the header region -- it's restored later. */ + rxf = (struct RxFD *)(skb->data - sizeof(struct RxFD)); + memcpy(&sp->saved_skhead[i], rxf, sizeof(struct RxFD)); +#endif + sp->rx_ringp[i] = rxf; + if (last_rxf) + last_rxf->link = virt_to_bus(rxf); + last_rxf = rxf; + rxf->status = 0x00000001; /* '1' is flag value only. */ + rxf->link = 0; /* None yet. */ +#if LINUX_VERSION_CODE < 0x10300 + /* This field unused by i82557, we use it as a consistency check. */ + rxf->rx_buf_addr = virt_to_bus(skb->data); +#else + rxf->rx_buf_addr = virt_to_bus(skb->tail); +#endif + rxf->count = 0; + rxf->size = PKT_BUF_SZ; + } + /* Mark the last entry as end-of-list. */ + last_rxf->status = 0xC0000002; /* '2' is flag value only. */ + sp->last_rxf = last_rxf; +} + +static void speedo_tx_timeout(struct device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int ioaddr = dev->base_addr; + int i; + + printk(KERN_WARNING "%s: Transmit timed out: status %4.4x " + "command %4.4x.\n", + dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd)); +#ifndef final_version + printk("%s: Tx timeout fill index %d scavenge index %d.\n", + dev->name, sp->cur_tx, sp->dirty_tx); + printk(" Tx queue "); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" %8.8x", (int)sp->tx_ring[i].status); + printk(".\n Rx ring "); + for (i = 0; i < RX_RING_SIZE; i++) + printk(" %8.8x", (int)sp->rx_ringp[i]->status); + printk(".\n"); + +#else + dev->if_port ^= 1; + printk(" (Media type switching not yet implemented.)\n"); + /* Do not do 'dev->tbusy = 0;' there -- it is incorrect. */ +#endif + if ((inw(ioaddr + SCBStatus) & 0x00C0) != 0x0080) { + printk("%s: Trying to restart the transmitter...\n", dev->name); + outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]), + ioaddr + SCBPointer); + outw(CU_START, ioaddr + SCBCmd); + } else { + outw(DRVR_INT, ioaddr + SCBCmd); + } + sp->stats.tx_errors++; + dev->trans_start = jiffies; + return; +} + +static int +speedo_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int ioaddr = dev->base_addr; + int entry; + + if (skb == NULL || skb->len <= 0) { + printk(KERN_ERR "%s: Obsolete driver layer request made: skbuff==NULL.\n", + dev->name); + dev_tint(dev); + return 0; + } + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. + If this ever occurs the queue layer is doing something evil! */ + if (set_bit(0, (void*)&dev->tbusy) != 0) { + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < TX_TIMEOUT - 2) + return 1; + if (tickssofar < TX_TIMEOUT) { + /* Reap sent packets from the full Tx queue. */ + outw(DRVR_INT, ioaddr + SCBCmd); + return 1; + } + speedo_tx_timeout(dev); + return 0; + } + + /* Caution: the write order is important here, set the base address + with the "ownership" bits last. */ + + { /* Prevent interrupts from changing the Tx ring from underneath us. */ + unsigned long flags; + + save_flags(flags); + cli(); + /* Calculate the Tx descriptor entry. */ + entry = sp->cur_tx++ % TX_RING_SIZE; + + sp->tx_skbuff[entry] = skb; + /* Todo: be a little more clever about setting the interrupt bit. */ + sp->tx_ring[entry].status = + (CmdSuspend | CmdTx | CmdTxFlex) << 16; + sp->tx_ring[entry].link = + virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]); + sp->tx_ring[entry].tx_desc_addr = + virt_to_bus(&sp->tx_ring[entry].tx_buf_addr); + /* The data region is always in one buffer descriptor, Tx FIFO + threshold of 256. */ + sp->tx_ring[entry].count = 0x01208000; + sp->tx_ring[entry].tx_buf_addr = virt_to_bus(skb->data); + sp->tx_ring[entry].tx_buf_size = skb->len; + /* Todo: perhaps leave the interrupt bit set if the Tx queue is more + than half full. Argument against: we should be receiving packets + and scavenging the queue. Argument for: if so, it shouldn't + matter. */ + sp->last_cmd->command &= ~(CmdSuspend | CmdIntr); + sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; + /* Trigger the command unit resume. */ + outw(CU_RESUME, ioaddr + SCBCmd); + restore_flags(flags); + } + + /* Leave room for set_rx_mode() to fill two entries. */ + if (sp->cur_tx - sp->dirty_tx > TX_RING_SIZE - 3) + sp->tx_full = 1; + else + dev->tbusy = 0; + + dev->trans_start = jiffies; + + return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +#ifdef SA_SHIRQ +static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +#else +static void speedo_interrupt(int irq, struct pt_regs *regs) +#endif +{ +#ifdef SA_SHIRQ + struct device *dev = (struct device *)dev_instance; +#else +#ifdef USE_SHARED_IRQ + struct device *dev = (struct device *)(irq == 0 ? regs : irq2dev_map[irq]); +#else + struct device *dev = (struct device *)(irq2dev_map[irq]); +#endif +#endif + struct speedo_private *sp; + int ioaddr, boguscnt = INTR_WORK; + unsigned short status; + +#ifndef final_version + if (dev == NULL) { + printk(KERN_ERR "speedo_interrupt(): irq %d for unknown device.\n", irq); + return; + } +#endif + + ioaddr = dev->base_addr; + sp = (struct speedo_private *)dev->priv; +#ifndef final_version + if (dev->interrupt) { + printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); + return; + } + dev->interrupt = 1; +#endif + + do { + status = inw(ioaddr + SCBStatus); + /* Acknowledge all of the current interrupt sources ASAP. */ + outw(status & 0xfc00, ioaddr + SCBStatus); + + if (speedo_debug > 4) + printk(KERN_DEBUG "%s: interrupt status=%#4.4x.\n", + dev->name, status); + + if ((status & 0xfc00) == 0) + break; + + if (status & 0x4000) /* Packet received. */ + speedo_rx(dev); + + if (status & 0x1000) { +#ifdef notdef + int i; + printk(KERN_WARNING"%s: The EEPro100 receiver left the ready" + " state -- %4.4x! Index %d (%d).\n", dev->name, status, + sp->cur_rx, sp->cur_rx % RX_RING_SIZE); + printk(" Rx ring:\n "); + for (i = 0; i < RX_RING_SIZE; i++) + printk(" %d %8.8x %8.8x %8.8x %d %d.\n", + i, sp->rx_ringp[i]->status, sp->rx_ringp[i]->link, + sp->rx_ringp[i]->rx_buf_addr, sp->rx_ringp[i]->count, + sp->rx_ringp[i]->size); +#endif + + if ((status & 0x003c) == 0x0028) /* No more Rx buffers. */ + outw(RX_RESUMENR, ioaddr + SCBCmd); + else if ((status & 0x003c) == 0x0008) { /* No resources (why?!) */ + /* No idea of what went wrong. Restart the receiver. */ + outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]), + ioaddr + SCBPointer); + outw(RX_START, ioaddr + SCBCmd); + } + sp->stats.rx_errors++; + } + + /* User interrupt, Command/Tx unit interrupt or CU not active. */ + if (status & 0xA400) { + unsigned int dirty_tx = sp->dirty_tx; + + while (sp->cur_tx - dirty_tx > 0) { + int entry = dirty_tx % TX_RING_SIZE; + int status = sp->tx_ring[entry].status; + + if (speedo_debug > 5) + printk(KERN_DEBUG " scavenge canidate %d status %4.4x.\n", + entry, status); + if ((status & 0x8000) == 0) + break; /* It still hasn't been processed. */ + /* Free the original skb. */ + if (sp->tx_skbuff[entry]) { + sp->stats.tx_packets++; /* Count only user packets. */ + dev_kfree_skb(sp->tx_skbuff[entry], FREE_WRITE); + sp->tx_skbuff[entry] = 0; + } + dirty_tx++; + } + +#ifndef final_version + if (sp->cur_tx - dirty_tx > TX_RING_SIZE) { + printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dirty_tx, sp->cur_tx, sp->tx_full); + dirty_tx += TX_RING_SIZE; + } +#endif + + if (sp->tx_full && dev->tbusy + && dirty_tx > sp->cur_tx - TX_RING_SIZE + 2) { + /* The ring is no longer full, clear tbusy. */ + sp->tx_full = 0; + dev->tbusy = 0; + mark_bh(NET_BH); + } + + sp->dirty_tx = dirty_tx; + } + + if (--boguscnt < 0) { + printk("%s: Too much work at interrupt, status=0x%4.4x.\n", + dev->name, status); + /* Clear all interrupt sources. */ + outl(0xfc00, ioaddr + SCBStatus); + break; + } + } while (1); + + if (speedo_debug > 3) + printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", + dev->name, inw(ioaddr + SCBStatus)); + +#ifndef final_version + /* Special code for testing *only*. */ + { + static int stopit = 100; + if (dev->start == 0 && --stopit < 0) { + printk(KERN_ALERT "%s: Emergency stop, interrupt is stuck.\n", + dev->name); +#ifdef SA_SHIRQ + free_irq(irq, dev); +#else + free_irq(irq); +#endif + } + } +#endif + + dev->interrupt = 0; + return; +} + +static int +speedo_rx(struct device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int entry = sp->cur_rx % RX_RING_SIZE; + int status; + + if (speedo_debug > 4) + printk(KERN_DEBUG " In speedo_rx().\n"); + /* If we own the next entry, it's a new packet. Send it up. */ + while ((status = sp->rx_ringp[entry]->status) & RX_COMPLETE) { + + if (speedo_debug > 4) + printk(KERN_DEBUG " speedo_rx() status %8.8x len %d.\n", status, + sp->rx_ringp[entry]->count & 0x3fff); + if (status & 0x0200) { + printk("%s: Ethernet frame overran the Rx buffer, status %8.8x!\n", + dev->name, status); + } else if ( ! (status & 0x2000)) { + /* There was a fatal error. This *should* be impossible. */ + sp->stats.rx_errors++; + printk("%s: Anomalous event in speedo_rx(), status %8.8x.\n", + dev->name, status); + } else { + /* Malloc up new buffer, compatible with net-2e. */ + short pkt_len = sp->rx_ringp[entry]->count & 0x3fff; + 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 > SKBUFF_RX_COPYBREAK) { + struct sk_buff *newskb; + char *temp; + + /* Pass up the skb already on the Rx ring. */ + skb = sp->rx_skbuff[entry]; +#ifdef KERNEL_1_2 + temp = skb->data; + if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp) + printk("%s: Warning -- the skbuff addresses do not match" + " in speedo_rx: %p vs. %p / %p.\n", dev->name, + bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), + temp, skb->data); + /* Get a fresh skbuff to replace the filled one. */ + newskb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC); +#else + temp = skb_put(skb, pkt_len); + if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp) + printk("%s: Warning -- the skbuff addresses do not match" + " in speedo_rx: %8.8x vs. %p / %p.\n", dev->name, + sp->rx_ringp[entry]->rx_buf_addr, skb->head, temp); + /* Get a fresh skbuff to replace the filled one. */ + newskb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD)); +#endif + if (newskb) { + struct RxFD *rxf; + rx_in_place = 1; + sp->rx_skbuff[entry] = newskb; + newskb->dev = dev; +#ifdef KERNEL_1_2 + /* Restore the data in the old header region. */ + memcpy(skb->data - sizeof(struct RxFD), + &sp->saved_skhead[entry], sizeof(struct RxFD)); + /* Save the data in this header region. */ + rxf = (struct RxFD *)(newskb->data - sizeof(struct RxFD)); + sp->rx_ringp[entry] = rxf; + memcpy(&sp->saved_skhead[entry], rxf, sizeof(struct RxFD)); + rxf->rx_buf_addr = virt_to_bus(newskb->data); +#else + rxf = sp->rx_ringp[entry] = (struct RxFD *)newskb->tail; + skb_reserve(newskb, sizeof(struct RxFD)); + /* Unused by i82557, consistency check only. */ + rxf->rx_buf_addr = virt_to_bus(newskb->tail); +#endif + rxf->status = 0x00000001; + } else /* No memory, drop the packet. */ + skb = 0; + } else +#ifdef KERNEL_1_2 + skb = alloc_skb(pkt_len, GFP_ATOMIC); +#else + skb = dev_alloc_skb(pkt_len + 2); +#endif + 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++. */ + /* ToDo: This is not correct!!!! We should count the number + of linked-in Rx buffer to very that we have at least two + remaining. */ + for (i = 0; i < RX_RING_SIZE; i++) + if (! ((sp->rx_ringp[(entry+i) % RX_RING_SIZE]->status) + & RX_COMPLETE)) + break; + + if (i > RX_RING_SIZE -2) { + sp->stats.rx_dropped++; + sp->rx_ringp[entry]->status = 0; + sp->cur_rx++; + } + break; + } + skb->dev = dev; +#if (LINUX_VERSION_CODE >= VERSION(1,3,44)) + if (! rx_in_place) { + skb_reserve(skb, 2); /* 16 byte align the data fields */ + memcpy(skb_put(skb, pkt_len), + bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len); + } + skb->protocol = eth_type_trans(skb, dev); +#else +#ifdef KERNEL_1_3 +#warning This code has only been tested with later 1.3.* kernels. + skb->len = pkt_len; + memcpy(skb->data, bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), + pkt_len); + /* Needed for 1.3.*. */ + skb->protocol = eth_type_trans(skb, dev); +#else /* KERNEL_1_2 */ + skb->len = pkt_len; + if (! rx_in_place) { + memcpy(skb->data, + bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len); + } +#endif +#endif + netif_rx(skb); + sp->stats.rx_packets++; + } + + /* ToDo: This is better than before, but should be checked. */ + { + struct RxFD *rxf = sp->rx_ringp[entry]; + rxf->status = 0xC0000003; /* '3' for verification only */ + rxf->link = 0; /* None yet. */ + rxf->count = 0; + rxf->size = PKT_BUF_SZ; + sp->last_rxf->link = virt_to_bus(rxf); + sp->last_rxf->status &= ~0xC0000000; + sp->last_rxf = rxf; + entry = (++sp->cur_rx) % RX_RING_SIZE; + } + } + + sp->last_rx_time = jiffies; + return 0; +} + +static int +speedo_close(struct device *dev) +{ + int ioaddr = dev->base_addr; + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int i; + + dev->start = 0; + dev->tbusy = 1; + + if (speedo_debug > 1) + printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n", + dev->name, inw(ioaddr + SCBStatus)); + + /* Shut off the media monitoring timer. */ + del_timer(&sp->timer); + + /* Disable interrupts, and stop the chip's Rx process. */ + outw(INT_MASK, ioaddr + SCBCmd); + outw(INT_MASK | RX_ABORT, ioaddr + SCBCmd); + +#ifdef SA_SHIRQ + free_irq(dev->irq, dev); +#else + free_irq(dev->irq); + irq2dev_map[dev->irq] = 0; +#endif + + /* Free all the skbuffs in the Rx and Tx queues. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = sp->rx_skbuff[i]; + sp->rx_skbuff[i] = 0; + /* Clear the Rx descriptors. */ + if (skb) + dev_kfree_skb(skb, FREE_WRITE); + } + + for (i = 0; i < TX_RING_SIZE; i++) { + struct sk_buff *skb = sp->tx_skbuff[i]; + sp->tx_skbuff[i] = 0; + /* Clear the Tx descriptors. */ + if (skb) + dev_kfree_skb(skb, FREE_WRITE); + } + if (sp->mc_setup_frm) { + kfree(sp->mc_setup_frm); + sp->mc_setup_frm_len = 0; + } + + /* Print a few items for debugging. */ + if (speedo_debug > 3) { + printk("%s:Printing Rx ring (next to receive into %d).\n", + dev->name, sp->cur_rx); + + for (i = 0; i < RX_RING_SIZE; i++) + printk(" Rx ring entry %d %8.8x.\n", + i, (int)sp->rx_ringp[i]->status); + + for (i = 0; i < 5; i++) + printk(" PHY index %d register %d is %4.4x.\n", + 1, i, mdio_read(ioaddr, 1, i)); + for (i = 21; i < 26; i++) + printk(" PHY index %d register %d is %4.4x.\n", + 1, i, mdio_read(ioaddr, 1, i)); + } + MOD_DEC_USE_COUNT; + + return 0; +} + +/* The Speedo-3 has an especially awkward and unusable method of getting + statistics out of the chip. It takes an unpredictable length of time + for the dump-stats command to complete. To avoid a busy-wait loop we + update the stats with the previous dump results, and then trigger a + new dump. + + These problems are mitigated by the current /proc implementation, which + calls this routine first to judge the output length, and then to emit the + output. + + Oh, and incoming frames are dropped while executing dump-stats! + */ +static struct enet_statistics * +speedo_get_stats(struct device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int ioaddr = dev->base_addr; + + if (sp->lstats.done_marker == 0xA007) { /* Previous dump finished */ + sp->stats.tx_aborted_errors += sp->lstats.tx_coll16_errs; + sp->stats.tx_window_errors += sp->lstats.tx_late_colls; + sp->stats.tx_fifo_errors += sp->lstats.tx_underruns; + sp->stats.tx_fifo_errors += sp->lstats.tx_lost_carrier; + /*sp->stats.tx_deferred += sp->lstats.tx_deferred;*/ + sp->stats.collisions += sp->lstats.tx_total_colls; + sp->stats.rx_crc_errors += sp->lstats.rx_crc_errs; + sp->stats.rx_frame_errors += sp->lstats.rx_align_errs; + sp->stats.rx_over_errors += sp->lstats.rx_resource_errs; + sp->stats.rx_fifo_errors += sp->lstats.rx_overrun_errs; + sp->stats.rx_length_errors += sp->lstats.rx_runt_errs; + sp->lstats.done_marker = 0x0000; + if (dev->start) + outw(CU_DUMPSTATS, ioaddr + SCBCmd); + } + return &sp->stats; +} + +/* Set or clear the multicast filter for this adaptor. + This is very ugly with Intel chips -- we usually have to execute an + entire configuration command, plus process a multicast command. + This is complicated. We must put a large configuration command and + an arbitrarily-sized multicast command in the transmit list. + To minimize the disruption -- the previous command might have already + loaded the link -- we convert the current command block, normally a Tx + command, into a no-op and link it to the new command. +*/ +static void +set_rx_mode(struct device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int ioaddr = dev->base_addr; + char new_rx_mode; + unsigned long flags; + int entry, i; + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + new_rx_mode = 3; + } else if (dev->flags & IFF_ALLMULTI) { + new_rx_mode = 1; + } else + new_rx_mode = 0; + + if (sp->cur_tx - sp->dirty_tx >= TX_RING_SIZE - 1) { + /* The Tx ring is full -- don't add anything! Presumably the new mode + is in config_cmd_data and will be added anyway. */ + sp->rx_mode = -1; + return; + } + + if (new_rx_mode != sp->rx_mode) { + /* We must change the configuration. Construct a CmdConfig frame. */ + memcpy(sp->config_cmd_data, basic_config_cmd,sizeof(basic_config_cmd)); + sp->config_cmd_data[1] = (txfifo << 4) | rxfifo; + sp->config_cmd_data[4] = rxdmacount; + sp->config_cmd_data[5] = txdmacount + 0x80; + sp->config_cmd_data[15] = (new_rx_mode & 2) ? 0x49 : 0x48; + sp->config_cmd_data[19] = sp->full_duplex ? 0xC0 : 0x80; + sp->config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05; + if (sp->phy[0] & 0x8000) { /* Use the AUI port instead. */ + sp->config_cmd_data[15] |= 0x80; + sp->config_cmd_data[8] = 0; + } + save_flags(flags); + cli(); + /* Fill the "real" tx_ring frame with a no-op and point it to us. */ + entry = sp->cur_tx++ % TX_RING_SIZE; + sp->tx_skbuff[entry] = 0; /* Nothing to free. */ + sp->tx_ring[entry].status = CmdNOp << 16; + sp->tx_ring[entry].link = virt_to_bus(&sp->config_cmd); + sp->config_cmd.status = 0; + sp->config_cmd.command = CmdSuspend | CmdConfigure; + sp->config_cmd.link = + virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE])); + sp->last_cmd->command &= ~CmdSuspend; + /* Immediately trigger the command unit resume. */ + outw(CU_RESUME, ioaddr + SCBCmd); + sp->last_cmd = &sp->config_cmd; + restore_flags(flags); + if (speedo_debug > 5) { + int i; + printk(" CmdConfig frame in entry %d.\n", entry); + for(i = 0; i < 32; i++) + printk(" %2.2x", ((unsigned char *)&sp->config_cmd)[i]); + printk(".\n"); + } + } + + if (new_rx_mode == 0 && dev->mc_count < 3) { + /* The simple case of 0-2 multicast list entries occurs often, and + fits within one tx_ring[] entry. */ + u16 *setup_params; + unsigned short *eaddrs; + struct dev_mc_list *mclist; + + save_flags(flags); + cli(); + entry = sp->cur_tx++ % TX_RING_SIZE; + sp->tx_skbuff[entry] = 0; + sp->tx_ring[entry].status = (CmdSuspend | CmdMulticastList) << 16; + sp->tx_ring[entry].link = + virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]); + sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */ + setup_params = (short *)&sp->tx_ring[entry].tx_desc_addr; + *setup_params++ = dev->mc_count*6; + /* Fill in the multicast addresses. */ + for (i = 0, mclist = dev->mc_list; i < dev->mc_count; + i++, mclist = mclist->next) { + eaddrs = (unsigned short *)mclist->dmi_addr; + *setup_params++ = *eaddrs++; + *setup_params++ = *eaddrs++; + *setup_params++ = *eaddrs++; + } + + sp->last_cmd->command &= ~CmdSuspend; + /* Immediately trigger the command unit resume. */ + outw(CU_RESUME, ioaddr + SCBCmd); + sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; + restore_flags(flags); + } else if (new_rx_mode == 0) { + /* This does not work correctly, but why not? */ + struct dev_mc_list *mclist; + unsigned short *eaddrs; + struct descriptor *mc_setup_frm = sp->mc_setup_frm; + u16 *setup_params = (short *)mc_setup_frm->params; + int i; + + if (sp->mc_setup_frm_len < 10 + dev->mc_count*6 + || sp->mc_setup_frm == NULL) { + /* Allocate a new frame, 10bytes + addrs, with a few + extra entries for growth. */ + if (sp->mc_setup_frm) + kfree(sp->mc_setup_frm); + sp->mc_setup_frm_len = 10 + dev->mc_count*6 + 24; + printk("%s: Allocating a setup frame of size %d.\n", + dev->name, sp->mc_setup_frm_len); + sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len, + intr_count ? GFP_ATOMIC : GFP_KERNEL); + if (sp->mc_setup_frm == NULL) { + printk("%s: Failed to allocate a setup frame.\n", dev->name); + sp->rx_mode = -1; /* We failed, try again. */ + return; + } + } + mc_setup_frm = sp->mc_setup_frm; + /* Construct the new setup frame. */ + printk("%s: Constructing a setup frame at %p, %d bytes.\n", + dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len); + mc_setup_frm->status = 0; + mc_setup_frm->command = CmdSuspend | CmdIntr | CmdMulticastList; + /* Link set below. */ + setup_params = (short *)mc_setup_frm->params; + *setup_params++ = dev->mc_count*6; + /* Fill in the multicast addresses. */ + for (i = 0, mclist = dev->mc_list; i < dev->mc_count; + i++, mclist = mclist->next) { + eaddrs = (unsigned short *)mclist->dmi_addr; + *setup_params++ = *eaddrs++; + *setup_params++ = *eaddrs++; + *setup_params++ = *eaddrs++; + } + + /* Disable interrupts while playing with the Tx Cmd list. */ + save_flags(flags); + cli(); + entry = sp->cur_tx++ % TX_RING_SIZE; + + if (speedo_debug > 5) + printk(" CmdMCSetup frame length %d in entry %d.\n", + dev->mc_count, entry); + + /* Change the command to a NoOp, pointing to the CmdMulti command. */ + sp->tx_skbuff[entry] = 0; + sp->tx_ring[entry].status = CmdNOp << 16; + sp->tx_ring[entry].link = virt_to_bus(mc_setup_frm); + + /* Set the link in the setup frame. */ + mc_setup_frm->link = + virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE])); + + sp->last_cmd->command &= ~CmdSuspend; + /* Immediately trigger the command unit resume. */ + outw(CU_RESUME, ioaddr + SCBCmd); + sp->last_cmd = mc_setup_frm; + restore_flags(flags); + printk("%s: Last command at %p is %4.4x.\n", + dev->name, sp->last_cmd, sp->last_cmd->command); + } + + sp->rx_mode = new_rx_mode; +} + +#ifdef MODULE +#if (LINUX_VERSION_CODE < VERSION(1,3,38)) /* 1.3.38 and later */ +char kernel_version[] = UTS_RELEASE; +#endif + +int +init_module(void) +{ + int cards_found; + + if (debug >= 0) + speedo_debug = debug; + if (speedo_debug) + printk(KERN_INFO "%s", version); + + root_speedo_dev = NULL; + cards_found = eepro100_init(NULL); + return cards_found < 0 ? cards_found : 0; +} + +void +cleanup_module(void) +{ + struct device *next_dev; + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (root_speedo_dev) { + next_dev = ((struct speedo_private *)root_speedo_dev->priv)->next_module; + unregister_netdev(root_speedo_dev); + release_region(root_speedo_dev->base_addr, SPEEDO3_TOTAL_SIZE); + kfree(root_speedo_dev); + root_speedo_dev = next_dev; + } +} +#else /* not MODULE */ +int eepro100_probe(struct device *dev) +{ + int cards_found = 0; + + cards_found = eepro100_init(dev); + + if (speedo_debug > 0 && cards_found) + printk(version); + + return cards_found ? 0 : -ENODEV; +} +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -DCONFIG_MODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c" + * c-indent-level: 4 + * tab-width: 4 + * End: + */ diff -u --recursive --new-file v2.0.30/linux/drivers/net/eql.c linux/drivers/net/eql.c --- v2.0.30/linux/drivers/net/eql.c Sun Sep 22 11:17:08 1996 +++ linux/drivers/net/eql.c Tue Aug 12 14:15:56 1997 @@ -17,7 +17,7 @@ */ static const char *version = - "Equalizer1996: $Revision: 1.2.1 $ $Date: 1996/09/22 13:52:00 $ Simon Janes (simon@ncm.com)\n"; + "Equalizer1996: $Revision: 1.9 $ $Date: 1996/10/12 11:14:37 $ Simon Janes (simon@ncm.com)\n"; /* * Sources: @@ -31,6 +31,9 @@ /* * $Log: eql.c,v $ + * Revision 1.9 1996/10/12 11:14:37 davem + * Quick merge to 2.0.20 + * * Revision 1.2 1996/04/11 17:51:52 guru * Added one-line eql_remove_slave patch. * @@ -262,6 +265,7 @@ dev->hard_header = eql_header; dev->rebuild_header = eql_rebuild_header; + dev->hard_header_len = MAX_HEADER; /* enough space for any slave */ /* * Now we undo some of the things that eth_setup does @@ -371,10 +375,19 @@ equalizer_t *eql = (equalizer_t *) dev->priv; struct device *slave_dev = 0; slave_t *slave; + struct sk_buff *skb2; if (skb == NULL) return 0; +#if 0 + /* Make a copy we can free so we don't mess up the skb->dev pointer */ + skb2 = skb_clone(skb, GFP_ATOMIC); + + if (skb2 == NULL) + return 1; +#endif + eql_schedule_slaves (eql->queue); slave_dev = eql_best_slave_dev (eql->queue); @@ -388,20 +401,48 @@ dev->name, eql_number_slaves (eql->queue), skb->len, slave_dev->name); #endif - dev_queue_xmit (skb, slave_dev, 1); - eql->stats->tx_packets++; - slave->bytes_queued += skb->len; - } - else - { - /* - * The alternative for this is the return 1 and have - * dev_queue_xmit just queue it up on the eql's queue. + + /* Rip off the fake header */ + skb_pull(skb,MAX_HEADER); + + /* The original code had no hard header constructed. + * If a frame is fragmented on EQL and then passed to PPP, + * or ISDN, the result will be a panic. + * The solution is to call the hard_header constructor + * for the device we point to just before we send the packet. + * If this fails we drop the packet. + * We don't know any special parameters for the hard_header + * constructor at this point, so we pass in made up values + * that will cause the constructor to fail on every device + * except those that we are allowed to use EQL on: + * PPP, SLIP and ISDN (in some cases!). + * The worst thing that happens is if some fool + * configures EQL to enslave something that needs + * these parameters it throws out packets. + * This is not an issue for the things EQL is intended for + * anyway, and probably would have crashed the kernel + * or sent garbage down the wire previously. */ - eql->stats->tx_dropped++; - dev_kfree_skb(skb, FREE_WRITE); - } + if (slave_dev->hard_header == NULL + || slave_dev->hard_header(skb,slave_dev, + ETH_P_IP,NULL,NULL,skb->len) >= 0) { + dev_queue_xmit (skb, slave_dev, 1); + eql->stats->tx_packets++; + slave->bytes_queued += skb->len; + /* dev_kfree_skb(skb, FREE_WRITE); */ + return 0; + } + } + + /* + * The alternative for this is the return 1 and have + * dev_queue_xmit just queue it up on the eql's queue. + */ + + eql->stats->tx_dropped++; + /* dev_kfree_skb(skb2, FREE_WRITE); */ + dev_kfree_skb(skb, FREE_WRITE); return 0; } @@ -417,7 +458,9 @@ unsigned short type, void *daddr, void *saddr, unsigned len) { - return 0; + /* Fake header to keep space during buggy IP fragmentation. */ + skb_push(skb,MAX_HEADER); + return MAX_HEADER; } diff -u --recursive --new-file v2.0.30/linux/drivers/net/hp100.c linux/drivers/net/hp100.c --- v2.0.30/linux/drivers/net/hp100.c Thu Apr 11 23:49:38 1996 +++ linux/drivers/net/hp100.c Tue Aug 5 09:11:35 1997 @@ -1,89 +1,66 @@ /* - * hp100.c: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux. - * - * Author: Jaroslav Kysela, - * - * Supports only the following Hewlett Packard cards: - * - * HP J2577 10/100 EISA card with REVA Cascade chip - * HP J2573 10/100 ISA card with REVA Cascade chip - * HP 27248B 10 only EISA card with Cascade chip - * HP J2577 10/100 EISA card with Cascade chip - * HP J2573 10/100 ISA card with Cascade chip - * HP J2585 10/100 PCI card - * - * Other ATT2MD01 Chip based boards might be supported in the future - * (there are some minor changes needed). - * - * This driver is based on the 'hpfepkt' crynwr packet driver. - * - * This source/code is public free; you can distribute it and/or modify - * it under terms of the GNU General Public License (published by the - * Free Software Foundation) either version two of this License, or any - * later version. - * ---------------------------------------------------------------------------- - * - * Note: Some routines (interrupt handling, transmit) assumes that - * there is the PERFORMANCE page selected... - * - * ---------------------------------------------------------------------------- - * - * If you are going to use the module version of this driver, you may - * change this values at the "insert time" : - * - * Variable Description - * - * hp100_rx_ratio Range 1-99 - onboard memory used for RX - * packets in %. - * hp100_priority_tx If this variable is nonzero - all outgoing - * packets will be transmitted as priority. - * hp100_port Adapter port (for example 0x380). - * - * ---------------------------------------------------------------------------- - * MY BEST REGARDS GOING TO: - * - * IPEX s.r.o which lend me two HP J2573 cards and - * the HP AdvanceStack 100VG Hub-15 for debugging. - * - * Russel Nellson for help with obtaining sources - * of the 'hpfepkt' packet driver. - * - * Also thanks to Abacus Electric s.r.o which let me to use their - * motherboard for my second computer. - * - * ---------------------------------------------------------------------------- - * - * TO DO: - * ====== - * - ioctl handling - some runtime setup things - * - 100Mb/s Voice Grade AnyLAN network adapter/hub services support - * - 802.5 frames - * - promiscuous mode - * - bridge mode - * - cascaded repeater mode - * - 100Mbit MAC - * - * Revision history: - * ================= - * - * Version Date Description - * - * 0.1 14-May-95 Initial writing. ALPHA code was released. - * Only HP J2573 on 10Mb/s (two machines) tested. - * 0.11 14-Jun-95 Reset interface bug fixed? - * Little bug in hp100_close function fixed. - * 100Mb/s connection debugged. - * 0.12 14-Jul-95 Link down is now handled better. - * 0.20 01-Aug-95 Added PCI support for HP J2585A card. - * Statistics bug fixed. - * 0.21 04-Aug-95 Memory mapped access support for PCI card. - * Added priority transmit support for 100Mb/s - * Voice Grade AnyLAN network. - * - */ +** hp100.c +** HP CASCADE Architecture Driver for 100VG-AnyLan Network Adapters +** +** $Id: hp100.c,v 1.54 1997/06/12 10:37:07 perex Exp perex $ +** +** Based on the HP100 driver written by Jaroslav Kysela +** Extended for new busmaster capable chipsets by +** Siegfried "Frieder" Loeffler (dg1sek) +** +** Maintained by: Jaroslav Kysela +** +** This driver has only been tested with +** -- HP J2585B 10/100 Mbit/s PCI Busmaster +** -- HP J2585A 10/100 Mbit/s PCI +** -- HP J2970 10 Mbit/s PCI Combo 10base-T/BNC +** -- HP J2973 10 Mbit/s PCI 10base-T +** -- HP J2573 10/100 ISA +** -- Compex ReadyLink ENET100-VG4 10/100 Mbit/s PCI / EISA +** +** but it should also work with the other CASCADE based adapters. +** +** TODO: +** - J2573 seems to hang sometimes when in shared memory mode. +** - Mode for Priority TX +** - Check PCI registers, performance might be improved? +** - To reduce interrupt load in busmaster, one could switch off +** the interrupts that are used to refill the queues whenever the +** queues are filled up to more than a certain threshold. +** +** +** This source/code is public free; you can distribute it and/or modify +** it under terms of the GNU General Public License (published by the +** Free Software Foundation) either version two of this License, or any +** later version. +** +** 1.53 -> 1.54 +** - added hardware multicast filter support (doesn't work) +** - little changes in hp100_sense_lan routine +** - added support for Coax and AUI (J2970) +** - fix for multiple cards and hp100_mode parameter (insmod) +** - fix for shared IRQ +** +** 1.52 -> 1.53 +** - fixed bug in multicast support +** +*/ + +#define HP100_DEFAULT_PRIORITY_TX 0 + +#undef HP100_DEBUG +#undef HP100_DEBUG_B /* Trace */ +#undef HP100_DEBUG_BM /* Debug busmaster code (PDL stuff) */ + +#undef HP100_DEBUG_TRAINING /* Debug login-to-hub procedure */ +#undef HP100_DEBUG_TX +#undef HP100_DEBUG_IRQ +#undef HP100_DEBUG_RX -#include +#undef HP100_MULTICAST_FILTER /* Need to be debugged... */ +#include +#include #include #include #include @@ -101,7 +78,23 @@ #include #include -#include /* for CONFIG_PCI */ +#include /* for CONFIG_PCI */ +#include + +#if LINUX_VERSION_CODE < 0x020100 +#define ioremap vremap +#define iounmap vfree +typedef struct enet_statistics hp100_stats_t; +#else +#define LINUX_2_1 +typedef struct net_device_stats hp100_stats_t; +#endif + +#ifndef __initfunc +#define __initfunc(__initarg) __initarg +#else +#include +#endif #include "hp100.h" @@ -109,18 +102,28 @@ * defines */ -#define HP100_BUS_ISA 0 -#define HP100_BUS_EISA 1 -#define HP100_BUS_PCI 2 +#define HP100_BUS_ISA 0 +#define HP100_BUS_EISA 1 +#define HP100_BUS_PCI 2 + +#ifndef PCI_DEVICE_ID_HP_J2585B +#define PCI_DEVICE_ID_HP_J2585B 0x1031 +#endif +#ifndef PCI_VENDOR_ID_COMPEX +#define PCI_VENDOR_ID_COMPEX 0x11f6 +#endif +#ifndef PCI_DEVICE_ID_COMPEX_ENET100VG4 +#define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112 +#endif -#define HP100_REGION_SIZE 0x20 +#define HP100_REGION_SIZE 0x20 /* for ioports */ -#define HP100_MAX_PACKET_SIZE (1536+4) -#define HP100_MIN_PACKET_SIZE 60 +#define HP100_MAX_PACKET_SIZE (1536+4) +#define HP100_MIN_PACKET_SIZE 60 #ifndef HP100_DEFAULT_RX_RATIO -/* default - 65% onboard memory on the card are used for RX packets */ -#define HP100_DEFAULT_RX_RATIO 65 +/* default - 75% onboard memory on the card are used for RX packets */ +#define HP100_DEFAULT_RX_RATIO 75 #endif #ifndef HP100_DEFAULT_PRIORITY_TX @@ -140,193 +143,289 @@ struct hp100_private { struct hp100_eisa_id *id; + u_short chip; u_short soft_model; - u_int memory_size; - u_short rx_ratio; /* 1 - 99 */ - u_short priority_tx; /* != 0 - priority tx */ - short mem_mapped; /* memory mapped access */ - u_char *mem_ptr_virt; /* virtual memory mapped area, maybe NULL */ - u_char *mem_ptr_phys; /* physical memory mapped area */ - short lan_type; /* 10Mb/s, 100Mb/s or -1 (error) */ - int hub_status; /* login to hub was successful? */ + u_int memory_size; + u_short rx_ratio; /* 1 - 99 */ + u_short priority_tx; /* != 0 - priority tx */ + u_short mode; /* PIO, Shared Mem or Busmaster */ + u_char bus; + u_char pci_bus; + u_char pci_device_fn; + short mem_mapped; /* memory mapped access */ + u_int *mem_ptr_virt; /* virtual memory mapped area, maybe NULL */ + u_int *mem_ptr_phys; /* physical memory mapped area */ + short lan_type; /* 10Mb/s, 100Mb/s or -1 (error) */ + int hub_status; /* was login to hub successful? */ u_char mac1_mode; u_char mac2_mode; - struct enet_statistics stats; + u_char hash_bytes[ 8 ]; + hp100_stats_t stats; + + /* Rings for busmaster mode: */ + hp100_ring_t *rxrhead; /* Head (oldest) index into rxring */ + hp100_ring_t *rxrtail; /* Tail (newest) index into rxring */ + hp100_ring_t *txrhead; /* Head (oldest) index into txring */ + hp100_ring_t *txrtail; /* Tail (newest) index into txring */ + + hp100_ring_t rxring[ MAX_RX_PDL ]; + hp100_ring_t txring[ MAX_TX_PDL ]; + + u_int *page_vaddr; /* Virtual address of allocated page */ + u_int *page_vaddr_algn; /* Aligned virtual address of allocated page */ + int rxrcommit; /* # Rx PDLs commited to adapter */ + int txrcommit; /* # Tx PDLs commited to adapter */ }; /* * variables */ - + static struct hp100_eisa_id hp100_eisa_ids[] = { - /* 10/100 EISA card with REVA Cascade chip */ - { 0x080F1F022, "HP J2577 rev A", HP100_BUS_EISA }, + /* 10/100 EISA card with revision A Cascade chip */ + { 0x80F1F022, "HP J2577 rev A", HP100_BUS_EISA }, - /* 10/100 ISA card with REVA Cascade chip */ - { 0x050F1F022, "HP J2573 rev A", HP100_BUS_ISA }, + /* 10/100 ISA card with revision A Cascade chip */ + { 0x50F1F022, "HP J2573 rev A", HP100_BUS_ISA }, /* 10 only EISA card with Cascade chip */ - { 0x02019F022, "HP 27248B", HP100_BUS_EISA }, + { 0x2019F022, "HP 27248B", HP100_BUS_EISA }, /* 10/100 EISA card with Cascade chip */ - { 0x04019F022, "HP J2577", HP100_BUS_EISA }, + { 0x4019F022, "HP J2577", HP100_BUS_EISA }, /* 10/100 ISA card with Cascade chip */ - { 0x05019F022, "HP J2573", HP100_BUS_ISA }, + { 0x5019F022, "HP J2573", HP100_BUS_ISA }, + + /* 10/100 PCI card - old J2585A */ + { 0x1030103c, "HP J2585A", HP100_BUS_PCI }, + + /* 10/100 PCI card - new J2585B - master capable */ + { 0x1041103c, "HP J2585B", HP100_BUS_PCI }, + + /* 10 Mbit Combo Adapter */ + { 0x1042103c, "HP J2970", HP100_BUS_PCI }, - /* 10/100 PCI card */ - /* Note: ID for this card is same as PCI vendor/device numbers. */ - { 0x01030103c, "HP J2585", HP100_BUS_PCI }, + /* 10 Mbit 10baseT Adapter */ + { 0x1040103c, "HP J2973", HP100_BUS_PCI }, + + /* 10/100 EISA card from Compex */ + { 0x0103180e, "ReadyLink ENET100-VG4", HP100_BUS_EISA }, + + /* 10/100 PCI card from Compex (J2585A compatible) */ + { 0x011211f6, "ReadyLink ENET100-VG4", HP100_BUS_PCI } }; -int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO; -int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX; +static int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO; +static int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX; +static int hp100_mode = 1; + +#ifdef LINUX_2_1 +MODULE_PARM( hp100_rx_ratio, "1i" ); +MODULE_PARM( hp100_priority_tx, "1i" ); +MODULE_PARM( hp100_mode, "1i" ); +#endif /* * prototypes */ -static int hp100_probe1( struct device *dev, int ioaddr, int bus ); -static int hp100_open( struct device *dev ); -static int hp100_close( struct device *dev ); -static int hp100_start_xmit( struct sk_buff *skb, struct device *dev ); +static int hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn ); +static int hp100_open( struct device *dev ); +static int hp100_close( struct device *dev ); +static int hp100_start_xmit( struct sk_buff *skb, struct device *dev ); +static int hp100_start_xmit_bm (struct sk_buff *skb, struct device *dev ); static void hp100_rx( struct device *dev ); -static struct enet_statistics *hp100_get_stats( struct device *dev ); +static hp100_stats_t *hp100_get_stats( struct device *dev ); static void hp100_update_stats( struct device *dev ); static void hp100_clear_stats( int ioaddr ); static void hp100_set_multicast_list( struct device *dev); static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs ); - static void hp100_start_interface( struct device *dev ); static void hp100_stop_interface( struct device *dev ); static void hp100_load_eeprom( struct device *dev ); -static int hp100_sense_lan( struct device *dev ); -static int hp100_login_to_vg_hub( struct device *dev ); -static int hp100_down_vg_link( struct device *dev ); +static int hp100_sense_lan( struct device *dev ); +static int hp100_login_to_vg_hub( struct device *dev, u_short force_relogin ); +static int hp100_down_vg_link( struct device *dev ); +static void hp100_cascade_reset( struct device *dev, u_short enable ); +static void hp100_BM_shutdown( struct device *dev ); +static void hp100_mmuinit( struct device *dev ); +static void hp100_init_pdls( struct device *dev ); +static int hp100_init_rxpdl( struct device *dev, register hp100_ring_t *ringptr, register u_int *pdlptr); +static int hp100_init_txpdl( struct device *dev, register hp100_ring_t *ringptr, register u_int *pdlptr); +static void hp100_rxfill( struct device *dev ); +static void hp100_hwinit( struct device *dev ); +static void hp100_clean_txring( struct device *dev ); +#ifdef HP100_DEBUG +static void hp100_RegisterDump( struct device *dev ); +#endif + +/* TODO: This function should not really be needed in a good design... */ +static void wait( void ) +{ + udelay( 1000 ); +} /* * probe functions + * These functions should - if possible - avoid doing write operations + * since this could cause problems when the card is not installed. */ - -int hp100_probe( struct device *dev ) + +__initfunc(int hp100_probe( struct device *dev )) { int base_addr = dev ? dev -> base_addr : 0; - int ioaddr; + int ioaddr = 0; #ifdef CONFIG_PCI int pci_start_index = 0; #endif +#ifdef HP100_DEBUG_B + hp100_outw( 0x4200, TRACE ); + printk( "hp100: %s: probe\n", dev->name ); +#endif + if ( base_addr > 0xff ) /* Check a single specified location. */ { if ( check_region( base_addr, HP100_REGION_SIZE ) ) return -EINVAL; if ( base_addr < 0x400 ) - return hp100_probe1( dev, base_addr, HP100_BUS_ISA ); - else - return hp100_probe1( dev, base_addr, HP100_BUS_EISA ); + return hp100_probe1( dev, base_addr, HP100_BUS_ISA, 0, 0 ); + if ( EISA_bus && base_addr >= 0x1c38 && ( (base_addr - 0x1c38) & 0x3ff ) == 0 ) + return hp100_probe1( dev, base_addr, HP100_BUS_EISA, 0, 0 ); +#ifdef CONFIG_PCI + printk( "hp100: %s: You may specify card # in i/o address parameter for PCI bus...", dev->name ); + return hp100_probe1( dev, base_addr, HP100_BUS_PCI, 0, 0 ); +#else + return -ENODEV; +#endif } - else + else #ifdef CONFIG_PCI - if ( base_addr > 0 && base_addr < 8 + 1 ) - pci_start_index = 0x100 | ( base_addr - 1 ); - else + if ( base_addr > 0 && base_addr < 8 + 1 ) + pci_start_index = 0x100 | ( base_addr - 1 ); + else #endif - if ( base_addr != 0 ) return -ENXIO; + if ( base_addr != 0 ) return -ENXIO; /* at first - scan PCI bus(es) */ - + #ifdef CONFIG_PCI if ( pcibios_present() ) { int pci_index; - + #ifdef HP100_DEBUG_PCI - printk( "hp100: PCI BIOS is present, checking for devices..\n" ); + printk( "hp100: %s: PCI BIOS is present, checking for devices..\n", dev->name ); #endif for ( pci_index = pci_start_index & 7; pci_index < 8; pci_index++ ) { u_char pci_bus, pci_device_fn; u_short pci_command; - - if ( pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A, - pci_index, &pci_bus, - &pci_device_fn ) != 0 ) break; + + if ((pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A, + pci_index, &pci_bus, + &pci_device_fn ) != 0 ) && + (pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585B, + pci_index, &pci_bus, + &pci_device_fn ) != 0 ) && + (pcibios_find_device( PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_ENET100VG4, + pci_index, &pci_bus, + &pci_device_fn ) != 0 ) ) break; + pcibios_read_config_dword( pci_bus, pci_device_fn, - PCI_BASE_ADDRESS_0, &ioaddr ); - - ioaddr &= ~3; /* remove I/O space marker in bit 0. */ - + PCI_BASE_ADDRESS_0, &ioaddr ); + + ioaddr &= ~3; /* remove I/O space marker in bit 0. */ + if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue; - + pcibios_read_config_word( pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command ); + PCI_COMMAND, &pci_command ); if ( !( pci_command & PCI_COMMAND_MASTER ) ) { -#ifdef HP100_DEBUG_PCI - printk( "hp100: PCI Master Bit has not been set. Setting...\n" ); +#ifdef HP100_DEBUG + printk( "hp100: %s: PCI Master Bit has not been set. Setting...\n", dev->name ); #endif pci_command |= PCI_COMMAND_MASTER; pcibios_write_config_word( pci_bus, pci_device_fn, - PCI_COMMAND, pci_command ); + PCI_COMMAND, pci_command ); } -#ifdef HP100_DEBUG_PCI - printk( "hp100: PCI adapter found at 0x%x\n", ioaddr ); +#ifdef HP100_DEBUG + printk( "hp100: %s: PCI adapter found at 0x%x\n", dev->name, ioaddr ); #endif - if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI ) == 0 ) return 0; + if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI, pci_bus, pci_device_fn ) == 0 ) + return 0; } } if ( pci_start_index > 0 ) return -ENODEV; #endif /* CONFIG_PCI */ - - /* at second - probe all EISA possible port regions (if EISA bus present) */ - + + /* Second: Probe all EISA possible port regions (if EISA bus present) */ for ( ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400 ) { if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue; - if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA ) == 0 ) return 0; + if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA, 0, 0 ) == 0 ) return 0; } - - /* at third - probe all ISA possible port regions */ - + + /* Third Probe all ISA possible port regions */ for ( ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20 ) { if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue; - if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA ) == 0 ) return 0; + if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA, 0, 0 ) == 0 ) return 0; } - + return -ENODEV; } -static int hp100_probe1( struct device *dev, int ioaddr, int bus ) + +__initfunc(static int hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn )) { int i; + u_char uc, uc_1; u_int eisa_id; + u_int chip; + u_int memory_size = 0; + u_short local_mode, lsw; short mem_mapped; - u_char *mem_ptr_phys, *mem_ptr_virt; + u_int *mem_ptr_phys, *mem_ptr_virt; struct hp100_private *lp; struct hp100_eisa_id *eid; +#ifdef HP100_DEBUG_B + hp100_outw( 0x4201, TRACE ); + printk("hp100: %s: probe1\n",dev->name); +#endif + if ( dev == NULL ) { #ifdef HP100_DEBUG - printk( "hp100_probe1: dev == NULL ?\n" ); + printk( "hp100_probe1: %s: dev == NULL ?\n", dev->name ); #endif return EIO; } + + if ( hp100_inw( HW_ID ) != HP100_HW_ID_CASCADE ) + { + return -ENODEV; + } + else + { + chip = hp100_inw( PAGING ) & HP100_CHIPID_MASK; +#ifdef HP100_DEBUG + if ( chip == HP100_CHIPID_SHASTA ) + printk("hp100: %s: Shasta Chip detected. (This is a pre 802.12 chip)\n", dev->name); + else if ( chip == HP100_CHIPID_RAINIER ) + printk("hp100: %s: Rainier Chip detected. (This is a pre 802.12 chip)\n", dev->name); + else if ( chip == HP100_CHIPID_LASSEN ) + printk("hp100: %s: Lassen Chip detected.\n", dev->name); + else + printk("hp100: %s: Warning: Unknown CASCADE chip (id=0x%.4x).\n",dev->name,chip); +#endif + } - if ( bus != HP100_BUS_PCI ) /* don't check PCI cards again */ - if ( inb( ioaddr + 0 ) != HP100_HW_ID_0 || - inb( ioaddr + 1 ) != HP100_HW_ID_1 || - ( inb( ioaddr + 2 ) & 0xf0 ) != HP100_HW_ID_2_REVA || - inb( ioaddr + 3 ) != HP100_HW_ID_3 ) - return -ENODEV; - - dev -> base_addr = ioaddr; - -#ifdef HP100_DEBUG_PROBE1 - printk( "hp100_probe1: card found at port 0x%x\n", ioaddr ); -#endif + dev->base_addr = ioaddr; hp100_page( ID_MAC_ADDR ); for ( i = uc = eisa_id = 0; i < 4; i++ ) @@ -338,29 +437,25 @@ } uc += hp100_inb( BOARD_ID + 4 ); -#ifdef HP100_DEBUG_PROBE1 - printk( "hp100_probe1: EISA ID = 0x%08x checksum = 0x%02x\n", eisa_id, uc ); -#endif - - if ( uc != 0xff ) /* bad checksum? */ + if ( uc != 0xff ) /* bad checksum? */ { - printk( "hp100_probe: bad EISA ID checksum at base port 0x%x\n", ioaddr ); + printk("hp100_probe: %s: bad EISA ID checksum at base port 0x%x\n", dev->name, ioaddr ); return -ENODEV; - } + } - for ( i = 0; i < sizeof( hp100_eisa_ids ) / sizeof( struct hp100_eisa_id ); i++ ) + for ( i=0; i= sizeof( hp100_eisa_ids ) / sizeof( struct hp100_eisa_id ) ) { - printk( "hp100_probe1: card at port 0x%x isn't known (id = 0x%x)\n", ioaddr, eisa_id ); + printk( "hp100_probe: %s: card at port 0x%x isn't known (id = 0x%x)\n", dev -> name, ioaddr, eisa_id ); return -ENODEV; } eid = &hp100_eisa_ids[ i ]; - if ( ( eid -> id & 0x0f000000 ) < ( eisa_id & 0x0f000000 ) ) + if ( ( eid->id & 0x0f000000 ) < ( eisa_id & 0x0f000000 ) ) { - printk( "hp100_probe1: newer version of card %s at port 0x%x - unsupported\n", - eid -> name, ioaddr ); + printk( "hp100_probe: %s: newer version of card %s at port 0x%x - unsupported\n", + dev->name, eid->name, ioaddr ); return -ENODEV; } @@ -368,469 +463,1607 @@ uc += hp100_inb( LAN_ADDR + i ); if ( uc != 0xff ) { - printk( "hp100_probe1: bad lan address checksum (card %s at port 0x%x)\n", - eid -> name, ioaddr ); + printk("hp100_probe: %s: bad lan address checksum (card %s at port 0x%x)\n", + dev->name, eid->name, ioaddr ); return -EIO; } -#ifndef HP100_IO_MAPPED + /* Determine driver operation mode + * + * Use the variable "hp100_mode" upon insmod or as kernel parameter to + * force driver modes: + * hp100_mode=1 -> default, use busmaster mode if configured. + * hp100_mode=2 -> enable shared memory mode + * hp100_mode=3 -> force use of i/o mapped mode. + * hp100_mode=4 -> same as 1, but re-set the enable bit on the card. + */ + + /* hp100_mode value maybe used in future by another card */ + local_mode=hp100_mode; + if ( local_mode < 1 || local_mode > 4 ) + local_mode = 1; /* default */ +#ifdef HP100_DEBUG + printk( "hp100: %s: original LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) ); +#endif + + if(local_mode==3) + { + hp100_outw(HP100_MEM_EN|HP100_RESET_LB, OPTION_LSW); + hp100_outw(HP100_IO_EN|HP100_SET_LB, OPTION_LSW); + hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW); + printk("hp100: %s: IO mapped mode forced.\n", dev->name); + } + else if(local_mode==2) + { + hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW); + hp100_outw(HP100_IO_EN |HP100_SET_LB, OPTION_LSW); + hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW); + printk("hp100: %s: Shared memory mode requested.\n", dev->name); + } + else if(local_mode==4) + { + if(chip==HP100_CHIPID_LASSEN) + { + hp100_outw(HP100_BM_WRITE| + HP100_BM_READ | HP100_SET_HB, OPTION_LSW); + hp100_outw(HP100_IO_EN | + HP100_MEM_EN | HP100_RESET_LB, OPTION_LSW); + printk("hp100: %s: Busmaster mode requested.\n",dev->name); + } + local_mode=1; + } + + if(local_mode==1) /* default behaviour */ + { + lsw = hp100_inw(OPTION_LSW); + + if ( (lsw & HP100_IO_EN) && + (~lsw & HP100_MEM_EN) && + (~lsw & (HP100_BM_WRITE|HP100_BM_READ)) ) + { +#ifdef HP100_DEBUG + printk("hp100: %s: IO_EN bit is set on card.\n",dev->name); +#endif + local_mode=3; + } + else if ( chip == HP100_CHIPID_LASSEN && + ( lsw & (HP100_BM_WRITE|HP100_BM_READ) ) == + (HP100_BM_WRITE|HP100_BM_READ) ) + { + printk("hp100: %s: Busmaster mode enabled.\n",dev->name); + hp100_outw(HP100_MEM_EN|HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); + } + else + { +#ifdef HP100_DEBUG + printk("hp100: %s: Card not configured for BM or BM not supported with this card. Trying shared memory mode.\n", dev->name); +#endif + /* In this case, try shared memory mode */ + local_mode=2; + hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW); + /* hp100_outw(HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); */ + } + } + +#ifdef HP100_DEBUG + printk( "hp100: %s: new LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) ); +#endif + + /* Check for shared memory on the card, eventually remap it */ hp100_page( HW_MAP ); - mem_mapped = ( hp100_inw( OPTION_LSW ) & - ( HP100_MEM_EN | HP100_BM_WRITE | HP100_BM_READ ) ) != 0; + mem_mapped = (( hp100_inw( OPTION_LSW ) & ( HP100_MEM_EN ) ) != 0); mem_ptr_phys = mem_ptr_virt = NULL; - if ( mem_mapped ) + memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07)); + + /* For memory mapped or busmaster mode, we want the memory address */ + if ( mem_mapped || (local_mode==1)) { - mem_ptr_phys = (u_char *)( hp100_inw( MEM_MAP_LSW ) | - ( hp100_inw( MEM_MAP_MSW ) << 16 ) ); - (u_int)mem_ptr_phys &= ~0x1fff; /* 8k alignment */ + mem_ptr_phys = (u_int *)( hp100_inw( MEM_MAP_LSW ) | + ( hp100_inw( MEM_MAP_MSW ) << 16 ) ); + (u_int)mem_ptr_phys &= ~0x1fff; /* 8k alignment */ + if ( bus == HP100_BUS_ISA && ( (u_long)mem_ptr_phys & ~0xfffff ) != 0 ) { + printk("hp100: %s: Can only use programmed i/o mode.\n", dev->name); mem_ptr_phys = NULL; mem_mapped = 0; + local_mode=3; /* Use programmed i/o */ } - if ( mem_mapped && bus == HP100_BUS_PCI ) - { - if ( ( mem_ptr_virt = vremap( (u_long)mem_ptr_phys, 0x2000 ) ) == NULL ) - { - printk( "hp100: vremap for high PCI memory at 0x%lx failed\n", (u_long)mem_ptr_phys ); - mem_ptr_phys = NULL; - mem_mapped = 0; - } - } - } -#else - mem_mapped = 0; - mem_ptr_phys = mem_ptr_virt = NULL; + + /* We do not need access to shared memory in busmaster mode */ + /* However in slave mode we need to remap high (>1GB) card memory */ + if(local_mode!=1) /* = not busmaster */ + { + if ( bus == HP100_BUS_PCI ) + { + /* We try with smaller memory sizes, if ioremap fails */ + for(; memory_size>16383; memory_size=memory_size/2) + { + if((mem_ptr_virt=ioremap((u_long)mem_ptr_phys,memory_size))==NULL) + { +#ifdef HP100_DEBUG + printk( "hp100: %s: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", dev->name, memory_size, (u_long)mem_ptr_phys ); +#endif + } + else + { +#ifdef HP100_DEBUG + printk( "hp100: %s: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", dev->name, memory_size, (u_long)mem_ptr_phys, (u_long)mem_ptr_virt); #endif + break; + } + } + + if(mem_ptr_virt==NULL) /* all ioremap tries failed */ + { + printk("hp100: %s: Failed to ioremap the PCI card memory. Will have to use i/o mapped mode.\n", dev->name); + local_mode=3; + memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07) ); + } + } + } + + } + + if(local_mode==3) /* io mapped forced */ + { + mem_mapped = 0; + mem_ptr_phys = mem_ptr_virt = NULL; + printk("hp100: %s: Using (slow) programmed i/o mode.\n", dev->name); + } - if ( ( dev -> priv = kmalloc( sizeof( struct hp100_private ), GFP_KERNEL ) ) == NULL ) + /* Initialise the "private" data structure for this card. */ + if ( (dev->priv=kmalloc(sizeof(struct hp100_private), GFP_KERNEL)) == NULL) return -ENOMEM; - memset( dev -> priv, 0, sizeof( struct hp100_private ) ); + memset( dev->priv, 0, sizeof(struct hp100_private) ); - lp = (struct hp100_private *)dev -> priv; - lp -> id = eid; - lp -> mem_mapped = mem_mapped; - lp -> mem_ptr_phys = mem_ptr_phys; - lp -> mem_ptr_virt = mem_ptr_virt; + lp = (struct hp100_private *)dev->priv; + lp->id = eid; + lp->chip = chip; + lp->mode = local_mode; + lp->pci_bus = pci_bus; + lp->bus = bus; + lp->pci_device_fn = pci_device_fn; + lp->priority_tx = hp100_priority_tx; + lp->rx_ratio = hp100_rx_ratio; + lp->mem_ptr_phys = mem_ptr_phys; + lp->mem_ptr_virt = mem_ptr_virt; hp100_page( ID_MAC_ADDR ); - lp -> soft_model = hp100_inb( SOFT_MODEL ); - lp -> mac1_mode = HP100_MAC1MODE3; - lp -> mac2_mode = HP100_MAC2MODE3; - - dev -> base_addr = ioaddr; + lp->soft_model = hp100_inb( SOFT_MODEL ); + lp->mac1_mode = HP100_MAC1MODE3; + lp->mac2_mode = HP100_MAC2MODE3; + memset( &lp->hash_bytes, 0x00, 8 ); + + dev->base_addr = ioaddr; + + lp->memory_size = memory_size; + lp->rx_ratio = hp100_rx_ratio; /* can be conf'd with insmod */ + + /* memory region for programmed i/o */ + request_region( dev->base_addr, HP100_REGION_SIZE, eid->name ); + + dev->open = hp100_open; + dev->stop = hp100_close; + + if (lp->mode==1) /* busmaster */ + dev->hard_start_xmit = hp100_start_xmit_bm; + else + dev->hard_start_xmit = hp100_start_xmit; + + dev->get_stats = hp100_get_stats; + dev->set_multicast_list = &hp100_set_multicast_list; + + /* Ask the card for which IRQ line it is configured */ hp100_page( HW_MAP ); - dev -> irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQ_MASK; - if ( dev -> irq == 2 ) dev -> irq = 9; - lp -> memory_size = 0x200 << ( ( hp100_inb( SRAM ) & 0xe0 ) >> 5 ); - lp -> rx_ratio = hp100_rx_ratio; - - dev -> open = hp100_open; - dev -> stop = hp100_close; - dev -> hard_start_xmit = hp100_start_xmit; - dev -> get_stats = hp100_get_stats; - dev -> set_multicast_list = &hp100_set_multicast_list; + dev->irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQMASK; + if ( dev->irq == 2 ) + dev->irq = 9; - request_region( dev -> base_addr, HP100_REGION_SIZE, eid -> name ); + if(lp->mode==1) /* busmaster */ + dev->dma=4; + /* Ask the card for its MAC address and store it for later use. */ hp100_page( ID_MAC_ADDR ); for ( i = uc = 0; i < 6; i++ ) - dev -> dev_addr[ i ] = hp100_inb( LAN_ADDR + i ); + dev->dev_addr[ i ] = hp100_inb( LAN_ADDR + i ); + /* Reset statistics (counters) */ hp100_clear_stats( ioaddr ); ether_setup( dev ); - lp -> lan_type = hp100_sense_lan( dev ); + /* If busmaster mode is wanted, a dma-capable memory area is needed for + * the rx and tx PDLs + * PCI cards can access the whole PC memory. Therefore GFP_DMA is not + * needed for the allocation of the memory area. + */ + + /* TODO: We do not need this with old cards, where PDLs are stored + * in the cards shared memory area. But currently, busmaster has been + * implemented/tested only with the lassen chip anyway... */ + if(lp->mode==1) /* busmaster */ + { + /* Get physically continous memory for TX & RX PDLs */ + if ( (lp->page_vaddr=kmalloc(MAX_RINGSIZE+0x0f,GFP_KERNEL) ) == NULL) + return -ENOMEM; + lp->page_vaddr_algn=((u_int *) ( ((u_int)(lp->page_vaddr)+0x0f) &~0x0f)); + memset(lp->page_vaddr, 0, MAX_RINGSIZE+0x0f); + +#ifdef HP100_DEBUG_BM + printk("hp100: %s: Reserved DMA memory from 0x%x to 0x%x\n", + dev->name, + (u_int)lp->page_vaddr_algn, + (u_int)lp->page_vaddr_algn+MAX_RINGSIZE); +#endif + lp->rxrcommit = lp->txrcommit = 0; + lp->rxrhead = lp->rxrtail = &(lp->rxring[0]); + lp->txrhead = lp->txrtail = &(lp->txring[0]); + } + + /* Initialise the card. */ + /* (I'm not really sure if it's a good idea to do this during probing, but + * like this it's assured that the lan connection type can be sensed + * correctly) + */ + hp100_hwinit( dev ); + + /* Try to find out which kind of LAN the card is connected to. */ + lp->lan_type = hp100_sense_lan( dev ); - printk( "%s: %s at 0x%x, IRQ %d, ", - dev -> name, lp -> id -> name, ioaddr, dev -> irq ); + /* Print out a message what about what we think we have probed. */ + printk( "hp100: %s: %s at 0x%x, IRQ %d, ", + dev->name, lp->id->name, ioaddr, dev->irq ); switch ( bus ) { - case HP100_BUS_EISA: printk( "EISA" ); break; - case HP100_BUS_PCI: printk( "PCI" ); break; - default: printk( "ISA" ); break; + case HP100_BUS_EISA: printk( "EISA" ); break; + case HP100_BUS_PCI: printk( "PCI" ); break; + default: printk( "ISA" ); break; } printk( " bus, %dk SRAM (rx/tx %d%%).\n", - lp -> memory_size >> ( 10 - 4 ), lp -> rx_ratio ); - if ( mem_mapped ) + lp->memory_size >> 10, lp->rx_ratio ); + + if ( lp->mode==2 ) /* memory mapped */ { - printk( "%s: Memory area at 0x%lx-0x%lx", - dev -> name, (u_long)mem_ptr_phys, (u_long)mem_ptr_phys + 0x1fff ); + printk( "hp100: %s: Memory area at 0x%lx-0x%lx", + dev->name,(u_long)mem_ptr_phys,(u_long)mem_ptr_phys+(u_long)lp->memory_size ); if ( mem_ptr_virt ) - printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt ); + printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt ); printk( ".\n" ); + + /* Set for info when doing ifconfig */ + dev->mem_start = (u_long)mem_ptr_phys; + dev->mem_end = (u_long)mem_ptr_phys+(u_long)lp->memory_size; } - printk( "%s: ", dev -> name ); - if ( lp -> lan_type != HP100_LAN_ERR ) + printk( "hp100: %s: ", dev->name ); + if ( lp->lan_type != HP100_LAN_ERR ) printk( "Adapter is attached to " ); - switch ( lp -> lan_type ) { - case HP100_LAN_100: - printk( "100Mb/s Voice Grade AnyLAN network.\n" ); - break; - case HP100_LAN_10: - printk( "10Mb/s network.\n" ); - break; - default: - printk( "Warning! Link down.\n" ); + switch ( lp->lan_type ) { + case HP100_LAN_100: + printk( "100Mb/s Voice Grade AnyLAN network.\n" ); + break; + case HP100_LAN_10: + printk( "10Mb/s network.\n" ); + break; + default: + printk( "Warning! Link down.\n" ); } + + return 0; +} + + +/* This procedure puts the card into a stable init state */ +static void hp100_hwinit( struct device *dev ) +{ + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4202, TRACE ); + printk("hp100: %s: hwinit\n", dev->name); +#endif + + /* Initialise the card. -------------------------------------------- */ + + /* Clear all pending Ints and disable Ints */ + hp100_page( PERFORMANCE ); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* clear all pending ints */ + + hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW ); + hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW ); + + if(lp->mode==1) + { + hp100_BM_shutdown( dev ); /* disables BM, puts cascade in reset */ + wait(); + } + else + { + hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW ); + hp100_cascade_reset( dev, TRUE ); + hp100_page( MAC_CTRL ); + hp100_andb( ~(HP100_RX_EN|HP100_TX_EN), MAC_CFG_1); + } - hp100_stop_interface( dev ); + /* Initiate EEPROM reload */ + hp100_load_eeprom( dev ); + + wait(); + + /* Go into reset again. */ + hp100_cascade_reset( dev, TRUE ); + + /* Set Option Registers to a safe state */ + hp100_outw( HP100_DEBUG_EN | + HP100_RX_HDR | + HP100_EE_EN | + HP100_BM_WRITE | + HP100_BM_READ | HP100_RESET_HB | + HP100_FAKE_INT | + HP100_INT_EN | + HP100_MEM_EN | + HP100_IO_EN | HP100_RESET_LB, OPTION_LSW); + + hp100_outw( HP100_TRI_INT | + HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); + + hp100_outb( HP100_PRIORITY_TX | + HP100_ADV_NXT_PKT | + HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW ); + + /* TODO: Configure MMU for Ram Test. */ + /* TODO: Ram Test. */ + + /* Re-check if adapter is still at same i/o location */ + /* (If the base i/o in eeprom has been changed but the */ + /* registers had not been changed, a reload of the eeprom */ + /* would move the adapter to the address stored in eeprom */ - return 0; + /* TODO: Code to implement. */ + + /* Until here it was code from HWdiscover procedure. */ + /* Next comes code from mmuinit procedure of SCO BM driver which is + * called from HWconfigure in the SCO driver. */ + + /* Initialise MMU, eventually switch on Busmaster Mode, initialise + * multicast filter... + */ + hp100_mmuinit( dev ); + + /* We don't turn the interrupts on here - this is done by start_interface. */ + wait(); /* TODO: Do we really need this? */ + + /* Enable Hardware (e.g. unreset) */ + hp100_cascade_reset( dev, FALSE ); + + /* ------- initialisation complete ----------- */ + + /* Finally try to log in the Hub if there may be a VG connection. */ + if( lp->lan_type != HP100_LAN_10 ) + hp100_login_to_vg_hub( dev, FALSE ); /* relogin */ } -/* - * open/close functions + +/* + * mmuinit - Reinitialise Cascade MMU and MAC settings. + * Note: Must already be in reset and leaves card in reset. */ - -static int hp100_open( struct device *dev ) +static void hp100_mmuinit( struct device *dev ) { + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; int i; - int ioaddr = dev -> base_addr; - struct hp100_private *lp = (struct hp100_private *)dev -> priv; - if ( request_irq( dev -> irq, hp100_interrupt, SA_INTERRUPT, lp -> id -> name, NULL ) ) +#ifdef HP100_DEBUG_B + hp100_outw( 0x4203, TRACE ); + printk("hp100: %s: mmuinit\n",dev->name); +#endif + +#ifdef HP100_DEBUG + if( 0!=(hp100_inw(OPTION_LSW)&HP100_HW_RST) ) { - printk( "%s: unable to get IRQ %d\n", dev -> name, dev -> irq ); - return -EAGAIN; + printk("hp100: %s: Not in reset when entering mmuinit. Fix me.\n",dev->name); + return; } - irq2dev_map[ dev -> irq ] = dev; +#endif - MOD_INC_USE_COUNT; - - dev -> tbusy = 0; - dev -> trans_start = jiffies; - dev -> interrupt = 0; - dev -> start = 1; - - lp -> lan_type = hp100_sense_lan( dev ); - lp -> mac1_mode = HP100_MAC1MODE3; - lp -> mac2_mode = HP100_MAC2MODE3; + /* Make sure IRQs are masked off and ack'ed. */ + hp100_page( PERFORMANCE ); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */ + + /* + * Enable Hardware + * - Clear Debug En, Rx Hdr Pipe, EE En, I/O En, Fake Int and Intr En + * - Set Tri-State Int, Bus Master Rd/Wr, and Mem Map Disable + * - Clear Priority, Advance Pkt and Xmit Cmd + */ + + hp100_outw( HP100_DEBUG_EN | + HP100_RX_HDR | + HP100_EE_EN | HP100_RESET_HB | + HP100_IO_EN | + HP100_FAKE_INT | + HP100_INT_EN | HP100_RESET_LB, OPTION_LSW ); + + hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW); + + if(lp->mode==1) /* busmaster */ + { + hp100_outw( HP100_BM_WRITE | + HP100_BM_READ | + HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); + } + else if(lp->mode==2) /* memory mapped */ + { + hp100_outw( HP100_BM_WRITE | + HP100_BM_READ | HP100_RESET_HB, OPTION_LSW ); + hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW ); + hp100_outw( HP100_MEM_EN | HP100_SET_LB, OPTION_LSW ); + hp100_outw( HP100_IO_EN | HP100_SET_LB, OPTION_LSW ); + } + else if( lp->mode==3 ) /* i/o mapped mode */ + { + hp100_outw( HP100_MMAP_DIS | HP100_SET_HB | + HP100_IO_EN | HP100_SET_LB, OPTION_LSW ); + } + + hp100_page( HW_MAP ); + hp100_outb( 0, EARLYRXCFG ); + hp100_outw( 0, EARLYTXCFG ); - hp100_page( MAC_CTRL ); - hp100_orw( HP100_LINK_BEAT_DIS | HP100_RESET_LB, LAN_CFG_10 ); + /* + * Enable Bus Master mode + */ + if(lp->mode==1) /* busmaster */ + { + /* Experimental: Set some PCI configuration bits */ + hp100_page( HW_MAP ); + hp100_andb( ~HP100_PDL_USE3, MODECTRL1 ); /* BM engine read maximum */ + hp100_andb( ~HP100_TX_DUALQ, MODECTRL1 ); /* No Queue for Priority TX */ + + /* PCI Bus failures should result in a Misc. Interrupt */ + hp100_orb( HP100_EN_BUS_FAIL, MODECTRL2); + + hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_SET_HB, OPTION_LSW ); + hp100_page( HW_MAP ); + /* Use Burst Mode and switch on PAGE_CK */ + hp100_orb( HP100_BM_BURST_RD | + HP100_BM_BURST_WR, BM); + if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA)) + hp100_orb( HP100_BM_PAGE_CK, BM ); + hp100_orb( HP100_BM_MASTER, BM ); + } + else /* not busmaster */ + { + hp100_page(HW_MAP); + hp100_andb(~HP100_BM_MASTER, BM ); + } + + /* + * Divide card memory into regions for Rx, Tx and, if non-ETR chip, PDLs + */ + hp100_page( MMU_CFG ); + if(lp->mode==1) /* only needed for Busmaster */ + { + int xmit_stop, recv_stop; - hp100_stop_interface( dev ); - hp100_load_eeprom( dev ); + if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA)) + { + int pdl_stop; + + /* + * Each pdl is 508 bytes long. (63 frags * 4 bytes for address and + * 4 bytes for for header). We will leave NUM_RXPDLS * 508 (rounded + * to the next higher 1k boundary) bytes for the rx-pdl's + * Note: For non-etr chips the transmit stop register must be + * programmed on a 1k boundary, i.e. bits 9:0 must be zero. + */ + pdl_stop = lp->memory_size; + xmit_stop = ( pdl_stop-508*(MAX_RX_PDL)-16 )& ~(0x03ff); + recv_stop = ( xmit_stop * (lp->rx_ratio)/100 ) &~(0x03ff); + hp100_outw( (pdl_stop>>4)-1, PDL_MEM_STOP ); +#ifdef HP100_DEBUG_BM + printk("hp100: %s: PDL_STOP = 0x%x\n", dev->name, pdl_stop); +#endif + } + else /* ETR chip (Lassen) in busmaster mode */ + { + xmit_stop = ( lp->memory_size ) - 1; + recv_stop = ( ( lp->memory_size * lp->rx_ratio ) / 100 ) & ~(0x03ff); + } + + hp100_outw( xmit_stop>>4 , TX_MEM_STOP ); + hp100_outw( recv_stop>>4 , RX_MEM_STOP ); +#ifdef HP100_DEBUG_BM + printk("hp100: %s: TX_STOP = 0x%x\n",dev->name,xmit_stop>>4); + printk("hp100: %s: RX_STOP = 0x%x\n",dev->name,recv_stop>>4); +#endif + } + else /* Slave modes (memory mapped and programmed io) */ + { + hp100_outw( (((lp->memory_size*lp->rx_ratio)/100)>>4), RX_MEM_STOP ); + hp100_outw( ((lp->memory_size - 1 )>>4), TX_MEM_STOP ); +#ifdef HP100_DEBUG + printk("hp100: %s: TX_MEM_STOP: 0x%x\n", dev->name,hp100_inw(TX_MEM_STOP)); + printk("hp100: %s: RX_MEM_STOP: 0x%x\n", dev->name,hp100_inw(RX_MEM_STOP)); +#endif + } - hp100_outw( HP100_MMAP_DIS | HP100_SET_HB | - HP100_IO_EN | HP100_SET_LB, OPTION_LSW ); - hp100_outw( HP100_DEBUG_EN | HP100_RX_HDR | HP100_EE_EN | HP100_RESET_HB | - HP100_FAKE_INT | HP100_RESET_LB, OPTION_LSW ); - hp100_outw( HP100_ADV_NXT_PKT | HP100_TX_CMD | HP100_RESET_LB | - HP100_PRIORITY_TX | ( hp100_priority_tx ? HP100_SET_HB : HP100_RESET_HB ), - OPTION_MSW ); - + /* Write MAC address into page 1 */ hp100_page( MAC_ADDRESS ); for ( i = 0; i < 6; i++ ) - hp100_outb( dev -> dev_addr[ i ], MAC_ADDR + i ); - for ( i = 0; i < 8; i++ ) /* setup multicast filter to receive all */ - hp100_outb( 0xff, HASH_BYTE0 + i ); + hp100_outb( dev->dev_addr[ i ], MAC_ADDR + i ); + + /* Zero the multicast hash registers */ + for ( i = 0; i < 8; i++ ) + hp100_outb( 0x0, HASH_BYTE0 + i ); + + /* Set up MAC defaults */ + hp100_page( MAC_CTRL ); + + /* Go to LAN Page and zero all filter bits */ + /* Zero accept error, accept multicast, accept broadcast and accept */ + /* all directed packet bits */ + hp100_andb( ~(HP100_RX_EN| + HP100_TX_EN| + HP100_ACC_ERRORED| + HP100_ACC_MC| + HP100_ACC_BC| + HP100_ACC_PHY), MAC_CFG_1 ); + + hp100_outb( 0x00, MAC_CFG_2 ); + + /* Zero the frame format bit. This works around a training bug in the */ + /* new hubs. */ + hp100_outb( 0x00, VG_LAN_CFG_2); /* (use 802.3) */ + + if(lp->priority_tx) + hp100_outb( HP100_PRIORITY_TX | HP100_SET_LB, OPTION_MSW ); + else + hp100_outb( HP100_PRIORITY_TX | HP100_RESET_LB, OPTION_MSW ); + + hp100_outb( HP100_ADV_NXT_PKT | + HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW ); + + /* If busmaster, initialize the PDLs */ + if(lp->mode==1) + hp100_init_pdls( dev ); + + /* Go to performance page and initalize isr and imr registers */ hp100_page( PERFORMANCE ); - hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ - hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */ - hp100_outw( (HP100_RX_PACKET | HP100_RX_ERROR | HP100_SET_HB) | - (HP100_TX_ERROR | HP100_SET_LB ), IRQ_MASK ); - /* and enable few */ - hp100_reset_card(); - hp100_page( MMU_CFG ); - hp100_outw( ( lp -> memory_size * lp -> rx_ratio ) / 100, RX_MEM_STOP ); - hp100_outw( lp -> memory_size - 1, TX_MEM_STOP ); - hp100_unreset_card(); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */ +} + + +/* + * open/close functions + */ + +static int hp100_open( struct device *dev ) +{ + struct hp100_private *lp = (struct hp100_private *)dev->priv; +#ifdef HP100_DEBUG_B + int ioaddr=dev->base_addr; +#endif + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4204, TRACE ); + printk("hp100: %s: open\n",dev->name); +#endif + + /* New: if bus is PCI or EISA, interrupts might be shared interrupts */ + if ( request_irq(dev->irq, hp100_interrupt, + lp->bus==HP100_BUS_PCI||lp->bus==HP100_BUS_EISA?SA_SHIRQ:SA_INTERRUPT, + lp->id->name, dev)) + { + printk( "hp100: %s: unable to get IRQ %d\n", dev->name, dev->irq ); + return -EAGAIN; + } - if ( lp -> lan_type == HP100_LAN_100 ) - lp -> hub_status = hp100_login_to_vg_hub( dev ); + MOD_INC_USE_COUNT; + + dev->tbusy = 0; + dev->trans_start = jiffies; + dev->interrupt = 0; + dev->start = 1; + + lp->lan_type = hp100_sense_lan( dev ); + lp->mac1_mode = HP100_MAC1MODE3; + lp->mac2_mode = HP100_MAC2MODE3; + memset( &lp->hash_bytes, 0x00, 8 ); + + hp100_stop_interface( dev ); + + hp100_hwinit( dev ); - hp100_start_interface( dev ); + hp100_start_interface( dev ); /* sets mac modes, enables interrupts */ return 0; } + +/* The close function is called when the interface is to be brought down */ static int hp100_close( struct device *dev ) { - int ioaddr = dev -> base_addr; - struct hp100_private *lp = (struct hp100_private *)dev -> priv; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4205, TRACE ); + printk("hp100: %s: close\n", dev->name); +#endif hp100_page( PERFORMANCE ); - hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all IRQs */ + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all IRQs */ hp100_stop_interface( dev ); - if ( lp -> lan_type == HP100_LAN_100 ) /* relogin */ - hp100_login_to_vg_hub( dev ); + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status=hp100_login_to_vg_hub( dev, FALSE ); + + dev->tbusy = 1; + dev->start = 0; - dev -> tbusy = 1; - dev -> start = 0; + free_irq( dev->irq, dev ); + +#ifdef HP100_DEBUG + printk( "hp100: %s: close LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) ); +#endif - free_irq( dev -> irq, NULL ); - irq2dev_map[ dev -> irq ] = NULL; MOD_DEC_USE_COUNT; return 0; } -/* - * transmit + +/* + * Configure the PDL Rx rings and LAN */ - -static int hp100_start_xmit( struct sk_buff *skb, struct device *dev ) +static void hp100_init_pdls( struct device *dev ) { - int i, ok_flag; - int ioaddr = dev -> base_addr; - u_short val; - struct hp100_private *lp = (struct hp100_private *)dev -> priv; - - if ( lp -> lan_type < 0 ) - { - hp100_stop_interface( dev ); - if ( ( lp -> lan_type = hp100_sense_lan( dev ) ) < 0 ) + struct hp100_private *lp = (struct hp100_private *)dev->priv; + hp100_ring_t *ringptr; + u_int *pageptr; + int i; + +#ifdef HP100_DEBUG_B + int ioaddr = dev->base_addr; +#endif + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4206, TRACE ); + printk("hp100: %s: init pdls\n", dev->name); +#endif + + if(0==lp->page_vaddr_algn) + printk("hp100: %s: Warning: lp->page_vaddr_algn not initialised!\n",dev->name); + else + { + /* pageptr shall point into the DMA accessible memory region */ + /* we use this pointer to status the upper limit of allocated */ + /* memory in the allocated page. */ + /* note: align the pointers to the pci cache line size */ + memset(lp->page_vaddr_algn, 0, MAX_RINGSIZE); /* Zero Rx/Tx ring page */ + pageptr=lp->page_vaddr_algn; + + lp->rxrcommit =0; + ringptr = lp->rxrhead = lp-> rxrtail = &(lp->rxring[0]); + + /* Initialise Rx Ring */ + for (i=MAX_RX_PDL-1; i>=0; i--) { - printk( "%s: no connection found - check wire\n", dev -> name ); - hp100_start_interface( dev ); /* 10Mb/s RX packets maybe handled */ - return -EIO; + lp->rxring[i].next = ringptr; + ringptr=&(lp->rxring[i]); + pageptr+=hp100_init_rxpdl(dev, ringptr, pageptr); } - if ( lp -> lan_type == HP100_LAN_100 ) - lp -> hub_status = hp100_login_to_vg_hub( dev ); - hp100_start_interface( dev ); - } - - if ( ( i = ( hp100_inl( TX_MEM_FREE ) & ~0x7fffffff ) ) < skb -> len + 16 ) - { -#ifdef HP100_DEBUG - printk( "hp100_start_xmit: rx free mem = 0x%x\n", i ); -#endif - if ( jiffies - dev -> trans_start < 2 * HZ ) return -EAGAIN; - if ( lp -> lan_type == HP100_LAN_100 && lp -> hub_status < 0 ) - /* 100Mb/s adapter isn't connected to hub */ + + /* Initialise Tx Ring */ + lp->txrcommit = 0; + ringptr = lp->txrhead = lp->txrtail = &(lp->txring[0]); + for (i=MAX_TX_PDL-1; i>=0; i--) { - printk( "%s: login to 100Mb/s hub retry\n", dev -> name ); - hp100_stop_interface( dev ); - lp -> hub_status = hp100_login_to_vg_hub( dev ); - hp100_start_interface( dev ); + lp->txring[i].next = ringptr; + ringptr=&(lp->txring[i]); + pageptr+=hp100_init_txpdl(dev, ringptr, pageptr); } - else + } +} + + +/* These functions "format" the entries in the pdl structure */ +/* They return how much memory the fragments need. */ +static int hp100_init_rxpdl( struct device *dev, register hp100_ring_t *ringptr, register u32 *pdlptr ) +{ + /* pdlptr is starting adress for this pdl */ + + if( 0!=( ((unsigned)pdlptr) & 0xf) ) + printk("hp100: %s: Init rxpdl: Unaligned pdlptr 0x%x.\n",dev->name,(unsigned)pdlptr); + + ringptr->pdl = pdlptr+1; + ringptr->pdl_paddr = virt_to_bus(pdlptr+1); + ringptr->skb = (void *) NULL; + + /* + * Write address and length of first PDL Fragment (which is used for + * storing the RX-Header + * We use the 4 bytes _before_ the PDH in the pdl memory area to + * store this information. (PDH is at offset 0x04) + */ + /* Note that pdlptr+1 and not pdlptr is the pointer to the PDH */ + + *(pdlptr+2) =(u_int) virt_to_bus(pdlptr); /* Address Frag 1 */ + *(pdlptr+3) = 4; /* Length Frag 1 */ + + return( ( ((MAX_RX_FRAG*2+2)+3) /4)*4 ); +} + + +static int hp100_init_txpdl( struct device *dev, register hp100_ring_t *ringptr, register u32 *pdlptr ) +{ + if( 0!=( ((unsigned)pdlptr) & 0xf) ) + printk("hp100: %s: Init txpdl: Unaligned pdlptr 0x%x.\n",dev->name,(unsigned) pdlptr); + + ringptr->pdl = pdlptr; /* +1; */ + ringptr->pdl_paddr = virt_to_bus(pdlptr); /* +1 */ + ringptr->skb = (void *) NULL; + + return((((MAX_TX_FRAG*2+2)+3)/4)*4); +} + + +/* + * hp100_build_rx_pdl allocates an skb_buff of maximum size plus two bytes + * for possible odd word alignment rounding up to next dword and set PDL + * address for fragment#2 + * Returns: 0 if unable to allocate skb_buff + * 1 if successful + */ +int hp100_build_rx_pdl( hp100_ring_t *ringptr, struct device *dev ) +{ +#ifdef HP100_DEBUG_B + int ioaddr = dev->base_addr; +#endif +#ifdef HP100_DEBUG_BM + u_int *p; +#endif + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4207, TRACE ); + printk("hp100: %s: build rx pdl\n", dev->name); +#endif + + /* Allocate skb buffer of maximum size */ + /* Note: This depends on the alloc_skb functions allocating more + * space than requested, i.e. aligning to 16bytes */ + + ringptr->skb = dev_alloc_skb( ((MAX_ETHER_SIZE+2+3)/4)*4 ); + + if(NULL!=ringptr->skb) + { + /* + * Reserve 2 bytes at the head of the buffer to land the IP header + * on a long word boundary (According to the Network Driver section + * in the Linux KHG, this should help to increase performance.) + */ + skb_reserve(ringptr->skb, 2); + + ringptr->skb->dev=dev; + ringptr->skb->data=(u_char *)skb_put(ringptr->skb, MAX_ETHER_SIZE ); + + /* ringptr->pdl points to the beginning of the PDL, i.e. the PDH */ + /* Note: 1st Fragment is used for the 4 byte packet status + * (receive header). Its PDL entries are set up by init_rxpdl. So + * here we only have to set up the PDL fragment entries for the data + * part. Those 4 bytes will be stored in the DMA memory region + * directly before the PDL. + */ +#ifdef HP100_DEBUG_BM + printk("hp100: %s: build_rx_pdl: PDH@0x%x, skb->data (len %d) at 0x%x\n", + dev->name, + (u_int) ringptr->pdl, + ((MAX_ETHER_SIZE+2+3)/4)*4, + (unsigned int) ringptr->skb->data); +#endif + + ringptr->pdl[0] = 0x00020000; /* Write PDH */ + ringptr->pdl[3] = ((u_int)virt_to_bus(ringptr->skb->data)); + ringptr->pdl[4] = MAX_ETHER_SIZE; /* Length of Data */ + +#ifdef HP100_DEBUG_BM + for(p=(ringptr->pdl); p<(ringptr->pdl+5); p++) + printk("hp100: %s: Adr 0x%.8x = 0x%.8x\n",dev->name,(u_int) p,(u_int) *p ); +#endif + return(1); + } + /* else: */ + /* alloc_skb failed (no memory) -> still can receive the header + * fragment into PDL memory. make PDL safe by clearing msgptr and + * making the PDL only 1 fragment (i.e. the 4 byte packet status) + */ +#ifdef HP100_DEBUG_BM + printk("hp100: %s: build_rx_pdl: PDH@0x%x, No space for skb.\n", + dev->name, + (u_int) ringptr->pdl); +#endif + + ringptr->pdl[0]=0x00010000; /* PDH: Count=1 Fragment */ + + return(0); +} + + +/* + * hp100_rxfill - attempt to fill the Rx Ring will empty skb's + * + * Makes assumption that skb's are always contiguous memory areas and + * therefore PDLs contain only 2 physical fragments. + * - While the number of Rx PDLs with buffers is less than maximum + * a. Get a maximum packet size skb + * b. Put the physical address of the buffer into the PDL. + * c. Output physical address of PDL to adapter. + */ +static void hp100_rxfill( struct device *dev ) +{ + int ioaddr=dev->base_addr; + + struct hp100_private *lp = (struct hp100_private *)dev->priv; + hp100_ring_t *ringptr; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4208, TRACE ); + printk("hp100: %s: rxfill\n",dev->name); +#endif + + hp100_page( PERFORMANCE ); + + while (lp->rxrcommit < MAX_RX_PDL) + { + /* + ** Attempt to get a buffer and build a Rx PDL. + */ + ringptr = lp->rxrtail; + if (0 == hp100_build_rx_pdl( ringptr, dev )) { - hp100_ints_off(); - i = hp100_sense_lan( dev ); - hp100_page( PERFORMANCE ); - hp100_ints_on(); - if ( i == HP100_LAN_ERR ) - printk( "%s: link down detected\n", dev -> name ); - else - if ( lp -> lan_type != i ) + return; /* None available, return */ + } + + /* Hand this PDL over to the card */ + /* Note: This needs performance page selected! */ +#ifdef HP100_DEBUG_BM + printk("hp100: %s: rxfill: Hand to card: pdl #%d @0x%x phys:0x%x, buffer: 0x%x\n", + dev->name, + lp->rxrcommit, + (u_int)ringptr->pdl, + (u_int)ringptr->pdl_paddr, + (u_int)ringptr->pdl[3]); +#endif + + hp100_outl( (u32)ringptr->pdl_paddr, RX_PDA); + + lp->rxrcommit += 1; + lp->rxrtail = ringptr->next; + } +} + + +/* + * BM_shutdown - shutdown bus mastering and leave chip in reset state + */ + +static void hp100_BM_shutdown( struct device *dev ) +{ + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + unsigned long time; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4209, TRACE ); + printk("hp100: %s: bm shutdown\n",dev->name); +#endif + + hp100_page( PERFORMANCE ); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* Ack all ints */ + + /* Ensure Interrupts are off */ + hp100_outw( HP100_INT_EN | HP100_RESET_LB , OPTION_LSW ); + + /* Disable all MAC activity */ + hp100_page( MAC_CTRL ); + hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); /* stop rx/tx */ + + /* If cascade MMU is not already in reset */ + if (0 != (hp100_inw(OPTION_LSW)&HP100_HW_RST) ) + { + /* Wait 1.3ms (10Mb max packet time) to ensure MAC is idle so + * MMU pointers will not be reset out from underneath + */ + hp100_page( MAC_CTRL ); + for(time=0; time<5000; time++) + { + if( (hp100_inb(MAC_CFG_1)&(HP100_TX_IDLE|HP100_RX_IDLE))== + (HP100_TX_IDLE|HP100_RX_IDLE) ) break; + } + + /* Shutdown algorithm depends on the generation of Cascade */ + if( lp->chip==HP100_CHIPID_LASSEN ) + { /* ETR shutdown/reset */ + /* Disable Busmaster mode and wait for bit to go to zero. */ + hp100_page(HW_MAP); + hp100_andb( ~HP100_BM_MASTER, BM ); + /* 100 ms timeout */ + for(time=0; time<32000; time++) { - /* it's very heavy - all network setting must be changed!!! */ - printk( "%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev -> name ); - lp -> lan_type = i; - hp100_stop_interface( dev ); - if ( lp -> lan_type == HP100_LAN_100 ) - lp -> hub_status = hp100_login_to_vg_hub( dev ); - hp100_start_interface( dev ); + if ( 0 == (hp100_inb( BM ) & HP100_BM_MASTER) ) break; } - else + } + else + { /* Shasta or Rainier Shutdown/Reset */ + /* To ensure all bus master inloading activity has ceased, + * wait for no Rx PDAs or no Rx packets on card. + */ + hp100_page( PERFORMANCE ); + /* 100 ms timeout */ + for(time=0; time<10000; time++) { - printk( "%s: interface reset\n", dev -> name ); - hp100_stop_interface( dev ); - hp100_start_interface( dev ); + /* RX_PDL: PDLs not executed. */ + /* RX_PKT_CNT: RX'd packets on card. */ + if ( (hp100_inb( RX_PDL ) == 0) && + (hp100_inb( RX_PKT_CNT ) == 0) ) break; } - } - dev -> trans_start = jiffies; - return -EAGAIN; + + if(time>=10000) + printk("hp100: %s: BM shutdown error.\n", dev->name); + + /* To ensure all bus master outloading activity has ceased, + * wait until the Tx PDA count goes to zero or no more Tx space + * available in the Tx region of the card. + */ + /* 100 ms timeout */ + for(time=0; time<10000; time++) { + if ( (0 == hp100_inb( TX_PKT_CNT )) && + (0 != (hp100_inb( TX_MEM_FREE )&HP100_AUTO_COMPARE))) break; + } + + /* Disable Busmaster mode */ + hp100_page(HW_MAP); + hp100_andb( ~HP100_BM_MASTER, BM ); + } /* end of shutdown procedure for non-etr parts */ + + hp100_cascade_reset( dev, TRUE ); } - - if ( skb == NULL ) + hp100_page( PERFORMANCE ); + /* hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_RESET_HB, OPTION_LSW ); */ + /* Busmaster mode should be shut down now. */ +} + + + +/* + * transmit functions + */ + +/* tx function for busmaster mode */ +static int hp100_start_xmit_bm( struct sk_buff *skb, struct device *dev ) +{ + int i, ok_flag; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + hp100_ring_t *ringptr; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4210, TRACE ); + printk("hp100: %s: start_xmit_bm\n",dev->name); +#endif + + if ( skb==NULL ) { dev_tint( dev ); return 0; } - - if ( skb -> len <= 0 ) return 0; + + if ( skb->len <= 0 ) return 0; + + /* Get Tx ring tail pointer */ + if( lp->txrtail->next==lp->txrhead ) + { + /* No memory. */ +#ifdef HP100_DEBUG + printk("hp100: %s: start_xmit_bm: No TX PDL available.\n", dev->name); +#endif + /* not waited long enough since last tx? */ + if ( jiffies - dev->trans_start < HZ/10 ) return -EAGAIN; + + if ( lp->lan_type < 0 ) /* no LAN type detected yet? */ + { + hp100_stop_interface( dev ); + if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 ) + { + printk( "hp100: %s: no connection found - check wire\n", dev->name ); + hp100_start_interface( dev ); /* 10Mb/s RX pkts maybe handled */ + return -EIO; + } + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */ + hp100_start_interface( dev ); + } + + if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 ) + /* we have a 100Mb/s adapter but it isn't connected to hub */ + { + printk( "hp100: %s: login to 100Mb/s hub retry\n", dev->name ); + hp100_stop_interface( dev ); + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); + hp100_start_interface( dev ); + } + else + { + hp100_ints_off(); + i = hp100_sense_lan( dev ); + hp100_ints_on(); + if ( i == HP100_LAN_ERR ) + printk( "hp100: %s: link down detected\n", dev->name ); + else + if ( lp->lan_type != i ) /* cable change! */ + { + /* it's very hard - all network setting must be changed!!! */ + printk( "hp100: %s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name ); + lp->lan_type = i; + hp100_stop_interface( dev ); + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); + hp100_start_interface( dev ); + } + else + { + printk( "hp100: %s: interface reset\n", dev->name ); + hp100_stop_interface( dev ); + hp100_start_interface( dev ); + } + } + + dev->trans_start = jiffies; + return -EAGAIN; + } + + /* + * we have to turn int's off before modifying this, otherwise + * a tx_pdl_cleanup could occur at the same time + */ + cli(); + ringptr=lp->txrtail; + lp->txrtail=ringptr->next; + + /* Check whether packet has minimal packet size */ + ok_flag = skb->len >= HP100_MIN_PACKET_SIZE; + i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE; + + ringptr->skb=skb; + ringptr->pdl[0]=((1<<16) | i); /* PDH: 1 Fragment & length */ + ringptr->pdl[1]=(u32)virt_to_bus(skb->data); /* 1st Frag: Adr. of data */ + if(lp->chip==HP100_CHIPID_SHASTA) + { + /* TODO:Could someone who has the EISA card please check if this works? */ + ringptr->pdl[2]=i; + } + else /* Lassen */ + { + /* In the PDL, don't use the padded size but the real packet size: */ + ringptr->pdl[2]=skb->len; /* 1st Frag: Length of frag */ + } + + /* Hand this PDL to the card. */ + hp100_outl( ringptr->pdl_paddr, TX_PDA_L ); /* Low Prio. Queue */ + + lp->txrcommit++; + sti(); + + /* Update statistics */ + lp->stats.tx_packets++; +#ifdef LINUX_2_1 + lp->stats.tx_bytes += skb->len; +#endif + dev->trans_start = jiffies; + + return 0; +} + + +/* clean_txring checks if packets have been sent by the card by reading + * the TX_PDL register from the performance page and comparing it to the + * number of commited packets. It then frees the skb's of the packets that + * obviously have been sent to the network. + * + * Needs the PERFORMANCE page selected. + */ +static void hp100_clean_txring( struct device *dev ) +{ + struct hp100_private *lp = (struct hp100_private *)dev->priv; + int ioaddr = dev->base_addr; + int donecount; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4211, TRACE ); + printk("hp100: %s: clean txring\n", dev->name); +#endif - for ( i = 0; i < 6000 && ( hp100_inw( OPTION_MSW ) & HP100_TX_CMD ); i++ ) + /* How many PDLs have been transmitted? */ + donecount=(lp->txrcommit)-hp100_inb(TX_PDL); + +#ifdef HP100_DEBUG + if(donecount>MAX_TX_PDL) + printk("hp100: %s: Warning: More PDLs transmitted than commited to card???\n",dev->name); +#endif + + for( ; 0!=donecount; donecount-- ) { -#ifdef HP100_DEBUG_TX - printk( "hp100_start_xmit: busy\n" ); -#endif +#ifdef HP100_DEBUG_BM + printk("hp100: %s: Free skb: data @0x%.8x txrcommit=0x%x TXPDL=0x%x, done=0x%x\n", + dev->name, + (u_int) lp->txrhead->skb->data, + lp->txrcommit, + hp100_inb(TX_PDL), + donecount); +#endif + dev_kfree_skb( lp->txrhead->skb, FREE_WRITE ); + lp->txrhead->skb=(void *)NULL; + lp->txrhead=lp->txrhead->next; + lp->txrcommit--; } +} - hp100_ints_off(); - val = hp100_inw( IRQ_STATUS ); - hp100_outw( val & HP100_TX_COMPLETE, IRQ_STATUS ); -#ifdef HP100_DEBUG_TX - printk( "hp100_start_xmit: irq_status = 0x%x, len = %d\n", val, (int)skb -> len ); + +/* tx function for slave modes */ +static int hp100_start_xmit( struct sk_buff *skb, struct device *dev ) +{ + int i, ok_flag; + int ioaddr = dev->base_addr; + u_short val; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4212, TRACE ); + printk("hp100: %s: start_xmit\n", dev->name); #endif - ok_flag = skb -> len >= HP100_MIN_PACKET_SIZE; - i = ok_flag ? skb -> len : HP100_MIN_PACKET_SIZE; - hp100_outw( i, DATA32 ); /* length to memory manager */ - hp100_outw( i, FRAGMENT_LEN ); - if ( lp -> mem_mapped ) - { - if ( lp -> mem_ptr_virt ) - { - memcpy( lp -> mem_ptr_virt, skb -> data, skb -> len ); - if ( !ok_flag ) - memset( lp -> mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb -> len ); + + if ( lp->lan_type < 0 ) /* no LAN type detected yet? */ + { + hp100_stop_interface( dev ); + if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 ) + { + printk( "hp100: %s: no connection found - check wire\n", dev->name ); + hp100_start_interface( dev ); /* 10Mb/s RX packets maybe handled */ + return -EIO; } - else + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */ + hp100_start_interface( dev ); + } + + /* If there is not enough free memory on the card... */ + i=hp100_inl(TX_MEM_FREE)&0x7fffffff; + if ( !(((i/2)-539)>(skb->len+16) && (hp100_inb(TX_PKT_CNT)<255)) ) + { +#ifdef HP100_DEBUG + printk( "hp100: %s: start_xmit: tx free mem = 0x%x\n", dev->name, i ); +#endif + /* not waited long enough since last failed tx try? */ + if ( jiffies - dev->trans_start < HZ/2 ) + { +#ifdef HP100_DEBUG + printk("hp100: %s: trans_start timing problem\n", dev->name); +#endif + return -EAGAIN; + } + if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 ) + /* we have a 100Mb/s adapter but it isn't connected to hub */ { - memcpy_toio( lp -> mem_ptr_phys, skb -> data, skb -> len ); - if ( !ok_flag ) - memset_io( lp -> mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb -> len ); + printk( "hp100: %s: login to 100Mb/s hub retry\n", dev->name ); + hp100_stop_interface( dev ); + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); + hp100_start_interface( dev ); } + else + { + hp100_ints_off(); + i = hp100_sense_lan( dev ); + hp100_ints_on(); + if ( i == HP100_LAN_ERR ) + printk( "hp100: %s: link down detected\n", dev->name ); + else + if ( lp->lan_type != i ) /* cable change! */ + { + /* it's very hard - all network setting must be changed!!! */ + printk( "hp100: %s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name ); + lp->lan_type = i; + hp100_stop_interface( dev ); + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); + hp100_start_interface( dev ); + } + else + { + printk( "hp100: %s: interface reset\n", dev->name ); + hp100_stop_interface( dev ); + hp100_start_interface( dev ); + udelay(1000); + } + } + dev->trans_start = jiffies; + return -EAGAIN; } - else + + for ( i=0; i<6000 && ( hp100_inb( OPTION_MSW ) & HP100_TX_CMD ); i++ ) + { +#ifdef HP100_DEBUG_TX + printk( "hp100: %s: start_xmit: busy\n", dev->name ); +#endif + } + + hp100_ints_off(); + val = hp100_inw( IRQ_STATUS ); + /* Ack / clear the interrupt TX_COMPLETE interrupt - this interrupt is set + * when the current packet being transmitted on the wire is completed. */ + hp100_outw( HP100_TX_COMPLETE, IRQ_STATUS ); +#ifdef HP100_DEBUG_TX + printk("hp100: %s: start_xmit: irq_status=0x%.4x, irqmask=0x%.4x, len=%d\n",dev->name,val,hp100_inw(IRQ_MASK),(int)skb->len ); +#endif + + ok_flag = skb->len >= HP100_MIN_PACKET_SIZE; + i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE; + + hp100_outw( i, DATA32 ); /* tell card the total packet length */ + hp100_outw( i, FRAGMENT_LEN ); /* and first/only fragment length */ + + if ( lp->mode==2 ) /* memory mapped */ + { + if ( lp->mem_ptr_virt ) /* high pci memory was remapped */ + { + /* Note: The J2585B needs alignment to 32bits here! */ + memcpy( lp->mem_ptr_virt, skb->data, ( skb->len +3 ) & ~3 ); + if ( !ok_flag ) + memset( lp->mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb->len ); + } + else + { + memcpy_toio( lp->mem_ptr_phys, skb->data, skb->len ); + if ( !ok_flag ) + memset_io( lp->mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb->len ); + } + } + else /* programmed i/o */ { - outsl( ioaddr + HP100_REG_DATA32, skb -> data, ( skb -> len + 3 ) >> 2 ); + outsl( ioaddr + HP100_REG_DATA32, skb->data, ( skb->len + 3 ) >> 2 ); if ( !ok_flag ) - for ( i = ( skb -> len + 3 ) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4 ) - hp100_outl( 0, DATA32 ); + for ( i = ( skb->len + 3 ) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4 ) + hp100_outl( 0, DATA32 ); } - hp100_outw( HP100_TX_CMD | HP100_SET_LB, OPTION_MSW ); /* send packet */ - lp -> stats.tx_packets++; - dev -> trans_start = jiffies; + + hp100_outb( HP100_TX_CMD | HP100_SET_LB, OPTION_MSW ); /* send packet */ + + lp->stats.tx_packets++; +#ifdef LINUX_2_1 + lp->stats.tx_bytes += skb->len; +#endif + dev->trans_start=jiffies; hp100_ints_on(); - + dev_kfree_skb( skb, FREE_WRITE ); - + #ifdef HP100_DEBUG_TX - printk( "hp100_start_xmit: end\n" ); + printk( "hp100: %s: start_xmit: end\n", dev->name ); #endif - + return 0; } + /* - * receive - called from interrupt handler + * Receive Function (Non-Busmaster mode) + * Called when an "Receive Packet" interrupt occurs, i.e. the receive + * packet counter is non-zero. + * For non-busmaster, this function does the whole work of transfering + * the packet to the host memory and then up to higher layers via skb + * and netif_rx. */ static void hp100_rx( struct device *dev ) { int packets, pkt_len; - int ioaddr = dev -> base_addr; - struct hp100_private *lp = (struct hp100_private *)dev -> priv; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; u_int header; struct sk_buff *skb; -#if 0 - if ( lp -> lan_type < 0 ) - { - if ( ( lp -> lan_type = hp100_sense_lan( dev ) ) == HP100_LAN_100 ) - lp -> hub_status = hp100_login_to_vg_hub( dev ); - hp100_page( PERFORMANCE ); - } +#ifdef DEBUG_B + hp100_outw( 0x4213, TRACE ); + printk("hp100: %s: rx\n", dev->name); #endif - + + /* First get indication of received lan packet */ + /* RX_PKT_CND indicates the number of packets which have been fully */ + /* received onto the card but have not been fully transfered of the card */ packets = hp100_inb( RX_PKT_CNT ); -#ifdef HP100_DEBUG +#ifdef HP100_DEBUG_RX if ( packets > 1 ) - printk( "hp100_rx: waiting packets = %d\n", packets ); + printk( "hp100: %s: rx: waiting packets = %d\n", dev->name,packets ); #endif + while ( packets-- > 0 ) { - for ( pkt_len = 0; pkt_len < 6000 && ( hp100_inw( OPTION_MSW ) & HP100_ADV_NXT_PKT ); pkt_len++ ) + /* If ADV_NXT_PKT is still set, we have to wait until the card has */ + /* really advanced to the next packet. */ + for (pkt_len=0; pkt_len<6000 &&(hp100_inb(OPTION_MSW)&HP100_ADV_NXT_PKT); + pkt_len++ ) { -#ifdef HP100_DEBUG_TX - printk( "hp100_rx: busy, remaining packets = %d\n", packets ); +#ifdef HP100_DEBUG_RX + printk( "hp100: %s: rx: busy, remaining packets = %d\n", dev->name, packets ); #endif } - if ( lp -> mem_mapped ) + + /* First we get the header, which contains information about the */ + /* actual length of the received packet. */ + if( lp->mode==2 ) /* memory mapped mode */ { - if ( lp -> mem_ptr_virt ) - header = *(__u32 *)lp -> mem_ptr_virt; - else - header = readl( lp -> mem_ptr_phys ); + if ( lp->mem_ptr_virt ) /* if memory was remapped */ + header = *(__u32 *)lp->mem_ptr_virt; + else + header = readl( lp->mem_ptr_phys ); } - else + else /* programmed i/o */ header = hp100_inl( DATA32 ); + pkt_len = header & HP100_PKT_LEN_MASK; + #ifdef HP100_DEBUG_RX - printk( "hp100_rx: new packet - length = %d, errors = 0x%x, dest = 0x%x\n", - header & HP100_PKT_LEN_MASK, ( header >> 16 ) & 0xfff8, ( header >> 16 ) & 7 ); + printk( "hp100: %s: rx: new packet - length=%d, errors=0x%x, dest=0x%x\n", + dev->name, + header & HP100_PKT_LEN_MASK, (header>>16)&0xfff8, + (header>>16)&7); #endif - /* - * NOTE! This (and the skb_put() below) depends on the skb-functions + + /* Now we allocate the skb and transfer the data into it. */ + /* NOTE! This (and the skb_put() below) depends on the skb-functions * allocating more than asked (notably, aligning the request up to * the next 16-byte length). */ skb = dev_alloc_skb( pkt_len ); - if ( skb == NULL ) - { + if ( skb == NULL ) /* Not enough memory->drop packet */ + { #ifdef HP100_DEBUG - printk( "hp100_rx: couldn't allocate a sk_buff of size %d\n", pkt_len ); + printk( "hp100: %s: rx: couldn't allocate a sk_buff of size %d\n", dev->name, pkt_len ); #endif - lp -> stats.rx_dropped++; - } - else - { - u_char *ptr; - - skb -> dev = dev; - ptr = (u_char *)skb_put( skb, pkt_len ); - if ( lp -> mem_mapped ) + lp->stats.rx_dropped++; + } + else /* skb successfully allocated */ + { + u_char *ptr; + + skb->dev = dev; + + /* ptr to start of the sk_buff data area */ + ptr = (u_char *)skb_put( skb, pkt_len ); + + /* Now transfer the data from the card into that area */ + if ( lp->mode==2 ) { - if ( lp -> mem_ptr_virt ) - memcpy( ptr, lp -> mem_ptr_virt, ( pkt_len + 3 ) & ~3 ); - else - memcpy_fromio( ptr, lp -> mem_ptr_phys, ( pkt_len + 3 ) & ~3 ); + if ( lp->mem_ptr_virt ) + memcpy( ptr, lp->mem_ptr_virt, ( pkt_len + 3 ) & ~3 ); + /* Note alignment to 32bit transfers */ + else + memcpy_fromio( ptr, lp->mem_ptr_phys, ( pkt_len + 3 ) & ~3 ); } - else - insl( ioaddr + HP100_REG_DATA32, ptr, ( pkt_len + 3 ) >> 2 ); - skb -> protocol = eth_type_trans( skb, dev ); - netif_rx( skb ); - lp -> stats.rx_packets++; + else /* io mapped */ + insl( ioaddr + HP100_REG_DATA32, ptr, ( pkt_len + 3 ) >> 2 ); + + skb->protocol = eth_type_trans( skb, dev ); + + netif_rx( skb ); + lp->stats.rx_packets++; +#ifdef LINUX_2_1 + lp->stats.rx_bytes += skb->len; +#endif + #ifdef HP100_DEBUG_RX - printk( "rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ], ptr[ 4 ], ptr[ 5 ], - ptr[ 6 ], ptr[ 7 ], ptr[ 8 ], ptr[ 9 ], ptr[ 10 ], ptr[ 11 ] ); + printk( "hp100: %s: rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + dev->name, + ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ], ptr[ 4 ], ptr[ 5 ], + ptr[ 6 ], ptr[ 7 ], ptr[ 8 ], ptr[ 9 ], ptr[ 10 ], ptr[ 11 ] ); #endif - } - hp100_outw( HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW ); + } + + /* Indicate the card that we have got the packet */ + hp100_outb( HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW ); + switch ( header & 0x00070000 ) { - case (HP100_MULTI_ADDR_HASH<<16): - case (HP100_MULTI_ADDR_NO_HASH<<16): - lp -> stats.multicast++; break; + case (HP100_MULTI_ADDR_HASH<<16): + case (HP100_MULTI_ADDR_NO_HASH<<16): + lp->stats.multicast++; break; } - } + } /* end of while(there are packets) loop */ #ifdef HP100_DEBUG_RX - printk( "hp100_rx: end\n" ); + printk( "hp100_rx: %s: end\n", dev->name ); +#endif +} + + +/* + * Receive Function for Busmaster Mode + */ +static void hp100_rx_bm( struct device *dev ) +{ + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + hp100_ring_t *ptr; + u_int header; + int pkt_len; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4214, TRACE ); + printk("hp100: %s: rx_bm\n", dev->name); +#endif + +#ifdef HP100_DEBUG + if(0==lp->rxrcommit) + { + printk("hp100: %s: rx_bm called although no PDLs were committed to adapter?\n", dev->name); + return; + } + else + + /* RX_PKT_CNT states how many PDLs are currently formatted and available to + * the cards BM engine */ + if( (hp100_inw(RX_PKT_CNT)&0x00ff) >= lp->rxrcommit) + { + printk("hp100: %s: More packets received than commited? RX_PKT_CNT=0x%x, commit=0x%x\n", dev->name, hp100_inw(RX_PKT_CNT)&0x00ff, lp->rxrcommit); + return; + } #endif + + while( (lp->rxrcommit > hp100_inb(RX_PDL)) ) + { + /* + * The packet was received into the pdl pointed to by lp->rxrhead ( + * the oldest pdl in the ring + */ + + /* First we get the header, which contains information about the */ + /* actual length of the received packet. */ + + ptr=lp->rxrhead; + + header = *(ptr->pdl-1); + pkt_len = (header & HP100_PKT_LEN_MASK); + +#ifdef HP100_DEBUG_BM + printk( "hp100: %s: rx_bm: header@0x%x=0x%x length=%d, errors=0x%x, dest=0x%x\n", + dev->name, + (u_int) (ptr->pdl-1),(u_int) header, + pkt_len, + (header>>16)&0xfff8, + (header>>16)&7); + printk( "hp100: %s: RX_PDL_COUNT:0x%x TX_PDL_COUNT:0x%x, RX_PKT_CNT=0x%x PDH=0x%x, Data@0x%x len=0x%x\n", + dev->name, + hp100_inb( RX_PDL ), + hp100_inb( TX_PDL ), + hp100_inb( RX_PKT_CNT ), + (u_int) *(ptr->pdl), + (u_int) *(ptr->pdl+3), + (u_int) *(ptr->pdl+4)); +#endif + + if( (pkt_len>=MIN_ETHER_SIZE) && + (pkt_len<=MAX_ETHER_SIZE) ) + { + if(ptr->skb==NULL) + { + printk("hp100: %s: rx_bm: skb null\n", dev->name); + /* can happen if we only allocated room for the pdh due to memory shortage. */ + lp->stats.rx_dropped++; + } + else + { + skb_trim( ptr->skb, pkt_len ); /* Shorten it */ + ptr->skb->protocol = eth_type_trans( ptr->skb, dev ); + + netif_rx( ptr->skb ); /* Up and away... */ + + lp->stats.rx_packets++; +#ifdef LINUX_2_1 + lp->stats.rx_bytes += ptr->skb->len; +#endif + } + + switch ( header & 0x00070000 ) { + case (HP100_MULTI_ADDR_HASH<<16): + case (HP100_MULTI_ADDR_NO_HASH<<16): + lp->stats.multicast++; break; + } + } + else + { +#ifdef HP100_DEBUG + printk("hp100: %s: rx_bm: Received bad packet (length=%d)\n",dev->name,pkt_len); +#endif + if(ptr->skb!=NULL) + dev_kfree_skb( ptr->skb, FREE_READ ); + lp->stats.rx_errors++; + } + + lp->rxrhead=lp->rxrhead->next; + + /* Allocate a new rx PDL (so lp->rxrcommit stays the same) */ + if (0 == hp100_build_rx_pdl( lp->rxrtail, dev )) + { + /* No space for skb, header can still be received. */ +#ifdef HP100_DEBUG + printk("hp100: %s: rx_bm: No space for new PDL.\n", dev->name); +#endif + return; + } + else + { /* successfully allocated new PDL - put it in ringlist at tail. */ + hp100_outl((u32)lp->rxrtail->pdl_paddr, RX_PDA); + lp->rxrtail=lp->rxrtail->next; + } + + } } + + /* * statistics */ - -static struct enet_statistics *hp100_get_stats( struct device *dev ) +static hp100_stats_t *hp100_get_stats( struct device *dev ) { - int ioaddr = dev -> base_addr; + int ioaddr = dev->base_addr; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4215, TRACE ); +#endif hp100_ints_off(); hp100_update_stats( dev ); hp100_ints_on(); - return &((struct hp100_private *)dev -> priv) -> stats; + return &((struct hp100_private *)dev->priv)->stats; } static void hp100_update_stats( struct device *dev ) { - int ioaddr = dev -> base_addr; + int ioaddr = dev->base_addr; u_short val; - struct hp100_private *lp = (struct hp100_private *)dev -> priv; - - hp100_page( MAC_CTRL ); /* get all statistics bytes */ + struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4216, TRACE ); + printk("hp100: %s: update-stats\n", dev->name); +#endif + + /* Note: Statistics counters clear when read. */ + hp100_page( MAC_CTRL ); val = hp100_inw( DROPPED ) & 0x0fff; - lp -> stats.rx_errors += val; - lp -> stats.rx_over_errors += val; + lp->stats.rx_errors += val; + lp->stats.rx_over_errors += val; val = hp100_inb( CRC ); - lp -> stats.rx_errors += val; - lp -> stats.rx_crc_errors += val; + lp->stats.rx_errors += val; + lp->stats.rx_crc_errors += val; val = hp100_inb( ABORT ); - lp -> stats.tx_errors += val; - lp -> stats.tx_aborted_errors += val; + lp->stats.tx_errors += val; + lp->stats.tx_aborted_errors += val; hp100_page( PERFORMANCE ); } static void hp100_clear_stats( int ioaddr ) { +#ifdef HP100_DEBUG_B + hp100_outw( 0x4217, TRACE ); + printk("hp100: %s: clear_stats\n", dev->name); +#endif + cli(); - hp100_page( MAC_CTRL ); /* get all statistics bytes */ + hp100_page( MAC_CTRL ); /* get all statistics bytes */ hp100_inw( DROPPED ); hp100_inb( CRC ); hp100_inb( ABORT ); @@ -838,6 +2071,7 @@ sti(); } + /* * multicast setup */ @@ -848,293 +2082,899 @@ static void hp100_set_multicast_list( struct device *dev) { - int ioaddr = dev -> base_addr; - struct hp100_private *lp = (struct hp100_private *)dev -> priv; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; -#ifdef HP100_DEBUG_MULTI - printk( "hp100_set_multicast_list: num_addrs = %d\n", dev->mc_count); +#ifdef HP100_DEBUG_B + hp100_outw( 0x4218, TRACE ); + printk("hp100: %s: set_mc_list\n", dev->name); #endif + cli(); hp100_ints_off(); hp100_page( MAC_CTRL ); - hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); /* stop rx/tx */ + hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); /* stop rx/tx */ - if ( dev->flags&IFF_PROMISC) + if ( dev->flags & IFF_PROMISC ) { - lp -> mac2_mode = HP100_MAC2MODE6; /* promiscuous mode, all good */ - lp -> mac1_mode = HP100_MAC1MODE6; /* packets on the net */ + lp->mac2_mode = HP100_MAC2MODE6; /* promiscuous mode = get all good */ + lp->mac1_mode = HP100_MAC1MODE6; /* packets on the net */ + memset( &lp->hash_bytes, 0xff, 8 ); } - else - if ( dev->mc_count || dev->flags&IFF_ALLMULTI ) + else if ( dev->mc_count || (dev->flags&IFF_ALLMULTI) ) + { + lp->mac2_mode = HP100_MAC2MODE5; /* multicast mode = get packets for */ + lp->mac1_mode = HP100_MAC1MODE5; /* me, broadcasts and all multicasts */ +#ifdef HP100_MULTICAST_FILTER /* doesn't work!!! */ + if ( dev -> flags & IFF_ALLMULTI ) + { + /* set hash filter to receive all multicast packets */ + memset( &lp->hash_bytes, 0xff, 8 ); + } + else + { + int i, j, idx; + u_char *addrs; + struct dev_mc_list *dmi; + + memset( &lp->hash_bytes, 0x00, 8 ); +#ifdef HP100_DEBUG + printk("hp100: %s: computing hash filter - mc_count = %i\n", dev -> name, dev -> mc_count ); +#endif + for ( i = 0, dmi = dev -> mc_list; i < dev -> mc_count; i++, dmi = dmi -> next ) + { + addrs = dmi -> dmi_addr; + if ( ( *addrs & 0x01 ) == 0x01 ) /* multicast address? */ + { +#ifdef HP100_DEBUG + printk("hp100: %s: multicast = %02x:%02x:%02x:%02x:%02x:%02x, ", + dev -> name, + addrs[ 0 ], addrs[ 1 ], addrs[ 2 ], + addrs[ 3 ], addrs[ 4 ], addrs[ 5 ] ); +#endif + for ( j = idx = 0; j < 6; j++ ) + { + idx ^= *addrs++ & 0x3f; + printk( ":%02x:", idx ); + } +#ifdef HP100_DEBUG + printk("idx = %i\n", idx ); +#endif + lp->hash_bytes[ idx >> 3 ] |= ( 1 << ( idx & 7 ) ); + } + } + } +#else + memset( &lp->hash_bytes, 0xff, 8 ); +#endif + } + else + { + lp->mac2_mode = HP100_MAC2MODE3; /* normal mode = get packets for me */ + lp->mac1_mode = HP100_MAC1MODE3; /* and broadcasts */ + memset( &lp->hash_bytes, 0x00, 8 ); + } + + if ( ( (hp100_inb(MAC_CFG_1) & 0x0f)!=lp->mac1_mode ) || + ( hp100_inb(MAC_CFG_2)!=lp->mac2_mode ) ) { - lp -> mac2_mode = HP100_MAC2MODE5; /* multicast mode, packets for me */ - lp -> mac1_mode = HP100_MAC1MODE5; /* broadcasts and all multicasts */ + int i; + + hp100_outb( lp->mac2_mode, MAC_CFG_2 ); + hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 ); /* clear mac1 mode bits */ + hp100_orb( lp->mac1_mode, MAC_CFG_1 ); /* and set the new mode */ + + hp100_page( MAC_ADDRESS ); + for ( i = 0; i < 8; i++ ) + hp100_outb( lp->hash_bytes[ i ], HASH_BYTE0 + i ); +#ifdef HP100_DEBUG + printk("hp100: %s: mac1 = 0x%x, mac2 = 0x%x, multicast hash = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + dev->name, lp->mac1_mode, lp->mac2_mode, + lp->hash_bytes[ 0 ], lp->hash_bytes[ 1 ], + lp->hash_bytes[ 2 ], lp->hash_bytes[ 3 ], + lp->hash_bytes[ 4 ], lp->hash_bytes[ 5 ], + lp->hash_bytes[ 6 ], lp->hash_bytes[ 7 ] + ); +#endif + + if(lp->lan_type==HP100_LAN_100) + { +#ifdef HP100_DEBUG + printk("hp100: %s: 100VG MAC settings have changed - relogin.\n", dev->name); +#endif + lp->hub_status=hp100_login_to_vg_hub( dev, TRUE ); /* force a relogin to the hub */ + } } else { - lp -> mac2_mode = HP100_MAC2MODE3; /* normal mode, packets for me */ - lp -> mac1_mode = HP100_MAC1MODE3; /* and broadcasts */ + int i; + u_char old_hash_bytes[ 8 ]; + + hp100_page( MAC_ADDRESS ); + for ( i = 0; i < 8; i++ ) + old_hash_bytes[ i ] = hp100_inb( HASH_BYTE0 + i ); + if ( memcmp( old_hash_bytes, &lp->hash_bytes, 8 ) ) + { + for ( i = 0; i < 8; i++ ) + hp100_outb( lp->hash_bytes[ i ], HASH_BYTE0 + i ); +#ifdef HP100_DEBUG + printk("hp100: %s: multicast hash = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + dev->name, + lp->hash_bytes[ 0 ], lp->hash_bytes[ 1 ], + lp->hash_bytes[ 2 ], lp->hash_bytes[ 3 ], + lp->hash_bytes[ 4 ], lp->hash_bytes[ 5 ], + lp->hash_bytes[ 6 ], lp->hash_bytes[ 7 ] + ); +#endif + + if(lp->lan_type==HP100_LAN_100) + { +#ifdef HP100_DEBUG + printk("hp100: %s: 100VG MAC settings have changed - relogin.\n", dev->name); +#endif + lp->hub_status=hp100_login_to_vg_hub( dev, TRUE ); /* force a relogin to the hub */ + } + } } - hp100_outb( lp -> mac2_mode, MAC_CFG_2 ); - hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 ); - hp100_orb( lp -> mac1_mode | - HP100_RX_EN | HP100_RX_IDLE | /* enable rx */ - HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 ); /* enable tx */ + hp100_page( MAC_CTRL ); + hp100_orb( HP100_RX_EN | HP100_RX_IDLE | /* enable rx */ + HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 ); /* enable tx */ + hp100_page( PERFORMANCE ); hp100_ints_on(); sti(); } + /* * hardware interrupt handling */ static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs ) { - struct device *dev = (struct device *)irq2dev_map[ irq ]; - struct hp100_private *lp; + struct device *dev = (struct device *)dev_id; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + int ioaddr; - u_short val; + u_int val; if ( dev == NULL ) return; - ioaddr = dev -> base_addr; - if ( dev -> interrupt ) - printk( "%s: re-entering the interrupt handler\n", dev -> name ); + ioaddr = dev->base_addr; + + if ( dev->interrupt ) + printk( "hp100: %s: re-entering the interrupt handler\n", dev->name ); hp100_ints_off(); - dev -> interrupt = 1; - hp100_page( PERFORMANCE ); + dev->interrupt = 1; /* mark that we are inside the handler */ + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4219, TRACE ); +#endif + + /* hp100_page( PERFORMANCE ); */ val = hp100_inw( IRQ_STATUS ); #ifdef HP100_DEBUG_IRQ - printk( "hp100_interrupt: irq_status = 0x%x\n", val ); + printk( "hp100: %s: mode=%x,IRQ_STAT=0x%.4x,RXPKTCNT=0x%.2x RXPDL=0x%.2x TXPKTCNT=0x%.2x TXPDL=0x%.2x\n", + dev->name, + lp->mode, + (u_int)val, + hp100_inb( RX_PKT_CNT ), + hp100_inb( RX_PDL ), + hp100_inb( TX_PKT_CNT ), + hp100_inb( TX_PDL ) + ); #endif - if ( val & HP100_RX_PACKET ) + + if(val==0) /* might be a shared interrupt */ { - hp100_rx( dev ); - hp100_outw( HP100_RX_PACKET, IRQ_STATUS ); + dev->interrupt=0; + hp100_ints_on(); + return; } - if ( val & (HP100_TX_SPACE_AVAIL | HP100_TX_COMPLETE) ) - { - hp100_outw( val & (HP100_TX_SPACE_AVAIL | HP100_TX_COMPLETE), IRQ_STATUS ); + /* We're only interested in those interrupts we really enabled. */ + /* val &= hp100_inw( IRQ_MASK ); */ + + /* + * RX_PDL_FILL_COMPL is set whenever a RX_PDL has been executed. A RX_PDL + * is considered executed whenever the RX_PDL data structure is no longer + * needed. + */ + if ( val & HP100_RX_PDL_FILL_COMPL ) + { + if(lp->mode==1) + hp100_rx_bm( dev ); + else + { + printk("hp100: %s: rx_pdl_fill_compl interrupt although not busmaster?\n", dev->name); + } } + + /* + * The RX_PACKET interrupt is set, when the receive packet counter is + * non zero. We use this interrupt for receiving in slave mode. In + * busmaster mode, we use it to make sure we did not miss any rx_pdl_fill + * interrupts. If rx_pdl_fill_compl is not set and rx_packet is set, then + * we somehow have missed a rx_pdl_fill_compl interrupt. + */ + + if ( val & HP100_RX_PACKET ) /* Receive Packet Counter is non zero */ + { + if(lp->mode!=1) /* non busmaster */ + hp100_rx( dev ); + else if ( !(val & HP100_RX_PDL_FILL_COMPL )) + { + /* Shouldnt happen - maybe we missed a RX_PDL_FILL Interrupt? */ + hp100_rx_bm( dev ); + } + } + + /* + * Ack. that we have noticed the interrupt and thereby allow next one. + * Note that this is now done after the slave rx function, since first + * acknowledging and then setting ADV_NXT_PKT caused an extra interrupt + * on the J2573. + */ + hp100_outw( val, IRQ_STATUS ); + + /* + * RX_ERROR is set when a packet is dropped due to no memory resources on + * the card or when a RCV_ERR occurs. + * TX_ERROR is set when a TX_ABORT condition occurs in the MAC->exists + * only in the 802.3 MAC and happens when 16 collisions occur during a TX + */ if ( val & ( HP100_TX_ERROR | HP100_RX_ERROR ) ) { - lp = (struct hp100_private *)dev -> priv; - hp100_update_stats( dev ); - hp100_outw( val & (HP100_TX_ERROR | HP100_RX_ERROR), IRQ_STATUS ); - } #ifdef HP100_DEBUG_IRQ - printk( "hp100_interrupt: end\n" ); + printk("hp100: %s: TX/RX Error IRQ\n", dev->name); #endif - dev -> interrupt = 0; + hp100_update_stats( dev ); + if(lp->mode==1) + { + hp100_rxfill( dev ); + hp100_clean_txring( dev ); + } + } + + /* + * RX_PDA_ZERO is set when the PDA count goes from non-zero to zero. + */ + if ( (lp->mode==1)&&(val &(HP100_RX_PDA_ZERO)) ) + hp100_rxfill( dev ); + + /* + * HP100_TX_COMPLETE interrupt occurs when packet transmitted on wire + * is completed + */ + if ( (lp->mode==1) && ( val & ( HP100_TX_COMPLETE )) ) + hp100_clean_txring( dev ); + + /* + * MISC_ERROR is set when either the LAN link goes down or a detected + * bus error occurs. + */ + if ( val & HP100_MISC_ERROR ) /* New for J2585B */ + { + printk("hp100: %s: Misc. Error Interrupt - Check cabling.\n", dev->name); + if(lp->mode==1) + { + hp100_clean_txring( dev ); + hp100_rxfill( dev ); + } + } + + dev->interrupt = 0; hp100_ints_on(); } + /* * some misc functions */ static void hp100_start_interface( struct device *dev ) { - int ioaddr = dev -> base_addr; - struct hp100_private *lp = (struct hp100_private *)dev -> priv; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4220, TRACE ); + printk("hp100: %s: hp100_start_interface\n",dev->name); +#endif cli(); - hp100_unreset_card(); - hp100_page( MAC_CTRL ); - hp100_outb( lp -> mac2_mode, MAC_CFG_2 ); - hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 ); - hp100_orb( lp -> mac1_mode | - HP100_RX_EN | HP100_RX_IDLE | - HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 ); + + /* Ensure the adapter does not want to request an interrupt when */ + /* enabling the IRQ line to be active on the bus (i.e. not tri-stated) */ hp100_page( PERFORMANCE ); - hp100_outw( HP100_INT_EN | HP100_SET_LB, OPTION_LSW ); - hp100_outw( HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW ); - if ( lp -> mem_mapped ) + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* ack all IRQs */ + hp100_outw( HP100_FAKE_INT|HP100_INT_EN|HP100_RESET_LB, OPTION_LSW); + /* Un Tri-state int. TODO: Check if shared interrupts can be realised? */ + hp100_outw( HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW ); + + if(lp->mode==1) + { + /* Make sure BM bit is set... */ + hp100_page(HW_MAP); + hp100_orb( HP100_BM_MASTER, BM ); + hp100_rxfill( dev ); + } + else if(lp->mode==2) { - /* enable memory mapping */ + /* Enable memory mapping. Note: Don't do this when busmaster. */ hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW ); } - sti(); -} + hp100_page(PERFORMANCE); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */ + + /* enable a few interrupts: */ + if(lp->mode==1) /* busmaster mode */ + { + hp100_outw( HP100_RX_PDL_FILL_COMPL | + HP100_RX_PDA_ZERO | + HP100_RX_ERROR | + /* HP100_RX_PACKET | */ + /* HP100_RX_EARLY_INT | */ HP100_SET_HB | + /* HP100_TX_PDA_ZERO | */ + HP100_TX_COMPLETE | + /* HP100_MISC_ERROR | */ + HP100_TX_ERROR | HP100_SET_LB, IRQ_MASK ); + } + else + { + hp100_outw( HP100_RX_PACKET | + HP100_RX_ERROR | HP100_SET_HB | + HP100_TX_ERROR | HP100_SET_LB , IRQ_MASK ); + } + + /* Enable MAC Tx and RX, set MAC modes, ... */ + /* Note: This function also turns on the interrupts. */ + hp100_set_multicast_list( dev ); +} + + static void hp100_stop_interface( struct device *dev ) { - int ioaddr = dev -> base_addr; - u_short val; - - hp100_outw( HP100_INT_EN | HP100_RESET_LB | - HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); - val = hp100_inw( OPTION_LSW ); - hp100_page( HW_MAP ); - hp100_andb( HP100_BM_SLAVE, BM ); - hp100_page( MAC_CTRL ); - hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); - if ( !(val & HP100_HW_RST) ) return; - for ( val = 0; val < 6000; val++ ) - if ( ( hp100_inb( MAC_CFG_1 ) & (HP100_TX_IDLE | HP100_RX_IDLE) ) == - (HP100_TX_IDLE | HP100_RX_IDLE) ) - return; - printk( "%s: hp100_stop_interface - timeout\n", dev -> name ); + struct hp100_private *lp = (struct hp100_private *)dev->priv; + int ioaddr = dev->base_addr; + u_int val; + +#ifdef HP100_DEBUG_B + printk("hp100: %s: hp100_stop_interface\n",dev->name); + hp100_outw( 0x4221, TRACE ); +#endif + + if (lp->mode==1) + hp100_BM_shutdown( dev ); + else + { + /* Note: MMAP_DIS will be reenabled by start_interface */ + hp100_outw( HP100_INT_EN | HP100_RESET_LB | + HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); + val = hp100_inw( OPTION_LSW ); + + hp100_page( MAC_CTRL ); + hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); + + if ( !(val & HP100_HW_RST) ) return; /* If reset, imm. return ... */ + /* ... else: busy wait until idle */ + for ( val = 0; val < 6000; val++ ) + if ( ( hp100_inb( MAC_CFG_1 ) & (HP100_TX_IDLE | HP100_RX_IDLE) ) == + (HP100_TX_IDLE | HP100_RX_IDLE) ) + { + hp100_page(PERFORMANCE); + return; + } + printk( "hp100: %s: hp100_stop_interface - timeout\n", dev->name ); + hp100_page(PERFORMANCE); + } } + static void hp100_load_eeprom( struct device *dev ) { int i; - int ioaddr = dev -> base_addr; + int ioaddr = dev->base_addr; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4222, TRACE ); +#endif hp100_page( EEPROM_CTRL ); hp100_andw( ~HP100_EEPROM_LOAD, EEPROM_CTRL ); hp100_orw( HP100_EEPROM_LOAD, EEPROM_CTRL ); - for ( i = 0; i < 6000; i++ ) - if ( !( hp100_inw( OPTION_MSW ) & HP100_EE_LOAD ) ) return; - printk( "%s: hp100_load_eeprom - timeout\n", dev -> name ); + for ( i = 0; i < 10000; i++ ) + if ( !( hp100_inb( OPTION_MSW ) & HP100_EE_LOAD ) ) return; + printk( "hp100: %s: hp100_load_eeprom - timeout\n", dev->name ); } -/* return values: LAN_10, LAN_100 or LAN_ERR (not connected or hub is down)... */ - + +/* Sense connection status. + * return values: LAN_10 - Connected to 10Mbit/s network + * LAN_100 - Connected to 100Mbit/s network + * LAN_ERR - not connected or 100Mbit/s Hub down + */ static int hp100_sense_lan( struct device *dev ) { - int i; - int ioaddr = dev -> base_addr; + int ioaddr = dev->base_addr; u_short val_VG, val_10; - struct hp100_private *lp = (struct hp100_private *)dev -> priv; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4223, TRACE ); +#endif hp100_page( MAC_CTRL ); - hp100_orw( HP100_VG_RESET, LAN_CFG_VG ); - val_10 = hp100_inw( LAN_CFG_10 ); - val_VG = hp100_inw( LAN_CFG_VG ); -#ifdef HP100_DEBUG_SENSE - printk( "hp100_sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", val_VG, val_10 ); -#endif - if ( val_10 & HP100_LINK_BEAT_ST ) return HP100_LAN_10; - if ( lp -> id -> id == 0x02019F022 ) /* HP J27248B doesn't have 100Mb/s interface */ - return HP100_LAN_ERR; - for ( i = 0; i < 2500; i++ ) + val_10 = hp100_inb( 10_LAN_CFG_1 ); + val_VG = hp100_inb( VG_LAN_CFG_1 ); + hp100_page( PERFORMANCE ); +#ifdef HP100_DEBUG + printk( "hp100: %s: sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", dev->name, val_VG, val_10 ); +#endif + + if ( val_10 & HP100_LINK_BEAT_ST ) /* 10Mb connection is active */ + return HP100_LAN_10; + + if ( val_10 & HP100_AUI_ST ) /* have we BNC or AUI onboard? */ { - val_VG = hp100_inw( LAN_CFG_VG ); - if ( val_VG & HP100_LINK_CABLE_ST ) return HP100_LAN_100; + val_10 |= HP100_AUI_SEL | HP100_LOW_TH; + hp100_page( MAC_CTRL ); + hp100_outb( val_10, 10_LAN_CFG_1 ); + hp100_page( PERFORMANCE ); + return HP100_LAN_10; } + + if ( (lp->id->id == 0x02019F022) || + (lp->id->id == 0x01042103c) || + (lp->id->id == 0x01040103c) ) + return HP100_LAN_ERR; /* Those cards don't have a 100 Mbit connector */ + + if ( val_VG & HP100_LINK_CABLE_ST ) /* Can hear the HUBs tone. */ + return HP100_LAN_100; return HP100_LAN_ERR; } + + static int hp100_down_vg_link( struct device *dev ) { - int ioaddr = dev -> base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + int ioaddr = dev->base_addr; unsigned long time; - int i; + long savelan, newlan; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4224, TRACE ); + printk("hp100: %s: down_vg_link\n", dev->name); +#endif hp100_page( MAC_CTRL ); - for ( i = 2500; i > 0; i-- ) - if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break; - if ( i <= 0 ) /* not signal - not logout */ + time=jiffies+(HZ/4); + do{ + if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break; + } while (time>jiffies); + + if ( jiffies >= time ) /* no signal->no logout */ return 0; - hp100_andw( ~HP100_LINK_CMD, LAN_CFG_VG ); - time = jiffies + 10; - while ( time > jiffies ) - if ( !( hp100_inw( LAN_CFG_VG ) & ( HP100_LINK_UP_ST | - HP100_LINK_CABLE_ST | - HP100_LINK_GOOD_ST ) ) ) - return 0; + + /* Drop the VG Link by clearing the link up cmd and load addr.*/ + + hp100_andb( ~( HP100_LOAD_ADDR| HP100_LINK_CMD), VG_LAN_CFG_1); + hp100_orb( HP100_VG_SEL, VG_LAN_CFG_1); + + /* Conditionally stall for >250ms on Link-Up Status (to go down) */ + time=jiffies+(HZ/2); + do{ + if ( !(hp100_inb( VG_LAN_CFG_1) & HP100_LINK_UP_ST) ) break; + } while(time>jiffies); + #ifdef HP100_DEBUG - printk( "hp100_down_vg_link: timeout\n" ); + if (jiffies>=time) + printk("hp100: %s: down_vg_link: Link does not go down?\n", dev->name); #endif - return -EIO; -} -static int hp100_login_to_vg_hub( struct device *dev ) -{ - int i; - int ioaddr = dev -> base_addr; - u_short val; - unsigned long time; + /* To prevent condition where Rev 1 VG MAC and old hubs do not complete */ + /* logout under traffic (even though all the status bits are cleared), */ + /* do this workaround to get the Rev 1 MAC in its idle state */ + if ( lp->chip==HP100_CHIPID_LASSEN ) + { + /* Reset VG MAC to insure it leaves the logoff state even if */ + /* the Hub is still emitting tones */ + hp100_andb(~HP100_VG_RESET, VG_LAN_CFG_1); + udelay(1500); /* wait for >1ms */ + hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1); /* Release Reset */ + udelay(1500); + } + + /* New: For lassen, switch to 10 Mbps mac briefly to clear training ACK */ + /* to get the VG mac to full reset. This is not req.d with later chips */ + /* Note: It will take the between 1 and 2 seconds for the VG mac to be */ + /* selected again! This will be left to the connect hub function to */ + /* perform if desired. */ + if (lp->chip==HP100_CHIPID_LASSEN) + { + /* Have to write to 10 and 100VG control registers simultaneously */ + savelan=newlan=hp100_inl(10_LAN_CFG_1); /* read 10+100 LAN_CFG regs */ + newlan &= ~(HP100_VG_SEL<<16); + newlan |= (HP100_DOT3_MAC)<<8; + hp100_andb( ~HP100_AUTO_MODE, MAC_CFG_3); /* Autosel off */ + hp100_outl(newlan, 10_LAN_CFG_1); + + /* Conditionally stall for 5sec on VG selected. */ + time=jiffies+(HZ*5); + do{ + if( !(hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST) ) break; + } while(time>jiffies); - hp100_page( MAC_CTRL ); - hp100_orw( HP100_VG_RESET, LAN_CFG_VG ); - time = jiffies + ( HZ / 2 ); + hp100_orb( HP100_AUTO_MODE, MAC_CFG_3); /* Autosel back on */ + hp100_outl(savelan, 10_LAN_CFG_1); + } + + time=jiffies+(3*HZ); /* Timeout 3s */ do { - if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break; - } while ( time > jiffies ); - if ( time <= jiffies ) + if ( (hp100_inb( VG_LAN_CFG_1 )&HP100_LINK_CABLE_ST) == 0) break; + } while (time>jiffies); + + if(time<=jiffies) { #ifdef HP100_DEBUG - printk( "hp100_login_to_vg_hub: timeout for link\n" ); + printk( "hp100: %s: down_vg_link: timeout\n", dev->name ); #endif return -EIO; } - - if ( hp100_down_vg_link( dev ) < 0 ) /* if fail, try reset VG link */ - { - hp100_andw( ~HP100_VG_RESET, LAN_CFG_VG ); - hp100_orw( HP100_VG_RESET, LAN_CFG_VG ); - } - /* bring up link */ - hp100_orw( HP100_LOAD_ADDR | HP100_LINK_CMD, LAN_CFG_VG ); - for ( i = 2500; i > 0; i-- ) - if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break; - if ( i <= 0 ) + + time=jiffies+(2*HZ); /* This seems to take a while.... */ + do {} while (time>jiffies); + + return 0; +} + + +static int hp100_login_to_vg_hub( struct device *dev, u_short force_relogin ) +{ + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + u_short val=0; + unsigned long time; + int startst; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4225, TRACE ); + printk("hp100: %s: login_to_vg_hub\n", dev->name); +#endif + + /* Initiate a login sequence iff VG MAC is enabled and either Load Address + * bit is zero or the force relogin flag is set (e.g. due to MAC address or + * promiscuous mode change) + */ + hp100_page( MAC_CTRL ); + startst=hp100_inb( VG_LAN_CFG_1 ); + if((force_relogin==TRUE)||(hp100_inb( MAC_CFG_4 )&HP100_MAC_SEL_ST)) { -#ifdef HP100_DEBUG - printk( "hp100_login_to_vg_hub: timeout for link (bring up)\n" ); +#ifdef HP100_DEBUG_TRAINING + printk("hp100: %s: Start training\n", dev->name); #endif - goto down_link; - } - time = jiffies + ( HZ / 2 ); - do { - val = hp100_inw( LAN_CFG_VG ); - if ( ( val & ( HP100_LINK_UP_ST | HP100_LINK_GOOD_ST ) ) == - ( HP100_LINK_UP_ST | HP100_LINK_GOOD_ST ) ) - return 0; /* success */ - } while ( time > jiffies ); - if ( val & HP100_LINK_GOOD_ST ) - printk( "%s: 100Mb cable training failed, check cable.\n", dev -> name ); - else - printk( "%s: 100Mb node not accepted by hub, check frame type or security.\n", dev -> name ); + /* Ensure VG Reset bit is 1 (i.e., do not reset)*/ + hp100_orb( HP100_VG_RESET , VG_LAN_CFG_1 ); + + /* If Lassen AND auto-select-mode AND VG tones were sensed on */ + /* entry then temporarily put them into force 100Mbit mode */ + if((lp->chip==HP100_CHIPID_LASSEN)&&( startst & HP100_LINK_CABLE_ST ) ) + hp100_andb( ~HP100_DOT3_MAC, 10_LAN_CFG_2 ); + + /* Drop the VG link by zeroing Link Up Command and Load Address */ + hp100_andb( ~(HP100_LINK_CMD/* |HP100_LOAD_ADDR */), VG_LAN_CFG_1); + +#ifdef HP100_DEBUG_TRAINING + printk("hp100: %s: Bring down the link\n", dev->name); +#endif + + /* Wait for link to drop */ + time = jiffies + (HZ/10); + do { + if (~(hp100_inb( VG_LAN_CFG_1 )& HP100_LINK_UP_ST) ) break; + } while (time>jiffies); + + /* Start an addressed training and optionally request promiscuous port */ + if ( (dev->flags) & IFF_PROMISC ) + { + hp100_orb( HP100_PROM_MODE, VG_LAN_CFG_2); + if(lp->chip==HP100_CHIPID_LASSEN) + hp100_orw( HP100_MACRQ_PROMSC, TRAIN_REQUEST ); + } + else + { + hp100_andb( ~HP100_PROM_MODE, VG_LAN_CFG_2); + /* For ETR parts we need to reset the prom. bit in the training + * register, otherwise promiscious mode won't be disabled. + */ + if(lp->chip==HP100_CHIPID_LASSEN) + { + hp100_andw( ~HP100_MACRQ_PROMSC, TRAIN_REQUEST ); + } + } + + /* With ETR parts, frame format request bits can be set. */ + if(lp->chip==HP100_CHIPID_LASSEN) + hp100_orb( HP100_MACRQ_FRAMEFMT_EITHER, TRAIN_REQUEST); + + hp100_orb( HP100_LINK_CMD|HP100_LOAD_ADDR|HP100_VG_RESET, VG_LAN_CFG_1); + + /* Note: Next wait could be omitted for Hood and earlier chips under */ + /* certain circumstances */ + /* TODO: check if hood/earlier and skip wait. */ + + /* Wait for either short timeout for VG tones or long for login */ + /* Wait for the card hardware to signalise link cable status ok... */ + hp100_page( MAC_CTRL ); + time = jiffies + ( 1*HZ ); /* 1 sec timeout for cable st */ + do { + if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break; + } while ( jiffies < time ); + + if ( jiffies >= time ) + { +#ifdef HP100_DEBUG_TRAINING + printk( "hp100: %s: Link cable status not ok? Training aborted.\n", dev->name ); +#endif + } + else + { +#ifdef HP100_DEBUG_TRAINING + printk( "hp100: %s: HUB tones detected. Trying to train.\n", dev->name); +#endif + + time = jiffies + ( 2*HZ ); /* again a timeout */ + do { + val = hp100_inb( VG_LAN_CFG_1 ); + if ( (val & ( HP100_LINK_UP_ST )) ) + { +#ifdef HP100_DEBUG_TRAINING + printk( "hp100: %s: Passed training.\n", dev->name); +#endif + break; + } + } while ( time > jiffies ); + } + + /* If LINK_UP_ST is set, then we are logged into the hub. */ + if ( (jiffies<=time) && (val & HP100_LINK_UP_ST) ) + { +#ifdef HP100_DEBUG_TRAINING + printk( "hp100: %s: Successfully logged into the HUB.\n", dev->name); + if(lp->chip==HP100_CHIPID_LASSEN) + { + val = hp100_inw(TRAIN_ALLOW); + printk( "hp100: %s: Card supports 100VG MAC Version \"%s\" ", + dev->name,(hp100_inw(TRAIN_REQUEST)&HP100_CARD_MACVER) ? "802.12" : "Pre"); + printk( "Driver will use MAC Version \"%s\"\n", + ( val & HP100_HUB_MACVER) ? "802.12" : "Pre" ); + printk( "hp100: %s: Frame format is %s.\n",dev->name,(val&HP100_MALLOW_FRAMEFMT)?"802.5":"802.3"); + } +#endif + } + else + { + /* If LINK_UP_ST is not set, login was not successful */ + printk("hp100: %s: Problem logging into the HUB.\n",dev->name); + if(lp->chip==HP100_CHIPID_LASSEN) + { + /* Check allowed Register to find out why there is a problem. */ + val = hp100_inw( TRAIN_ALLOW ); /* wont work on non-ETR card */ +#ifdef HP100_DEBUG_TRAINING + printk("hp100: %s: MAC Configuration requested: 0x%04x, HUB allowed: 0x%04x\n", dev->name, hp100_inw(TRAIN_REQUEST), val); +#endif + if ( val & HP100_MALLOW_ACCDENIED ) + printk("hp100: %s: HUB access denied.\n", dev->name); + if ( val & HP100_MALLOW_CONFIGURE ) + printk("hp100: %s: MAC Configuration is incompatible with the Network.\n", dev->name); + if ( val & HP100_MALLOW_DUPADDR ) + printk("hp100: %s: Duplicate MAC Address on the Network.\n", dev->name); + } + } + + /* If we have put the chip into forced 100 Mbit mode earlier, go back */ + /* to auto-select mode */ + + if( (lp->chip==HP100_CHIPID_LASSEN)&&(startst & HP100_LINK_CABLE_ST) ) + { + hp100_page( MAC_CTRL ); + hp100_orb( HP100_DOT3_MAC, 10_LAN_CFG_2 ); + } + + val=hp100_inb(VG_LAN_CFG_1); -down_link: - hp100_down_vg_link( dev ); - hp100_page( MAC_CTRL ); - hp100_andw( ~( HP100_LOAD_ADDR | HP100_PROM_MODE ), LAN_CFG_VG ); - hp100_orw( HP100_LINK_CMD, LAN_CFG_VG ); + /* Clear the MISC_ERROR Interrupt, which might be generated when doing the relogin */ + hp100_page(PERFORMANCE); + hp100_outw( HP100_MISC_ERROR, IRQ_STATUS); + + if (val&HP100_LINK_UP_ST) + return(0); /* login was ok */ + else + { + printk("hp100: %s: Training failed.\n", dev->name); + hp100_down_vg_link( dev ); + return -EIO; + } + } + /* no forced relogin & already link there->no training. */ return -EIO; } + +static void hp100_cascade_reset( struct device *dev, u_short enable ) +{ + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + int i; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4226, TRACE ); + printk("hp100: %s: cascade_reset\n", dev->name); +#endif + + if (enable==TRUE) + { + hp100_outw( HP100_HW_RST | HP100_RESET_LB, OPTION_LSW ); + if(lp->chip==HP100_CHIPID_LASSEN) + { + /* Lassen requires a PCI transmit fifo reset */ + hp100_page( HW_MAP ); + hp100_andb( ~HP100_PCI_RESET, PCICTRL2 ); + hp100_orb( HP100_PCI_RESET, PCICTRL2 ); + /* Wait for min. 300 ns */ + /* we cant use jiffies here, because it may be */ + /* that we have disabled the timer... */ + for (i=0; i<0xffff; i++); + hp100_andb( ~HP100_PCI_RESET, PCICTRL2 ); + hp100_page( PERFORMANCE ); + } + } + else + { /* bring out of reset */ + hp100_outw(HP100_HW_RST|HP100_SET_LB, OPTION_LSW); + for (i=0; i<0xffff; i++ ); + hp100_page(PERFORMANCE); + } +} + +#ifdef HP100_DEBUG +void hp100_RegisterDump( struct device *dev ) +{ + int ioaddr=dev->base_addr; + int Page; + int Register; + + /* Dump common registers */ + printk("hp100: %s: Cascade Register Dump\n", dev->name); + printk("hardware id #1: 0x%.2x\n",hp100_inb(HW_ID)); + printk("hardware id #2/paging: 0x%.2x\n",hp100_inb(PAGING)); + printk("option #1: 0x%.4x\n",hp100_inw(OPTION_LSW)); + printk("option #2: 0x%.4x\n",hp100_inw(OPTION_MSW)); + + /* Dump paged registers */ + for (Page = 0; Page < 8; Page++) + { + /* Dump registers */ + printk("page: 0x%.2x\n",Page); + outw( Page, ioaddr+0x02); + for (Register = 0x8; Register < 0x22; Register += 2) + { + /* Display Register contents except data port */ + if (((Register != 0x10) && (Register != 0x12)) || (Page > 0)) + { + printk("0x%.2x = 0x%.4x\n",Register,inw(ioaddr+Register)); + } + } + } + hp100_page(PERFORMANCE); +} +#endif + + + /* * module section */ #ifdef MODULE -static int hp100_port = -1; +/* Parameters set by insmod */ +int hp100_port[5] = { 0, -1, -1, -1, -1 }; +#ifdef LINUX_2_1 +MODULE_PARM(hp100_port, "1-5i"); +#endif -static char devicename[9] = { 0, }; -static struct device dev_hp100 = { - devicename, /* device name is inserted by linux/drivers/net/net_init.c */ - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, hp100_probe -}; +#ifdef LINUX_2_1 +char hp100_name[5][IFNAMSIZ] = { "", "", "", "", "" }; +MODULE_PARM(hp100_name, "1-5c" __MODULE_STRING(IFNAMSIZ)); +#else +static char devname[5][IFNAMSIZ] = { "", "", "", "", "" }; +static char *hp100_name[5] = { devname[0], devname[1], + devname[2], devname[3], + devname[4] }; +#endif + +/* List of devices */ +static struct device *hp100_devlist[5] = { NULL, NULL, NULL, NULL, NULL }; + +/* + * Note: if you have more than five 100vg cards in your pc, feel free to + * increase this value + */ + +/* + * Note: to register three eisa or pci devices, use: + * option hp100 hp100_port=0,0,0 + * to register one card at io 0x280 as eth239, use: + * option hp100 hp100_port=0x280 hp100_name=eth239 + */ int init_module( void ) { - if (hp100_port == 0 && !EISA_bus) - printk("HP100: You should not use auto-probing with insmod!\n"); - if ( hp100_port > 0 ) - dev_hp100.base_addr = hp100_port; - if ( register_netdev( &dev_hp100 ) != 0 ) - return -EIO; - return 0; -} + int i; + int ret = 0; + + if (hp100_port == 0 && !EISA_bus && !pcibios_present()) + printk("hp100: You should not use auto-probing with insmod!\n"); + + /* Loop on all possible base addresses */ + i = -1; + while((hp100_port[++i] != -1) && (i < 5)) + { + /* Create device and set basics args */ + hp100_devlist[i] = kmalloc(sizeof(struct device), GFP_KERNEL); + memset(hp100_devlist[i], 0x00, sizeof(struct device)); + hp100_devlist[i]->name = hp100_name[i]; + hp100_devlist[i]->base_addr = hp100_port[i]; + hp100_devlist[i]->init = &hp100_probe; + + /* Try to create the device */ + if(register_netdev(hp100_devlist[i]) != 0) + { + /* DeAllocate everything */ + /* Note: if dev->priv is mallocated, there is no way to fail */ + kfree_s(hp100_devlist[i], sizeof(struct device)); + hp100_devlist[i] = (struct device *) NULL; + ret = -EIO; + } + } /* Loop over all devices */ + + return ret; +} void cleanup_module( void ) { - unregister_netdev( &dev_hp100 ); - release_region( dev_hp100.base_addr, HP100_REGION_SIZE ); - if ( ((struct hp100_private *)dev_hp100.priv) -> mem_ptr_virt ) - vfree( ((struct hp100_private *)dev_hp100.priv) -> mem_ptr_virt ); - kfree_s( dev_hp100.priv, sizeof( struct hp100_private ) ); - dev_hp100.priv = NULL; + int i; + + /* TODO: Check if all skb's are released/freed. */ + for(i = 0; i < 5; i++) + if(hp100_devlist[i] != (struct device *) NULL) + { + unregister_netdev( hp100_devlist[i] ); + release_region( hp100_devlist[i]->base_addr, HP100_REGION_SIZE ); + if( ((struct hp100_private *)hp100_devlist[i]->priv)->mode==1 ) /* busmaster */ + kfree_s( ((struct hp100_private *)hp100_devlist[i]->priv)->page_vaddr, MAX_RINGSIZE+0x0f); + if ( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt ) + iounmap( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt ); + kfree_s( hp100_devlist[i]->priv, sizeof( struct hp100_private ) ); + hp100_devlist[i]->priv = NULL; + kfree_s(hp100_devlist[i], sizeof(struct device)); + hp100_devlist[i] = (struct device *) NULL; + } } -#endif +#endif /* MODULE */ + + + +/* + * Local variables: + * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp100.c" + * c-indent-level: 2 + * tab-width: 8 + * End: + */ diff -u --recursive --new-file v2.0.30/linux/drivers/net/hp100.h linux/drivers/net/hp100.h --- v2.0.30/linux/drivers/net/hp100.h Mon May 6 02:26:08 1996 +++ linux/drivers/net/hp100.h Tue Aug 5 09:11:35 1997 @@ -1,9 +1,10 @@ /* * hp100.h: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux. * - * Author: Jaroslav Kysela, + * $Id: hp100.h,v 1.51 1997/04/08 14:26:42 floeff Exp floeff $ * - * Header file... + * Authors: Jaroslav Kysela, + * Siegfried Loeffler * * This driver is based on the 'hpfepkt' crynwr packet driver. * @@ -16,9 +17,10 @@ /**************************************************************************** * Hardware Constants ****************************************************************************/ - -/* - * ATT2MD01 Register Page Constants + +/* + * Page Identifiers + * (Swap Paging Register, PAGING, bits 3:0, Offset 0x02) */ #define HP100_PAGE_PERFORMANCE 0x0 /* Page 0 */ @@ -30,11 +32,8 @@ #define HP100_PAGE_ID_MAC_ADDR 0x6 /* Page 6 */ #define HP100_PAGE_MMU_POINTER 0x7 /* Page 7 */ -/* - * ATT2MD01 Register Addresses - */ -/* Present on all pages */ +/* Registers that are present on all pages */ #define HP100_REG_HW_ID 0x00 /* R: (16) Unique card ID */ #define HP100_REG_TRACE 0x00 /* W: (16) Used for debug output */ @@ -47,14 +46,29 @@ #define HP100_REG_IRQ_STATUS 0x08 /* RW: (16) Which ints are pending */ #define HP100_REG_IRQ_MASK 0x0a /* RW: (16) Select ints to allow */ -#define HP100_REG_FRAGMENT_LEN 0x0c /* RW: (16)12:0 Current fragment len */ +#define HP100_REG_FRAGMENT_LEN 0x0c /* W: (16)12:0 Current fragment len */ +/* Note: For 32 bit systems, fragment len and offset registers are available */ +/* at offset 0x28 and 0x2c, where they can be written as 32bit values. */ #define HP100_REG_OFFSET 0x0e /* RW: (16)12:0 Offset to start read */ #define HP100_REG_DATA32 0x10 /* RW: (32) I/O mode data port */ #define HP100_REG_DATA16 0x12 /* RW: WORDs must be read from here */ #define HP100_REG_TX_MEM_FREE 0x14 /* RD: (32) Amount of free Tx mem */ +#define HP100_REG_TX_PDA_L 0x14 /* W: (32) BM: Ptr to PDL, Low Pri */ +#define HP100_REG_TX_PDA_H 0x1c /* W: (32) BM: Ptr to PDL, High Pri */ #define HP100_REG_RX_PKT_CNT 0x18 /* RD: (8) Rx count of pkts on card */ #define HP100_REG_TX_PKT_CNT 0x19 /* RD: (8) Tx count of pkts on card */ - +#define HP100_REG_RX_PDL 0x1a /* R: (8) BM: # rx pdl not executed */ +#define HP100_REG_TX_PDL 0x1b /* R: (8) BM: # tx pdl not executed */ +#define HP100_REG_RX_PDA 0x18 /* W: (32) BM: Up to 31 addresses */ + /* which point to a PDL */ +#define HP100_REG_SL_EARLY 0x1c /* (32) Enhanced Slave Early Rx */ +#define HP100_REG_STAT_DROPPED 0x20 /* R (12) Dropped Packet Counter */ +#define HP100_REG_STAT_ERRORED 0x22 /* R (8) Errored Packet Counter */ +#define HP100_REG_STAT_ABORT 0x23 /* R (8) Abort Counter/OW Coll. Flag */ +#define HP100_REG_RX_RING 0x24 /* W (32) Slave: RX Ring Pointers */ +#define HP100_REG_32_FRAGMENT_LEN 0x28 /* W (13) Slave: Fragment Length Reg */ +#define HP100_REG_32_OFFSET 0x2c /* W (16) Slave: Offset Register */ + /* Page 1 - MAC Address/Hash Table */ #define HP100_REG_MAC_ADDR 0x08 /* RW: (8) Cards MAC address */ @@ -68,27 +82,46 @@ #define HP100_REG_IRQ_CHANNEL 0x0d /* RW: (8) IRQ and edge/level int */ #define HP100_REG_SRAM 0x0e /* RW: (8) How much RAM on card */ #define HP100_REG_BM 0x0f /* RW: (8) Controls BM functions */ + +/* New on Page 2 for ETR chips: */ +#define HP100_REG_MODECTRL1 0x10 /* RW: (8) Mode Control 1 */ +#define HP100_REG_MODECTRL2 0x11 /* RW: (8) Mode Control 2 */ +#define HP100_REG_PCICTRL1 0x12 /* RW: (8) PCI Cfg 1 */ +#define HP100_REG_PCICTRL2 0x13 /* RW: (8) PCI Cfg 2 */ +#define HP100_REG_PCIBUSMLAT 0x15 /* RW: (8) PCI Bus Master Latency */ +#define HP100_REG_EARLYTXCFG 0x16 /* RW: (16) Early TX Cfg/Cntrl Reg */ +#define HP100_REG_EARLYRXCFG 0x18 /* RW: (8) Early RX Cfg/Cntrl Reg */ +#define HP100_REG_ISAPNPCFG1 0x1a /* RW: (8) ISA PnP Cfg/Cntrl Reg 1 */ +#define HP100_REG_ISAPNPCFG2 0x1b /* RW: (8) ISA PnP Cfg/Cntrl Reg 2 */ /* Page 3 - EEPROM/Boot ROM */ #define HP100_REG_EEPROM_CTRL 0x08 /* RW: (16) Used to load EEPROM */ +#define HP100_REG_BOOTROM_CTRL 0x0a -/* Page 4 - LAN Configuration */ +/* Page 4 - LAN Configuration (MAC_CTRL) */ -#define HP100_REG_LAN_CFG_10 0x08 /* RW: (16) Set 10M XCVR functions */ -#define HP100_REG_LAN_CFG_VG 0x0a /* RW: (16) Set 100M XCVR functions */ +#define HP100_REG_10_LAN_CFG_1 0x08 /* RW: (8) Set 10M XCVR functions */ +#define HP100_REG_10_LAN_CFG_2 0x09 /* RW: (8) 10M XCVR functions */ +#define HP100_REG_VG_LAN_CFG_1 0x0a /* RW: (8) Set 100M XCVR functions */ +#define HP100_REG_VG_LAN_CFG_2 0x0b /* RW: (8) 100M LAN Training cfgregs */ #define HP100_REG_MAC_CFG_1 0x0c /* RW: (8) Types of pkts to accept */ #define HP100_REG_MAC_CFG_2 0x0d /* RW: (8) Misc MAC functions */ -/* The follow clear when read: */ +#define HP100_REG_MAC_CFG_3 0x0e /* RW: (8) Misc MAC functions */ +#define HP100_REG_MAC_CFG_4 0x0f /* R: (8) Misc MAC states */ #define HP100_REG_DROPPED 0x10 /* R: (16),11:0 Pkts cant fit in mem*/ #define HP100_REG_CRC 0x12 /* R: (8) Pkts with CRC */ #define HP100_REG_ABORT 0x13 /* R: (8) Aborted Tx pkts */ - +#define HP100_REG_TRAIN_REQUEST 0x14 /* RW: (16) Endnode MAC register.*/ +#define HP100_REG_TRAIN_ALLOW 0x16 /* R: (16) Hub allowed register */ + /* Page 5 - MMU */ #define HP100_REG_RX_MEM_STOP 0x0c /* RW: (16) End of Rx ring addr */ #define HP100_REG_TX_MEM_STOP 0x0e /* RW: (16) End of Tx ring addr */ - +#define HP100_REG_PDL_MEM_STOP 0x10 /* Not used by 802.12 devices */ +#define HP100_REG_ECB_MEM_STOP 0x14 /* I've no idea what this is */ + /* Page 6 - Card ID/Physical LAN Address */ #define HP100_REG_BOARD_ID 0x08 /* R: (8) EISA/ISA card ID */ @@ -99,32 +132,46 @@ /* Page 7 - MMU Current Pointers */ -#define HP100_REG_RX_MEM_BR 0x08 /* R: (16) Current begin of Rx ring */ -#define HP100_REG_RX_MEM_ER 0x0a /* R: (16) Current end of Rx ring */ -#define HP100_REG_TX_MEM_BR 0x0c /* R: (16) Current begin of Tx ring */ -#define HP100_REG_TX_MEM_ER 0x0e /* R: (16) Current end of Rx ring */ -#define HP100_REG_MEM_DEBUG 0x1a /* RW: (16) Used for memory tests */ - -/* - * HardwareIDReg bits/masks - */ - -#define HP100_HW_ID_0 0x50 /* Hardware ID bytes. */ -#define HP100_HW_ID_1 0x48 -#define HP100_HW_ID_2_REVA 0x50 /* Rev. A ID. NOTE: lower nibble not used */ -#define HP100_HW_ID_3 0x53 - -/* - * OptionLSWReg bits/masks - */ - -#define HP100_DEBUG_EN 0x8000 /* 0:Disable, 1:Enable Debug Dump Pointer */ -#define HP100_RX_HDR 0x4000 /* 0:Disable, 1:Enable putting pkt into */ - /* system memory before Rx interrupt */ -#define HP100_MMAP_DIS 0x2000 /* 0:Enable, 1:Disable memory mapping. */ - /* MMAP_DIS must be 0 and MEM_EN must */ - /* be 1 for memory-mapped mode to be */ - /* enabled */ +#define HP100_REG_PTR_RXSTART 0x08 /* R: (16) Current begin of Rx ring */ +#define HP100_REG_PTR_RXEND 0x0a /* R: (16) Current end of Rx ring */ +#define HP100_REG_PTR_TXSTART 0x0c /* R: (16) Current begin of Tx ring */ +#define HP100_REG_PTR_TXEND 0x0e /* R: (16) Current end of Rx ring */ +#define HP100_REG_PTR_RPDLSTART 0x10 +#define HP100_REG_PTR_RPDLEND 0x12 +#define HP100_REG_PTR_RINGPTRS 0x14 +#define HP100_REG_PTR_MEMDEBUG 0x1a +/* ------------------------------------------------------------------------ */ + + +/* + * Hardware ID Register I (Always available, HW_ID, Offset 0x00) + */ +#define HP100_HW_ID_CASCADE 0x4850 /* Identifies Cascade Chip */ + +/* + * Hardware ID Register 2 & Paging Register + * (Always available, PAGING, Offset 0x02) + * Bits 15:4 are for the Chip ID + */ +#define HP100_CHIPID_MASK 0xFFF0 +#define HP100_CHIPID_SHASTA 0x5350 /* Not 802.12 compliant */ + /* EISA BM/SL, MCA16/32 SL, ISA SL */ +#define HP100_CHIPID_RAINIER 0x5360 /* Not 802.12 compliant EISA BM,*/ + /* PCI SL, MCA16/32 SL, ISA SL */ +#define HP100_CHIPID_LASSEN 0x5370 /* 802.12 compliant PCI BM, PCI SL */ + /* LRF supported */ + +/* + * Option Registers I and II + * (Always available, OPTION_LSW, Offset 0x04-0x05) + */ +#define HP100_DEBUG_EN 0x8000 /* 0:Dis., 1:Enable Debug Dump Ptr. */ +#define HP100_RX_HDR 0x4000 /* 0:Dis., 1:Enable putting pkt into */ + /* system mem. before Rx interrupt */ +#define HP100_MMAP_DIS 0x2000 /* 0:Enable, 1:Disable mem.mapping. */ + /* MMAP_DIS must be 0 and MEM_EN */ + /* must be 1 for memory-mapped */ + /* mode to be enabled */ #define HP100_EE_EN 0x1000 /* 0:Disable,1:Enable EEPROM writing */ #define HP100_BM_WRITE 0x0800 /* 0:Slave, 1:Bus Master for Tx data */ #define HP100_BM_READ 0x0400 /* 0:Slave, 1:Bus Master for Rx data */ @@ -132,121 +179,236 @@ #define HP100_MEM_EN 0x0040 /* Config program set this to */ /* 0:Disable, 1:Enable mem map. */ /* See MMAP_DIS. */ -#define HP100_IO_EN 0x0020 /* 0:Disable, 1:Enable I/O transfers */ -#define HP100_BOOT_EN 0x0010 /* 0:Disable, 1:Enable boot ROM access */ -#define HP100_FAKE_INT 0x0008 /* 0:No int, 1:int */ -#define HP100_INT_EN 0x0004 /* 0:Disable, 1:Enable ints from card */ +#define HP100_IO_EN 0x0020 /* 1:Enable I/O transfers */ +#define HP100_BOOT_EN 0x0010 /* 1:Enable boot ROM access */ +#define HP100_FAKE_INT 0x0008 /* 1:int */ +#define HP100_INT_EN 0x0004 /* 1:Enable ints from card */ #define HP100_HW_RST 0x0002 /* 0:Reset, 1:Out of reset */ + /* NIC reset on 0 to 1 transition */ /* - * OptionMSWReg bits/masks + * Option Register III + * (Always available, OPTION_MSW, Offset 0x06) */ -#define HP100_PRIORITY_TX 0x0080 /* 0:Don't, 1:Do all Tx pkts as priority */ +#define HP100_PRIORITY_TX 0x0080 /* 1:Do all Tx pkts as priority */ #define HP100_EE_LOAD 0x0040 /* 1:EEPROM loading, 0 when done */ -#define HP100_ADV_NXT_PKT 0x0004 /* 1:Advance to next pkt in Rx queue, */ +#define HP100_ADV_NXT_PKT 0x0004 /* 1:Advance to next pkt in Rx queue */ /* h/w will set to 0 when done */ -#define HP100_TX_CMD 0x0002 /* 1:Tell h/w download done, h/w will set */ - /* to 0 when done */ +#define HP100_TX_CMD 0x0002 /* 1:Tell h/w download done, h/w */ + /* will set to 0 when done */ /* - * InterruptStatusReg/InterruptMaskReg bits/masks. These bits will 0 when a 1 - * is written to them. - */ + * Interrupt Status Registers I and II + * (Page PERFORMANCE, IRQ_STATUS, Offset 0x08-0x09) + * Note: With old chips, these Registers will clear when 1 is written to them + * with new chips this depends on setting of CLR_ISMODE + */ +#define HP100_RX_EARLY_INT 0x2000 +#define HP100_RX_PDA_ZERO 0x1000 +#define HP100_RX_PDL_FILL_COMPL 0x0800 #define HP100_RX_PACKET 0x0400 /* 0:No, 1:Yes pkt has been Rx */ #define HP100_RX_ERROR 0x0200 /* 0:No, 1:Yes Rx pkt had error */ +#define HP100_TX_PDA_ZERO 0x0020 /* 1 when PDA count goes to zero */ #define HP100_TX_SPACE_AVAIL 0x0010 /* 0:<8192, 1:>=8192 Tx free bytes */ #define HP100_TX_COMPLETE 0x0008 /* 0:No, 1:Yes a Tx has completed */ +#define HP100_MISC_ERROR 0x0004 /* 0:No, 1:Lan Link down or bus error*/ #define HP100_TX_ERROR 0x0002 /* 0:No, 1:Yes Tx pkt had error */ - + /* - * TxMemoryFreeCountReg bits/masks. + * Xmit Memory Free Count + * (Page PERFORMANCE, TX_MEM_FREE, Offset 0x14) (Read only, 32bit) */ -#define HP100_AUTO_COMPARE 0x8000 /* Says at least 8k is available for Tx. */ - /* NOTE: This mask is for the upper */ - /* word of the register. */ +#define HP100_AUTO_COMPARE 0x80000000 /* Tx Space avail & pkts<255 */ +#define HP100_FREE_SPACE 0x7fffffe0 /* Tx free memory */ /* - * IRQChannelReg bits/masks. + * IRQ Channel + * (Page HW_MAP, IRQ_CHANNEL, Offset 0x0d) */ #define HP100_ZERO_WAIT_EN 0x80 /* 0:No, 1:Yes asserts NOWS signal */ +#define HP100_IRQ_SCRAMBLE 0x40 +#define HP100_BOND_HP 0x20 #define HP100_LEVEL_IRQ 0x10 /* 0:Edge, 1:Level type interrupts. */ - /* Only valid on EISA cards. */ -#define HP100_IRQ_MASK 0x0F /* Isolate the IRQ bits */ + /* (Only valid on EISA cards) */ +#define HP100_IRQMASK 0x0F /* Isolate the IRQ bits */ /* - * SRAMReg bits/masks. + * SRAM Parameters + * (Page HW_MAP, SRAM, Offset 0x0e) */ #define HP100_RAM_SIZE_MASK 0xe0 /* AND to get SRAM size index */ -#define HP100_RAM_SIZE_SHIFT 0x05 /* Shift count to put index in lower bits */ +#define HP100_RAM_SIZE_SHIFT 0x05 /* Shift count(put index in lwr bits)*/ + +/* + * Bus Master Register + * (Page HW_MAP, BM, Offset 0x0f) + */ +#define HP100_BM_BURST_RD 0x01 /* EISA only: 1=Use burst trans. fm system */ + /* memory to chip (tx) */ +#define HP100_BM_BURST_WR 0x02 /* EISA only: 1=Use burst trans. fm system */ + /* memory to chip (rx) */ +#define HP100_BM_MASTER 0x04 /* 0:Slave, 1:BM mode */ +#define HP100_BM_PAGE_CK 0x08 /* This bit should be set whenever in*/ + /* an EISA system */ +#define HP100_BM_PCI_8CLK 0x40 /* ... cycles 8 clocks apart */ + + +/* + * Mode Control Register I + * (Page HW_MAP, MODECTRL1, Offset0x10) + */ +#define HP100_TX_DUALQ 0x10 + /* If set and BM -> dual tx pda queues*/ +#define HP100_ISR_CLRMODE 0x02 /* If set ISR will clear all pending */ + /* interrupts on read (etr only?) */ +#define HP100_EE_NOLOAD 0x04 /* Status whether res will be loaded */ + /* from the eeprom */ +#define HP100_TX_CNT_FLG 0x08 /* Controls Early TX Reg Cnt Field */ +#define HP100_PDL_USE3 0x10 /* If set BM engine will read only */ + /* first three data elements of a PDL */ + /* on the first access. */ +#define HP100_BUSTYPE_MASK 0xe0 /* Three bit bus type info */ + +/* + * Mode Control Register II + * (Page HW_MAP, MODECTRL2, Offset0x11) + */ +#define HP100_EE_MASK 0x0f /* Tell EEPROM circuit not to load */ + /* certain resources */ +#define HP100_DIS_CANCEL 0x20 /* For tx dualq mode operation */ +#define HP100_EN_PDL_WB 0x40 /* 1: Status of PDL completion may be */ + /* written back to system mem */ +#define HP100_EN_BUS_FAIL 0x80 /* Enables bus-fail portion of misc */ + /* interrupt */ + +/* + * PCI Configuration and Control Register I + * (Page HW_MAP, PCICTRL1, Offset 0x12) + */ +#define HP100_LO_MEM 0x01 /* 1: Mapped Mem requested below 1MB */ +#define HP100_NO_MEM 0x02 /* 1: Disables Req for sysmem to PCI */ + /* bios */ +#define HP100_USE_ISA 0x04 /* 1: isa type decodes will occur */ + /* simultaneously with PCI decodes */ +#define HP100_IRQ_HI_MASK 0xf0 /* pgmed by pci bios */ +#define HP100_PCI_IRQ_HI_MASK 0x78 /* Isolate 4 bits for PCI IRQ */ + +/* + * PCI Configuration and Control Register II + * (Page HW_MAP, PCICTRL2, Offset 0x13) + */ +#define HP100_RD_LINE_PDL 0x01 /* 1: PCI command Memory Read Line en */ +#define HP100_RD_TX_DATA_MASK 0x06 /* choose PCI memread cmds for TX */ +#define HP100_MWI 0x08 /* 1: en. PCI memory write invalidate */ +#define HP100_ARB_MODE 0x10 /* Select PCI arbitor type */ +#define HP100_STOP_EN 0x20 /* Enables PCI state machine to issue */ + /* pci stop if cascade not ready */ +#define HP100_IGNORE_PAR 0x40 /* 1: PCI state machine ignores parity*/ +#define HP100_PCI_RESET 0x80 /* 0->1: Reset PCI block */ + +/* + * Early TX Configuration and Control Register + * (Page HW_MAP, EARLYTXCFG, Offset 0x16) + */ +#define HP100_EN_EARLY_TX 0x8000 /* 1=Enable Early TX */ +#define HP100_EN_ADAPTIVE 0x4000 /* 1=Enable adaptive mode */ +#define HP100_EN_TX_UR_IRQ 0x2000 /* reserved, must be 0 */ +#define HP100_EN_LOW_TX 0x1000 /* reserved, must be 0 */ +#define HP100_ET_CNT_MASK 0x0fff /* bits 11..0: ET counters */ /* - * BMReg bits/masks. + * Early RX Configuration and Control Register + * (Page HW_MAP, EARLYRXCFG, Offset 0x18) */ -#define HP100_BM_SLAVE 0x04 /* 0:Slave, 1:BM mode */ +#define HP100_EN_EARLY_RX 0x80 /* 1=Enable Early RX */ +#define HP100_EN_LOW_RX 0x40 /* reserved, must be 0 */ +#define HP100_RX_TRIP_MASK 0x1f /* bits 4..0: threshold at which the + * early rx circuit will start the + * dma of received packet into system + * memory for BM */ /* - * EEPROMControlReg bits/masks. + * Serial Devices Control Register + * (Page EEPROM_CTRL, EEPROM_CTRL, Offset 0x08) */ -#define HP100_EEPROM_LOAD 0x0001 /* 0->1 loads the EEPROM into registers. */ - /* When it goes back to 0, load is */ - /* complete. This should take ~600us. */ +#define HP100_EEPROM_LOAD 0x0001 /* 0->1 loads EEPROM into registers. */ + /* When it goes back to 0, load is */ + /* complete. This should take ~600us.*/ /* - * LANCntrCfg10Reg bits/masks. + * 10MB LAN Control and Configuration Register I + * (Page MAC_CTRL, 10_LAN_CFG_1, Offset 0x08) */ -#define HP100_SQU_ST 0x0100 /* 0:No, 1:Yes collision signal sent */ - /* after Tx. Only used for AUI. */ -#define HP100_MAC10_SEL 0x00c0 /* Get bits to indicate MAC */ -#define HP100_AUI_SEL 0x0020 /* Status of AUI selection */ -#define HP100_LOW_TH 0x0010 /* 0:No, 1:Yes allow better cabling */ -#define HP100_LINK_BEAT_DIS 0x0008 /* 0:Enable, 1:Disable link beat */ -#define HP100_LINK_BEAT_ST 0x0004 /* 0:No, 1:Yes link beat being Rx */ -#define HP100_R_ROL_ST 0x0002 /* 0:No, 1:Yes Rx twisted pair has been */ - /* reversed */ -#define HP100_AUI_ST 0x0001 /* 0:No, 1:Yes use AUI on TP card */ +#define HP100_MAC10_SEL 0xc0 /* Get bits to indicate MAC */ +#define HP100_AUI_SEL 0x20 /* Status of AUI selection */ +#define HP100_LOW_TH 0x10 /* 0:No, 1:Yes allow better cabling */ +#define HP100_LINK_BEAT_DIS 0x08 /* 0:Enable, 1:Disable link beat */ +#define HP100_LINK_BEAT_ST 0x04 /* 0:No, 1:Yes link beat being Rx */ +#define HP100_R_ROL_ST 0x02 /* 0:No, 1:Yes Rx twisted pair has */ + /* been reversed */ +#define HP100_AUI_ST 0x01 /* 0:No, 1:Yes use AUI on TP card */ -/* MAC Selection, use with MAC10_SEL bits */ +/* + * 10 MB LAN Control and Configuration Register II + * (Page MAC_CTRL, 10_LAN_CFG_2, Offset 0x09) + */ +#define HP100_SQU_ST 0x01 /* 0:No, 1:Yes collision signal sent */ + /* after Tx.Only used for AUI. */ +#define HP100_FULLDUP 0x02 /* 1: LXT901 XCVR fullduplx enabled */ +#define HP100_DOT3_MAC 0x04 /* 1: DOT 3 Mac sel. unless Autosel */ + +/* + * MAC Selection, use with MAC10_SEL bits + */ #define HP100_AUTO_SEL_10 0x0 /* Auto select */ #define HP100_XCVR_LXT901_10 0x1 /* LXT901 10BaseT transceiver */ #define HP100_XCVR_7213 0x2 /* 7213 transceiver */ #define HP100_XCVR_82503 0x3 /* 82503 transceiver */ - /* - * LANCntrCfgVGReg bits/masks. + * 100MB LAN Training Register + * (Page MAC_CTRL, VG_LAN_CFG_2, Offset 0x0b) (old, pre 802.12) */ -#define HP100_FRAME_FORMAT 0x0800 /* 0:802.3, 1:802.5 frames */ -#define HP100_BRIDGE 0x0400 /* 0:No, 1:Yes tell hub it's a bridge */ -#define HP100_PROM_MODE 0x0200 /* 0:No, 1:Yes tell hub card is */ - /* promiscuous */ -#define HP100_REPEATER 0x0100 /* 0:No, 1:Yes tell hub MAC wants to be */ - /* a cascaded repeater */ -#define HP100_MAC100_SEL 0x0080 /* 0:No, 1:Yes use 100 Mbit MAC */ -#define HP100_LINK_UP_ST 0x0040 /* 0:No, 1:Yes endnode logged in */ -#define HP100_LINK_CABLE_ST 0x0020 /* 0:No, 1:Yes cable can hear tones from */ - /* hub */ -#define HP100_LOAD_ADDR 0x0010 /* 0->1 card addr will be sent to hub. */ - /* 100ms later the link status bits are */ - /* valid */ -#define HP100_LINK_CMD 0x0008 /* 0->1 link will attempt to log in. */ - /* 100ms later the link status bits are */ - /* valid */ -#define HP100_LINK_GOOD_ST 0x0002 /* 0:No, 1:Yes cable passed training */ -#define HP100_VG_RESET 0x0001 /* 0:Yes, 1:No reset the 100VG MAC */ +#define HP100_FRAME_FORMAT 0x08 /* 0:802.3, 1:802.5 frames */ +#define HP100_BRIDGE 0x04 /* 0:No, 1:Yes tell hub i am a bridge */ +#define HP100_PROM_MODE 0x02 /* 0:No, 1:Yes tell hub card is */ + /* promiscuous */ +#define HP100_REPEATER 0x01 /* 0:No, 1:Yes tell hub MAC wants to */ + /* be a cascaded repeater */ + +/* + * 100MB LAN Control and Configuration Register + * (Page MAC_CTRL, VG_LAN_CFG_1, Offset 0x0a) + */ +#define HP100_VG_SEL 0x80 /* 0:No, 1:Yes use 100 Mbit MAC */ +#define HP100_LINK_UP_ST 0x40 /* 0:No, 1:Yes endnode logged in */ +#define HP100_LINK_CABLE_ST 0x20 /* 0:No, 1:Yes cable can hear tones */ + /* from hub */ +#define HP100_LOAD_ADDR 0x10 /* 0->1 card addr will be sent */ + /* 100ms later the link status */ + /* bits are valid */ +#define HP100_LINK_CMD 0x08 /* 0->1 link will attempt to log in. */ + /* 100ms later the link status */ + /* bits are valid */ +#define HP100_TRN_DONE 0x04 /* NEW ETR-Chips only: Will be reset */ + /* after LinkUp Cmd is given and set */ + /* when training has completed. */ +#define HP100_LINK_GOOD_ST 0x02 /* 0:No, 1:Yes cable passed training */ +#define HP100_VG_RESET 0x01 /* 0:Yes, 1:No reset the 100VG MAC */ /* - * MACConfiguration1Reg bits/masks. + * MAC Configuration Register I + * (Page MAC_CTRL, MAC_CFG_1, Offset 0x0c) */ #define HP100_RX_IDLE 0x80 /* 0:Yes, 1:No currently receiving pkts */ #define HP100_TX_IDLE 0x40 /* 0:Yes, 1:No currently Txing pkts */ -#define HP100_RX_EN 0x20 /* 0:No, 1:Yes allow receiving of pkts */ -#define HP100_TX_EN 0x10 /* 0:No, 1:Yes allow transmitting of pkts */ +#define HP100_RX_EN 0x20 /* 1: allow receiving of pkts */ +#define HP100_TX_EN 0x10 /* 1: allow transmitting of pkts */ #define HP100_ACC_ERRORED 0x08 /* 0:No, 1:Yes allow Rx of errored pkts */ #define HP100_ACC_MC 0x04 /* 0:No, 1:Yes allow Rx of multicast pkts */ #define HP100_ACC_BC 0x02 /* 0:No, 1:Yes allow Rx of broadcast pkts */ -#define HP100_ACC_PHY 0x01 /* 0:No, 1:Yes allow Rx of ALL physical pkts */ - +#define HP100_ACC_PHY 0x01 /* 0:No, 1:Yes allow Rx of ALL phys. pkts */ #define HP100_MAC1MODEMASK 0xf0 /* Hide ACC bits */ #define HP100_MAC1MODE1 0x00 /* Receive nothing, must also disable RX */ #define HP100_MAC1MODE2 0x00 @@ -254,15 +416,14 @@ #define HP100_MAC1MODE4 HP100_MAC1MODE3 | HP100_ACC_MC #define HP100_MAC1MODE5 HP100_MAC1MODE4 /* set mc hash to all ones also */ #define HP100_MAC1MODE6 HP100_MAC1MODE5 | HP100_ACC_PHY /* Promiscuous */ - /* Note MODE6 will receive all GOOD packets on the LAN. This really needs a mode 7 defined to be LAN Analyzer mode, which will receive errored and runt packets, and keep the CRC bytes. */ - -#define HP100_MAC1MODE7 MAC1MODE6 OR ACC_ERRORED +#define HP100_MAC1MODE7 HP100_MAC1MODE6 | HP100_ACC_ERRORED /* - * MACConfiguration2Reg bits/masks. + * MAC Configuration Register II + * (Page MAC_CTRL, MAC_CFG_2, Offset 0x0d) */ #define HP100_TR_MODE 0x80 /* 0:No, 1:Yes support Token Ring formats */ #define HP100_TX_SAME 0x40 /* 0:No, 1:Yes Tx same packet continuous */ @@ -270,9 +431,12 @@ /* transceiver */ #define HP100_LBK_MAC 0x10 /* 0:No, 1:Yes loopback through MAC */ #define HP100_CRC_I 0x08 /* 0:No, 1:Yes inhibit CRC on Tx packets */ +#define HP100_ACCNA 0x04 /* 1: For 802.5: Accept only token ring + * group addr that maches NA mask */ #define HP100_KEEP_CRC 0x02 /* 0:No, 1:Yes keep CRC on Rx packets. */ /* The length will reflect this. */ - +#define HP100_ACCFA 0x01 /* 1: For 802.5: Accept only functional + * addrs that match FA mask (page1) */ #define HP100_MAC2MODEMASK 0x02 #define HP100_MAC2MODE1 0x00 #define HP100_MAC2MODE2 0x00 @@ -283,6 +447,65 @@ #define HP100_MAC2MODE7 KEEP_CRC /* + * MAC Configuration Register III + * (Page MAC_CTRL, MAC_CFG_3, Offset 0x0e) + */ +#define HP100_PACKET_PACE 0x03 /* Packet Pacing: + * 00: No packet pacing + * 01: 8 to 16 uS delay + * 10: 16 to 32 uS delay + * 11: 32 to 64 uS delay + */ +#define HP100_LRF_EN 0x04 /* 1: External LAN Rcv Filter and + * TCP/IP Checksumming enabled. */ +#define HP100_AUTO_MODE 0x10 /* 1: AutoSelect between 10/100 */ + +/* + * MAC Configuration Register IV + * (Page MAC_CTRL, MAC_CFG_4, Offset 0x0f) + */ +#define HP100_MAC_SEL_ST 0x01 /* (R): Status of external VGSEL + * Signal, 1=100VG, 0=10Mbit sel. */ +#define HP100_LINK_FAIL_ST 0x02 /* (R): Status of Link Fail portion + * of the Misc. Interrupt */ + +/* + * 100 MB LAN Training Request/Allowed Registers + * (Page MAC_CTRL, TRAIN_REQUEST and TRAIN_ALLOW, Offset 0x14-0x16)(ETR parts only) + */ +#define HP100_MACRQ_REPEATER 0x0001 /* 1: MAC tells HUB it wants to be + * a cascaded repeater + * 0: ... wants to be a DTE */ +#define HP100_MACRQ_PROMSC 0x0006 /* 2 bits: Promiscious mode + * 00: Rcv only unicast packets + * specifically addr to this + * endnode + * 10: Rcv all pckts fwded by + * the local repeater */ +#define HP100_MACRQ_FRAMEFMT_EITHER 0x0018 /* 11: either format allowed */ +#define HP100_MACRQ_FRAMEFMT_802_3 0x0000 /* 00: 802.3 is requested */ +#define HP100_MACRQ_FRAMEFMT_802_5 0x0010 /* 10: 802.5 format is requested */ +#define HP100_CARD_MACVER 0xe000 /* R: 3 bit Cards 100VG MAC version */ +#define HP100_MALLOW_REPEATER 0x0001 /* If reset, requested access as an + * end node is allowed */ +#define HP100_MALLOW_PROMSC 0x0004 /* 2 bits: Promiscious mode + * 00: Rcv only unicast packets + * specifically addr to this + * endnode + * 10: Rcv all pckts fwded by + * the local repeater */ +#define HP100_MALLOW_FRAMEFMT 0x00e0 /* 2 bits: Frame Format + * 00: 802.3 format will be used + * 10: 802.5 format will be used */ +#define HP100_MALLOW_ACCDENIED 0x0400 /* N bit */ +#define HP100_MALLOW_CONFIGURE 0x0f00 /* C bit */ +#define HP100_MALLOW_DUPADDR 0x1000 /* D bit */ +#define HP100_HUB_MACVER 0xe000 /* R: 3 bit 802.12 MAC/RMAC training */ + /* protocol of repeater */ + +/* ****************************************************************************** */ + +/* * Set/Reset bits */ #define HP100_SET_HB 0x0100 /* 0:Set fields to 0 whose mask is 1 */ @@ -297,20 +520,45 @@ #define HP100_LAN_10 10 /* lan_type value for 10BaseT */ #define HP100_LAN_ERR (-1) /* lan_type value for link down */ -/* - * Receive Header Definition. +#define TRUE 1 +#define FALSE 0 + + +/* + * Bus Master Data Structures ---------------------------------------------- */ -struct hp100_rx_header { - u_short rx_length; /* Pkt length is bits 12:0 */ - u_short rx_status; /* status of the packet */ -}; +#define MAX_RX_PDL 30 /* Card limit = 31 */ +#define MAX_RX_FRAG 2 /* Dont need more... */ +#define MAX_TX_PDL 29 +#define MAX_TX_FRAG 2 /* Limit = 31 */ + +/* Define total PDL area size in bytes (should be 4096) */ +/* This is the size of kernel (dma) memory that will be allocated. */ +#define MAX_RINGSIZE ((MAX_RX_FRAG*8+4+4)*MAX_RX_PDL+(MAX_TX_FRAG*8+4+4)*MAX_TX_PDL)+16 + +/* Ethernet Packet Sizes */ +#define MIN_ETHER_SIZE 60 +#define MAX_ETHER_SIZE 1514 /* Needed for preallocation of */ + /* skb buffer when busmastering */ + +/* Tx or Rx Ring Entry */ +typedef struct hp100_ring { + u_int *pdl; /* Address of PDLs PDH, dword before + * this address is used for rx hdr */ + u_int pdl_paddr; /* Physical address of PDL */ + struct sk_buff *skb; + struct hp100_ring *next; +} hp100_ring_t; + + + +/* Mask for Header Descriptor */ +#define HP100_PKT_LEN_MASK 0x1FFF /* AND with RxLength to get length */ -#define HP100_PKT_LEN_MASK 0x1FFF /* AND with RxLength to get length bits */ /* Receive Packet Status. Note, the error bits are only valid if ACC_ERRORED bit in the MAC Configuration Register 1 is set. */ - #define HP100_RX_PRI 0x8000 /* 0:No, 1:Yes packet is priority */ #define HP100_SDF_ERR 0x4000 /* 0:No, 1:Yes start of frame error */ #define HP100_SKEW_ERR 0x2000 /* 0:No, 1:Yes skew out of range */ @@ -368,7 +616,11 @@ outw( HP100_MMAP_DIS | HP100_RESET_HB, ioaddr + HP100_REG_OPTION_LSW ) #define hp100_mem_map_disable() \ outw( HP100_MMAP_DIS | HP100_SET_HB, ioaddr + HP100_REG_OPTION_LSW ) -#define hp100_reset_card() \ - outw( HP100_HW_RST | HP100_RESET_LB, ioaddr + HP100_REG_OPTION_LSW ) -#define hp100_unreset_card() \ - outw( HP100_HW_RST | HP100_SET_LB, ioaddr + HP100_REG_OPTION_LSW ) + + +/* + * Local variables: + * c-indent-level: 2 + * tab-width: 8 + * End: +*/ diff -u --recursive --new-file v2.0.30/linux/drivers/net/ibmtr.c linux/drivers/net/ibmtr.c --- v2.0.30/linux/drivers/net/ibmtr.c Tue Apr 8 08:47:46 1997 +++ linux/drivers/net/ibmtr.c Tue Aug 12 13:21:12 1997 @@ -971,14 +971,15 @@ } /* ARB response */ if (status & SSB_RESP_INT) { /* SSB response */ - + unsigned char retcode; switch (readb(ti->ssb)) { /* SSB command check */ case XMIT_DIR_FRAME: case XMIT_UI_FRAME: - if (readb(ti->ssb+2)) /* checks ret_code */ + retcode = readb(ti->ssb+2); + if (retcode && (retcode != 0x22)) /* checks ret_code */ DPRINTK("xmit ret_code: %02X xmit error code: %02X\n", - (int)readb(ti->ssb+2), (int)readb(ti->ssb+6)); + (int)retcode, (int)readb(ti->ssb+6)); else ti->tr_stats.tx_packets++; break; diff -u --recursive --new-file v2.0.30/linux/drivers/net/lance32.c linux/drivers/net/lance32.c --- v2.0.30/linux/drivers/net/lance32.c Tue Apr 8 08:47:46 1997 +++ linux/drivers/net/lance32.c Mon Aug 4 12:11:05 1997 @@ -273,6 +273,9 @@ outw(0x0002, ioaddr+LANCE_ADDR); outw(0x0002, ioaddr+LANCE_BUS_IF); + /* Reset the LANCE - this should prevent any more interrupts from arriving */ + inw(ioaddr+LANCE_RESET); + if (lance32_debug > 0) printk(version); @@ -451,7 +454,7 @@ /* Transmitter timeout, serious problems. */ if (dev->tbusy) { int tickssofar = jiffies - dev->trans_start; - if (tickssofar < 20) + if (tickssofar < 60) /* It can take this long to run through the 16 retries */ return 1; outw(0, ioaddr+LANCE_ADDR); printk("%s: transmit timed out, status %4.4x, resetting.\n", @@ -469,8 +472,9 @@ lp->rx_ring[i].base, -lp->rx_ring[i].buf_length, lp->rx_ring[i].msg_length); for (i = 0 ; i < TX_RING_SIZE; i++) - printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ", + printk("%s %08x %04x %04x %08x ", i & 0x1 ? "" : "\n ", lp->tx_ring[i].base, -lp->tx_ring[i].length, + lp->tx_ring[i].status, lp->tx_ring[i].misc); printk("\n"); } @@ -633,6 +637,9 @@ dirty_tx += TX_RING_SIZE; } #endif + + if (dev->tbusy) + dev->trans_start = jiffies; /* We are just starting the next transmit */ if (lp->tx_full && dev->tbusy && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) { diff -u --recursive --new-file v2.0.30/linux/drivers/net/ne.c linux/drivers/net/ne.c --- v2.0.30/linux/drivers/net/ne.c Sat Mar 1 18:09:39 1997 +++ linux/drivers/net/ne.c Wed Aug 13 10:02:39 1997 @@ -24,6 +24,7 @@ Paul Gortmaker : multiple card support for module users. Paul Gortmaker : Support for PCI ne2k clones, similar to lance.c Paul Gortmaker : Allow users with bad cards to avoid full probe. + Paul Gortmaker : PCI probe changes, more PCI cards supported. */ @@ -61,11 +62,22 @@ /* Do we have a non std. amount of memory? (in units of 256 byte pages) */ /* #define PACKETBUF_MEMSIZE 0x40 */ -/* ---- No user-serviceable parts below ---- */ - +#if defined(HAVE_DEVLIST) || !defined(MODULE) /* A zero-terminated list of I/O addresses to be probed. */ static unsigned int netcard_portlist[] = { 0x300, 0x280, 0x320, 0x340, 0x360, 0}; +#endif /* defined(HAVE_DEVLIST) || !defined(MODULE) */ + +#ifdef CONFIG_PCI +/* Ack! People are making PCI ne2000 clones! Oh the horror, the horror... */ +static struct { unsigned short vendor, dev_id;} +pci_clone_list[] = { + {PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8029}, + {PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_89C940}, + {PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_RL2000}, + {0,} +}; +#endif #ifdef SUPPORT_NE_BAD_CLONES /* A list of bad clones that we none-the-less recognize. */ @@ -80,10 +92,14 @@ {"4-DIM8","4-DIM16", {0x00,0x00,0x4d,}}, /* Outlaw 4-Dimension cards. */ {"Con-Intl_8", "Con-Intl_16", {0x00, 0x00, 0x24}}, /* Connect Int'nl */ {"ET-100","ET-200", {0x00, 0x45, 0x54}}, /* YANG and YA clone */ + {"COMPEX","COMPEX16",{0x00,0x80,0x48}}, /* Broken ISA Compex cards */ + {"E-LAN100", "E-LAN200", {0x00, 0x00, 0x5d}}, /* Broken ne1000 clones */ {0,} }; #endif +/* ---- No user-serviceable parts below ---- */ + #define NE_BASE (dev->base_addr) #define NE_CMD 0x00 #define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */ @@ -99,6 +115,7 @@ static unsigned char pci_irq_line = 0; int ne_probe(struct device *dev); +static int ne_probe_pci(struct device *dev); static int ne_probe1(struct device *dev, int ioaddr); static int ne_open(struct device *dev); @@ -145,7 +162,9 @@ int ne_probe(struct device *dev) { +#ifndef MODULE int i; +#endif /* MODULE */ int base_addr = dev ? dev->base_addr : 0; /* First check any supplied i/o locations. User knows best. */ @@ -154,42 +173,13 @@ else if (base_addr != 0) /* Don't probe at all. */ return ENXIO; +#ifdef CONFIG_PCI /* Then look for any installed PCI clones */ -#if defined(CONFIG_PCI) - if (pcibios_present()) { - int pci_index; - for (pci_index = 0; pci_index < 8; pci_index++) { - unsigned char pci_bus, pci_device_fn; - unsigned int pci_ioaddr; - - /* Currently only Realtek are making PCI ne2k clones. */ - if (pcibios_find_device (PCI_VENDOR_ID_REALTEK, - PCI_DEVICE_ID_REALTEK_8029, pci_index, - &pci_bus, &pci_device_fn) != 0) - break; /* OK, now try to probe for std. ISA card */ - 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); - /* Strip the I/O address out of the returned value */ - pci_ioaddr &= PCI_BASE_ADDRESS_IO_MASK; - /* Avoid already found cards from previous ne_probe() calls */ - if (check_region(pci_ioaddr, NE_IO_EXTENT)) { - pci_irq_line=0; - continue; - } - printk("ne.c: PCI BIOS reports ne2000 clone at i/o %#x, irq %d.\n", - pci_ioaddr, pci_irq_line); - if (ne_probe1(dev, pci_ioaddr) != 0) { /* Shouldn't happen. */ - printk(KERN_ERR "ne.c: Probe of PCI card at %#x failed.\n", pci_ioaddr); - break; /* Hrmm, try to probe for ISA card... */ - } - pci_irq_line = 0; - return 0; - } - } -#endif /* defined(CONFIG_PCI) */ + if (pcibios_present() && (ne_probe_pci(dev) == 0)) + return 0; +#endif +#ifndef MODULE /* Last resort. The semi-risky ISA auto-probe. */ for (i = 0; netcard_portlist[i]; i++) { int ioaddr = netcard_portlist[i]; @@ -198,11 +188,53 @@ if (ne_probe1(dev, ioaddr) == 0) return 0; } +#endif return ENODEV; } #endif +#ifdef CONFIG_PCI +static int ne_probe_pci(struct device *dev) +{ + int i; + + for (i = 0; pci_clone_list[i].vendor != 0; i++) { + unsigned char pci_bus, pci_device_fn; + unsigned int pci_ioaddr; + int pci_index; + + for (pci_index = 0; pci_index < 8; pci_index++) { + if (pcibios_find_device (pci_clone_list[i].vendor, + pci_clone_list[i].dev_id, pci_index, + &pci_bus, &pci_device_fn) != 0) + break; /* No more of these type of cards */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + /* Strip the I/O address out of the returned value */ + pci_ioaddr &= PCI_BASE_ADDRESS_IO_MASK; + /* Avoid already found cards from previous calls */ + if (check_region(pci_ioaddr, NE_IO_EXTENT)) + continue; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + break; /* Beauty -- got a valid card. */ + } + if (pci_irq_line == 0) continue; /* Try next PCI ID */ + printk("ne.c: PCI BIOS reports NE 2000 clone at i/o %#x, irq %d.\n", + pci_ioaddr, pci_irq_line); + if (ne_probe1(dev, pci_ioaddr) != 0) { /* Shouldn't happen. */ + printk(KERN_ERR "ne.c: Probe of PCI card at %#x failed.\n", pci_ioaddr); + pci_irq_line = 0; + return -ENXIO; + } + pci_irq_line = 0; + return 0; + } + return -ENODEV; +} +#endif /* CONFIG_PCI */ + static int ne_probe1(struct device *dev, int ioaddr) { int i; @@ -310,8 +342,8 @@ for (i = 0; i < 16; i++) SA_prom[i] = SA_prom[i+i]; - if (pci_irq_line) - wordlength = 2; /* Catch broken cards mentioned above. */ + if (pci_irq_line || ioaddr >= 0x400) + wordlength = 2; /* Catch broken PCI cards mentioned above. */ if (wordlength == 2) { /* We must set the 8390 for word mode. */ @@ -361,9 +393,8 @@ } - if (pci_irq_line) { + if (pci_irq_line) dev->irq = pci_irq_line; - } if (dev->irq < 2) { autoirq_setup(0); @@ -713,17 +744,17 @@ dev->irq = irq[this_dev]; dev->base_addr = io[this_dev]; dev->init = ne_probe; - if (io[this_dev] == 0) { - if (this_dev != 0) break; /* only complain once */ - printk(KERN_NOTICE "ne.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n"); - return -EPERM; - } - if (register_netdev(dev) != 0) { - printk(KERN_WARNING "ne.c: No NE*000 card found (i/o = 0x%x).\n", io[this_dev]); - if (found != 0) return 0; /* Got at least one. */ - return -ENXIO; + if (register_netdev(dev) == 0) { + found++; + continue; } - found++; + if (found != 0) /* Got at least one. */ + return 0; + if (io[this_dev] != 0) + printk(KERN_WARNING "ne.c: No NE*000 card found at i/o = %#x\n", io[this_dev]); + else + printk(KERN_NOTICE "ne.c: No PCI cards found. Use \"io=0xNNN\" value(s) for ISA cards.\n"); + return -ENXIO; } return 0; diff -u --recursive --new-file v2.0.30/linux/drivers/net/new_tunnel.c linux/drivers/net/new_tunnel.c --- v2.0.30/linux/drivers/net/new_tunnel.c Tue Apr 8 08:47:46 1997 +++ linux/drivers/net/new_tunnel.c Tue Aug 12 13:21:12 1997 @@ -161,7 +161,7 @@ * routing tables */ iph = (struct iphdr *) skb->data; - if ((rt = ip_rt_route(iph->daddr, 0)) == NULL) + if ((rt = ip_rt_route(iph->daddr, 0, skb->sk?skb->sk->bound_device:NULL)) == NULL) { /* No route to host */ /* Where did the packet come from? */ @@ -194,7 +194,7 @@ } ip_rt_put(rt); - if ((rt = ip_rt_route(target, 0)) == NULL) + if ((rt = ip_rt_route(target, 0, skb->sk?skb->sk->bound_device:NULL)) == NULL) { /* No route to host */ /* Where did the packet come from? */ @@ -267,6 +267,7 @@ /* Tack on our header */ new_skb->h.iph = (struct iphdr *) skb_push(new_skb, tunnel_hlen); + new_skb->mac.raw = new_skb->ip_hdr; /* Free the old packet, we no longer need it */ dev_kfree_skb(skb, FREE_WRITE); diff -u --recursive --new-file v2.0.30/linux/drivers/net/ppp.c linux/drivers/net/ppp.c --- v2.0.30/linux/drivers/net/ppp.c Tue Oct 8 11:21:03 1996 +++ linux/drivers/net/ppp.c Tue Aug 12 14:15:56 1997 @@ -6,7 +6,7 @@ * Dynamic PPP devices by Jim Freeman . * ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid * - * ==FILEVERSION 960528== + * ==FILEVERSION 970703== * * NOTE TO MAINTAINERS: * If you modify this file at all, please set the number above to the @@ -406,7 +406,14 @@ skb_queue_head_init (&dev->buffs[indx]); /* New-style flags */ +#ifdef IFF_SOFTHEADERS + /* Needed to make SOCK_PACKET work correctly in + * memory fussy kernels. + */ + dev->flags = IFF_POINTOPOINT|IFF_SOFTHEADERS; +#else dev->flags = IFF_POINTOPOINT; +#endif dev->family = AF_INET; dev->pa_addr = 0; dev->pa_brdaddr = 0; @@ -697,7 +704,7 @@ if (dev && dev->flags & IFF_UP) { dev_close (dev); /* close the device properly */ - dev->flags = 0; /* prevent recursion */ + dev->flags &= ~IFF_UP; /* prevent recursion */ } ppp_free_buf (ppp->rbuf); @@ -3079,7 +3086,7 @@ * Fetch the pointer to the data */ len = skb->len; - data = skb_data(skb); + data = skb_data(skb) + PPP_HARD_HDR_LEN; /* * Bug trap for null data. Release the skb and bail out. */ @@ -3152,7 +3159,12 @@ __u16 type, void *daddr, void *saddr, unsigned int len) { - return (0); + /* On the PPP device the hard header must be ignored + * by the SOCK_PACKET layer. (Backward compatability). + */ + skb->mac.raw = skb->data; + skb_push(skb,PPP_HARD_HDR_LEN); + return PPP_HARD_HDR_LEN; } static int diff -u --recursive --new-file v2.0.30/linux/drivers/net/smc-ultra32.c linux/drivers/net/smc-ultra32.c --- v2.0.30/linux/drivers/net/smc-ultra32.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/smc-ultra32.c Tue Aug 12 16:04:57 1997 @@ -0,0 +1,413 @@ +/* smc-ultra32.c: An SMC Ultra32 EISA ethernet driver for linux. + +Sources: + + This driver is based on (cloned from) the ISA SMC Ultra driver + written by Donald Becker. Modifications to support the EISA + version of the card by Paul Gortmaker and Leonard N. Zubkoff. + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + +Theory of Operation: + + The SMC Ultra32C card uses the SMC 83c790 chip which is also + found on the ISA SMC Ultra cards. It has a shared memory mode of + operation that makes it similar to the ISA version of the card. + The main difference is that the EISA card has 32KB of RAM, but + only an 8KB window into that memory. The EISA card also can be + set for a bus-mastering mode of operation via the ECU, but that + is not (and probably will never be) supported by this driver. + The ECU should be run to enable shared memory and to disable the + bus-mastering feature for use with linux. + + By programming the 8390 to use only 8KB RAM, the modifications + to the ISA driver can be limited to the probe and initialization + code. This allows easy integration of EISA support into the ISA + driver. However, the driver development kit from SMC provided the + register information for sliding the 8KB window, and hence the 8390 + is programmed to use the full 32KB RAM. + + Unfortunately this required code changes outside the probe/init + routines, and thus we decided to separate the EISA driver from + the ISA one. In this way, ISA users don't end up with a larger + driver due to the EISA code, and EISA users don't end up with a + larger driver due to the ISA EtherEZ PIO code. The driver is + similar to the 3c503/16 driver, in that the window must be set + back to the 1st 8KB of space for access to the two 8390 Tx slots. + + In testing, using only 8KB RAM (3 Tx / 5 Rx) didn't appear to + be a limiting factor, since the EISA bus could get packets off + the card fast enough, but having the use of lots of RAM as Rx + space is extra insurance if interrupt latencies become excessive. + +*/ + +static const char *version = "smc-ultra32.c: 06/97 v1.00\n"; + + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "8390.h" + +int ultra32_probe(struct device *dev); +int ultra32_probe1(struct device *dev, int ioaddr); +static int ultra32_open(struct device *dev); +static void ultra32_reset_8390(struct device *dev); +static void ultra32_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, + int ring_page); +static void ultra32_block_input(struct device *dev, int count, + struct sk_buff *skb, int ring_offset); +static void ultra32_block_output(struct device *dev, int count, + const unsigned char *buf, const start_page); +static int ultra32_close(struct device *dev); + +#define ULTRA32_CMDREG 0 /* Offset to ASIC command register. */ +#define ULTRA32_RESET 0x80 /* Board reset, in ULTRA32_CMDREG. */ +#define ULTRA32_MEMENB 0x40 /* Enable the shared memory. */ +#define ULTRA32_NIC_OFFSET 16 /* NIC register offset from the base_addr. */ +#define ULTRA32_IO_EXTENT 32 +#define EN0_ERWCNT 0x08 /* Early receive warning count. */ + +/* + * Defines that apply only to the Ultra32 EISA card. Note that + * "smc" = 10011 01101 00011 = 0x4da3, and hence !smc8010.cfg translates + * into an EISA ID of 0x1080A34D + */ +#define ULTRA32_BASE 0xca0 +#define ULTRA32_ID 0x1080a34d +#define ULTRA32_IDPORT (-0x20) /* 0xc80 */ +/* Config regs 1->7 from the EISA !SMC8010.CFG file. */ +#define ULTRA32_CFG1 0x04 /* 0xca4 */ +#define ULTRA32_CFG2 0x05 /* 0xca5 */ +#define ULTRA32_CFG3 (-0x18) /* 0xc88 */ +#define ULTRA32_CFG4 (-0x17) /* 0xc89 */ +#define ULTRA32_CFG5 (-0x16) /* 0xc8a */ +#define ULTRA32_CFG6 (-0x15) /* 0xc8b */ +#define ULTRA32_CFG7 0x0d /* 0xcad */ + + +/* Probe for the Ultra32. This looks like a 8013 with the station + address PROM at I/O ports +8 to +13, with a checksum + following. +*/ + +int ultra32_probe(struct device *dev) +{ + const char *ifmap[] = {"UTP No Link", "", "UTP/AUI", "UTP/BNC"}; + int ioaddr, edge, media; + + if (!EISA_bus) return ENODEV; + + /* EISA spec allows for up to 16 slots, but 8 is typical. */ + for (ioaddr = 0x1000 + ULTRA32_BASE; ioaddr < 0x9000; ioaddr += 0x1000) + if (check_region(ioaddr, ULTRA32_IO_EXTENT) == 0 && + inb(ioaddr + ULTRA32_IDPORT) != 0xff && + inl(ioaddr + ULTRA32_IDPORT) == ULTRA32_ID) { + media = inb(ioaddr + ULTRA32_CFG7) & 0x03; + edge = inb(ioaddr + ULTRA32_CFG5) & 0x08; + printk("SMC Ultra32 in EISA Slot %d, Media: %s, %s IRQs.\n", + ioaddr >> 12, ifmap[media], + (edge ? "Edge Triggered" : "Level Sensitive")); + if (ultra32_probe1(dev, ioaddr) == 0) + return 0; + } + return ENODEV; +} + +int ultra32_probe1(struct device *dev, int ioaddr) +{ + int i; + int checksum = 0; + const char *model_name; + static unsigned version_printed = 0; + /* Values from various config regs. */ + unsigned char idreg = inb(ioaddr + 7); + unsigned char reg4 = inb(ioaddr + 4) & 0x7f; + + /* Check the ID nibble. */ + if ((idreg & 0xf0) != 0x20) /* SMC Ultra */ + return ENODEV; + + /* Select the station address register set. */ + outb(reg4, ioaddr + 4); + + for (i = 0; i < 8; i++) + checksum += inb(ioaddr + 8 + i); + if ((checksum & 0xff) != 0xff) + return ENODEV; + + /* We should have a "dev" from Space.c or the static module table. */ + if (dev == NULL) { + printk("smc-ultra32.c: Passed a NULL device.\n"); + dev = init_etherdev(0, 0); + } + + if (ei_debug && version_printed++ == 0) + printk(version); + + model_name = "SMC Ultra32"; + + printk("%s: %s at 0x%X,", dev->name, model_name, ioaddr); + + for (i = 0; i < 6; i++) + printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i)); + + /* Switch from the station address to the alternate register set and + read the useful registers there. */ + outb(0x80 | reg4, ioaddr + 4); + + /* Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */ + outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c); + + /* Reset RAM addr. */ + outb(0x00, ioaddr + 0x0b); + + /* Switch back to the station address register set so that the + MS-DOS driver can find the card after a warm boot. */ + outb(reg4, ioaddr + 4); + + if ((inb(ioaddr + ULTRA32_CFG5) & 0x40) == 0) { + printk("\nsmc-ultra32: Card RAM is disabled! " + "Run EISA config utility.\n"); + return ENODEV; + } + if ((inb(ioaddr + ULTRA32_CFG2) & 0x04) == 0) + printk("\nsmc-ultra32: Ignoring Bus-Master enable bit. " + "Run EISA config utility.\n"); + + if (dev->irq < 2) { + unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15}; + int irq = irqmap[inb(ioaddr + ULTRA32_CFG5) & 0x07]; + if (irq == 0) { + printk(", failed to detect IRQ line.\n"); + return -EAGAIN; + } + dev->irq = irq; + } + + /* Allocate dev->priv and fill in 8390 specific dev fields. */ + if (ethdev_init(dev)) { + printk (", no memory for dev->priv.\n"); + return -ENOMEM; + } + + /* OK, we are certain this is going to work. Setup the device. */ + request_region(ioaddr, ULTRA32_IO_EXTENT, model_name); + + /* The 8390 isn't at the base address, so fake the offset */ + dev->base_addr = ioaddr + ULTRA32_NIC_OFFSET; + + /* Save RAM address in the unused reg0 to avoid excess inb's. */ + ei_status.reg0 = inb(ioaddr + ULTRA32_CFG3) & 0xfc; + + dev->mem_start = 0xc0000 + ((ei_status.reg0 & 0x7c) << 11); + + ei_status.name = model_name; + ei_status.word16 = 1; + ei_status.tx_start_page = 0; + ei_status.rx_start_page = TX_PAGES; + /* All Ultra32 cards have 32KB memory with an 8KB window. */ + ei_status.stop_page = 128; + + dev->rmem_start = dev->mem_start + TX_PAGES*256; + dev->mem_end = dev->rmem_end = dev->mem_start + 0x1fff; + + printk(", IRQ %d, 32KB memory, 8KB window at 0x%lx-0x%lx.\n", + dev->irq, dev->mem_start, dev->mem_end); + ei_status.block_input = &ultra32_block_input; + ei_status.block_output = &ultra32_block_output; + ei_status.get_8390_hdr = &ultra32_get_8390_hdr; + ei_status.reset_8390 = &ultra32_reset_8390; + dev->open = &ultra32_open; + dev->stop = &ultra32_close; + NS8390_init(dev, 0); + + return 0; +} + +static int ultra32_open(struct device *dev) +{ + int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC addr */ + + if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, NULL)) + return -EAGAIN; + + outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */ + outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */ + outb(0x84, ioaddr + 5); /* Enable MEM16 & Disable Bus Master. */ + outb(0x01, ioaddr + 6); /* Enable Interrupts. */ + /* Set the early receive warning level in window 0 high enough not + to receive ERW interrupts. */ + outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr); + outb(0xff, dev->base_addr + EN0_ERWCNT); + ei_open(dev); + MOD_INC_USE_COUNT; + return 0; +} + +static int ultra32_close(struct device *dev) +{ + int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* CMDREG */ + + dev->start = 0; + dev->tbusy = 1; + + if (ei_debug > 1) + printk("%s: Shutting down ethercard.\n", dev->name); + + outb(0x00, ioaddr + ULTRA32_CFG6); /* Disable Interrupts. */ + outb(0x00, ioaddr + 6); /* Disable interrupts. */ + free_irq(dev->irq, NULL); + irq2dev_map[dev->irq] = 0; + + NS8390_init(dev, 0); + + MOD_DEC_USE_COUNT; + + return 0; +} + +static void ultra32_reset_8390(struct device *dev) +{ + int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC base addr */ + + outb(ULTRA32_RESET, ioaddr); + if (ei_debug > 1) printk("resetting Ultra32, t=%ld...", jiffies); + ei_status.txing = 0; + + outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */ + outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */ + outb(0x84, ioaddr + 5); /* Enable MEM16 & Disable Bus Master. */ + outb(0x01, ioaddr + 6); /* Enable Interrupts. */ + if (ei_debug > 1) printk("reset done\n"); + return; +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but + we don't need to be concerned with ring wrap as the header will be at + the start of a page, so we optimize accordingly. */ + +static void ultra32_get_8390_hdr(struct device *dev, + struct e8390_pkt_hdr *hdr, + int ring_page) +{ + unsigned long hdr_start = dev->mem_start + ((ring_page & 0x1f) << 8); + unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3; + + /* Select correct 8KB Window. */ + outb(ei_status.reg0 | ((ring_page & 0x60) >> 5), RamReg); + +#ifdef notdef + /* Officially this is what we are doing, but the readl() is faster */ + memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); +#else + ((unsigned int*)hdr)[0] = readl(hdr_start); +#endif +} + +/* Block input and output are easy on shared memory ethercards, the only + complication is when the ring buffer wraps, or in this case, when a + packet spans an 8KB boundary. Note that the current 8KB segment is + already set by the get_8390_hdr routine. */ + +static void ultra32_block_input(struct device *dev, + int count, + struct sk_buff *skb, + int ring_offset) +{ + unsigned long xfer_start = dev->mem_start + (ring_offset & 0x1fff); + unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3; + + if ((ring_offset & ~0x1fff) != ((ring_offset + count - 1) & ~0x1fff)) { + int semi_count = 8192 - (ring_offset & 0x1FFF); + memcpy_fromio(skb->data, xfer_start, semi_count); + count -= semi_count; + if (ring_offset < 96*256) { + /* Select next 8KB Window. */ + ring_offset += semi_count; + outb(ei_status.reg0 | ((ring_offset & 0x6000) >> 13), RamReg); + memcpy_fromio(skb->data + semi_count, dev->mem_start, count); + } else { + /* Select first 8KB Window. */ + outb(ei_status.reg0, RamReg); + memcpy_fromio(skb->data + semi_count, dev->rmem_start, count); + } + } else { + /* Packet is in one chunk -- we can copy + cksum. */ + eth_io_copy_and_sum(skb, xfer_start, count, 0); + } +} + +static void ultra32_block_output(struct device *dev, + int count, + const unsigned char *buf, + int start_page) +{ + unsigned long xfer_start = dev->mem_start + (start_page<<8); + unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3; + + /* Select first 8KB Window. */ + outb(ei_status.reg0, RamReg); + + memcpy_toio(xfer_start, buf, count); +} + +#ifdef MODULE +#define MAX_ULTRA32_CARDS 4 /* Max number of Ultra cards per module */ +#define NAMELEN 8 /* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_ULTRA32_CARDS] = { 0, }; +static struct device dev_ultra[MAX_ULTRA32_CARDS] = { + { + NULL, /* assign a chunk of namelist[] below */ + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL + }, +}; + +int init_module(void) +{ + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) { + struct device *dev = &dev_ultra[this_dev]; + dev->name = namelist+(NAMELEN*this_dev); + dev->init = ultra32_probe; + if (register_netdev(dev) != 0) { + if (found > 0) return 0; /* Got at least one. */ + printk(KERN_WARNING "smc-ultra32.c: No SMC Ultra32 found.\n"); + return -ENXIO; + } + found++; + } + + return 0; +} + +void cleanup_module(void) +{ + int this_dev; + + for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) { + struct device *dev = &dev_ultra[this_dev]; + if (dev->priv != NULL) { + /* NB: ultra32_close_card() does free_irq + irq2dev */ + int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; + kfree(dev->priv); + dev->priv = NULL; + release_region(ioaddr, ULTRA32_IO_EXTENT); + unregister_netdev(dev); + } + } +} +#endif /* MODULE */ diff -u --recursive --new-file v2.0.30/linux/drivers/net/tulip.c linux/drivers/net/tulip.c --- v2.0.30/linux/drivers/net/tulip.c Tue Apr 8 08:47:46 1997 +++ linux/drivers/net/tulip.c Mon Aug 11 13:37:24 1997 @@ -1,7 +1,6 @@ -/* tulip.c: A DEC 21040 ethernet driver for linux. */ +/* tulip.c: A DEC 21040-family ethernet driver for linux. */ /* - NOTICE: this version works with kernels 1.1.82 and later only! - Written 1994,1995 by Donald Becker. + Written 1994-1997 by Donald Becker. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. @@ -13,44 +12,60 @@ Center of Excellence in Space Data and Information Sciences Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 - Subscribe to linux-tulip@cesdis.gsfc.nasa.gov and linux-tulip-bugs@cesdis.gsfc.nasa.gov - for late breaking news and exciting develovements. + Support and updates available at + http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html */ -static char *version = -"tulip.c:v0.10 8/11/95 becker@cesdis.gsfc.nasa.gov\n" -" +0.72 4/17/96 " -"http://www.dsl.tutics.tut.ac.jp/~linux/tulip\n" -" +0.02 12/15/96 mjacob@feral.com (2.0.27)\n"; +static const char *version = "tulip.c:v0.78 7/25/97 becker@cesdis.gsfc.nasa.gov\n"; /* A few user-configurable values. */ -/* Default to using 10baseT (i.e. AUI/10base2/100baseT port) port. */ -#define TULIP_10TP_PORT 0 -#define TULIP_100TP_PORT 1 -#define TULIP_AUI_PORT 1 -#define TULIP_BNC_PORT 2 -#define TULIP_MAX_PORT 3 -#define TULIP_AUTO_PORT -1 +/* Set if the PCI BIOS detects the chips on a multiport board backwards. */ +#ifdef REVERSE_PROBE_ORDER +static int reverse_probe = 1; +#else +static int reverse_probe = 0; +#endif -#ifndef TULIP_PORT -#define TULIP_PORT TULIP_10TP_PORT +/* Keep the ring sizes a power of two for efficiency. + Making the Tx ring too large decreases the effectiveness of channel + bonding and packet priority. + There are no ill effects from too-large receive rings. */ +#define TX_RING_SIZE 16 +#define RX_RING_SIZE 16 + +/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ +#define SKBUFF_RX_COPYBREAK 200 + +/* The following example shows how to always use the 10base2 port. */ +#ifdef notdef +#define TULIP_DEFAULT_MEDIA 1 /* 1 == 10base2 */ +#define TULIP_NO_MEDIA_SWITCH /* Don't switch from this port */ #endif /* Define to force full-duplex operation on all Tulip interfaces. */ /* #define TULIP_FULL_DUPLEX 1 */ -/* Define to fix port. */ -/* #define TULIP_FIX_PORT 1 */ - -/* Define to probe only first detected device */ -/*#define TULIP_MAX_CARDS 1*/ +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT ((2000*HZ)/1000) +#include +#ifdef MODULE +#ifdef MODVERSIONS +#include +#endif #include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif #include #include #include +#include #include #include #include @@ -58,8 +73,7 @@ #include #include #include -#include -#include +#include /* Processor type for cache alignment. */ #include #include #include @@ -68,35 +82,116 @@ #include #include +/* Kernel compatibility defines, common to David Hind's PCMCIA package. + This is only in the support-all-kernels source code. */ +#include /* Evil, but neccessary */ + +#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300 +#define RUN_AT(x) (x) /* What to put in timer->expires. */ +#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC) +#define virt_to_bus(addr) ((unsigned long)addr) +#define bus_to_virt(addr) ((void*)addr) + +#else /* 1.3.0 and later */ +#define RUN_AT(x) (jiffies + (x)) +#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2) +#endif + +#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338 +#ifdef MODULE +#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__) +char kernel_version[] = UTS_RELEASE; +#endif +#else +#undef MOD_INC_USE_COUNT +#define MOD_INC_USE_COUNT +#undef MOD_DEC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif +#endif /* 1.3.38 */ + +#if (LINUX_VERSION_CODE >= 0x10344) +#define NEW_MULTICAST +#include +#endif +#if (LINUX_VERSION_CODE >= 0x20100) +char kernel_version[] = UTS_RELEASE; +#endif +#ifdef SA_SHIRQ +#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs) +#else +#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs) +#endif + +#if (LINUX_VERSION_CODE < 0x20123) +#define test_and_set_bit(val, addr) set_bit(val, addr) +#endif + +/* This my implementation of shared IRQs, now only used for 1.2.13. */ +#ifdef HAVE_SHARED_IRQ +#define USE_SHARED_IRQ +#include +#endif + /* The total size is unusually large: The 21040 aligns each of its 16 longword-wide registers on a quadword boundary. */ #define TULIP_TOTAL_SIZE 0x80 +#ifdef HAVE_DEVLIST +struct netdev_entry tulip_drv = +{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL}; +#endif + +#ifdef TULIP_DEBUG +int tulip_debug = TULIP_DEBUG; +#else +int tulip_debug = 1; +#endif + /* Theory of Operation I. Board Compatibility -This device driver is designed for the DECchip 21040 "Tulip", Digital's -single-chip ethernet controller for PCI, as used on the SMC EtherPower -ethernet adapter. It also works with boards based the 21041 (new/experimental) -and 21140 (10/100mbps). +This device driver is designed for the DECchip "Tulip", Digital's +single-chip ethernet controllers for PCI. Supported members of the family +are the 21040, 21041, 21140, 21140A and 21142. These chips are used on +many PCI boards including the SMC EtherPower series. II. Board-specific settings PCI bus devices are configured by the system at boot time, so no jumpers -need to be set on the board. The system BIOS should be set to assign the -PCI INTA signal to an otherwise unused system IRQ line. While it's -physically possible to shared PCI interrupt lines, the kernel doesn't -support it. +need to be set on the board. The system BIOS preferably should assign the +PCI INTA signal to an otherwise unused system IRQ line. +Note: Kernel versions earlier than 1.3.73 do not support shared PCI +interrupt lines. III. Driver operation IIIa. Ring buffers + The Tulip can use either ring buffers or lists of Tx and Rx descriptors. -The current driver uses a statically allocated Rx ring of descriptors and -buffers, and a list of the Tx buffers. +This driver uses statically allocated rings of Rx and Tx descriptors, set at +compile time by RX/TX_RING_SIZE. This version of the driver allocates skbuffs +for the Rx ring buffers at open() time and passes the skb->data field to the +Tulip as receive data buffers. When an incoming frame is less than +SKBUFF_RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is +copied to the new skbuff. When the incoming frame is larger, the skbuff is +passed directly up the protocol stack and replaced by a newly allocated +skbuff. + +The SKBUFF_RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames. For small frames the copying cost is negligible (esp. considering +that we are pre-loading the cache with immediately useful header +information). For large frames the copying cost is non-trivial, and the +larger copy might flush the cache of useful data. A subtle aspect of this +choice is that the Tulip only receives into longword aligned buffers, thus +the IP header at offset 14 isn't longword aligned for further processing. +Copied frames are put into the new skbuff at an offset of "+2", thus copying +has the beneficial effect of aligning the IP header and preloading the +cache. IIIC. Synchronization The driver runs as two independent, single-threaded flows of control. One @@ -120,592 +215,807 @@ Thanks to Duke Kamstra of SMC for providing an EtherPower board. +IVb. References + +http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html +http://www.digital.com (search for current 21*4* datasheets and "21X4 SROM") +http://www.national.com/pf/DP/DP83840.html + +IVc. Errata + The DEC databook doesn't document which Rx filter settings accept broadcast packets. Nor does it document how to configure the part to configure the serial subsystem for normal (vs. loopback) operation or how to have it autoswitch between internal 10baseT, SIA and AUI transceivers. -The databook claims that CSR13, CSR14, and CSR15 should each be the last -register of the set CSR12-15 written. Hmmm, now how is that possible? -*/ +The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last +register of the set CSR12-15 written. Hmmm, now how is that possible? */ + /* A few values that may be tweaked. */ -/* Keep the ring sizes a power of two for efficiency. */ -#define TX_RING_SIZE 4 -#define RX_RING_SIZE 4 #define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ -/* This is a mysterious value that can be written to CSR11 in the 21040 - to detect a full-duplex frame. No one knows what it should be, but if - left at its default value some 10base2(!) packets trigger a - full-duplex-request interrupt. */ +/* This is a mysterious value that can be written to CSR11 in the 21040 (only) + to support a pre-NWay full-duplex signaling mechanism using short frames. + No one knows what it should be, but if left at its default value some + 10base2(!) packets trigger a full-duplex-request interrupt. */ #define FULL_DUPLEX_MAGIC 0x6969 +#ifndef PCI_VENDOR_ID_DEC /* Now defined in linux/pci.h */ +#define PCI_VENDOR_ID_DEC 0x1011 +#define PCI_DEVICE_ID_TULIP 0x0002 /* 21040. */ +#define PCI_DEVICE_ID_TULIP_FAST 0x0009 /* 21140. */ +#endif + +#ifndef PCI_DEVICE_ID_DEC_TULIP_PLUS +#define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014 /* 21041. */ +#endif + +#ifndef PCI_DEVICE_ID_DEC_TULIP_21142 +#define PCI_DEVICE_ID_DEC_TULIP_21142 0x0019 +#endif + /* The rest of these values should never change. */ -#define PCI_DEVICE_ID_NONE 0xFFFF -#define ETHNAMSIZ 8 -#define ROUND_UP(size, n) ((size + n - 1) & ~(n - 1)) +static void tulip_timer(unsigned long data); + +/* A table describing the chip types. */ +static struct tulip_chip_table { + int device_id; + char *chip_name; + int flags; + void (*media_timer)(unsigned long data); +} tulip_tbl[] = { + { PCI_DEVICE_ID_DEC_TULIP, "DS21040 Tulip", 0, tulip_timer }, + { PCI_DEVICE_ID_DEC_TULIP_PLUS, "DS21041 Tulip", 0, tulip_timer }, + { PCI_DEVICE_ID_DEC_TULIP_FAST, "DS21140 Tulip", 0, tulip_timer }, /* + 21140A*/ + { PCI_DEVICE_ID_DEC_TULIP_21142, "DS21142 Tulip", 0, tulip_timer }, /* + 21143 */ + {0, 0, 0, 0}, +}; +/* This matches the table above. */ +enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, }; + +static const char * const medianame[] = { + "10baseT", "10base2", "AUI", "100baseTx", + "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx", + "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII", + "", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4", +}; +/* A full-duplex map for above. */ +static const char media_fd[] = +{0,0,0,0, 0xff,0xff,0,0, 0xff,0,0xff,0x01, 0,0,0xff,0 }; +/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/ +static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; +static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, }; +static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; + +static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0001, 0x0001, }; +static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x7F3F, 0x7F3D, }; +static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; /* Offsets to the Command and Status Registers, "CSRs". All accesses must be longword instructions and quadword aligned. */ enum tulip_offsets { - /* 21040 21041 21140 */ - CSR0=0, /* BUS mode */ - CSR1=0x08, /* TX poll demand */ - CSR2=0x10, /* RX poll demand */ - CSR3=0x18, /* RX ring base addr */ - CSR4=0x20, /* TX ring base addr */ - CSR5=0x28, /* Status */ - CSR6=0x30, /* Command mode */ - CSR7=0x38, /* Interrupt Mask */ - CSR8=0x40, /* Missed frame counter */ - CSR9=0x48, /* Eth.addrROM SROM mii SROM mii */ - CSR10=0x50, /* Diagn. boot ROM - */ - CSR11=0x58, /* Full duplex G.P. timer G.P. timer */ - CSR12=0x60, /* SIA status G.P. */ - CSR13=0x68, /* SIA connectivity - */ - CSR14=0x70, /* SIA TX/RX - */ - CSR15=0x78 /* SIA general watchdog */ -}; + CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28, + CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58, + CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 }; -/* description of CSR0 bus mode register */ -#define TBMOD_RESERVED 0xfff80000 /* I don't know */ -#define TBMOD_RESET 0x00000001 -#define TBMOD_BIGENDIAN 0x00000080 -/* - Cache alignment bits 15:14 Burst length 13:8 - 0000 No alignment 0x00000000 unlimited 0800 8 longwords - 4000 8 longwords 0100 1 longword 1000 16 longwords - 8000 16 longwords 0200 2 longwords 2000 32 longwords - C000 32 longwords 0400 4 longwords -*/ -#define TBMOD_ALIGN0 0x00000000 /* no cache alignment */ -#define TBMOD_ALIGN8 0x00004000 /* 8 longwords */ -#define TBMOD_ALIGN16 0x00008000 -#define TBMOD_ALIGN32 (TBMOD_ALIGN8|TBMOD_ALIGN16) -#define TBMOD_BURST0 0x00000000 /* unlimited=rx buffer size */ -#define TBMOD_BURST1 0x00000100 /* 1 longwords */ -#define TBMOD_BURST2 0x00000200 -#define TBMOD_BURST4 0x00000400 -#define TBMOD_BURST8 0x00000800 -#define TBMOD_BURST16 0x00001000 -#define TBMOD_BURST32 0x00002000 - -/* description of CSR1 Tx poll demand register */ -/* description of CSR2 Rx poll demand register */ -#define TPOLL_START 0x00000001 /* ? */ -#define TPOLL_TRIGGER 0x00000000 /* ? */ - -/* description of CSR5 status register from de4x5.h */ -#define TSTAT_BUSERROR 0x03800000 -#define TSTAT_SYSERROR 0x00002000 -#define TSTAT_TxSTAT 0x00700000 -#define TSTAT_RxSTAT 0x000e0000 -#define TSTAT_LKFAIL 0x00001000 -#define TSTAT_NORINTR 0x00010000 /* Normal interrupt */ -#define TSTAT_ABNINTR 0x00008000 /* Abnormal interrupt */ -#define TSTAT_RxMISSED 0x00000100 /* Rx frame missed */ -#define TSTAT_RxUNABL 0x00000080 -#define TSTAT_RxINTR 0x00000040 -#define TSTAT_LKPASS 0x00000010 -#define TSTAT_TEXPIRED 0x00000800 /* Timer Expired */ -#define TSTAT_TxTOUT 0x00000008 -#define TSTAT_TxUNABL 0x00000004 -#define TSTAT_TxINTR 0x00000001 -#define TSTAT_CLEARINTR 0x0001ffff /* clear all interrupt sources */ - -/* description of CSR6 command mode register */ -#define TCMOD_SCRM 0x01000000 /* scrambler mode */ -#define TCMOD_PCS 0x00800000 /* PCS function */ -#define TCMOD_TxTHMODE 0x00400000 /* Tx threshold mode */ -#define TCMOD_SW100TP 0x00040000 /* 21140: 100MB */ -#define TCMOD_CAPTURE 0x00020000 /* capture effect */ -#define TCMOD_FULLDUPLEX 0x00000200 -#define TCMOD_TH128 0x00008000 /* 10 - 128 bytes threshold */ -#define TCMOD_TxSTART 0x00002000 -#define TCMOD_RxSTART 0x00000002 -#define TCMOD_ALLMCAST 0x00000080 /* pass all multicast */ -#define TCMOD_PROMISC 0x00000040 /* promisc */ -#define TCMOD_BOFFCOUNTER 0x00000020 /* backoff counter */ -#define TCMOD_INVFILTER 0x00000010 /* invert filtering */ -#define TCMOD_HONLYFILTER 0x00000004 /* hash only filtering */ -#define TCMOD_HPFILTER 0x00000001 /* hash/perfect Rx filtering */ -#define TCMOD_MODEMASK (TCMOD_ALLMCAST|TCMOD_PROMISC) -#define TCMOD_FILTERMASK (TCMOD_HONLYFILTER|TCMOD_HPFILTER|TCMOD_INVFILTER) -#define TCMOD_TRxSTART (TCMOD_TxSTART|TCMOD_RxSTART) -#define TCMOD_BASE (TCMOD_CAPTURE|TCMOD_BOFFCOUNTER) -#define TCMOD_10TP (TCMOD_TxTHMODE|TCMOD_BASE) -#define TCMOD_100TP (TCMOD_SCRM|TCMOD_PCS|TCMOD_SW100TP|TCMOD_BASE) -#define TCMOD_AUTO (TCMOD_SW100TP|TCMOD_TH128|TCMOD_10TP) - -/* description of CSR7 interrupt mask register */ -#define TINTR_ENABLE 0xFFFFFFFF -#define TINTR_DISABLE 0x00000000 - -/* description of CSR11 G.P. timer (21041/21140) register */ -#define TGEPT_COUNT 0x0001FFFF - -/* description of CSR12 SIA status(2104x)/GP(21140) register */ -#define TSIAS_CONERROR 0x00000002 /* connection error */ -#define TSIAS_LNKERROR 0x00000004 /* link error */ -#define TSIAS_ACTERROR 0x00000200 /* port Rx activity */ -#define TSIAS_RxACTIVE 0x00000100 /* port Rx activity */ - -#define TGEPR_LK10NG 0x00000080 /* 10Mbps N.G. (R) */ -#define TGEPR_LK100NG 0x00000040 /* 100Mbps N.G. (R) */ -#define TGEPR_DETECT 0x00000020 /* detect signal (R) */ -#define TGEPR_HALFDUPLEX 0x00000008 /* half duplex (W) */ -#define TGEPR_PHYLOOPBACK 0x00000004 /* PHY loopback (W) */ -#define TGEPR_FORCEALED 0x00000002 /* force activity LED on (W) */ -#define TGEPR_FORCE100 0x00000001 /* force 100Mbps mode */ - -/* description of CSR13 SIA connectivity register */ -#define TSIAC_OUTEN 0x0000e000 /* 21041: Output enable */ -#define TSIAC_SELED 0x00000f00 /* 21041: AUI or TP with LEDs */ -#define TSIAC_INEN 0x00001000 /* 21041: Input enable */ -#define TSIAC_NO10TP 0x00000008 /* 10baseT(0) or not(1) */ -#define TSIAC_CONFIG 0x00000004 /* Configuration */ -#define TSIAC_SWRESET 0x00000001 /* 21041: software reset */ -#define TSIAC_RESET 0x00000000 /* reset */ -#define TSIAC_C21041 (TSIAC_OUTEN|TSIAC_SELED|TSIAC_SWRESET) -#define TSIAC_C21040 TSIAC_CONFIG - -/* description of CSR14 SIA TX/RX register */ -#define TSIAX_NO10TP 0x0000f73d -#define TSIAX_10TP 0x0000ff3f - -/* description of CSR15 SIA general register */ -#define TSIAG_SWBNCAUI 0x00000008 /* BNC(0) or AUI(1) */ -#define TSIAG_BNC 0x00000006 -#define TSIAG_AUI (TSIAG_BNC|TSIAG_SWBNCAUI) -#define TSIAG_10TP 0x00000000 - -/* description of rx_ring.status */ -#define TRING_OWN 0x80000000 /* Owned by chip */ -#define TRING_CLEAR 0x00000000 /* clear */ -#define TRING_ERROR 0x00008000 /* error summary */ -#define TRING_ETxTO 0x00004000 /* Tx time out */ -#define TRING_ELCOLL 0x00000200 /* late collision */ -#define TRING_EFCOLL 0x00000100 /* fatal collision */ -#define TRING_ELCARR 0x00000800 /* carrier lost */ -#define TRING_ENCARR 0x00000400 /* no carrier */ -#define TRING_ENOHB 0x00000080 /* heartbeat fail */ -#define TRING_ELINK 0x00000004 /* link fail */ -#define TRING_EUFLOW 0x00000002 /* underflow */ - -#define TRING_ELEN 0x00004000 /* length error */ -#define TRING_FDESC 0x00000200 /* first descriptor */ -#define TRING_LDESC 0x00000100 /* last descriptor */ -#define TRING_ERUNT 0x00000800 /* runt frame */ -#define TRING_ELONG 0x00000080 /* frame too long */ -#define TRING_EWATCHDOG 0x00000010 /* receive watchdog */ -#define TRING_EDRBIT 0x00000004 /* dribble bit */ -#define TRING_ECRC 0x00000002 /* CRC error */ -#define TRING_EOVERFLOW 0x00000001 /* overflow */ - -#define TRING_RxDESCMASK (TRING_FDESC|TRING_LDESC) -#define TRING_RxLENGTH (TRING_ERUNT|TRING_ELONG|TRING_EWATCHDOG) -#define TRING_RxFRAME (TRING_EDRBIT) -#define TRING_RxCRC (TRING_ECRC) -#define TRING_RxFIFO (TRING_EOVERFLOW) -#define TRING_TxABORT (TRING_ETxTO|TRING_EFCOLL|TRING_ELINK) -#define TRING_TxCARR (TRING_ELCARR|TRING_ENCARR) -#define TRING_TxWINDOW (TRING_ELCOLL) -#define TRING_TxFIFO (TRING_EUFLOW) -#define TRING_TxHEARTBEAT (TRING_ENOHB) /* The Tulip Rx and Tx buffer descriptors. */ struct tulip_rx_desc { s32 status; s32 length; - u32 buffer1, buffer2; /* We use only buffer 1. */ + u32 buffer1, buffer2; }; struct tulip_tx_desc { s32 status; s32 length; - u32 buffer1, buffer2; /* We use only buffer 1. */ + u32 buffer1, buffer2; /* We use only buffer 1. */ +}; + +struct medialeaf { + u8 type; + u8 media; + unsigned char *leafdata; +}; + +struct mediatable { + u16 defaultmedia; + u8 leafcount, csr12dir; /* General purpose pin directions. */ + unsigned has_mii:1; + struct medialeaf mleaf[0]; +}; + +struct mediainfo { + struct mediainfo *next; + int info_type; + int index; + struct non_mii { char media; unsigned char csr12val; char bitnum, flags;} non_mii; + unsigned char *info; }; struct tulip_private { + char devname[8]; /* Used only for kernel debugging. */ + const char *product_name; + struct device *next_module; struct tulip_rx_desc rx_ring[RX_RING_SIZE]; struct tulip_tx_desc tx_ring[TX_RING_SIZE]; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ struct sk_buff* tx_skbuff[TX_RING_SIZE]; - char rx_buffs[RX_RING_SIZE][PKT_BUF_SZ]; - /* temporary Rx buffers. */ + /* The addresses of receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + char *rx_buffs; /* Address of temporary Rx buffers. */ + int setup_frame[48]; /* Pseudo-Tx frame to init address table. */ + int chip_id; + int revision; struct enet_statistics stats; - int setup_frame[48]; /* Pseudo-Tx frame to init address table. */ - void (*port_select)(struct device *dev); - int (*port_fail)(struct device *dev); - struct device *next_module; - char *signature; + struct timer_list timer; /* Media selection timer. */ unsigned int cur_rx, cur_tx; /* The next free ring entry */ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ unsigned int tx_full:1; /* The Tx queue is full. */ unsigned int full_duplex:1; /* Full-duplex operation requested. */ - unsigned int port_fix:1; /* Fix if_port to specified port. */ + unsigned int default_port:4; /* Last dev->if_port value. */ + unsigned int media2:4; /* Secondary monitored media port. */ + unsigned int medialock:1; /* Don't sense media type. */ + unsigned int mediasense:1; /* Media sensing in progress. */ + unsigned int csr6; /* Current CSR6 control settings. */ + unsigned char eeprom[128]; /* Serial EEPROM contents. */ + struct mediatable *mtable; + int cur_index; /* Current media index. */ + int pad0, pad1; /* Used for 8-byte alignment */ }; -struct eeprom { - union { - struct { /* broken EEPROM structure */ - u_char addr[ETH_ALEN]; - } ng; - struct { /* DEC EtherWorks - and other cards which have correct eeprom structure */ - u_char dum1[20]; - u_char addr[ETH_ALEN]; - } ok; - } hw; -#define ng_addr hw.ng.addr -#define ok_addr hw.ok.addr -#define EE_SIGNLEN 14 /* should be 102 ? */ - u_char sign[EE_SIGNLEN]; -}; +#ifdef MODULE +/* Used to pass the full-duplex flag, etc. */ +static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1}; +#endif -static int read_eeprom(int ioaddr, struct eeprom *eepp); +static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq, + int chip_id, int options); +static void parse_eeprom(struct device *dev); +static int read_eeprom(int ioaddr, int location); +static int mdio_read(int ioaddr, int phy_id, int location); +static void select_media(struct device *dev, int startup); static int tulip_open(struct device *dev); +static void tulip_timer(unsigned long data); +static void tulip_tx_timeout(struct device *dev); static void tulip_init_ring(struct device *dev); static int tulip_start_xmit(struct sk_buff *skb, struct device *dev); static int tulip_rx(struct device *dev); -static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs); static int tulip_close(struct device *dev); static struct enet_statistics *tulip_get_stats(struct device *dev); +#ifdef NEW_MULTICAST static void set_multicast_list(struct device *dev); +#else +static void set_multicast_list(struct device *dev, int num_addrs, void *addrs); +#endif -#define generic21140_fail NULL -static void generic21040_select(struct device *dev); -static void generic21140_select(struct device *dev); -static void generic21041_select(struct device *dev); -static void auto21140_select(struct device *dev); -static void cogent21140_select(struct device *dev); -static int generic21040_fail(struct device *dev); -static int generic21041_fail(struct device *dev); + #ifdef MODULE /* A list of all installed Tulip devices, for removing the driver module. */ static struct device *root_tulip_dev = NULL; #endif -static struct { - void (*port_select)(struct device *dev); - int (*port_fail)(struct device *dev); - unsigned int vendor_id, device_id; - char *signature; - unsigned int array:1; -} cardVendor[] = { - {generic21140_select, generic21140_fail, - 0x0000c000, PCI_DEVICE_ID_DEC_TULIP_FAST, "smc9332", 0}, - {generic21041_select, generic21041_fail, - 0x0000c000, PCI_DEVICE_ID_DEC_TULIP_PLUS, "smc8432", 0}, - {generic21040_select, generic21040_fail, - 0x0000c000, PCI_DEVICE_ID_DEC_TULIP, "old smc8432", 0}, - {auto21140_select, generic21140_fail, - 0x0000f400, PCI_DEVICE_ID_DEC_TULIP_FAST, "LA100PCI", 0}, - {cogent21140_select, generic21140_fail, - 0x00009200, PCI_DEVICE_ID_DEC_TULIP_FAST, "cogent_em110", 0}, - {generic21040_select, generic21040_fail, - 0x00009200, PCI_DEVICE_ID_DEC_TULIP, "cogent_em96x", 1}, - {generic21140_select, generic21140_fail, - 0x0000f800, PCI_DEVICE_ID_DEC_TULIP_FAST, "DE500", 0}, - {generic21041_select, generic21041_fail, - 0x0000f800, PCI_DEVICE_ID_DEC_TULIP_PLUS, "DE450", 0}, - {generic21040_select, generic21040_fail, - 0x0000f800, PCI_DEVICE_ID_DEC_TULIP, "DE43x", 0}, - {generic21040_select, generic21040_fail, - 0x0040c700, PCI_DEVICE_ID_DEC_TULIP, "EN9400", 0}, - {generic21040_select, generic21040_fail, - 0x00c09500, PCI_DEVICE_ID_DEC_TULIP, "ZNYX312", 1}, - {generic21040_select, generic21040_fail, - 0x08002b00, PCI_DEVICE_ID_DEC_TULIP, "QSILVER's", 0}, - {generic21040_select, generic21040_fail, - 0, PCI_DEVICE_ID_DEC_TULIP, "21040", 0}, - {generic21140_select, generic21140_fail, - 0, PCI_DEVICE_ID_DEC_TULIP_FAST, "21140", 0}, - {generic21041_select, generic21041_fail, - 0, PCI_DEVICE_ID_DEC_TULIP_PLUS, "21041", 0}, - {NULL, NULL, 0, 0, "Unknown", 0} -}; +/* This 21040 probe no longer uses a large fixed contiguous Rx buffer region, + but now receives directly into full-sized skbuffs that are allocated + at open() time. + This allows the probe routine to use the old driver initialization + interface. */ - -/* Serial EEPROM section. - A "bit" grungy, but we work our way through bit-by-bit :->. */ -/* EEPROM_Ctrl bits. */ -#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */ -#define EE_CS 0x01 /* EEPROM chip select. */ -#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ -#define EE_WRITE_0 0x01 -#define EE_WRITE_1 0x05 -#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ -#define EE_ENB (0x4800 | EE_CS) +int tulip_probe(struct device *dev) +{ + int cards_found = 0; + static int pci_index = 0; /* Static, for multiple probe calls. */ -/* The EEPROM commands include the alway-set leading bit. */ -#define EE_WRITE_CMD (5 << 6) -#define EE_READ_CMD (6 << 6) -#define EE_ERASE_CMD (7 << 6) + /* Ideally we would detect all network cards in slot order. That would + be best done a central PCI probe dispatch, which wouldn't work + well with the current structure. So instead we detect just the + Tulip cards in slot order. */ + + if (pcibios_present()) { + unsigned char pci_bus, pci_device_fn; + + for (;pci_index < 0xff; pci_index++) { + unsigned char pci_irq_line, pci_latency; + unsigned short pci_command, vendor, device; + unsigned int pci_ioaddr, chip_idx = 0; + + if (pcibios_find_class + (PCI_CLASS_NETWORK_ETHERNET << 8, + reverse_probe ? 0xfe - pci_index : pci_index, + &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) + if (reverse_probe) + continue; + else + break; + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_DEVICE_ID, &device); + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + /* Remove I/O space marker in bit 0. */ + pci_ioaddr &= ~3; + + if (vendor != PCI_VENDOR_ID_DEC) + continue; + + for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++) + if (device == tulip_tbl[chip_idx].device_id) + break; + if (tulip_tbl[chip_idx].chip_name == 0) { + printk("Unknown Digital PCI ethernet chip type %4.4x detected:" + " not configured.\n", device); + continue; + } + if (tulip_debug > 2) + printk("Found DEC PCI Tulip at I/O %#x, IRQ %d.\n", + pci_ioaddr, pci_irq_line); + + if (check_region(pci_ioaddr, TULIP_TOTAL_SIZE)) + continue; #ifdef MODULE -static int if_port=TULIP_AUTO_PORT; -#ifdef TULIP_FULL_DUPLEX -static int full_duplex=1; + dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx, + options[cards_found]); #else -static int full_duplex=0; -#endif + dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx, + dev ? dev->mem_start : 0); #endif -#define tio_write(val, port) outl(val, ioaddr + port) -#define tio_read(port) inl(ioaddr + port) + if (dev) { + /* Get and check the bus-master and latency values. */ + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + if ( ! (pci_command & PCI_COMMAND_MASTER)) { + printk(" PCI Master Bit has not been set! Setting...\n"); + pci_command |= PCI_COMMAND_MASTER; + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, pci_command); + } + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < 10) { + printk(" PCI latency timer (CFLT) is unreasonably low at %d." + " Setting to 64 clocks.\n", pci_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, 64); + } else if (tulip_debug > 1) + printk(" PCI latency timer (CFLT) is %#x.\n", pci_latency); + /* Bring the 21143 out power-down mode. */ + if (device == PCI_DEVICE_ID_DEC_TULIP_21142) + pcibios_write_config_dword(pci_bus, pci_device_fn, + 0x40, 0x40000000); + dev = 0; + cards_found++; + } + } + } -static void inline -tio_sia_write(u32 ioaddr, u32 val13, u32 val14, u32 val15) -{ - tio_write(0,CSR13); - tio_write(val15,CSR15); - tio_write(val14,CSR14); - tio_write(val13,CSR13); +#if defined (MODULE) + return cards_found; +#else + return cards_found > 0 ? 0 : -ENODEV; +#endif } -/* - card_type returns 1 if the card is 'etherarray' -*/ - -static int -card_type(struct tulip_private *tp, int device_id, int vendor_id) +static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq, + int chip_id, int options) { - int n; + static int did_version = 0; /* Already printed version info. */ + struct tulip_private *tp; + /* See note below on the multiport cards. */ + static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'}; + static int last_irq = 0; + int i; + unsigned short sum; - for (n = 0; cardVendor[n].device_id; n ++) - if (cardVendor[n].device_id == device_id - && (cardVendor[n].vendor_id == vendor_id - || cardVendor[n].vendor_id == 0)) break; - tp->port_select = cardVendor[n].port_select; - tp->port_fail = cardVendor[n].port_fail; - tp->signature = cardVendor[n].signature; - return(cardVendor[n].array ? 1: 0); -} + if (tulip_debug > 0 && did_version++ == 0) + printk(version); -static int -read_eeprom(int ioaddr, struct eeprom *eepp) -{ - int i, n; - unsigned short val = 0; - int read_cmd = EE_READ_CMD; - u_char *p=(u_char *)eepp; - - for (n = 0; n < sizeof(struct eeprom) / 2; n ++, read_cmd ++) { - tio_write(EE_ENB & ~EE_CS, CSR9); - tio_write(EE_ENB, CSR9); - - /* Shift the read command bits out. */ - for (i = 10; i >= 0; i--) { - short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; - tio_write(EE_ENB | dataval, CSR9); - udelay(100); - tio_write(EE_ENB | dataval | EE_SHIFT_CLK, CSR9); - udelay(150); - tio_write(EE_ENB | dataval, CSR9); - udelay(250); - } - tio_write(EE_ENB, CSR9); - - for (i = 16; i > 0; i--) { - tio_write(EE_ENB | EE_SHIFT_CLK, CSR9); - udelay(100); - val = (val << 1) - | ((tio_read(CSR9) & EE_DATA_READ) ? 1 : 0); - tio_write(EE_ENB, CSR9); - udelay(100); - } - - /* Terminate the EEPROM access. */ - tio_write(EE_ENB & ~EE_CS, CSR9); - *p ++ = val; - *p ++ = val >> 8; - } - /* broken eeprom ? */ - p = (u_char *)eepp; - for (i = 0; i < 8; i ++) - if (p[i] != p[15 - i] || p[i] != p[16 + i]) return(0); - return(-1); /* broken */ -} + dev = init_etherdev(dev, 0); -/* Is this required ? */ -static int -generic21040_fail(struct device *dev) -{ - int ioaddr = dev->base_addr; + printk("%s: DEC %s at %#3x,", + dev->name, tulip_tbl[chip_id].chip_name, ioaddr); - return(tio_read(CSR12) & TSIAS_CONERROR); -} + /* Stop the chip's Tx and Rx processes. */ + outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); + /* Clear the missed-packet counter. */ + (volatile)inl(ioaddr + CSR8); -static int -generic21041_fail(struct device *dev) -{ - int ioaddr = dev->base_addr; - u32 csr12 = tio_read(CSR12); + if (chip_id == DC21041) { + if (inl(ioaddr + CSR9) & 0x8000) { + printk(" 21040 compatible mode,"); + chip_id = DC21040; + } else { + printk(" 21041 mode,"); + } + } - return((!(csr12 & TSIAS_CONERROR) - || !(csr12 & TSIAS_LNKERROR)) ? 0: 1); -} + /* The station address ROM is read byte serially. The register must + be polled, waiting for the value to be read bit serially from the + EEPROM. + */ + sum = 0; + if (chip_id == DC21040) { + outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */ + for (i = 0; i < 6; i++) { + int value, boguscnt = 100000; + do + value = inl(ioaddr + CSR9); + while (value < 0 && --boguscnt > 0); + dev->dev_addr[i] = value; + sum += value & 0xff; + } + } else { /* Must be a new chip, with a serial EEPROM interface. */ + /* We read the whole EEPROM, and sort it out later. DEC has a + specification _Digital Semiconductor 21X4 Serial ROM Format_ + but early vendor boards just put the address in the first six + EEPROM locations. */ + unsigned char ee_data[128]; + int sa_offset = 0; + + for (i = 0; i < sizeof(ee_data)/2; i++) + ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i); + + /* Detect the simple EEPROM format by the duplicated station addr. */ + for (i = 0; i < 8; i ++) + if (ee_data[i] != ee_data[16+i]) + sa_offset = 20; + for (i = 0; i < 6; i ++) { + dev->dev_addr[i] = ee_data[i + sa_offset]; + sum += ee_data[i + sa_offset]; + } + } + /* On the Zynx 315 Etherarray and other multiport boards only the + first Tulip has an EEPROM. + The addresses of the subsequent ports are derived from the first. + Many PCI BIOSes also incorrectly report the IRQ line, so we correct + that here as well. */ + if (sum == 0 || sum == 6*0xff) { + printk(" EEPROM not present,"); + for (i = 0; i < 5; i++) + dev->dev_addr[i] = last_phys_addr[i]; + dev->dev_addr[i] = last_phys_addr[i] + 1; + irq = last_irq; + } + for (i = 0; i < 6; i++) + printk(" %2.2x", last_phys_addr[i] = dev->dev_addr[i]); + printk(", IRQ %d\n", irq); + last_irq = irq; -static void -generic21040_select(struct device *dev) -{ - int ioaddr = dev->base_addr; - const char *media; + /* We do a request_region() only to register /proc/ioports info. */ + request_region(ioaddr, TULIP_TOTAL_SIZE, tulip_tbl[chip_id].chip_name); - dev->if_port &= 3; - switch (dev->if_port) - { - case TULIP_10TP_PORT: - media = "10baseT"; - break; - case TULIP_AUI_PORT: - media = "AUI"; - break; - case TULIP_BNC_PORT: - media = "BNC"; - break; - default: - media = "unknown type"; - break; - } - printk("%s: enabling %s port.\n", dev->name, media); - /* Set the full duplex match frame. */ - tio_write(FULL_DUPLEX_MAGIC, CSR11); - tio_write(TSIAC_RESET, CSR13); - /* Reset the serial interface */ - tio_write((dev->if_port ? TSIAC_NO10TP: 0) | TSIAC_C21040, CSR13); -} + dev->base_addr = ioaddr; + dev->irq = irq; -#if 0 -static void -generic_timer(struct device *dev, u32 count) -{ - int ioaddr = dev->base_addr; + /* Make certain the data structures are quadword aligned. */ + tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7); + memset(tp, 0, sizeof(*tp)); + dev->priv = tp; - tio_write(count, CSR11); - while (tio_read(CSR11) & TGEPT_COUNT); -} +#ifdef MODULE + tp->next_module = root_tulip_dev; + root_tulip_dev = dev; #endif -static void -generic21041_select(struct device *dev) -{ - int ioaddr = dev->base_addr; - u32 tsiac = TSIAC_C21041; - u32 tsiax = TSIAX_10TP; - u32 tsiag = TSIAG_10TP; - - switch(dev->if_port) { - case TULIP_AUI_PORT: - tsiac |= TSIAC_NO10TP; - tsiax = TSIAX_NO10TP; - tsiag = TSIAG_AUI; + tp->chip_id = chip_id; + +#ifdef TULIP_FULL_DUPLEX + tp->full_duplex = 1; +#endif +#ifdef TULIP_DEFAULT_MEDIA + tp->default_port = TULIP_DEFAULT_MEDIA; +#endif +#ifdef TULIP_NO_MEDIA_SWITCH + tp->medialock = 1; +#endif + + /* The lower four bits are the media type. */ + if (options > 0) { + tp->full_duplex = (options & 16) ? 1 : 0; + tp->default_port = options & 15; + if (tp->default_port) + tp->medialock = 1; + } + + /* The Tulip-specific entries in the device structure. */ + dev->open = &tulip_open; + dev->hard_start_xmit = &tulip_start_xmit; + dev->stop = &tulip_close; + dev->get_stats = &tulip_get_stats; +#ifdef HAVE_MULTICAST + dev->set_multicast_list = &set_multicast_list; +#endif + + /* This is logically part of probe1(), but too complex to write inline. */ + if (chip_id != DC21040) + parse_eeprom(dev); + + /* Reset the xcvr interface and turn on heartbeat. */ + switch (chip_id) { + case DC21041: + outl(0x00000000, ioaddr + CSR13); + outl(0xFFFFFFFF, ioaddr + CSR14); + outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */ + outl(inl(ioaddr + CSR6) | 0x200, ioaddr + CSR6); + outl(0x0000EF05, ioaddr + CSR13); break; - case TULIP_BNC_PORT: - tsiac |= TSIAC_NO10TP; - tsiax = TSIAX_NO10TP; - tsiag = TSIAG_BNC; + case DC21140: case DC21142: + if (tp->mtable) + outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12); break; - default: - dev->if_port = TULIP_10TP_PORT; + case DC21040: + outl(0x00000000, ioaddr + CSR13); + outl(0x00000004, ioaddr + CSR13); break; } - tio_sia_write(ioaddr, tsiac, tsiax, tsiag); - if (dev->start) - printk("%s: enabling %s port.\n", dev->name, - (dev->if_port == TULIP_AUI_PORT) ? "AUI": - (dev->if_port == TULIP_BNC_PORT) ? "BNC": "10TP"); -} -static void -auto21140_select(struct device *dev) -{ - int i, ioaddr = dev->base_addr; + return dev; +} + +/* Serial EEPROM section. */ +/* The main routine to parse the very complicated SROM structure. + Search www.digital.com for "21X4 SROM" to get details. + This code is very complex, and will require changes to support + additional cards, so I'll be verbose about what is going on. + */ + +/* Known cards that have old-style EEPROMs. */ +static struct fixups { + char *name; + unsigned char addr0, addr1, addr2; + u16 newtable[32]; /* Max length below. */ +} eeprom_fixups[] = { + {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c, + 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }}, + {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f, + 0x0000, 0x009E, /* 10baseT */ + 0x0903, 0x006D, /* 100baseTx */ }}, + {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x013f, + 0x0103, 0x006D, /* 100baseTx */ }}, + {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313, + 0x1001, 0x009E, /* 10base2, CSR12 0x10*/ + 0x0000, 0x009E, /* 10baseT */ + 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }}, + {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F, + 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */ + 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ + 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */ + }}, + {0, 0, 0, 0, {}}}; + +static const char * block_name[] = {"21140 non-MII", "21140 MII PHY", + "21142 non-MII PHY", "21142 MII PHY", }; + +#define EEPROM_SIZE 128 +static void parse_eeprom(struct device *dev) +{ + /* The last media info list parsed, for multiport boards. */ + static struct mediatable *last_mediatable = NULL; + static unsigned char *last_ee_data = NULL; + static controller_index = 0; struct tulip_private *tp = (struct tulip_private *)dev->priv; + int ioaddr = dev->base_addr; + unsigned char *ee_data = tp->eeprom; + int i; - /* kick port */ - tio_write(TPOLL_TRIGGER, CSR1); - tio_write(TINTR_ENABLE, CSR7); - tio_write(TCMOD_AUTO|TCMOD_TRxSTART, CSR6); - dev->if_port = !(tio_read(CSR12) & TGEPR_FORCEALED); - printk("%s: probed %s port.\n", - dev->name, dev->if_port ? "100TX" : "10TP"); - tio_write((dev->if_port ? TGEPR_FORCE100: 0) - | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12); - tio_write(TINTR_DISABLE, CSR7); - i = tio_read(CSR8) & 0xffff; - tio_write(TCMOD_AUTO, CSR6); -} + { + static int done_did_that = 0; + if (done_did_that++ == 0) + printk("\n THIS IS AN ALPHA TEST DRIVER.\n" + " The following verbose information is emitted for\n" + " bug reports on media selection.\n"); + } + tp->mtable = 0; + for (i = 0; i < EEPROM_SIZE/2; i++) + ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i); -static void -cogent21140_select(struct device *dev) -{ - int ioaddr = dev->base_addr, csr6; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - dev->if_port &= 1; - csr6 = tio_read(CSR6) & - ~(TCMOD_10TP|TCMOD_100TP|TCMOD_TRxSTART|TCMOD_SCRM); - /* Stop the transmit process. */ - tio_write(csr6 | TCMOD_RxSTART, CSR6); - printk("%s: enabling %s port.\n", - dev->name, dev->if_port ? "100baseTx" : "10baseT"); - /* Turn on the output drivers */ - tio_write(0x0000013F, CSR12); - tio_write((dev->if_port ? TGEPR_FORCE100: 0) - | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12); - tio_write((dev->if_port ? TCMOD_100TP: TCMOD_10TP) - | TCMOD_TRxSTART | TCMOD_TH128 | csr6, CSR6); + /* Detect an old-style (SA only) EEPROM layout: + memcmp(eedata, eedata+16, 8). */ + for (i = 0; i < 8; i ++) + if (ee_data[i] != ee_data[16+i]) + break; + if (i >= 8) { + if (ee_data[0] == 0xff) { + if (last_mediatable) { + controller_index++; + printk("%s: Controller %d of multiport board.\n", + dev->name, controller_index); + tp->mtable = last_mediatable; + ee_data = last_ee_data; + goto subsequent_board; + } else + printk("%s: Missing EEPROM, this device may not work correctly!\n", + dev->name); + return; + } + /* Do a fix-up based on the vendor half of the station address prefix. */ + for (i = 0; eeprom_fixups[i].name; i++) { + if (dev->dev_addr[0] == eeprom_fixups[i].addr0 + && dev->dev_addr[1] == eeprom_fixups[i].addr1 + && dev->dev_addr[2] == eeprom_fixups[i].addr2) { + if (dev->dev_addr[2] == 0xE8 && ee_data[0x1a] == 0x55) + i++; /* An Accton EN1207, not an outlaw Maxtech. */ + memcpy(ee_data + 26, eeprom_fixups[i].newtable, + sizeof(eeprom_fixups[i].newtable)); + printk("\n%s: Old format EEPROM on '%s' board. Using substitute" + " media control info.\n", + dev->name, eeprom_fixups[i].name); + break; + } + } + if (eeprom_fixups[i].name == NULL) { /* No fixup found. */ + printk("\n %s: Old style EEPROM -- no media selection information.\n", + dev->name); + return; + } + } + if (tulip_debug > 1) { + printk("\nread_eeprom:"); + for (i = 0; i < 64; i++) { + printk("%c%4.4x", (i & 7) == 0 ? '\n':' ', + read_eeprom(ioaddr, i)); + } + printk("\n"); + } + + controller_index = 0; + if (ee_data[19] > 1) { /* Multiport board. */ + last_ee_data = ee_data; + } +subsequent_board: + + if (tp->chip_id == DC21041) { + unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3]; + short media = *(u16 *)p; + int count = p[2]; + + printk("%s:21041 Media information at %d, default media %4.4x" + " (%s).\n", dev->name, ee_data[27], media, + media & 0x0800 ? "Autosense" : medianame[media & 15]); + for (i = 0; i < count; i++) { + unsigned char media_code = p[3 + i*7]; + unsigned short *csrvals = (unsigned short *)&p[3 + i*7 + 1]; + printk("%s: 21041 media %2.2x (%s)," + " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n", + dev->name, media_code & 15, medianame[media_code & 15], + csrvals[0], csrvals[1], csrvals[2]); + } + } else { + unsigned char *p = (void *)ee_data + ee_data[27]; + unsigned char csr12dir = 0; + int count; + struct mediatable *mtable; + short media = *((u16 *)p)++; + + if (tp->chip_id == DC21140) + csr12dir = *p++; + count = *p++; + mtable = (struct mediatable *) + kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf), + GFP_KERNEL); + if (mtable == NULL) + return; /* Horrible, impossible failure. */ + last_mediatable = tp->mtable = mtable; + mtable->defaultmedia = media; + mtable->leafcount = count; + mtable->csr12dir = csr12dir; + mtable->has_mii = 0; + + printk("%s: EEPROM default media type %s.\n", dev->name, + media & 0x0800 ? "Autosense" : medianame[media & 15]); + for (i = 0; i < count; i++) { + struct medialeaf *leaf = &mtable->mleaf[i]; + + if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */ + leaf->type = 0; + leaf->media = p[0] & 0x3f; + leaf->leafdata = p; + p += 4; + } else { + leaf->type = p[1]; + if (p[1] & 1) { + mtable->has_mii = 1; + leaf->media = 11; + } else + leaf->media = p[2] & 0x0f; + leaf->leafdata = p + 2; + p += (p[0] & 0x3f) + 1; + } + if (tulip_debug > 1 && leaf->media == 11) { + unsigned char *bp = leaf->leafdata; + printk("%s: MII interface PHY %d, setup/reset sequences" + " %d/%d long, capabilities %2.2x %2.2x.\n", + dev->name, bp[0], bp[1], bp[1 + bp[1]*2], + bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]); + if (tulip_debug > 2) { /* DEBUG only, should be > 3 */ + int mii_reg; + printk("%s: MII xcvr control registers:", dev->name); + for (mii_reg = 0; mii_reg < 32; mii_reg++) + printk(" %4.4x", mdio_read(ioaddr,bp[0], mii_reg)); + printk(".\n"); + } + } + + printk("%s: Index #%d - Media %s (#%d) described by a %s (%d) block.\n", + dev->name, i, medianame[leaf->media], leaf->media, + block_name[leaf->type], leaf->type); + } + } } +/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/ -static void -generic21140_select(struct device *dev) -{ - int ioaddr = dev->base_addr, csr6; - struct tulip_private *tp = (struct tulip_private *)dev->priv; +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */ +#define EE_CS 0x01 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x01 +#define EE_WRITE_1 0x05 +#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define EE_ENB (0x4800 | EE_CS) + +/* Delay between EEPROM clock transitions. + The 1.2 code is a "nasty" timing loop, but PC compatible machines are + *supposed* to delay an ISA-compatible period for the SLOW_DOWN_IO macro. */ +#ifdef _LINUX_DELAY_H +#define eeprom_delay(nanosec) udelay((nanosec + 999)/1000) +#else +#define eeprom_delay(nanosec) do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) +#endif - dev->if_port &= 1; - csr6 = tio_read(CSR6) & - ~(TCMOD_10TP|TCMOD_100TP|TCMOD_TRxSTART|TCMOD_SCRM); +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5 << 6) +#define EE_READ_CMD (6 << 6) +#define EE_ERASE_CMD (7 << 6) - /* Stop the transmit process. */ - tio_write(csr6 | TCMOD_RxSTART, CSR6); - if (dev->start) - printk("%s: enabling %s port.\n", - dev->name, dev->if_port ? "100TX" : "10TP"); - tio_write((dev->if_port ? TCMOD_100TP: TCMOD_10TP) - | TCMOD_TRxSTART | TCMOD_TH128 | csr6, CSR6); - tio_write((dev->if_port ? TGEPR_FORCE100: 0) - | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12); +static int read_eeprom(int ioaddr, int location) +{ + int i; + unsigned short retval = 0; + int ee_addr = ioaddr + CSR9; + int read_cmd = location | EE_READ_CMD; + + outl(EE_ENB & ~EE_CS, ee_addr); + outl(EE_ENB, ee_addr); + + /* Shift the read command bits out. */ + for (i = 10; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + outl(EE_ENB | dataval, ee_addr); + eeprom_delay(100); + outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(150); + outl(EE_ENB | dataval, ee_addr); /* Finish EEPROM a clock tick. */ + eeprom_delay(250); + } + outl(EE_ENB, ee_addr); + + for (i = 16; i > 0; i--) { + outl(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(100); + retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); + outl(EE_ENB, ee_addr); + eeprom_delay(100); + } + + /* Terminate the EEPROM access. */ + outl(EE_ENB & ~EE_CS, ee_addr); + return retval; +} + +/* Read and write the MII registers using software-generated serial + MDIO protocol. It is just different enough from the EEPROM protocol + to not share code. The maxium data clock rate is 2.5 Mhz. */ +#define MDIO_SHIFT_CLK 0x10000 +#define MDIO_DATA_WRITE 0x20000 +#define MDIO_ENB 0x40000 +#define MDIO_DATA_READ 0x80000 +static int mdio_read(int ioaddr, int phy_id, int location) +{ + int i; + int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; + unsigned short retval = 0; + int ee_addr = ioaddr + CSR9; + + /* Shift the read command bits out. */ + for (i = 18; i >= 0; i--) { + int dataval = + (read_cmd & (1 << i)) ? MDIO_DATA_WRITE : 0; + + outl(MDIO_ENB | dataval, ee_addr); + eeprom_delay(100); + outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, ee_addr); + eeprom_delay(250); + outl(MDIO_ENB | dataval, ee_addr); + eeprom_delay(150); + } + + for (i = 16; i > 0; i--) { + outl(MDIO_SHIFT_CLK, ee_addr); + eeprom_delay(250); + retval = (retval << 1) | ((inl(ee_addr) & MDIO_DATA_READ) ? 1 : 0); + outl(0, ee_addr); + eeprom_delay(250); + } + return retval; } + static int tulip_open(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; int ioaddr = dev->base_addr; - int i; + int i = 0; - /* Reset the chip, holding bit 0 set at least 10 PCI cycles. */ - tio_write(tio_read(CSR0)|TBMOD_RESET, CSR0); - udelay(1000); - /* Deassert reset. Set 8 longword cache alignment, 8 longword burst. - -> Set 32 longword cache alignment, unlimited longword burst ? + /* On some chip revs we must set the MII/SYM port before the reset!? */ + if (tp->mtable && tp->mtable->has_mii) + outl(0x00040000, ioaddr + CSR6); + + /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ + outl(0x00000001, ioaddr + CSR0); +#ifdef _LINUX_DELAY_H + udelay(2); +#else + SLOW_DOWN_IO; +#endif + /* Deassert reset. + 486: Set 8 longword cache alignment, 8 longword burst. + 586: Set 16 longword cache alignment, no burst limit. + Cache alignment bits 15:14 Burst length 13:8 + 0000 No alignment 0x00000000 unlimited 0800 8 longwords + 4000 8 longwords 0100 1 longword 1000 16 longwords + 8000 16 longwords 0200 2 longwords 2000 32 longwords + C000 32 longwords 0400 4 longwords Wait the specified 50 PCI cycles after a reset by initializing Tx and Rx queues and the address filter list. */ - tio_write(tio_read(CSR0)|TBMOD_ALIGN32|TBMOD_BURST0, CSR0); +#if defined(__alpha) + /* ToDo: Alpha setting could be better. */ + outl(0x00200000 | 0xE000, ioaddr + CSR0); +#else +#if defined(MODULE) + /* When a module we don't have 'x86' to check. */ + outl(0x00200000 | 0x4800, ioaddr + CSR0); +#else + outl(0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0); + if (x86 <= 4) + printk("This is a 386/486 PCI system, setting cache alignment to %x.\n", + 0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000)); +#endif +#endif - if (request_irq(dev->irq, (void *)&tulip_interrupt, SA_SHIRQ, - tp->signature, dev)) +#ifdef SA_SHIRQ + if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, + tulip_tbl[tp->chip_id].chip_name, dev)) { return -EAGAIN; + } +#else + if (irq2dev_map[dev->irq] != NULL + || (irq2dev_map[dev->irq] = dev) == NULL + || dev->irq == 0 + || request_irq(dev->irq, &tulip_interrupt, 0, + tulip_tbl[tp->chip_id].chip_name)) { + return -EAGAIN; + } +#endif + + if (tulip_debug > 1) + printk("%s: tulip_open() irq %d.\n", dev->name, dev->irq); + + MOD_INC_USE_COUNT; tulip_init_ring(dev); + /* This is set_rx_mode(), but without starting the transmitter. */ /* Fill the whole address filter table with our physical address. */ - { - unsigned short *eaddrs = (unsigned short *)dev->dev_addr; + { + u16 *eaddrs = (u16 *)dev->dev_addr; int *setup_frm = tp->setup_frame, i; /* You must add the broadcast address when doing perfect filtering! */ @@ -721,57 +1031,463 @@ /* Put the setup frame on the Tx list. */ tp->tx_ring[0].length = 0x08000000 | 192; tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame); - tp->tx_ring[0].buffer2 = 0; - tp->tx_ring[0].status = TRING_OWN; - barrier(); - tp->cur_tx++, tp->dirty_tx++; + tp->tx_ring[0].status = 0x80000000; + + tp->cur_tx++; } - tio_write(virt_to_bus(tp->rx_ring), CSR3); - tio_write(virt_to_bus(tp->tx_ring), CSR4); + outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3); + outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4); + + if (dev->if_port == 0) + dev->if_port = tp->default_port; + if (tp->chip_id == DC21041 && dev->if_port > 4) + /* Invalid: Select initial TP, autosense, autonegotiate. */ + dev->if_port = 4; + + /* Allow selecting a default media. */ + if (tp->mtable == NULL) + goto media_picked; + if (dev->if_port) + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == + (dev->if_port == 12 ? 0 : dev->if_port)) { + printk("%s: Using user-specified media %s.\n", + dev->name, medianame[dev->if_port]); + goto media_picked; + } + if ((tp->mtable->defaultmedia & 0x0800) == 0) + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == (tp->mtable->defaultmedia & 15)) { + printk("%s: Using EEPROM-set media %s.\n", + dev->name, medianame[tp->mtable->mleaf[i].media]); + goto media_picked; + } + for (i = tp->mtable->leafcount - 1; + (media_fd[tp->mtable->mleaf[i].media] & 2) && i > 0; i--) + ; +media_picked: + + tp->cur_index = i; + tp->csr6 = 0; + select_media(dev, 1); + + /* Start the chip's Tx to process setup frame. */ + outl(tp->csr6, ioaddr + CSR6); + outl(tp->csr6 | 0x2000, ioaddr + CSR6); dev->tbusy = 0; dev->interrupt = 0; dev->start = 1; - /* - * process setup frame completely prior to fiddling with media. - */ - tio_write((tio_read(CSR6) & ~TCMOD_PROMISC) | TCMOD_TxSTART, CSR6); - tio_write(TPOLL_TRIGGER, CSR1); - sti(); - for (i = 0; i < 1000; i++) { - if (tp->tx_ring[0].status >= 0) { + + + /* Enable interrupts by setting the interrupt mask. */ + outl(0x0001fbff, ioaddr + CSR7); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + outl(0, ioaddr + CSR2); /* Rx poll demand */ + + if (tulip_debug > 2) { + printk("%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR13 %8.8x.\n", + dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5), + inl(ioaddr + CSR13)); + } + /* Set the timer to switch to check for link beat and perhaps switch + to an alternate media type. */ + init_timer(&tp->timer); + tp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ + tp->timer.data = (unsigned long)dev; + tp->timer.function = &tulip_timer; /* timer handler */ + add_timer(&tp->timer); + + return 0; +} + +/* Set up the transceiver control registers for the selected media type. */ +static void select_media(struct device *dev, int startup) +{ + int ioaddr = dev->base_addr; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + struct mediatable *mtable = tp->mtable; + u32 new_csr6; + int i; + + if (mtable) { + struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index]; + unsigned char *p = mleaf->leafdata; + switch (mleaf->type) { + case 0: /* 21140 non-MII xcvr. */ + if (tulip_debug > 1) + printk("%s: Using a 21140 non-MII transceiver with control" + " setting %2.2x.\n", + dev->name, p[1]); + dev->if_port = p[0]; + if (startup) + outl(mtable->csr12dir | 0x100, ioaddr + CSR12); + outl(p[1], ioaddr + CSR12); + new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18); + break; + case 1: + if (startup) { + outl(mtable->csr12dir | 0x100, ioaddr + CSR12); + dev->if_port = 11; + if (tulip_debug > 2) + printk("%s: Doing a reset sequence of length %d.\n", + dev->name, p[2 + p[1]]); + for (i = 0; i < p[2 + p[1]]; i++) + outl(p[3 + p[1] + i], ioaddr + CSR12); + if (tulip_debug > 2) + printk("%s Doing a transceiver setup sequence of length %d.\n", + dev->name, p[1]); + for (i = 0; i < p[1]; i++) + outl(p[2 + i], ioaddr + CSR12); + } + new_csr6 = 0x020C0000; break; + case 2: case 4: { + u16 *setup = (u16*)&p[1]; + dev->if_port = p[0] & 15; + if (tulip_debug > 1) + printk("%s: 21142 non-MII %s transceiver control %4.4x/%4.4x.\n", + dev->name, medianame[dev->if_port], setup[0], setup[1]); + if (p[0] & 0x40) { /* SIA (CSR13-15) setup values are provided. */ + outl(0, ioaddr + CSR13); + outl(setup[1], ioaddr + CSR14); + outl(setup[2], ioaddr + CSR15); + outl(setup[0], ioaddr + CSR13); + setup += 3; + } else { + outl(0, ioaddr + CSR13); + outl(t21142_csr14[dev->if_port], ioaddr + CSR14); + outl(t21142_csr15[dev->if_port], ioaddr + CSR15); + outl(t21142_csr13[dev->if_port], ioaddr + CSR13); + } + outl(setup[0]<<16, ioaddr + CSR15); /* Direction */ + outl(setup[1]<<16, ioaddr + CSR15); /* Data */ + new_csr6 = 0x02000000; + break; + } + case 3: { + int init_length = p[1]; + u16 * init_sequence = (u16*)(p + 2); + int reset_length = p[2 + init_length*2]; + u16 * reset_sequence = (u16*)&p[3 + init_length*2]; + + dev->if_port = 11; + if (startup) { + if (tulip_debug > 2) + printk("%s: Doing a 21142 reset sequence of length %d.\n", + dev->name, reset_length); + for (i = 0; i < reset_length; i++) + outl(reset_sequence[i] << 16, ioaddr + CSR15); + } + if (tulip_debug > 2) + printk("%s: Doing a 21142 xcvr setup sequence of length %d.\n", + dev->name, init_length); + for (i = 0; i < init_length; i++) + outl(init_sequence[i] << 16, ioaddr + CSR15); + new_csr6 = 0x020C0000 | (tp->full_duplex ? 0x0200 : 0); + break; + } + default: + new_csr6 = 0x020C0000; + } + if (tulip_debug > 1) + printk("%s: Using media type %s, CSR12 is %2.2x.\n", + dev->name, medianame[dev->if_port], + inl(ioaddr + CSR12) & 0xff); + } else if (tp->chip_id == DC21140) { + /* Set media type to MII @ 100mbps: 0x020C0000 */ + new_csr6 = 0x020C0000; + dev->if_port = 11; + if (tulip_debug > 1) { + printk("%s: Unknown media control, assuming MII, CSR12 %2.2x.\n", + dev->name, inl(ioaddr + CSR12) & 0xff); } - udelay(1000); + } else if (tp->chip_id == DC21041) { + if (tulip_debug > 1) + printk("%s: 21041 using media %s, CSR12 is %4.4x.\n", + dev->name, medianame[dev->if_port & 15], + inl(ioaddr + CSR12) & 0xffff); + outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ + outl(t21041_csr14[dev->if_port], ioaddr + CSR14); + outl(t21041_csr15[dev->if_port], ioaddr + CSR15); + outl(t21041_csr13[dev->if_port], ioaddr + CSR13); + new_csr6 = 0x80020000; + } else { /* 21040 */ + /* Turn on the xcvr interface. */ + int csr12 = inl(ioaddr + CSR12); + if (tulip_debug > 1) + printk("%s: 21040 media type is %s, CSR12 is %2.2x.\n", + dev->name, dev->if_port ? "AUI" : "10baseT", csr12); + new_csr6 = (dev->if_port ? 0x01860000 : 0x00420000); + /* Set the full duplux match frame. */ + outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11); + outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ + outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); } - if (i == 500) { - printk("%s: initial setup frame didn't complete.\n", dev->name); - dev->start = 0; - dev->tbusy = 1; - tio_write(TINTR_DISABLE, CSR7); - tio_write(tio_read(CSR6) & ~(TCMOD_TRxSTART), CSR6); - tio_write(TSIAC_CONFIG, CSR13); - tio_write(0, CSR13); - free_irq(dev->irq, dev); - return (-EIO); - } - /* - * Whack the chip to stop it and *then* do initial media setup. - */ - tio_write((tio_read(CSR6) & ~(TCMOD_PROMISC|TCMOD_TxSTART)), CSR6); - if (tp->port_select) - tp->port_select(dev); - /* Start the chip's Tx and Rx processes. */ - tio_write(tio_read(CSR6) | TCMOD_TRxSTART - | (tp->full_duplex ? TCMOD_FULLDUPLEX:0), CSR6); - /* Enable interrupts by setting the interrupt mask. */ - tio_write(TINTR_ENABLE, CSR7); - MOD_INC_USE_COUNT; - return 0; + tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0); + return; } +static void tulip_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int ioaddr = dev->base_addr; + u32 csr12 = inl(ioaddr + CSR12); + int next_tick = 0; + + if (tulip_debug > 3) { + printk("%s: Media selection tick, status %8.8x mode %8.8x " + "SIA %8.8x %8.8x %8.8x %8.8x.\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6), + csr12, inl(ioaddr + CSR13), + inl(ioaddr + CSR14), inl(ioaddr + CSR15)); + } + switch (tp->chip_id) { + case DC21040: + if (csr12 & 0x0002) { /* Network error */ + printk("%s: No 10baseT link beat found, switching to %s media.\n", + dev->name, dev->if_port ? "10baseT" : "AUI"); + dev->if_port ^= 1; + outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); + dev->trans_start = jiffies; + } + break; + case DC21041: + if (tulip_debug > 2) + printk("%s: 21041 media tick CSR12 %8.8x.\n", + dev->name, csr12); + switch (dev->if_port) { + case 0: case 3: case 4: + if (csr12 & 0x0004) { /*LnkFail */ + /* 10baseT is dead. Check for activity on alternate port. */ + tp->mediasense = 1; + if (csr12 & 0x0200) + dev->if_port = 2; + else + dev->if_port = 1; + printk("%s: No 21041 10baseT link beat, Media switched to %s.\n", + dev->name, medianame[dev->if_port]); + outl(0, ioaddr + CSR13); /* Reset */ + outl(t21041_csr14[dev->if_port], ioaddr + CSR14); + outl(t21041_csr15[dev->if_port], ioaddr + CSR15); + outl(t21041_csr13[dev->if_port], ioaddr + CSR13); + next_tick = 10*HZ; /* 2.4 sec. */ + } else + next_tick = 30*HZ; + break; + case 1: /* 10base2 */ + case 2: /* AUI */ + if (csr12 & 0x0100) { + next_tick = (30*HZ); /* 30 sec. */ + tp->mediasense = 0; + } else if ((csr12 & 0x0004) == 0) { + printk("%s: 21041 media switched to 10baseT.\n", dev->name); + dev->if_port = 0; + select_media(dev, 0); + next_tick = (24*HZ)/10; /* 2.4 sec. */ + } else if (tp->mediasense || (csr12 & 0x0002)) { + dev->if_port = 3 - dev->if_port; /* Swap ports. */ + select_media(dev, 0); + next_tick = 20*HZ; + } else { + next_tick = 20*HZ; + } + break; + } + break; + case DC21140: case DC21142: { + struct medialeaf *mleaf; + unsigned char *p; + if (tp->mtable == NULL) { /* No EEPROM info, use generic code. */ + /* Assume this is like a SMC card, and check its link beat bit. */ + if ((dev->if_port == 0 && (csr12 & 0x0080)) || + (dev->if_port == 1 && (csr12 & 0x0040) == 0)) { + dev->if_port ^= 1; + /* Stop the transmit process. */ + tp->csr6 = (dev->if_port ? 0x03860000 : 0x02420000); + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + printk("%s: link beat timed out, CSR12 is 0x%2.2x, switching to" + " %s media.\n", dev->name, + csr12 & 0xff, + dev->if_port ? "100baseTx" : "10baseT"); + outl(tp->csr6 | 0xA002, ioaddr + CSR6); + dev->trans_start = jiffies; + next_tick = (24*HZ)/10; + } else { + next_tick = 10*HZ; + if (tulip_debug > 2) + printk("%s: network media monitor 0x%2.2x, link" + " beat detected as %s.\n", dev->name, + csr12 & 0xff, + dev->if_port ? "100baseTx" : "10baseT"); + } + break; + } + mleaf = &tp->mtable->mleaf[tp->cur_index]; + p = mleaf->leafdata; + switch (mleaf->type) { + case 0: case 4: { + /* Type 0 non-MII or #4 SYM transceiver. Check the link beat bit. */ + s8 bitnum = p[mleaf->type == 4 ? 5 : 2]; + if (tulip_debug > 2) + printk("%s: Transceiver monitor tick: CSR12=%#2.2x bit %d is" + " %d, expecting %d.\n", + dev->name, csr12, (bitnum >> 1) & 7, + (csr12 & (1 << ((bitnum >> 1) & 7))) != 0, + (bitnum >= 0)); + /* Check that the specified bit has the proper value. */ + if ((bitnum < 0) != + ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) { + if (tulip_debug > 1) + printk("%s: Link beat detected for %s.\n", dev->name, + medianame[mleaf->media]); + break; + } + if (tp->medialock) + break; + select_next_media: + if (--tp->cur_index < 0) { + /* We start again, but should instead look for default. */ + tp->cur_index = tp->mtable->leafcount - 1; + } + dev->if_port = tp->mtable->mleaf[tp->cur_index].media; + if (media_fd[dev->if_port]) + goto select_next_media; /* Skip FD entries. */ + if (tulip_debug > 1) + printk("%s: No link beat on media %s," + " trying transceiver type %s.\n", + dev->name, medianame[mleaf->media & 15], + medianame[tp->mtable->mleaf[tp->cur_index].media]); + select_media(dev, 0); + /* Restart the transmit process. */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + next_tick = (24*HZ)/10; + break; + } + case 1: + printk(" %s: MII monitoring tick: CSR12 status %2.2x.\n", + dev->name, csr12); + /* Hack for D-Link: Full duplex indication is on bit 3. */ + if (dev->dev_addr[0] == 0 && dev->dev_addr[1] == 0x80 + && dev->dev_addr[2] == 0xC8) { + /* The first message is for information only. */ + if (tp->full_duplex) { + printk("%s: D-Link card in full-duplex mode, csr6 setting" + " %8.8x.\n", dev->name, tp->csr6); + } else if (csr12 & 0x08) { + tp->full_duplex = 0; + tp->csr6 &= ~0x0200; + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } else { + tp->full_duplex = 1; + tp->csr6 |= 0x0200; + printk("%s: Switching D-Link card to full-duplex.\n", dev->name); + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } + } + break; + case 2: /* 21142 non-MII */ + case 3: /* 21142 MII */ + next_tick = (24*HZ)/10; + break; + default: + break; + } + } + default: /* Invalid chip type. */ + break; + } + if (next_tick) { + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); + } +} + +static void tulip_tx_timeout(struct device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int ioaddr = dev->base_addr; + int i; + + if (tp->mtable && tp->mtable->has_mii) { + /* Do nothing -- the media monitor should handle this. */ + if (tulip_debug > 1) + printk("%s: Transmit timeout using MII device.\n", dev->name); + } else if (tp->chip_id == DC21040) { + if (inl(ioaddr + CSR12) & 0x0002) { + printk("%s: transmit timed out, switching to %s media.\n", + dev->name, dev->if_port ? "10baseT" : "AUI"); + dev->if_port ^= 1; + outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); + } + dev->trans_start = jiffies; + return; + } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142) { + /* Stop the transmit process. */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + dev->if_port ^= 1; + printk("%s: 21140 transmit timed out, status %8.8x, SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12), + inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15)); + printk("%s: transmit timed out, switching to %s media.\n", + dev->name, dev->if_port ? "100baseTx" : "10baseT"); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + tp->stats.tx_errors++; + dev->trans_start = jiffies; + return; + } else if (tp->chip_id == DC21041) { + u32 csr12 = inl(ioaddr + CSR12); + + printk("%s: 21041 transmit timed out, status %8.8x, CSR12 %8.8x," + " CSR13 %8.8x, CSR14 %8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), csr12, + inl(ioaddr + CSR13), inl(ioaddr + CSR14)); + tp->mediasense = 1; + if (dev->if_port == 1 || dev->if_port == 2) + if (csr12 & 0x0004) { + dev->if_port = 2 - dev->if_port; + } else + dev->if_port = 0; + else + dev->if_port = 1; + select_media(dev, 0); + tp->stats.tx_errors++; + dev->trans_start = jiffies; + return; + } else + printk("%s: transmit timed out, status %8.8x, CSR12 %8.8x," + " resetting...\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12)); +#ifndef __alpha__ + printk(" Rx ring %8.8x: ", (int)tp->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); + printk("\n Tx ring %8.8x: ", (int)tp->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); + printk("\n"); +#endif + + /* Perhaps we should reinitialize the hardware here. */ + dev->if_port = 0; + /* Stop and restart the chip's Tx processes . */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + /* Trigger an immediate transmit demand. */ + outl(0, ioaddr + CSR1); + + dev->trans_start = jiffies; + tp->stats.tx_errors++; + return; +} + + /* Initialize the Rx and Tx rings, along with various 'dev' bits. */ static void tulip_init_ring(struct device *dev) @@ -784,85 +1500,62 @@ tp->dirty_rx = tp->dirty_tx = 0; for (i = 0; i < RX_RING_SIZE; i++) { - tp->rx_ring[i].status = TRING_OWN; + tp->rx_ring[i].status = 0x80000000; /* Owned by Tulip chip */ tp->rx_ring[i].length = PKT_BUF_SZ; - tp->rx_ring[i].buffer1 = virt_to_bus(tp->rx_buffs[i]); + { + /* Note the receive buffer must be longword aligned. + dev_alloc_skb() provides 16 byte alignment. But do *not* + use skb_reserve() to align the IP header! */ + struct sk_buff *skb; + skb = DEV_ALLOC_SKB(PKT_BUF_SZ); + tp->rx_skbuff[i] = skb; + if (skb == NULL) + break; /* Bad news! */ + skb->dev = dev; /* Mark as being used by this device. */ +#if LINUX_VERSION_CODE > 0x10300 + tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail); +#else + tp->rx_ring[i].buffer1 = virt_to_bus(skb->data); +#endif + } tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]); } - /* Mark the last entry as wrapping the ring. */ + /* Mark the last entry as wrapping the ring. */ tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000; tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]); /* The Tx buffer descriptor is filled in as needed, but we do need to clear the ownership bit. */ for (i = 0; i < TX_RING_SIZE; i++) { + tp->tx_skbuff[i] = 0; tp->tx_ring[i].status = 0x00000000; + tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]); } + tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]); } static int tulip_start_xmit(struct sk_buff *skb, struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; - int ioaddr = dev->base_addr; - int entry, len; - unsigned long daddr; + int entry; + u32 flag; - /* Transmitter timeout, serious problems. */ - if (dev->tbusy || (tp->port_fail && tp->port_fail(dev))) { - int tickssofar = jiffies - dev->trans_start; - int i; - if (tickssofar < 40) return(1); - if (tp->port_select) { - if (!tp->port_fix) dev->if_port ++; - tp->port_select(dev); - dev->trans_start = jiffies; - dev_kfree_skb(skb, FREE_WRITE); - return(0); - } - printk("%s: transmit timed out, status %8.8x," - "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", - dev->name, tio_read(CSR5), tio_read(CSR12), - tio_read(CSR13), tio_read(CSR14), tio_read(CSR15)); -#ifndef __alpha__ - printk(" Rx ring %8.8x: ", (int)tp->rx_ring); -#endif - for (i = 0; i < RX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); -#ifndef __alpha__ - printk("\n Tx ring %8.8x: ", (int)tp->tx_ring); -#endif - for (i = 0; i < TX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); - printk("\n"); - - tp->stats.tx_errors++; - /* Perhaps we should reinitialize the hardware here. */ - dev->if_port = 0; - tio_write(TSIAC_CONFIG, CSR13); - /* Start the chip's Tx and Rx processes . */ - tio_write(TCMOD_10TP | TCMOD_TRxSTART, CSR6); - /* Trigger an immediate transmit demand. */ - tio_write(TPOLL_TRIGGER, CSR1); - - dev->tbusy=0; - dev->trans_start = jiffies; - dev_kfree_skb(skb, FREE_WRITE); - return(0); - } - - if (skb == NULL || (skb != (struct sk_buff *) -1 && skb->len <= 0)) { +#ifndef final_version + if (skb == NULL || skb->len <= 0) { printk("%s: Obsolete driver layer request made: skbuff==NULL.\n", dev->name); dev_tint(dev); - return(0); + return 0; } +#endif /* Block a timer-based transmit from overlapping. This could better be - done with atomic_swap(1, dev->tbusy), but set_bit() works as well. - If this ever occurs the queue layer is doing something evil! */ - if (set_bit(0, (void*)&dev->tbusy) != 0) { - printk("%s: Transmitter access conflict.\n", dev->name); + 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; + tulip_tx_timeout(dev); return 1; } @@ -872,43 +1565,49 @@ /* Calculate the next Tx descriptor entry. */ entry = tp->cur_tx % TX_RING_SIZE; - tp->tx_full = 1; - /* - * If skb is == -1, then this is a funky setup_frame redo. - */ - if (skb == (struct sk_buff *) -1) { - daddr = virt_to_bus((char *)tp->setup_frame); - len = 192; - skb = NULL; + tp->tx_skbuff[entry] = skb; + tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data); + + if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ + flag = 0x60000000; /* No interrupt */ + dev->tbusy = 0; + } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) { + flag = 0xe0000000; /* Tx-done intr. */ + dev->tbusy = 0; + } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) { + flag = 0x60000000; /* No Tx-done intr. */ + dev->tbusy = 0; } else { - daddr = virt_to_bus(skb->data); - len = skb->len; + /* Leave room for set_rx_mode() to fill entries. */ + flag = 0xe0000000; /* Tx-done intr. */ + tp->tx_full = 1; } - tp->tx_skbuff[entry] = skb; - tp->tx_ring[entry].length = len | - (entry == TX_RING_SIZE-1 ? 0xe2000000 : 0xe0000000); - tp->tx_ring[entry].buffer1 = daddr; - tp->tx_ring[entry].buffer2 = 0; - tp->tx_ring[entry].status = TRING_OWN; /* Pass ownership to the chip. */ - barrier(); + if (entry == TX_RING_SIZE-1) + flag |= 0xe2000000; + tp->tx_ring[entry].length = skb->len | flag; + tp->tx_ring[entry].status = 0x80000000; /* Pass ownership to the chip. */ tp->cur_tx++; - /* Trigger an immediate transmit demand. */ - tio_write(TPOLL_TRIGGER, CSR1); + outl(0, dev->base_addr + CSR1); dev->trans_start = jiffies; - return(0); + return 0; } /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ -static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs) { - struct device *dev = (struct device *)dev_id; +#ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */ + struct device *dev = (struct device *)dev_instance; +#else + struct device *dev = (struct device *)(irq2dev_map[irq]); +#endif + struct tulip_private *lp; - int csr5, ioaddr, boguscnt=10; + int csr5, ioaddr, boguscnt = 10; if (dev == NULL) { printk ("tulip_interrupt(): irq %d for unknown device.\n", irq); @@ -923,33 +1622,46 @@ dev->interrupt = 1; do { - csr5 = tio_read(CSR5); + csr5 = inl(ioaddr + CSR5); /* Acknowledge all of the current interrupt sources ASAP. */ - tio_write(csr5 & TSTAT_CLEARINTR, CSR5); - /* check interrupt ? */ - if ((csr5 & (TSTAT_NORINTR|TSTAT_ABNINTR)) == 0) break; + outl(csr5 & 0x0001ffff, ioaddr + CSR5); - if (csr5 & TSTAT_RxINTR) /* Rx interrupt */ + if (tulip_debug > 4) + printk("%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n", + dev->name, csr5, inl(dev->base_addr + CSR5)); + + if ((csr5 & 0x00018000) == 0) + break; + + if (csr5 & 0x0040) /* Rx interrupt */ tulip_rx(dev); - if (csr5 & TSTAT_TxINTR) { /* Tx-done interrupt */ - int dirty_tx = lp->dirty_tx; + if (csr5 & 0x0007) { /* Tx-done interrupt */ + int dirty_tx; - while (dirty_tx < lp->cur_tx) { + for (dirty_tx = lp->dirty_tx; dirty_tx < lp->cur_tx; dirty_tx++) { int entry = dirty_tx % TX_RING_SIZE; int status = lp->tx_ring[entry].status; if (status < 0) break; /* It still hasn't been Txed */ + /* Check for Rx filter setup frames. */ + if (lp->tx_skbuff[entry] == NULL) + continue; - if (status & TRING_ERROR) { + if (status & 0x8000) { /* There was an major error, log it. */ +#ifndef final_version + if (tulip_debug > 1) + printk("%s: Transmit error, Tx status %8.8x.\n", + dev->name, status); +#endif lp->stats.tx_errors++; - if (status & TRING_TxABORT) lp->stats.tx_aborted_errors++; - if (status & TRING_TxCARR) lp->stats.tx_carrier_errors++; - if (status & TRING_TxWINDOW) lp->stats.tx_window_errors++; - if (status & TRING_TxFIFO) lp->stats.tx_fifo_errors++; - if ((status & TRING_TxHEARTBEAT) && !lp->full_duplex) + if (status & 0x4104) lp->stats.tx_aborted_errors++; + if (status & 0x0C00) lp->stats.tx_carrier_errors++; + if (status & 0x0200) lp->stats.tx_window_errors++; + if (status & 0x0002) lp->stats.tx_fifo_errors++; + if ((status & 0x0080) && lp->full_duplex == 0) lp->stats.tx_heartbeat_errors++; #ifdef ETHER_STATS if (status & 0x0100) lp->stats.collisions16++; @@ -963,15 +1675,14 @@ } /* Free the original skb. */ - if (lp->tx_skbuff[entry] != NULL) - dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); - dirty_tx++; + dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); + lp->tx_skbuff[entry] = 0; } #ifndef final_version - if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) { - printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n", - dirty_tx, lp->cur_tx, lp->tx_full); + if (lp->cur_tx - dirty_tx > TX_RING_SIZE) { + printk("%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dev->name, dirty_tx, lp->cur_tx, lp->tx_full); dirty_tx += TX_RING_SIZE; } #endif @@ -988,34 +1699,50 @@ } /* Log errors. */ - if (csr5 & TSTAT_ABNINTR) { /* Abnormal error summary bit. */ - if (csr5 & TSTAT_TxTOUT) lp->stats.tx_errors++; /* Tx babble. */ - if (csr5 & TSTAT_RxMISSED) { /* Missed a Rx frame. */ + if (csr5 & 0x8000) { /* Abnormal error summary bit. */ + if (csr5 & 0x0008) lp->stats.tx_errors++; /* Tx babble. */ + if (csr5 & 0x0020) { /* Tx FIFO underflow. */ + lp->csr6 |= 0x00200000; /* Reconfigure to store-n-forward. */ + /* Restart the transmit process. */ + outl(lp->csr6 | 0x0002, ioaddr + CSR6); + outl(lp->csr6 | 0x2002, ioaddr + CSR6); + } + if (csr5 & 0x0100) { /* Missed a Rx frame. */ lp->stats.rx_errors++; - lp->stats.rx_missed_errors += tio_read(CSR8) & 0xffff; + lp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; } - if (csr5 & TSTAT_TEXPIRED) { + if (csr5 & 0x0800) { printk("%s: Something Wicked happened! %8.8x.\n", dev->name, csr5); /* Hmmmmm, it's not clear what to do here. */ } + /* Clear all error sources, included undocumented ones! */ + outl(0x000f7ba, ioaddr + CSR5); } if (--boguscnt < 0) { printk("%s: Too much work at interrupt, csr5=0x%8.8x.\n", dev->name, csr5); /* Clear all interrupt sources. */ - tio_write(TSTAT_CLEARINTR, CSR5); + outl(0x0001ffff, ioaddr + CSR5); break; } } while (1); - /* Special code for testing *only*. */ + if (tulip_debug > 3) + printk("%s: exiting interrupt, csr5=%#4.4x.\n", + dev->name, inl(ioaddr + CSR5)); + + /* Code that should never be run! Perhaps remove after testing.. */ { static int stopit = 10; if (dev->start == 0 && --stopit < 0) { printk("%s: Emergency stop, looping startup interrupt.\n", dev->name); +#ifdef SA_SHIRQ free_irq(irq, dev); +#else + free_irq(irq); +#endif } } @@ -1028,59 +1755,100 @@ { struct tulip_private *lp = (struct tulip_private *)dev->priv; int entry = lp->cur_rx % RX_RING_SIZE; - int i; + if (tulip_debug > 4) + printk(" In tulip_rx(), entry %d %8.8x.\n", entry, + lp->rx_ring[entry].status); /* If we own the next entry, it's a new packet. Send it up. */ while (lp->rx_ring[entry].status >= 0) { int status = lp->rx_ring[entry].status; - if ((status & TRING_RxDESCMASK) != TRING_RxDESCMASK) { - printk("%s: Ethernet frame spanned multiple buffers," - "status %8.8x!\n", dev->name, status); - } else if (status & TRING_ERROR) { + if (tulip_debug > 4) + printk(" tulip_rx() status was %8.8x.\n", status); + if ((status & 0x0300) != 0x0300) { + if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */ + printk("%s: Oversized Ethernet frame spanned multiple buffers," + " status %8.8x!\n", dev->name, status); + lp->stats.rx_length_errors++; + } + } else if (status & 0x8000) { /* There was a fatal error. */ lp->stats.rx_errors++; /* end of a packet.*/ - if (status & TRING_RxLENGTH) lp->stats.rx_length_errors++; - if (status & TRING_RxFRAME) lp->stats.rx_frame_errors++; - if (status & TRING_RxCRC) lp->stats.rx_crc_errors++; - if (status & TRING_RxFIFO) lp->stats.rx_fifo_errors++; + if (status & 0x0890) lp->stats.rx_length_errors++; + if (status & 0x0004) lp->stats.rx_frame_errors++; + if (status & 0x0002) lp->stats.rx_crc_errors++; + if (status & 0x0001) lp->stats.rx_fifo_errors++; } else { /* Malloc up new buffer, compatible with net-2e. */ /* Omit the four octet CRC from the length. */ short pkt_len = (lp->rx_ring[entry].status >> 16) - 4; struct sk_buff *skb; + int rx_in_place = 0; - skb = dev_alloc_skb(pkt_len + 2); + /* Check if the packet is long enough to just accept without + copying to a properly sized skbuff. */ + if (pkt_len > SKBUFF_RX_COPYBREAK) { + struct sk_buff *newskb; + char *temp; + + /* Get a fresh skbuff to replace the filled one. */ + newskb = DEV_ALLOC_SKB(PKT_BUF_SZ); + if (newskb == NULL) { + skb = 0; /* No memory, drop the packet. */ + goto memory_squeeze; + } + /* 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].buffer1) != temp) + printk("%s: Warning -- the skbuff addresses do not match" + " in tulip_rx: %p vs. %p / %p.\n", dev->name, + bus_to_virt(lp->rx_ring[entry].buffer1), + skb->head, temp); + rx_in_place = 1; + lp->rx_skbuff[entry] = newskb; + newskb->dev = dev; + /* Longword alignment required: do not skb_reserve(2)! */ + lp->rx_ring[entry].buffer1 = virt_to_bus(newskb->tail); + } else + skb = DEV_ALLOC_SKB(pkt_len + 2); + memory_squeeze: if (skb == NULL) { - printk("%s: Memory squeeze, deferring packet.\n", - dev->name); + 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++) + for (i = 0; i < RX_RING_SIZE; i++) if (lp->rx_ring[(entry+i) % RX_RING_SIZE].status < 0) break; if (i > RX_RING_SIZE -2) { lp->stats.rx_dropped++; - lp->rx_ring[entry].status = TRING_OWN; + lp->rx_ring[entry].status = 0x80000000; lp->cur_rx++; } break; } skb->dev = dev; - skb_reserve(skb, 2); - memcpy(skb_put(skb, pkt_len), - bus_to_virt(lp->rx_ring[entry].buffer1), pkt_len); - /* Needed for 1.3.x */ - skb->protocol = eth_type_trans(skb,dev); + if (! rx_in_place) { + skb_reserve(skb, 2); /* 16 byte align the data fields */ + memcpy(skb_put(skb, pkt_len), + bus_to_virt(lp->rx_ring[entry].buffer1), pkt_len); + } +#if LINUX_VERSION_CODE > 0x10300 + skb->protocol = eth_type_trans(skb, dev); +#else + skb->len = pkt_len; +#endif netif_rx(skb); lp->stats.rx_packets++; } - lp->rx_ring[entry].status = TRING_OWN; + lp->rx_ring[entry].status = 0x80000000; entry = (++lp->cur_rx) % RX_RING_SIZE; } - return(0); + + return 0; } static int @@ -1088,342 +1856,223 @@ { int ioaddr = dev->base_addr; struct tulip_private *tp = (struct tulip_private *)dev->priv; + int i; dev->start = 0; dev->tbusy = 1; + if (tulip_debug > 1) + printk("%s: Shutting down ethercard, status was %2.2x.\n", + dev->name, inl(ioaddr + CSR5)); + /* Disable interrupts by clearing the interrupt mask. */ - tio_write(TINTR_DISABLE, CSR7); + outl(0x00000000, ioaddr + CSR7); /* Stop the chip's Tx and Rx processes. */ - tio_write(tio_read(CSR6) & ~(TCMOD_TRxSTART), CSR6); - /* Leave the card in 10baseT state. */ - tio_write(TSIAC_CONFIG, CSR13); + outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); + /* 21040 -- Leave the card in 10baseT state. */ + if (tp->chip_id == DC21040) + outl(0x00000004, ioaddr + CSR13); - tp->stats.rx_missed_errors += tio_read(CSR8) & 0xffff; + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; - tio_write(0, CSR13); -/* tio_write(0, CSR8); wake up chip ? */ + del_timer(&tp->timer); +#ifdef SA_SHIRQ free_irq(dev->irq, dev); +#else + free_irq(dev->irq); + irq2dev_map[dev->irq] = 0; +#endif - MOD_DEC_USE_COUNT; - return(0); -} + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = tp->rx_skbuff[i]; + tp->rx_skbuff[i] = 0; + tp->rx_ring[i].status = 0; /* Not owned by Tulip chip. */ + tp->rx_ring[i].length = 0; + tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */ + if (skb) { +#if LINUX_VERSION_CODE < 0x20100 + skb->free = 1; +#endif + dev_kfree_skb(skb, FREE_WRITE); + } + } + for (i = 0; i < TX_RING_SIZE; i++) { + if (tp->tx_skbuff[i]) + dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE); + tp->tx_skbuff[i] = 0; + } -static int -tulip_config(struct device *dev, struct ifmap *map) -{ - struct tulip_private *tp = (struct tulip_private *)dev->priv; - if (map->port == 0xff) return(-EINVAL); - dev->if_port = map->port; - tp->port_fix = 1; - if (tp->port_select) tp->port_select(dev); - return(0); + MOD_DEC_USE_COUNT; + + return 0; } static struct enet_statistics * tulip_get_stats(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; - /* short ioaddr = dev->base_addr;*/ + int ioaddr = dev->base_addr; + + if (dev->start) + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; - return(&tp->stats); + return &tp->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 + when re-entered but still correct. */ + +/* The little-endian AUTODIN32 ethernet CRC calculation. + N.B. Do not use for bulk data, use a table-based routine instead. + This is common code and should be moved to net/core/crc.c */ +static unsigned const ethernet_polynomial_le = 0xedb88320U; +static inline unsigned ether_crc_le(int length, unsigned char *data) +{ + unsigned int crc = 0xffffffff; /* Initial value. */ + while(--length >= 0) { + unsigned char current_octet = *data++; + int bit; + for (bit = 8; --bit >= 0; current_octet >>= 1) { + if ((crc ^ current_octet) & 1) { + crc >>= 1; + crc ^= ethernet_polynomial_le; + } else + crc >>= 1; + } + } + return crc; } -/* - * Set or clear the multicast filter for this adaptor. - */ -static void set_multicast_list(struct device *dev) +static void +#ifdef NEW_MULTICAST +set_multicast_list(struct device *dev) +#else +static void set_multicast_list(struct device *dev, int num_addrs, void *addrs); +#endif { - short ioaddr = dev->base_addr; - int csr6 = tio_read(CSR6) & ~(TCMOD_MODEMASK|TCMOD_FILTERMASK); + int ioaddr = dev->base_addr; + int csr6 = inl(ioaddr + CSR6) & ~0x00D5; + struct tulip_private *tp = (struct tulip_private *)dev->priv; - if (dev->flags&IFF_PROMISC) - { /* Set promiscuous. why ALLMULTI ? */ - tio_write(csr6 | TCMOD_PROMISC | TCMOD_ALLMCAST, CSR6); - /* Log any net taps. */ + tp->csr6 &= ~0x00D5; + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + outl(csr6 | 0x00C0, ioaddr + CSR6); + /* Unconditionally log net taps. */ printk("%s: Promiscuous mode enabled.\n", dev->name); - } - else if (dev->mc_count > 14 || (dev->flags&IFF_ALLMULTI)) - { + tp->csr6 |= 0xC0; + } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ - tio_write(csr6 | TCMOD_ALLMCAST, CSR6); - } - else - { - struct tulip_private *tp = (struct tulip_private *)dev->priv; - struct dev_mc_list *dmi=dev->mc_list; - int *setup_frm = tp->setup_frame; - unsigned short *eaddrs; + outl(csr6 | 0x0080, ioaddr + CSR6); + tp->csr6 |= 0x80; + } else { + u32 *setup_frm = tp->setup_frame; + struct dev_mc_list *mclist; + u16 *eaddrs; + u32 tx_flags; int i; - /* We have < 15 addresses that we can use the wonderful - 16 address perfect filtering of the Tulip. Note that only - the low shortword of setup_frame[] is valid. */ - tio_write(csr6 | 0x0000, CSR6); - for (i = 0; i < dev->mc_count; i ++) { - eaddrs=(unsigned short *)dmi->dmi_addr; - dmi=dmi->next; + if (dev->mc_count > 14) { /* Must use a multicast hash table. */ + u16 hash_table[32]; + memset(hash_table, 0, sizeof(hash_table)); + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) + set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff, + hash_table); + /* Copy the hash table to the setup frame. + NOTE that only the LOW SHORTWORD of setup_frame[] is valid! + This code may require tweaking for non-x86 architectures! */ + for (i = 0; i < 32; i++) + *setup_frm++ = hash_table[i]; + setup_frm += 7; + tx_flags = 0x08400000 | 192; + /* Too clever: i > 15 for fall-though. */ + } else { + /* We have <= 15 addresses so we can use the wonderful + 16 address perfect filtering of the Tulip. */ + for (i = 0, mclist = dev->mc_list; i < dev->mc_count; + i++, mclist = mclist->next) { + /* Note that only the low shortword of setup_frame[] is valid! + This code may require tweaking for non-x86 architectures! */ + eaddrs = (u16 *)mclist->dmi_addr; *setup_frm++ = *eaddrs++; *setup_frm++ = *eaddrs++; *setup_frm++ = *eaddrs++; + } + /* Fill the rest of the table with our physical address. + Once again, only the low shortword or setup_frame[] is valid! */ + *setup_frm++ = 0xffff; + *setup_frm++ = 0xffff; + *setup_frm++ = 0xffff; + tx_flags = 0x08000000 | 192; } - /* Fill the rest of the table with our physical address. */ - eaddrs = (unsigned short *)dev->dev_addr; - /* Always accept broadcast packets */ - *setup_frm++ = 0xffff; - *setup_frm++ = 0xffff; - *setup_frm++ = 0xffff; + eaddrs = (u16 *)dev->dev_addr; do { - *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; + *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; } while (++i < 15); - /* Now add this frame to the Tx list. */ - tulip_start_xmit((struct sk_buff *) -1, dev); - } -} - -int -tulip_hwinit(struct device *dev, int ioaddr, - int irq, int device_id) -{ - /* See note below on the Znyx 315 etherarray. */ - static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'}; - static int last_irq; - char detect_mesg[80], *mesgp=detect_mesg; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - int i; - unsigned short sum, bitsum; - - if (check_region(ioaddr, TULIP_TOTAL_SIZE) != 0) { - printk("tulip_hwinit: region already allocated at %#3x.\n", - ioaddr); - return(-1); - } - - mesgp += sprintf(mesgp, "(DEC 21%d4%d Tulip", - device_id == PCI_DEVICE_ID_DEC_TULIP_FAST, - device_id == PCI_DEVICE_ID_DEC_TULIP_PLUS); - - /* Stop the chip's Tx and Rx processes. */ - tio_write(tio_read(CSR6) & ~TCMOD_TRxSTART, CSR6); - /* Clear the missed-packet counter. */ - i = tio_read(CSR8) & 0xffff; - - if (device_id == PCI_DEVICE_ID_DEC_TULIP_PLUS - && (tio_read(CSR9) & 0x8000)) { - mesgp += sprintf(mesgp, " treat as 21040"); - device_id = PCI_DEVICE_ID_DEC_TULIP; - } - - /* The station address ROM is read byte serially. The register must - be polled, waiting for the value to be read bit serially from the - EEPROM. - */ - sum = 0; - if (device_id == PCI_DEVICE_ID_DEC_TULIP) { - tio_write(0, CSR9); - /* Reset the pointer with a dummy write. */ - bitsum = 0xff; - for (i = 0; i < 6; i++) { - int value, boguscnt = 100000; - do - value = tio_read(CSR9); - while (value < 0 && --boguscnt > 0); - dev->dev_addr[i] = value; - sum += value & 0xFF; - bitsum &= value; - } - } else { - /* Must be a 21140/21041, with a serial EEPROM interface. */ - struct eeprom eep; - u_char *addr; - - if (read_eeprom(ioaddr, &eep) < 0) { - addr = eep.ng_addr;/* broken EEPROM structure */ - } else { - addr = eep.ok_addr;/* DEC EtherWorks */ - } - for (i = 0; i < ETH_ALEN; i++) { - sum += addr[i]; - dev->dev_addr[i] = addr[i]; - } - } - /* Make certain the data structures are quadword aligned. */ - - mesgp += sprintf(mesgp, ") at %#3x, ", ioaddr); - - /* On the Zynx 315 etherarray boards only the first Tulip has an EEPROM. - The addresses of the subsequent ports are derived from the first. */ - if (sum == 0) { - for (i = 0; i < ETH_ALEN - 1; i++) - dev->dev_addr[i] = last_phys_addr[i]; - dev->dev_addr[i] = last_phys_addr[i] + 1; - irq = last_irq; - } - for (i = 0; i < ETH_ALEN - 1; i++) - mesgp += sprintf(mesgp, "%2.2x:", dev->dev_addr[i]); - mesgp += sprintf(mesgp, "%2.2x, IRQ %d\n", - last_phys_addr[i] = dev->dev_addr[i], irq); - last_irq = irq; - - /* copy ethernet address */ - if (card_type(tp, device_id, - htonl((*(int*)dev->dev_addr) & 0xFFFFFF))) - for (i = 0; i < ETH_ALEN - 1; i++) - last_phys_addr[i] = dev->dev_addr[i]; - /* We do a request_region() only to register /proc/ioports info. */ - request_region(ioaddr, TULIP_TOTAL_SIZE, tp->signature); - - dev->base_addr = ioaddr; - dev->irq = irq; - - /* The Tulip-specific entries in the device structure. */ - dev->open = &tulip_open; - dev->hard_start_xmit = &tulip_start_xmit; - dev->stop = &tulip_close; - dev->get_stats = &tulip_get_stats; - dev->set_config = &tulip_config; - dev->set_multicast_list = &set_multicast_list; - -#ifdef MODULE - if (if_port == TULIP_AUTO_PORT) - if_port = TULIP_PORT; - else - tp->port_fix = 1; - dev->if_port = if_port; - tp->full_duplex = full_duplex; - tp->next_module = root_tulip_dev; - root_tulip_dev = dev; -#else -#ifdef TULIP_FULL_DUPLEX - tp->full_duplex = 1; -#endif - dev->if_port = TULIP_PORT; -#endif -#ifdef TULIP_FIX_PORT - tp->port_fix = 1; -#endif - - printk("%s: %s %s", dev->name, tp->signature, detect_mesg); - - /* Reset the xcvr interface and turn on heartbeat. */ - tio_write(TSIAC_RESET, CSR13); - tio_write(TSIAC_CONFIG, CSR13); - - return(0); -} - -int tulip_probe(struct device *dev) -{ - static struct device *tulip_head=NULL; - u_char pci_bus, pci_device_fn, pci_latency, pci_irq; - u_int pci_ioaddr; - u_short pci_command, vendor_id, device_id; - u_int pci_chips[] = { - PCI_DEVICE_ID_DEC_TULIP, - PCI_DEVICE_ID_DEC_TULIP_FAST, - PCI_DEVICE_ID_DEC_TULIP_PLUS, - PCI_DEVICE_ID_NONE - }; - int num=0, cno; - static int pci_index = 0; - - if (!pcibios_present()) return(-ENODEV); - - for (; pci_index < 0xff; pci_index++) { - if (pcibios_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pci_index, - &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) - break; - - /* get vendor id */ - pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, - &vendor_id); - /* get IRQ */ - pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, - &pci_irq); - - /* get device id */ - pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, - &device_id); - - /* get IO address */ - pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, - &pci_ioaddr); - - /* Remove I/O space marker in bit 0. */ - pci_ioaddr &= ~3; - if (vendor_id != PCI_VENDOR_ID_DEC) - continue; - - for (cno = 0; pci_chips[cno] != PCI_DEVICE_ID_NONE; cno++) - if (device_id == pci_chips[cno]) - break; - if (pci_chips[cno] == PCI_DEVICE_ID_NONE) { - printk("Unknown Digital PCI ethernet chip type %4.4x detected:" - " not configured.\n", device_id); - continue; - } - dev = init_etherdev(NULL, ROUND_UP(sizeof(struct device) + - sizeof (struct tulip_private) + - ETHNAMSIZ, 8)); - if (dev == NULL) - break; + if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) { + /* Same setup recently queued, we need not add it. */ + } else { + unsigned long flags; + unsigned int entry; + + save_flags(flags); cli(); + entry = tp->cur_tx++ % TX_RING_SIZE; + + if (entry != 0) { + /* Avoid a chip errata by prefixing a dummy entry. */ + tp->tx_skbuff[entry] = 0; + tp->tx_ring[entry].length = + (entry == TX_RING_SIZE-1) ? 0x02000000 : 0; + tp->tx_ring[entry].buffer1 = 0; + tp->tx_ring[entry].status = 0x80000000; + entry = tp->cur_tx++ % TX_RING_SIZE; + } - if (!tulip_head) { - printk(version); - tulip_head = dev; - } - - /* Get and check the bus-master and latency values. */ - pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, - &pci_command); - if ((pci_command & PCI_COMMAND_MASTER) == 0) { - printk(" PCI Master Bit has not been set!" - " Setting...\n"); - pci_command |= PCI_COMMAND_MASTER; - pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, - pci_command); - } - - pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, - &pci_latency); - - if (pci_latency < 10) { - printk(" PCI latency timer (CFLT) is" - " unreasonably low at %d." - " Setting to 100 clocks.\n", pci_latency); - pcibios_write_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, 100); - } - - if (tulip_hwinit(dev, pci_ioaddr, pci_irq, pci_chips[cno]) < 0) { - continue; - } - num++; -#ifdef TULIP_MAX_CARDS - if (num >= TULIP_MAX_CARDS) return(0); -#endif + tp->tx_skbuff[entry] = 0; + /* Put the setup frame on the Tx list. */ + if (entry == TX_RING_SIZE-1) + tx_flags |= 0x02000000; /* Wrap ring. */ + tp->tx_ring[entry].length = tx_flags; + tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame); + tp->tx_ring[entry].status = 0x80000000; + if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) { + dev->tbusy = 1; + tp->tx_full = 1; + } + restore_flags(flags); + /* Trigger an immediate transmit demand. */ + outl(0, ioaddr + CSR1); + } + outl(csr6 | 0x0000, ioaddr + CSR6); } - return(num > 0 ? 0: -ENODEV); } - + #ifdef MODULE -/* The parameters that may be passed in... */ -/* This driver does nothing with options yet. It will later be used to - pass the full-duplex flag, etc. */ -int debug = -1; +/* An additional parameter that may be passed in... */ +static int debug = -1; int init_module(void) { + int cards_found; + + if (debug >= 0) + tulip_debug = debug; + root_tulip_dev = NULL; - return tulip_probe(NULL); + cards_found = tulip_probe(0); + + return cards_found ? 0 : -ENODEV; } void @@ -1433,21 +2082,21 @@ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (root_tulip_dev) { - next_dev = - ((struct tulip_private *) root_tulip_dev->priv)->next_module; + next_dev = ((struct tulip_private *)root_tulip_dev->priv)->next_module; unregister_netdev(root_tulip_dev); release_region(root_tulip_dev->base_addr, TULIP_TOTAL_SIZE); kfree(root_tulip_dev); root_tulip_dev = next_dev; } } -#endif /* MODULE */ +#endif /* MODULE */ /* * Local variables: - * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c tulip.c" + * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c" * c-indent-level: 4 + * c-basic-offset: 4 * tab-width: 4 * End: */ diff -u --recursive --new-file v2.0.30/linux/drivers/net/wavelan.c linux/drivers/net/wavelan.c --- v2.0.30/linux/drivers/net/wavelan.c Thu Mar 6 10:03:51 1997 +++ linux/drivers/net/wavelan.c Sun Aug 3 15:17:45 1997 @@ -112,7 +112,7 @@ * Read from card's Host Adaptor Status Register. */ static inline u_short -hasr_read(u_short ioaddr) +hasr_read(u_long ioaddr) { return(inw(HASR(ioaddr))); } /* hasr_read */ @@ -122,7 +122,7 @@ * Write to card's Host Adapter Command Register. */ static inline void -hacr_write(u_short ioaddr, +hacr_write(u_long ioaddr, u_short hacr) { outw(hacr, HACR(ioaddr)); @@ -134,7 +134,7 @@ * those times when it is needed. */ static inline void -hacr_write_slow(u_short ioaddr, +hacr_write_slow(u_long ioaddr, u_short hacr) { hacr_write(ioaddr, hacr); @@ -147,7 +147,7 @@ * Set the channel attention bit. */ static inline void -set_chan_attn(u_short ioaddr, +set_chan_attn(u_long ioaddr, u_short hacr) { hacr_write(ioaddr, hacr | HACR_CA); @@ -158,7 +158,7 @@ * Reset, and then set host adaptor into default mode. */ static inline void -wv_hacr_reset(u_short ioaddr) +wv_hacr_reset(u_long ioaddr) { hacr_write_slow(ioaddr, HACR_RESET); hacr_write(ioaddr, HACR_DEFAULT); @@ -169,7 +169,7 @@ * Set the i/o transfer over the ISA bus to 8 bits mode */ static inline void -wv_16_off(u_short ioaddr, +wv_16_off(u_long ioaddr, u_short hacr) { hacr &= ~HACR_16BITS; @@ -181,7 +181,7 @@ * Set the i/o transfer over the ISA bus to 8 bits mode */ static inline void -wv_16_on(u_short ioaddr, +wv_16_on(u_long ioaddr, u_short hacr) { hacr |= HACR_16BITS; @@ -196,7 +196,7 @@ wv_ints_off(device * dev) { net_local * lp = (net_local *)dev->priv; - u_short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; u_long x; x = wv_splhi(); @@ -215,7 +215,7 @@ wv_ints_on(device * dev) { net_local * lp = (net_local *)dev->priv; - u_short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; u_long x; x = wv_splhi(); @@ -239,7 +239,7 @@ * Read bytes from the PSA. */ static void -psa_read(u_short ioaddr, +psa_read(u_long ioaddr, u_short hacr, int o, /* offset in PSA */ u_char * b, /* buffer to fill */ @@ -262,7 +262,7 @@ * Write the Paramter Storage Area to the WaveLAN card's memory */ static void -psa_write(u_short ioaddr, +psa_write(u_long ioaddr, u_short hacr, int o, /* Offset in psa */ u_char * b, /* Buffer in memory */ @@ -329,7 +329,7 @@ * Write 1 byte to the MMC. */ static inline void -mmc_out(u_short ioaddr, +mmc_out(u_long ioaddr, u_short o, u_char d) { @@ -347,7 +347,7 @@ * We start by the end because it is the way it should be ! */ static inline void -mmc_write(u_short ioaddr, +mmc_write(u_long ioaddr, u_char o, u_char * b, int n) @@ -365,7 +365,7 @@ * Optimised version for 1 byte, avoid using memory... */ static inline u_char -mmc_in(u_short ioaddr, +mmc_in(u_long ioaddr, u_short o) { while(inw(HASR(ioaddr)) & HASR_MMC_BUSY) @@ -386,7 +386,7 @@ * We start by the end because it is the way it should be ! */ static inline void -mmc_read(u_short ioaddr, +mmc_read(u_long ioaddr, u_char o, u_char * b, int n) @@ -400,11 +400,27 @@ /*------------------------------------------------------------------*/ /* + * Get the type of encryption available... + */ +static inline int +mmc_encr(u_long ioaddr) /* i/o port of the card */ +{ + int temp; + + temp = mmc_in(ioaddr, mmroff(0, mmr_des_avail)); + if((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES)) + return 0; + else + return temp; +} + +/*------------------------------------------------------------------*/ +/* * Wait for the frequency EEprom to complete a command... * I hope this one will be optimally inlined... */ static inline void -fee_wait(u_short ioaddr, /* i/o port of the card */ +fee_wait(u_long ioaddr, /* i/o port of the card */ int delay, /* Base delay to wait for */ int number) /* Number of time to wait */ { @@ -420,7 +436,7 @@ * Read bytes from the Frequency EEprom (frequency select cards). */ static void -fee_read(u_short ioaddr, /* i/o port of the card */ +fee_read(u_long ioaddr, /* i/o port of the card */ u_short o, /* destination offset */ u_short * b, /* data buffer */ int n) /* number of registers */ @@ -445,6 +461,8 @@ } } +#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */ + /*------------------------------------------------------------------*/ /* * Write bytes from the Frequency EEprom (frequency select cards). @@ -453,7 +471,7 @@ * Jean II */ static void -fee_write(u_short ioaddr, /* i/o port of the card */ +fee_write(u_long ioaddr, /* i/o port of the card */ u_short o, /* destination offset */ u_short * b, /* data buffer */ int n) /* number of registers */ @@ -528,6 +546,7 @@ fee_wait(ioaddr, 10, 100); #endif /* EEPROM_IS_PROTECTED */ } +#endif /* WIRELESS_EXT */ /************************ I82586 SUBROUTINES *************************/ /* @@ -540,7 +559,7 @@ * Why inlining this function make it fail ??? */ static /*inline*/ void -obram_read(u_short ioaddr, +obram_read(u_long ioaddr, u_short o, u_char * b, int n) @@ -554,7 +573,7 @@ * Write bytes to the on-board RAM. */ static inline void -obram_write(u_short ioaddr, +obram_write(u_long ioaddr, u_short o, u_char * b, int n) @@ -571,7 +590,7 @@ wv_ack(device * dev) { net_local * lp = (net_local *)dev->priv; - u_short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; u_short scb_cs; int i; @@ -614,7 +633,7 @@ const char * str) { net_local * lp = (net_local *)dev->priv; - u_short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; u_short scb_cmd; ach_t cb; int i; @@ -660,7 +679,7 @@ */ static inline int wv_config_complete(device * dev, - u_short ioaddr, + u_long ioaddr, net_local * lp) { unsigned short mcs_addr; @@ -722,7 +741,7 @@ */ static int wv_complete(device * dev, - u_short ioaddr, + u_long ioaddr, net_local * lp) { int nreaped = 0; @@ -992,7 +1011,7 @@ static void wv_mmc_show(device * dev) { - u_short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; net_local * lp = (net_local *)dev->priv; mmr_t m; @@ -1077,7 +1096,7 @@ * Print the last block of the i82586 memory */ static void -wv_scb_show(unsigned short ioaddr) +wv_scb_show(u_long ioaddr) { scb_t scb; @@ -1162,7 +1181,7 @@ int i, u_short p) { - unsigned short ioaddr; + u_long ioaddr; ac_tx_t actx; ioaddr = dev->base_addr; @@ -1529,7 +1548,7 @@ * (called in wavelan_ioctl) */ static inline int -wv_set_frequency(u_short ioaddr, /* i/o port of the card */ +wv_set_frequency(u_long ioaddr, /* i/o port of the card */ iw_freq * frequency) { const int BAND_NUM = 10; /* Number of bands */ @@ -1550,7 +1569,7 @@ } /* Setting by channel (same as wfreqsel) */ - /* Warning : each channel is 11MHz wide, so some of the channels + /* Warning : each channel is 22MHz wide, so some of the channels * will interfere... */ if((frequency->e == 0) && (frequency->m >= 0) && (frequency->m < BAND_NUM)) @@ -1729,7 +1748,7 @@ * Give the list of available frequencies */ static inline int -wv_frequency_list(u_short ioaddr, /* i/o port of the card */ +wv_frequency_list(u_long ioaddr, /* i/o port of the card */ iw_freq * list, /* List of frequency to fill */ int max) /* Maximum number of frequencies */ { @@ -1826,7 +1845,7 @@ struct ifreq * rq, /* Data passed */ int cmd) /* Ioctl number */ { - unsigned short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; net_local * lp = (net_local *)dev->priv; /* lp is not unused */ struct iwreq * wrq = (struct iwreq *) rq; psa_t psa; @@ -1866,9 +1885,7 @@ m.w.mmw_netw_id_h = (wrq->u.nwid.nwid & 0xFF00) >> 8; mmc_write(ioaddr, (char *)&m.w.mmw_netw_id_l - (char *)&m, (unsigned char *)&m.w.mmw_netw_id_l, 2); - m.w.mmw_loopt_sel = 0x00; - mmc_write(ioaddr, (char *)&m.w.mmw_loopt_sel - (char *)&m, - (unsigned char *)&m.w.mmw_loopt_sel, 1); + mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00); } else { @@ -1878,10 +1895,8 @@ (char *)&psa.psa_nwid_select - (char *)&psa, (unsigned char *)&psa.psa_nwid_select, 1); - /* Disable nwid in the mmc (no check) */ - m.w.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID; - mmc_write(ioaddr, (char *)&m.w.mmw_loopt_sel - (char *)&m, - (unsigned char *)&m.w.mmw_loopt_sel, 1); + /* Disable nwid in the mmc (no filtering) */ + mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), MMW_LOOPT_SEL_DIS_NWID); } break; @@ -1950,6 +1965,82 @@ wrq->u.sensitivity = psa.psa_thr_pre_set & 0x3F; break; + case SIOCSIWENCODE: + /* Set encryption key */ + if(!mmc_encr(ioaddr)) + { + ret = -EOPNOTSUPP; + break; + } + + if(wrq->u.encoding.method) + { /* enable encryption */ + int i; + long long key = wrq->u.encoding.code; + + for(i = 7; i >= 0; i--) + { + psa.psa_encryption_key[i] = key & 0xFF; + key >>= 8; + } + psa.psa_encryption_select = 1; + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_encryption_select - (char *) &psa, + (unsigned char *) &psa.psa_encryption_select, 8+1); + + mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), + MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE); + mmc_write(ioaddr, mmwoff(0, mmw_encr_key), + (unsigned char *) &psa.psa_encryption_key, 8); + } + else + { /* disable encryption */ + psa.psa_encryption_select = 0; + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_encryption_select - (char *) &psa, + (unsigned char *) &psa.psa_encryption_select, 1); + + mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0); + } + break; + + case SIOCGIWENCODE: + /* Read the encryption key */ + if(!mmc_encr(ioaddr)) + { + ret = -EOPNOTSUPP; + break; + } + + /* only super-user can see encryption key */ + if(!suser()) + { + ret = -EPERM; + break; + } + else + { + int i; + long long key = 0; + + psa_read(ioaddr, lp->hacr, + (char *) &psa.psa_encryption_select - (char *) &psa, + (unsigned char *) &psa.psa_encryption_select, 1+8); + for(i = 0; i < 8; i++) + { + key <<= 8; + key += psa.psa_encryption_key[i]; + } + wrq->u.encoding.code = key; + + /* encryption is enabled */ + if(psa.psa_encryption_select) + wrq->u.encoding.method = mmc_encr(ioaddr); + else + wrq->u.encoding.method = 0; + } + break; + case SIOCGIWRANGE: /* Basic checking... */ if(wrq->u.data.pointer != (caddr_t) 0) @@ -2207,7 +2298,7 @@ static iw_stats * wavelan_get_wireless_stats(device * dev) { - unsigned short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; net_local * lp = (net_local *) dev->priv; mmr_t m; iw_stats * wstats; @@ -2281,7 +2372,7 @@ int sksize) { net_local * lp = (net_local *) dev->priv; - u_short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; struct sk_buff * skb; #ifdef DEBUG_RX_TRACE @@ -2369,7 +2460,7 @@ static inline void wv_receive(device * dev) { - u_short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; net_local * lp = (net_local *)dev->priv; int nreaped = 0; @@ -2555,7 +2646,7 @@ short length) { net_local * lp = (net_local *) dev->priv; - u_short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; unsigned short txblock; unsigned short txpred; unsigned short tx_addr; @@ -2760,7 +2851,7 @@ static inline int wv_mmc_init(device * dev) { - u_short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; net_local * lp = (net_local *)dev->priv; psa_t psa; mmw_t m; @@ -2789,6 +2880,9 @@ /* As NWID is not set : no NWID checking */ psa.psa_nwid_select = 0; + /* Disable encryption */ + psa.psa_encryption_select = 0; + /* Set to standard values * 0x04 for AT, * 0x01 for MCA, @@ -2806,7 +2900,7 @@ #ifdef USE_PSA_CONFIG /* Write the psa */ psa_write(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa, - (unsigned char *)psa.psa_nwid, 3); + (unsigned char *)psa.psa_nwid, 4); psa_write(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa, (unsigned char *)&psa.psa_thr_pre_set, 1); psa_write(ioaddr, lp->hacr, (char *)&psa.psa_quality_thr - (char *)&psa, @@ -2828,6 +2922,14 @@ else m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID; + memcpy(&m.mmw_encr_key, &psa.psa_encryption_key, + sizeof(m.mmw_encr_key)); + + if(psa.psa_encryption_select) + m.mmw_encr_enable = MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE; + else + m.mmw_encr_enable = 0; + m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F; m.mmw_quality_thr = psa.psa_quality_thr & 0x0F; @@ -2919,7 +3021,7 @@ wv_ru_start(device * dev) { net_local * lp = (net_local *) dev->priv; - u_short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; u_short scb_cs; fd_t fd; rbd_t rbd; @@ -3013,7 +3115,7 @@ wv_cu_start(device * dev) { net_local * lp = (net_local *) dev->priv; - u_short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; int i; u_short txblock; u_short first_nop; @@ -3114,7 +3216,7 @@ wv_82586_start(device * dev) { net_local * lp = (net_local *) dev->priv; - u_short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; scp_t scp; /* system configuration pointer */ iscp_t iscp; /* intermediate scp */ scb_t scb; /* system control block */ @@ -3244,7 +3346,7 @@ wv_82586_config(device * dev) { net_local * lp = (net_local *) dev->priv; - u_short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; unsigned short txblock; unsigned short txpred; unsigned short tx_addr; @@ -3440,7 +3542,7 @@ wv_82586_stop(device * dev) { net_local * lp = (net_local *) dev->priv; - u_short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; u_short scb_cmd; #ifdef DEBUG_CONFIG_TRACE @@ -3475,7 +3577,7 @@ wv_hw_reset(device * dev) { net_local * lp = (net_local *)dev->priv; - u_short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; #ifdef DEBUG_CONFIG_TRACE printk(KERN_DEBUG "%s: ->wv_hw_reset(dev=0x%x)\n", dev->name, @@ -3520,7 +3622,7 @@ * (called in wavelan_probe() and init_module()) */ static int -wv_check_ioaddr(u_short ioaddr, +wv_check_ioaddr(u_long ioaddr, u_char * mac) { int i; /* Loop counter */ @@ -3567,7 +3669,7 @@ struct pt_regs * regs) { device * dev; - u_short ioaddr; + u_long ioaddr; net_local * lp; u_short hasr; u_short status; @@ -3714,7 +3816,7 @@ { device * dev; net_local * lp; - unsigned short ioaddr; + u_long ioaddr; unsigned long x; unsigned int nreaped; @@ -3909,7 +4011,7 @@ static int wavelan_config(device * dev) { - u_short ioaddr = dev->base_addr; + u_long ioaddr = dev->base_addr; u_char irq_mask; int irq; net_local * lp; diff -u --recursive --new-file v2.0.30/linux/drivers/net/wavelan.p.h linux/drivers/net/wavelan.p.h --- v2.0.30/linux/drivers/net/wavelan.p.h Thu Mar 6 10:03:51 1997 +++ linux/drivers/net/wavelan.p.h Sun Aug 3 15:17:45 1997 @@ -35,6 +35,12 @@ /* ------------------------ SPECIFIC NOTES ------------------------ */ /* + * wavelan.o is darn too big + * ------------------------- + * That's true ! There is a very simple way to reduce the driver + * object by 33% (yes !). Comment out the following line : + * #include + * * MAC address and hardware detection : * ---------------------------------- * The detection code of the wavelan chech that the first 3 @@ -80,14 +86,12 @@ * caracteristics of the hardware in a standard way and support for * applications for taking advantage of it (like Mobile IP). * - * By default, these wireless extensions are disabled, because they - * need a patch to the Linux Kernel. This simple patch may be found - * with the driver + some utilities to access those wireless - * extensions (iwconfig...). Hopefully, those wireless extensions will - * make their way in the kernel someday. + * You will need to enable the CONFIG_NET_RADIO define in the kernel + * configuration to enable the wireless extensions (this is the one + * giving access to the radio network device choice). * - * You also will need to enable the CONFIG_NET_RADIO in the kernel - * configuration to enable the wireless extensions. + * It might also be a good idea as well to fetch the wireless tools to + * configure the device and play a bit. */ /* ---------------------------- FILES ---------------------------- */ @@ -161,6 +165,7 @@ * Ajay Bakre (bakre@paul.rutgers.edu), * Donald Becker (becker@cesdis.gsfc.nasa.gov), * Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com), + * Brent Elphick , * Anders Klemets (klemets@it.kth.se), * Vladimir V. Kolpakov (w@stier.koenig.ru), * Marc Meertens (Marc.Meertens@Utrecht.NCR.com), @@ -185,6 +190,7 @@ * John Rosenberg (johnr@cs.usyd.edu.au), * George Rossi (george@phm.gov.au), * Arthur Scott (arthur@cs.usyd.edu.au), + * Stanislav Sinyagin * Peter Storey, * for their assistance and advice. * @@ -260,9 +266,13 @@ * - Remove extern kerword for wavelan_probe() * - Level threshold is now a standard wireless extension (version 4 !) * + * Changes made for release in 2.1.36 : + * ---------------------------------- + * - Encryption setting from Brent Elphick (thanks a lot !) + * - 'ioaddr' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin) + * * Wishes & dreams : * --------------- - * - Encryption stuff * - Roaming */ @@ -345,7 +355,7 @@ /************************ CONSTANTS & MACROS ************************/ #ifdef DEBUG_VERSION_SHOW -static const char *version = "wavelan.c : v15 (wireless extensions) 12/2/97\n"; +static const char *version = "wavelan.c : v16 (wireless extensions) 17/4/97\n"; #endif /* Watchdog temporisation */ @@ -442,69 +452,65 @@ wv_psa_to_irq(u_char); /* ------------------- HOST ADAPTER SUBROUTINES ------------------- */ static inline u_short /* data */ - hasr_read(u_short); /* Read the host interface : base address */ + hasr_read(u_long); /* Read the host interface : base address */ static inline void - hacr_write(u_short, /* Write to host interface : base address */ + hacr_write(u_long, /* Write to host interface : base address */ u_short), /* data */ - hacr_write_slow(u_short, + hacr_write_slow(u_long, u_short), - set_chan_attn(u_short, /* ioaddr */ + set_chan_attn(u_long, /* ioaddr */ u_short), /* hacr */ - wv_hacr_reset(u_short), /* ioaddr */ - wv_16_off(u_short, /* ioaddr */ + wv_hacr_reset(u_long), /* ioaddr */ + wv_16_off(u_long, /* ioaddr */ u_short), /* hacr */ - wv_16_on(u_short, /* ioaddr */ + wv_16_on(u_long, /* ioaddr */ u_short), /* hacr */ wv_ints_off(device *), wv_ints_on(device *); /* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */ static void - psa_read(u_short, /* Read the Parameter Storage Area */ + psa_read(u_long, /* Read the Parameter Storage Area */ u_short, /* hacr */ int, /* offset in PSA */ u_char *, /* buffer to fill */ int), /* size to read */ - psa_write(u_short, /* Write to the PSA */ + psa_write(u_long, /* Write to the PSA */ u_short, /* hacr */ int, /* Offset in psa */ u_char *, /* Buffer in memory */ int); /* Length of buffer */ static inline void - mmc_out(u_short, /* Write 1 byte to the Modem Manag Control */ + mmc_out(u_long, /* Write 1 byte to the Modem Manag Control */ u_short, u_char), - mmc_write(u_short, /* Write n bytes to the MMC */ + mmc_write(u_long, /* Write n bytes to the MMC */ u_char, u_char *, int); static inline u_char /* Read 1 byte from the MMC */ - mmc_in(u_short, + mmc_in(u_long, u_short); static inline void - mmc_read(u_short, /* Read n bytes from the MMC */ + mmc_read(u_long, /* Read n bytes from the MMC */ u_char, u_char *, int), - fee_wait(u_short, /* Wait for frequency EEprom : base address */ + fee_wait(u_long, /* Wait for frequency EEprom : base address */ int, /* Base delay to wait for */ int); /* Number of time to wait */ static void - fee_read(u_short, /* Read the frequency EEprom : base address */ + fee_read(u_long, /* Read the frequency EEprom : base address */ u_short, /* destination offset */ u_short *, /* data buffer */ - int), /* number of registers */ - fee_write(u_short, /* Write to frequency EEprom : base address */ - u_short, /* destination offset */ - u_short *, /* data buffer */ - int); /* number of registers */ + int); /* number of registers */ /* ---------------------- I82586 SUBROUTINES ----------------------- */ static /*inline*/ void - obram_read(u_short, /* ioaddr */ + obram_read(u_long, /* ioaddr */ u_short, /* o */ u_char *, /* b */ int); /* n */ static inline void - obram_write(u_short, /* ioaddr */ + obram_write(u_long, /* ioaddr */ u_short, /* o */ u_char *, /* b */ int); /* n */ @@ -514,11 +520,11 @@ wv_synchronous_cmd(device *, const char *), wv_config_complete(device *, - u_short, + u_long, net_local *); static int wv_complete(device *, - u_short, + u_long, net_local *); static inline void wv_82586_reconfig(device *); @@ -560,7 +566,7 @@ wv_82586_stop(device *); static int wv_hw_reset(device *), /* Reset the wavelan hardware */ - wv_check_ioaddr(u_short, /* ioaddr */ + wv_check_ioaddr(u_long, /* ioaddr */ u_char *); /* mac address (read) */ /* ---------------------- INTERRUPT HANDLING ---------------------- */ static void diff -u --recursive --new-file v2.0.30/linux/drivers/pci/pci.c linux/drivers/pci/pci.c --- v2.0.30/linux/drivers/pci/pci.c Mon Mar 31 13:23:23 1997 +++ linux/drivers/pci/pci.c Tue Aug 12 13:31:53 1997 @@ -47,9 +47,15 @@ DEVICE( NCR, NCR_53C820, "53c820"), DEVICE( NCR, NCR_53C825, "53c825"), DEVICE( NCR, NCR_53C815, "53c815"), + DEVICE( NCR, NCR_53C860, "53c860"), + DEVICE( NCR, NCR_53C896, "53c896"), + DEVICE( NCR, NCR_53C895, "53c895"), + DEVICE( NCR, NCR_53C885, "53c885"), + DEVICE( NCR, NCR_53C875, "53c875"), DEVICE( ATI, ATI_68800, "68800AX"), DEVICE( ATI, ATI_215CT222, "215CT222"), DEVICE( ATI, ATI_210888CX, "210888CX"), + DEVICE( ATI, ATI_215GT, "Mach64 GT (Rage II)"), DEVICE( ATI, ATI_210888GX, "210888GX"), DEVICE( VLSI, VLSI_82C592, "82C592-FC1"), DEVICE( VLSI, VLSI_82C593, "82C593-FC1"), @@ -61,6 +67,7 @@ DEVICE( TSENG, TSENG_W32P_b, "ET4000W32P rev B"), DEVICE( TSENG, TSENG_W32P_c, "ET4000W32P rev C"), DEVICE( TSENG, TSENG_W32P_d, "ET4000W32P rev D"), + DEVICE( TSENG, TSENG_ET6000, "ET6000"), DEVICE( WEITEK, WEITEK_P9000, "P9000"), DEVICE( WEITEK, WEITEK_P9100, "P9100"), BRIDGE( DEC, DEC_BRD, "DC21050", 0x00), @@ -69,16 +76,21 @@ DEVICE( DEC, DEC_TULIP_FAST, "DC21140"), DEVICE( DEC, DEC_FDDI, "DEFPA"), DEVICE( DEC, DEC_TULIP_PLUS, "DC21041"), + DEVICE( DEC, DEC_21142, "DC21142"), DEVICE( DEC, DEC_21052, "DC21052"), DEVICE( DEC, DEC_21152, "DC21152"), + DEVICE( CIRRUS, CIRRUS_7548, "GD 7548"), DEVICE( CIRRUS, CIRRUS_5430, "GD 5430"), DEVICE( CIRRUS, CIRRUS_5434_4, "GD 5434"), DEVICE( CIRRUS, CIRRUS_5434_8, "GD 5434"), DEVICE( CIRRUS, CIRRUS_5436, "GD 5436"), + DEVICE( CIRRUS, CIRRUS_5446, "GD 5446"), DEVICE( CIRRUS, CIRRUS_6729, "CL 6729"), DEVICE( CIRRUS, CIRRUS_7542, "CL 7542"), DEVICE( CIRRUS, CIRRUS_7543, "CL 7543"), + DEVICE( CIRRUS, CIRRUS_7541, "CL 7541"), DEVICE( IBM, IBM_82G2675, "82G2675"), + DEVICE( IBM, IBM_82351, "82351"), DEVICE( WD, WD_7197, "WD 7197"), DEVICE( AMD, AMD_LANCE, "79C970"), DEVICE( AMD, AMD_SCSI, "53C974"), @@ -93,6 +105,7 @@ DEVICE( CT, CT_65545, "65545"), DEVICE( CT, CT_65548, "65548"), DEVICE( CT, CT_65550, "65550"), + DEVICE( CT, CT_65554, "65554"), DEVICE( MIRO, MIRO_36050, "ZR36050"), DEVICE( FD, FD_36C70, "TMC-18C30"), DEVICE( SI, SI_6201, "6201"), @@ -104,7 +117,10 @@ DEVICE( SI, SI_601, "85C601"), DEVICE( SI, SI_5511, "85C5511"), DEVICE( SI, SI_5513, "85C5513"), + DEVICE( SI, SI_5571, "5571"), + DEVICE( SI, SI_7001, "7001"), DEVICE( HP, HP_J2585A, "J2585A"), + DEVICE( HP, HP_J2585B, "J2585B (Lassen)"), DEVICE( PCTECH, PCTECH_RZ1000, "RZ1000 (buggy)"), DEVICE( PCTECH, PCTECH_RZ1001, "RZ1001 (buggy?)"), DEVICE( DPT, DPT, "SmartCache/Raid"), @@ -119,6 +135,10 @@ DEVICE( BUSLOGIC, BUSLOGIC_MULTIMASTER, "MultiMaster"), DEVICE( BUSLOGIC, BUSLOGIC_FLASHPOINT, "FlashPoint"), DEVICE( OAK, OAK_OTI107, "OTI107"), + DEVICE( WINBOND2, WINBOND2_89C940,"NE2000-PCI"), + DEVICE( MOTOROLA, MOTOROLA_MPC105,"MPC105 Eagle"), + DEVICE( MOTOROLA, MOTOROLA_MPC106,"MPC106 Grackle"), + DEVICE( MOTOROLA, MOTOROLA_RAVEN, "Raven"), DEVICE( PROMISE, PROMISE_5300, "DC5030"), DEVICE( N9, N9_I128, "Imagine 128"), DEVICE( N9, N9_I128_2, "Imagine 128v2"), @@ -141,14 +161,19 @@ DEVICE( CMD, CMD_646, "646"), DEVICE( VISION, VISION_QD8500, "QD-8500"), DEVICE( VISION, VISION_QD8580, "QD-8580"), + DEVICE( BROOKTREE, BROOKTREE_848, "Bt848"), DEVICE( SIERRA, SIERRA_STB, "STB Horizon 64"), DEVICE( ACC, ACC_2056, "2056"), DEVICE( WINBOND, WINBOND_83769, "W83769F"), DEVICE( WINBOND, WINBOND_82C105, "SL82C105"), + DEVICE( WINBOND, WINBOND_83C553, "W83C553"), DEVICE( 3COM, 3COM_3C590, "3C590 10bT"), DEVICE( 3COM, 3COM_3C595TX, "3C595 100bTX"), DEVICE( 3COM, 3COM_3C595T4, "3C595 100bT4"), DEVICE( 3COM, 3COM_3C595MII, "3C595 100b-MII"), + DEVICE( 3COM, 3COM_3C900TPO, "3C900 10bTPO"), + DEVICE( 3COM, 3COM_3C900COMBO,"3C900 10b Combo"), + DEVICE( 3COM, 3COM_3C905TX, "3C905 100bTX"), DEVICE( AL, AL_M1445, "M1445"), DEVICE( AL, AL_M1449, "M1449"), DEVICE( AL, AL_M1451, "M1451"), @@ -157,18 +182,26 @@ DEVICE( AL, AL_M1511, "M1511"), DEVICE( AL, AL_M1513, "M1513"), DEVICE( AL, AL_M4803, "M4803"), + DEVICE( NEOMAGIC, NEOMAGIC_MAGICGRAPH_NM2070, "Magicgraph NM2070"), DEVICE( ASP, ASP_ABP940, "ABP940"), + DEVICE( ASP, ASP_ABP940U, "ABP940U"), DEVICE( CERN, CERN_SPSB_PMC, "STAR/RD24 SCI-PCI (PMC)"), DEVICE( CERN, CERN_SPSB_PCI, "STAR/RD24 SCI-PCI (PMC)"), DEVICE( IMS, IMS_8849, "8849"), DEVICE( TEKRAM2, TEKRAM2_690c, "DC690c"), + DEVICE( TUNDRA, TUNDRA_CA91C042,"CA91C042 Universe"), DEVICE( AMCC, AMCC_MYRINET, "Myrinet PCI (M2-PCI-32)"), + DEVICE( AMCC, AMCC_S5933, "S5933"), DEVICE( INTERG, INTERG_1680, "IGA-1680"), + DEVICE( INTERG, INTERG_1682, "IGA-1682"), DEVICE( REALTEK, REALTEK_8029, "8029"), DEVICE( INIT, INIT_320P, "320 P"), DEVICE( VIA, VIA_82C505, "VT 82C505"), DEVICE( VIA, VIA_82C561, "VT 82C561"), + DEVICE( VIA, VIA_82C586_1, "VT 82C586 Apollo VP-1"), DEVICE( VIA, VIA_82C576, "VT 82C576 3V"), + DEVICE( VIA, VIA_82C585, "VT 82C585VP Apollo VP-1"), + DEVICE( VIA, VIA_82C586_0, "VT 82C586 Apollo VP-1"), DEVICE( VIA, VIA_82C416, "VT 82C416MV"), DEVICE( VORTEX, VORTEX_GDT60x0, "GDT 60x0"), DEVICE( VORTEX, VORTEX_GDT6000B,"GDT 6000b"), @@ -189,16 +222,22 @@ DEVICE( IMAGINGTECH, IMAGINGTECH_ICPCI, "MVC IC-PCI"), DEVICE( FORE, FORE_PCA200PC, "PCA-200PC"), DEVICE( FORE, FORE_PCA200E, "PCA-200E"), + DEVICE( IMAGINGTECH, IMAGINGTECH_ICPCI, "MVC IC-PCI"), DEVICE( PLX, PLX_9060, "PCI9060 i960 bridge"), DEVICE( ALLIANCE, ALLIANCE_PROMOTIO, "Promotion-6410"), DEVICE( ALLIANCE, ALLIANCE_PROVIDEO, "Provideo"), DEVICE( VMIC, VMIC_VME, "VMIVME-7587"), DEVICE( DIGI, DIGI_RIGHTSWITCH, "RightSwitch SE-6"), DEVICE( MUTECH, MUTECH_MV1000, "MV-1000"), + DEVICE( TOSHIBA, TOSHIBA_601, "Laptop"), DEVICE( ZEITNET, ZEITNET_1221, "1221"), DEVICE( ZEITNET, ZEITNET_1225, "1225"), + DEVICE( OMEGA, OMEGA_PCMCIA, "PCMCIA"), DEVICE( SPECIALIX, SPECIALIX_XIO, "XIO/SIO host"), DEVICE( SPECIALIX, SPECIALIX_RIO, "RIO host"), + DEVICE( ZORAN, ZORAN_36120, "ZR36120"), + DEVICE( COMPEX, COMPEX_ENET100VG4, "Readylink ENET100-VG4"), + DEVICE( COMPEX, COMPEX_RL2000, "ReadyLink 2000"), DEVICE( RP, RP8OCTA, "RocketPort 8 Oct"), DEVICE( RP, RP8INTF, "RocketPort 8 Intf"), DEVICE( RP, RP16INTF, "RocketPort 16 Intf"), @@ -207,10 +246,20 @@ DEVICE( CYCLADES, CYCLOM_Y_Hi, "Cyclom-Y above 1Mbyte"), DEVICE( CYCLADES, CYCLOM_Z_Lo, "Cyclom-Z below 1Mbyte"), DEVICE( CYCLADES, CYCLOM_Z_Hi, "Cyclom-Z above 1Mbyte"), + DEVICE( 3DFX, 3DFX_VOODOO, "Voodoo"), + DEVICE( SIGMA_DESIGNS, SD_REALMAGIC64GX, "REALmagic64/GX (SD 6425)"), + DEVICE( OPTIBASE, OPTIBASE_FORGE, "MPEG Forge"), + DEVICE( OPTIBASE, OPTIBASE_FUSION,"MPEG Fusion"), + DEVICE( OPTIBASE, OPTIBASE_VPLEX, "VideoPlex"), + DEVICE( OPTIBASE, OPTIBASE_VPLEXCC,"VideoPlex CC"), + DEVICE( OPTIBASE, OPTIBASE_VQUEST,"VideoQuest"), DEVICE( SYMPHONY, SYMPHONY_101, "82C101"), DEVICE( TEKRAM, TEKRAM_DC290, "DC-290"), DEVICE( 3DLABS, 3DLABS_300SX, "GLINT 300SX"), + DEVICE( 3DLABS, 3DLABS_DELTA, "GLINT Delta"), + DEVICE( 3DLABS, 3DLABS_PERMEDIA,"PERMEDIA"), DEVICE( AVANCE, AVANCE_2302, "ALG-2302"), + DEVICE( S3, S3_PLATO_PXS, "PLATO/PX (system)"), DEVICE( S3, S3_ViRGE, "ViRGE"), DEVICE( S3, S3_TRIO, "Trio32/Trio64"), DEVICE( S3, S3_AURORA64VP, "Aurora64V+"), @@ -223,11 +272,16 @@ DEVICE( S3, S3_964_1, "Vision 964-P"), DEVICE( S3, S3_964_2, "Vision 964-P"), DEVICE( S3, S3_968, "Vision 968"), + DEVICE( S3, S3_TRIO64V2, "Trio64V2/DX or /GX"), + DEVICE( S3, S3_PLATO_PXG, "PLATO/PX (graphics)"), + DEVICE( S3, S3_ViRGE_DXGX, "ViRGE/DX or /GX"), DEVICE( INTEL, INTEL_82375, "82375EB"), BRIDGE( INTEL, INTEL_82424, "82424ZX Saturn", 0x00), DEVICE( INTEL, INTEL_82378, "82378IB"), DEVICE( INTEL, INTEL_82430, "82430ZX Aries"), BRIDGE( INTEL, INTEL_82434, "82434LX Mercury/Neptune", 0x00), + DEVICE( INTEL, INTEL_82092AA_0,"82092AA PCMCIA bridge"), + DEVICE( INTEL, INTEL_82092AA_1,"82092AA EIDE"), DEVICE( INTEL, INTEL_7116, "SAA7116"), DEVICE( INTEL, INTEL_82596, "82596"), DEVICE( INTEL, INTEL_82865, "82865"), @@ -235,13 +289,22 @@ DEVICE( INTEL, INTEL_82437, "82437"), DEVICE( INTEL, INTEL_82371_0, "82371 Triton PIIX"), DEVICE( INTEL, INTEL_82371_1, "82371 Triton PIIX"), + DEVICE( INTEL, INTEL_82371MX, "82371MX Mobile PCI I/O IDE Xcelerator (MPIIX)"), + DEVICE( INTEL, INTEL_82430MX, "82430MX Mobile PCIset"), DEVICE( INTEL, INTEL_82441, "82441FX Natoma"), DEVICE( INTEL, INTEL_82439, "82439HX Triton II"), DEVICE( INTEL, INTEL_82371SB_0,"82371SB Natoma/Triton II PIIX3"), DEVICE( INTEL, INTEL_82371SB_1,"82371SB Natoma/Triton II PIIX3"), DEVICE( INTEL, INTEL_82371SB_2,"82371SB Natoma/Triton II PIIX3"), DEVICE( INTEL, INTEL_82437VX, "82437VX Triton II"), + DEVICE( INTEL, INTEL_82439TX, "82439TX"), + DEVICE( INTEL, INTEL_82371AB_0,"82371AB PIIX4"), + DEVICE( INTEL, INTEL_82371AB, "82371AB PIIX4"), + DEVICE( INTEL, INTEL_82371AB_2,"82371AB PIIX4"), + DEVICE( INTEL, INTEL_82371AB_3,"82371AB PIIX4 Power Management"), DEVICE( INTEL, INTEL_P6, "Orion P6"), + DEVICE( INTEL, INTEL_82450GX, "82450GX Orion"), + DEVICE( KTI, KTI_ET32P2, "ET32P2"), DEVICE( ADAPTEC, ADAPTEC_7850, "AIC-7850"), DEVICE( ADAPTEC, ADAPTEC_7855, "AIC-7855"), DEVICE( ADAPTEC, ADAPTEC_7860, "AIC-7860"), @@ -257,8 +320,9 @@ DEVICE( ADAPTEC, ADAPTEC_7883, "AIC-7883U"), DEVICE( ADAPTEC, ADAPTEC_7884, "AIC-7884U"), DEVICE( ATRONICS, ATRONICS_2015, "IDE-2015PL"), - DEVICE( HER, HER_STING, "Stingray"), - DEVICE( HER, HER_STINGARK, "Stingray ARK 2000PV") + DEVICE( ARK, ARK_STING, "Stingray"), + DEVICE( ARK, ARK_STINGARK, "Stingray ARK 2000PV"), + DEVICE( ARK, ARK_2000MT, "2000MT") }; @@ -481,6 +545,8 @@ case PCI_VENDOR_ID_SGS: return "SGS Thomson"; case PCI_VENDOR_ID_BUSLOGIC: return "BusLogic"; case PCI_VENDOR_ID_OAK: return "OAK"; + case PCI_VENDOR_ID_WINBOND2: return "Winbond"; + case PCI_VENDOR_ID_MOTOROLA: return "Motorola"; case PCI_VENDOR_ID_PROMISE: return "Promise Technology"; case PCI_VENDOR_ID_N9: return "Number Nine"; case PCI_VENDOR_ID_UMC: return "UMC"; @@ -493,15 +559,18 @@ case PCI_VENDOR_ID_OLICOM: return "Olicom"; case PCI_VENDOR_ID_CMD: return "CMD"; case PCI_VENDOR_ID_VISION: return "Vision"; + case PCI_VENDOR_ID_BROOKTREE: return "Brooktree"; case PCI_VENDOR_ID_SIERRA: return "Sierra"; case PCI_VENDOR_ID_ACC: return "ACC MICROELECTRONICS"; case PCI_VENDOR_ID_WINBOND: return "Winbond"; case PCI_VENDOR_ID_3COM: return "3Com"; case PCI_VENDOR_ID_AL: return "Acer Labs"; + case PCI_VENDOR_ID_NEOMAGIC: return "Neomagic"; case PCI_VENDOR_ID_ASP: return "Advanced System Products"; case PCI_VENDOR_ID_CERN: return "CERN"; case PCI_VENDOR_ID_IMS: return "IMS"; case PCI_VENDOR_ID_TEKRAM2: return "Tekram"; + case PCI_VENDOR_ID_TUNDRA: return "Tundra"; case PCI_VENDOR_ID_AMCC: return "AMCC"; case PCI_VENDOR_ID_INTERG: return "Intergraphics"; case PCI_VENDOR_ID_REALTEK: return "Realtek"; @@ -519,8 +588,10 @@ case PCI_VENDOR_ID_TOSHIBA: return "Toshiba"; case PCI_VENDOR_ID_ZEITNET: return "ZeitNet"; case PCI_VENDOR_ID_SPECIALIX: return "Specialix"; + case PCI_VENDOR_ID_COMPEX: return "Compex"; case PCI_VENDOR_ID_RP: return "Comtrol"; case PCI_VENDOR_ID_CYCLADES: return "Cyclades"; + case PCI_VENDOR_ID_SIGMA_DESIGNS: return "Sigma Designs"; case PCI_VENDOR_ID_SYMPHONY: return "Symphony"; case PCI_VENDOR_ID_TEKRAM: return "Tekram"; case PCI_VENDOR_ID_3DLABS: return "3Dlabs"; @@ -529,7 +600,9 @@ case PCI_VENDOR_ID_INTEL: return "Intel"; case PCI_VENDOR_ID_ADAPTEC: return "Adaptec"; case PCI_VENDOR_ID_ATRONICS: return "Atronics"; +#if 0 case PCI_VENDOR_ID_HER: return "Hercules"; +#endif default: return "Unknown vendor"; } } diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/BusLogic.c linux/drivers/scsi/BusLogic.c --- v2.0.30/linux/drivers/scsi/BusLogic.c Sat Mar 29 09:01:00 1997 +++ linux/drivers/scsi/BusLogic.c Mon Aug 11 00:10:00 1997 @@ -27,8 +27,8 @@ */ -#define BusLogic_DriverVersion "2.0.9" -#define BusLogic_DriverDate "29 March 1997" +#define BusLogic_DriverVersion "2.0.10" +#define BusLogic_DriverDate "11 August 1997" #include @@ -505,13 +505,14 @@ Wait for the Host Adapter Ready bit to be set and the Command/Parameter Register Busy bit to be reset in the Status Register. */ - TimeoutCounter = loops_per_sec >> 3; + TimeoutCounter = 10000; while (--TimeoutCounter >= 0) { StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter); if (StatusRegister.Bits.HostAdapterReady && !StatusRegister.Bits.CommandParameterRegisterBusy) break; + udelay(100); } if (TimeoutCounter < 0) { @@ -587,11 +588,11 @@ case BusLogic_InquireInstalledDevicesID8to15: case BusLogic_InquireTargetDevices: /* Approximately 60 seconds. */ - TimeoutCounter = loops_per_sec << 2; + TimeoutCounter = 60*10000; break; default: /* Approximately 1 second. */ - TimeoutCounter = loops_per_sec >> 4; + TimeoutCounter = 10000; break; } /* @@ -611,6 +612,7 @@ else BusLogic_ReadDataInRegister(HostAdapter); if (OperationCode == BusLogic_FetchHostAdapterLocalRAM && StatusRegister.Bits.HostAdapterReady) break; + udelay(100); } if (TimeoutCounter < 0) { @@ -786,7 +788,7 @@ boolean StandardAddressSeen[BusLogic_ISA_StandardAddressesCount]; BusLogic_ProbeInfo_T *PrimaryProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount]; - int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount; + int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount + 1; int NonPrimaryPCIMultiMasterCount = 0, PCIMultiMasterCount = 0; boolean ForceBusDeviceScanningOrder = false; boolean ForceBusDeviceScanningOrderChecked = false; @@ -1240,13 +1242,16 @@ { FlashPoint_Info_T *FlashPointInfo = (FlashPoint_Info_T *) scsi_init_malloc(sizeof(FlashPoint_Info_T), GFP_ATOMIC); + int Retries = 10; if (FlashPointInfo == NULL) return BusLogic_Failure(HostAdapter, "ALLOCATING FLASHPOINT INFO"); FlashPointInfo->BaseAddress = HostAdapter->IO_Address; FlashPointInfo->IRQ_Channel = HostAdapter->IRQ_Channel; FlashPointInfo->Present = false; - if (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 && - FlashPointInfo->Present)) + while (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 && + FlashPointInfo->Present) && + --Retries >= 0) ; + if (!FlashPointInfo->Present) { scsi_init_free((char *) FlashPointInfo, sizeof(FlashPoint_Info_T)); BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at " @@ -1321,7 +1326,7 @@ *HostAdapter) { BusLogic_StatusRegister_T StatusRegister; - int TimeoutCounter = loops_per_sec; + int TimeoutCounter; /* FlashPoint Host Adapters are Hard Reset by the FlashPoint SCCB Manager. */ @@ -1344,10 +1349,12 @@ /* Wait until Diagnostic Active is set in the Status Register. */ + TimeoutCounter = 5*10000; while (--TimeoutCounter >= 0) { StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter); if (StatusRegister.Bits.DiagnosticActive) break; + udelay(100); } if (BusLogic_GlobalOptions.Bits.TraceHardReset) BusLogic_Notice("BusLogic_HardReset(0x%X): Diagnostic Active, " @@ -1363,10 +1370,12 @@ /* Wait until Diagnostic Active is reset in the Status Register. */ + TimeoutCounter = 10*10000; while (--TimeoutCounter >= 0) { StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter); if (!StatusRegister.Bits.DiagnosticActive) break; + udelay(100); } if (BusLogic_GlobalOptions.Bits.TraceHardReset) BusLogic_Notice("BusLogic_HardReset(0x%X): Diagnostic Completed, " @@ -1377,6 +1386,7 @@ Wait until at least one of the Diagnostic Failure, Host Adapter Ready, or Data In Register Ready bits is set in the Status Register. */ + TimeoutCounter = 10000; while (--TimeoutCounter >= 0) { StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter); @@ -1384,6 +1394,7 @@ StatusRegister.Bits.HostAdapterReady || StatusRegister.Bits.DataInRegisterReady) break; + udelay(100); } if (BusLogic_GlobalOptions.Bits.TraceHardReset) BusLogic_Notice("BusLogic_HardReset(0x%X): Host Adapter Ready, " @@ -2998,8 +3009,6 @@ "Incoming Mailbox\n", HostAdapter, CCB->SerialNumber, CCB->Status); } - else BusLogic_Warning("Aborted CCB #%ld to Target %d Not Found\n", - HostAdapter, CCB->SerialNumber, CCB->TargetID); NextIncomingMailbox->CompletionCode = BusLogic_IncomingMailboxFree; if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox) NextIncomingMailbox = HostAdapter->FirstIncomingMailbox; diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/BusLogic.h linux/drivers/scsi/BusLogic.h --- v2.0.30/linux/drivers/scsi/BusLogic.h Sat Mar 29 09:01:00 1997 +++ linux/drivers/scsi/BusLogic.h Mon Aug 11 00:10:00 1997 @@ -27,6 +27,9 @@ */ +#include + + /* Define types for some of the structures that interface with the rest of the Linux Kernel and SCSI Subsystem. diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx --- v2.0.30/linux/drivers/scsi/ChangeLog.ncr53c8xx Sat Aug 31 23:15:32 1996 +++ linux/drivers/scsi/ChangeLog.ncr53c8xx Thu Aug 14 10:30:08 1997 @@ -1,3 +1,421 @@ +Sat July 26 18:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.4 + Several clean-ups: + - Asynchronous pre-scaler calculation. + Synchronous divisor calculation. + - Use FE_ as feature identifier prefix instead of _F_. + - Change 'ns_sync' identifier to "minsync". + - Some others. + Apply some SPI2-R12 recommendations. + - Use Slow, Fast-10, Fast-20, Fast-40 SCSI instead of SCSI-2, + FAST SCSI-2, ULTRA, ULTRA-2. + - Reset the SCSI on bus mode change. + +Wed July 02 22:58 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.3c + - Add define SCSI_NCR_PCI_FIX_UP_SUPPORT for conditionnal compilation + of the corresponding pci fix-up code when a small driver is needed. + - Use "ncr53c8xx" as driver name for both request_irq() and + request_region(). Using different names confused 'lsdev'. + (Suggestion sent by Henrik Storner). + +Wed June 24 22:08 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.3b + - Print an error message on unexpected boot command line option. + - Switch to asynchronous data transfer mode after SCSI wide + negotiation. + +Wed June 24 22:08 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.3b + - Print an error message on unexpected boot command line option. + - Switch to asynchronous data transfer mode after SCSI wide + negotiation. + +Wed June 14 22:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.3a + - Add PCI LATENCY TIMER fixup code. + Increase it if necessary according to burst size. + Boot option bit : 'pcifix:4' + - On phase mismatch, calculate residual data size for all OUTPUT + phases. That's only required for interrupted DATA OUT phase, but + this information is usefull for problem solving. + - Add KERN_INFO to some messages printed to the log. + (Patch sent by Wolfram Kleff). + +Tue June 02 22:30 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.3 + - NvRAM support code slightly improved (I think): + Use IO or MMIO according to driver setup for reading the NvRAM. + Use structures for NvRAM data instead of raw data. + - Prevent from queuing more than 1 command to the scsi SCRIPT with + negotiation attached when tagged command queueing is enabled. + - Fix-up for old 53C8XX chips that support PCI READ LINE but not + CACHE LINE SIZE. If the cache line size is unknown, set burst + to 8 dwords and disable READ LINE, otherwise set burst max to + the cache line size value. + +Sat May 24 12:30 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.2c (for linux-2.1.40) + - Remove reference to 'x86' symbol when MODULE is defined, since this + symbol is not exported for module loading. + The value of 'x86' is used for fixing up the PCI CACHE LINE SIZE + configuration register. + - Bytes/words read one bit at a time from the serial NVRAM were'nt + initialized with zero. + - Some comments added. Minor cosmetic changes. + +Mon May 19 20:30 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.2b + - Patch for NVRAM support by Richard Waltham applied. + The code detects Symbios NVRAM format and Tekram NVRAM format. + This enhancement allows to get hosts and devices user set up + from the NVRAM. + - Use the NVRAM contents when present to initialize user definable + target parameters. + - Update the README file. + +Sun May 11 22:30 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.1b + - Cosmetic changes. + - Some heavy testings under pre-linux-2.1.37-6 + +Sun May 4 22:30 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.1a + - PFEN wrongly used for PREFETCH feature bit testing. + Changed to _F_PFEN. + - 2 SCR_COPY that need NO FLUSH bit to be removed had been missed + in tp->getscr[] script (loads SXFER and SCNTL3 on reselection). + +Sat May 3 22:30 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.1 + - Use the NO FLUSH option for MOVE MEMORY (COPY) each time it is + possible. More than 100 COPY with NO FLUSH and 6 with FLUSH for + my configuration (max queued command / device = 8). + This option bit is removed from the script instance for chips + that donnot support prefetching. + - Rewrite the ncr_exception() routine more simple (I think) and + remove useless code. + - Change the data_in and data_out script management. + Use the bottom part of these scripts instead of the beginning. + That avoids to zero the scatter/gather array when a command is + queued (1k) and to deal with some weird IID on MOVE 0 bytes when + a target wants to transfer more bytes than expected. + - Misc. improvements in the init code. + - Remove IOMAPPED/MMIO automatic switching option. + Was useless and reported not reliable. + - Fix a double read of DSTAT and remove DFE testing in the + Phase mismatch service routine. + - Etc... + +Fri Apr 26 20:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.0a + - Add support if the Diamond FirePort 40 (SYM53C875J chip) + +Mon Apr 22 22:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.0 + - incorporate __initdata and __initfunc directives in order to + allow 'init' to free unused memory after driver initialisations. + Patch sent by Roberto Fichera. + - rewrite the init code of the driver. Now a feature descriptor + is used for each real chip types. The code is a lot more clean, + since the driver uses device and revision ids only in the + detection procedure. + - add 'pcifix' boot command line. This command allows to fix up PCI + config space for new chips which support features based on the + cache line size and 'write and invalidate'. + - incorporate in the driver, the code used for error recovery + testing. This code is normally not compiled; have to define + SCSI_NCR_DEBUG_ERROR_RECOVERY in order to compile it. + - take into account actual SCSI bus mode for 53C895 LVD/SE controller. + In single ended mode only fast20 is supported. + (Just to not be late since such controllers are not yet available) + + +Sat Apr 20 21:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 1.18f + - fix an old bug included in the initial port (version 0.0). + The driver allocated 10 bytes of static data and uses 12 bytes. + No danger, since data are generally aligned on 4 bytes boundary + and so byte 10 and 11 are free (I hope ...) + +Wed Apr 16 12:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 1.18e + - reset all when an unexpected data cycle is detected while + disconnecting. + - make changes to abort() ans reset() functions according to + Leonard's documentation. + - small fix in some message for hard errors. + +Sat Apr 5 13:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 1.18d + - Probe NCR pci device ids in reverse order if asked by user from + the boot command line. Suggested by Richard Waltham. + - Make a separate function that prints out verbose information on + severe error (assumed from hardware). + - Add the transfer period factor and the max commands per lun value + to the proc info data. If debug flags are set or verbosity is + greater than 1, debug flags and verbosity are returned in proc + info data. + - Update the documentation. + +Thu Mar 20 23:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 1.18c + - Add special features support for NCR53C885 and NCR53C896 chip. + Quite obvious, but untested, and based on the fact that: + The 885 supports same features as the 875. + The 896 is a 64 bits PCI version of the 895. + - Improve recovery from SCSI GROSS ERRORS. + I can get such errors by making the driver negotiate offset 8 with + a disk and setting the ncr chip to a lower offset value. + I got bunches of errors that have been gracefully recovered by + the driver. + The driver now uses its timer handler in order to wait 2 sec. for + devices to settle after SCSI reset and so does not uselessly freeze + the system with interrupt masked for seconds. + - Enable 'burst op code fetch' and 'read line' for 815 chips. + - Use a 2 commands queue depth instead of 1 for devices that does + not support tagged command queuing. + - The ULTRA timing flag setting was based on the output resulting + period factor of the ncr and not on the negotiated one. + This flag setting was wrong only for 24 ns negotiated period factor. + - Some other minor changes and cleanups. + +Thu Feb 27 23:00 1997 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h revision 1.18b + - 'On paper' support of the NCR53C895 Ultra-2 chip. + (Clock quadrupler + 7 clock divisors) + - Load the main part of the script into the on-board RAM. + - 810A rev. 0x11 PCI problem fixed. + This chip is now supported with all PCI features enabled and + 16 dwords burst transfers. + - Align on 32 boundary some internal structures. + That fixes the 810A problem and allows cache line bursting when + moving the global header (64 bytes) from/to CCBs to/from NCB. + - Synchronous parameters calculation rewritten. The driver + now uses all available clock divisors and will be able to support + clock frequencies that are not multiple of 40 Mhz if necessary. + +Sat Feb 8 22:00 1997 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c - revision 1.17a + - IRQ mode set up from boot setup command. + irqm:0 open drain (default) + irqm:1 preserve initial setting (assumed from BIOS) + irqm:2 totem pole + - DIFF mode set up from boot setup command. + Suggested by Richard Waltham. + diff:0 never set up diff mode (default) + diff:1 set up diff mode according to initial setting (BIOS?) + diff:2 always set up diff mode + diff:3 set up diff mode if GPIO3 is zero (SYMBIOS boards) + - Change CONFIG option for LED support. + CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT allows LED support and + DIFF support for SYMBIOS boards and compatibles (clones?). + - Set 16 DWORD bursts for 810A rev. >= 0x12 since my SC200 with + such a chip have no problem with it (MB with Triton 2 HX). + 810A rev. 0x11 are set to 8 DWORD bursts since they may give + problems with PCI read multiple and Triton 2 HX. + Thanks to Stefan for this information. + +Sat Jan 25 22:00 1997 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c - revision 1.17 + - Controller LED support. + Only works with LED pin wired to GPIO_FETCHN, so probably with + all boards using SMDS BIOS. + This option can be enabled only if CONFIG_EXPERIMENTAL is set. + - Assume clock doubler for 875 chip when clock frequency measurement + result is 40 MHz. May help when some old stuff as SDMS BIOS 3.0 + or some old driver has broken the normal BIOS settings. + - Add wide negotiation control from boot setup command. + May be usefull with systems using a 875 based board connected to + a wide device through a 50 pins to 68 pins converter. + - Add a "boot fail safe option" to the boot setup command line. + - Rewrite the "reset_command" routine. + Low-level driver are responsible to keep the involved command + alive. The new code seems to behave correctly. + - Change some variables used by the script from u_long to u_int32. + - Remove some useless code. + +Sun Jan 12 12:00 1997 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c - revision 1.16e + - Add support of PCI burst length control from boot setup command. + burst:0 disable burst + burst:255 get burst from initial settings (BIOS settings?) + burst:#x set burst transfers to 1<<#x + - Only check xfer direction for common op-codes. + For all device specific / vendor specific opcodes the driver + now uses the xfer direction decided by the target. + +Sun Jan 05 12:00 1997 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c - revision 1.16d + - The driver is now able to process scsi commands without + knowledge of xfer data direction. + Stefan agreed with this change for Linux. This change is + not needed under FreeBSD since low-level drivers receive + the expected data direction for each scsi request. + - Save ctest5 features bits at start-up and restore them at + module release step. + Avoid side effects when a ncr driver which trusts bios + settings is reloaded (could be the ncr53c8xx itself). + + +Wed Jan 01 23:30 1997 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c - revision 1.16c + - Bad decision about 20MHz for 13 ns period factor. + Was wrong, so I restore the previous algorithm. + - Burst length 128 not correctly set in dmode. + +Thu Dec 26 22:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h README.ncr53c8xx - revision 1.16b + - Remove useless code. + - Try to improve error recovery in case of abort and reset. + - Remove DEBUG_NEGO by default. + - Add boot setup command support. + Now, all experimental config options can be removed. + - Update README file. + + +Mon Dec 23 23:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h - revision 1.16a + New display for speed ##.# MB/s (From Stefan) + - I add "WIDE" qualifier after ULTRA and FAST + - I get "FAST WIDE SCSI-2 20 MB/s" with my Atlas. That's nice. + + Richard Waltham reports SYMBIOS set the 875 to 20 MB/s for 13 ns + period factor. I decide to trust SYMBIOS. 20 MB/s output speed + instead of 19.2 MB/s should not cause problem. The ncr is only able + to use 16.67 MB/s when 20 MB/s is not possible. + + Fix from Markus Kossman: "Ultra SCSI enabled" wrongly printed + when not enabled. + + Set DEBUG_NEGO by default in order to get reports about sync nego. + Will remove it in the next patch. + +Thu Dec 19 21:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h README.ncr53c8xx - revision 1.16 + Incorporate new definitions in ncr53c8xx.h (From Stefan). + Check changes against Stefan's current version of the driver. + All seems ok. + +Sat Nov 30 21:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h + Make changes in order to support: + - Clock doubler and so 80 Mhz scsi clock for 875 chips. + - Sync transfers below 7.5 MB/sec. + Use Clock/2 between 5 and 10 Mega-transfers/s and Clock/4 below 5. + - Ultra SCSI data transfers. + - Offset 16. + + Works with my configuration. However I cannot test Ultra transfers, + since my disks are only fast scsi-2. + +Tue Nov 28 21:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c + I received yesterday my Promise SCSI Ultra board. + NCR53C875 rev. 3 with clock doubler. + Add the code to support some bus features, the large 536 dma fifo and + burst 128. Works. + +Mon Nov 4 21:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h - revision 1.14c + Severall control command improvements: + + - Allow to specify "all" to commands that apply to #target. + For example: "setsync all 255" sets asynchronous data + transfers for all targets on a bus. + + - Allow to control disconnection privilege per device, as follow: + "setflag #target no_sync" disables disconnection for #target. + "setflag #target" with no flag specified reenables it. + + Obviously #target may be specified as "all" in order to control + disconnection for all targets with a single control command. + + - README file updated and some hints about SCSI problems solving added. + +Sun Oct 27 22:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h - revision 1.14b + Add the following config parameters: + + - CONFIG_SCSI_NCR53C8XX_MAX_TAGS + Max number of queued tagged commands. + Allow from 2 to 12, default 4. + + - CONFIG_SCSI_NCR53C8XX_SYNC + Synchronous transfers frequency in MHz. + Allow from 5 to 10, default 5, 0 means asynchronous. + (And so remove CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS) + +Sun Oct 20 16:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c + ncr_scatter() rewritten. + remove "ncr dead" detection. + +Sun Oct 13 19:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h - revision 1.14a + Enabling some special features makes problems with some hardware. + So, disable them by default. + Add SCSI_NCR_SPECIAL_FEATURES define to play with. + +Sun Oct 13 14:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h + Incorporate Stefan's patch for clock frequency detection. + (Committed in FreeBSD/ncr.c rev. 1.81). + The driver then does about the following: + Assume 40 MHz clock for all ncr chips except: + - NCR53C860 chips: + Assume 80 Mhz clock. + - NCR53C875 chips: + If clock doubler enabled, disable it and assume 40 Mhz clock. + Else if (scntl3&7)=0 measure scsi clock frequency. + Else trust bios setting of scntl3&7 (3=40 Mhz, 5=80Mhz). + +Wed Oct 9 22:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c - release 1.14 + For now, just change the clock detection as follow: + - If clock doubler selected by BIOS, assume 40 MHz clock since + clock doubler will be disabled by chip reset. + - Else if NCR53C860 assume 80 MHz clock. + - Else trust BIOS setting if (scntl3&7 >= 3) + - Else assume 40 MHz clock. + +Sat Oct 05 17:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c + Stefan sent me a patch that improves the clock frequency detection + of the driver. Stefan uses the general timer register stime1 in + order to measure as accurately as possible the scsi clock. + Works ok with my 825, but needs still testing. So will be + released later. + +Sun Sep 29 17:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c + Preserve dcntl/dmode/ctest3/ctest4 features bits at start-up. + Add the define option SCSI_NCR_TRUST_BIOS_SETTING. + - If this option is defined, the driver will preserve the + corresponding bits of io registers. + - Else, the driver will set features bits according to chip + and revision ids. + +Sun Sep 22 17:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c + Remove useless fields and code and so spare cpu: + - profile data are accumulated in jiffies ticks and converted + to milli-seconds when read through proc fs. + - when IOMAPPED is not defined, try only MMIO. + (avoid testing a value in order to choose between IO and MMIO) + +Sun Sep 01 20:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.h, ncr53c8xx.c - Version 1.13 + Adaptation of the tagged command queuing depth control of the + FreeBSD driver to Linux. Now, tagged command queueing can be + disabled at run time by a "settags N 0" control command. + Add the following heuristic in order to manage intelligently (perhaps) + QUEUE_FULL status: + - Each time a QUEUE FULL status is returned by a device, disable tagged + command queuing for that device. + - Every 100 successfully complete commands, increment the maximum + queuable commands (up to the allowed limit). + Fri Aug 30 10:00 1996 Gerard Roudier (groudier@club-internet.fr) * ncr53c8xx.c - Version 1.12c Incorporate the changes of FreeBSD/ncr.c revision 1.76. diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in --- v2.0.30/linux/drivers/scsi/Config.in Fri Feb 28 15:14:18 1997 +++ linux/drivers/scsi/Config.in Thu Aug 14 10:31:20 1997 @@ -18,7 +18,17 @@ dep_tristate 'Adaptec AHA152X/2825 support' CONFIG_SCSI_AHA152X $CONFIG_SCSI dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI -dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI +dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI +if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then + bool ' Enable tagged command queueing' CONFIG_AIC7XXX_TAGGED_QUEUEING Y + dep_tristate ' Override driver defaults for commands per LUN' CONFIG_OVERRIDE_CMDS N + if [ "$CONFIG_OVERRIDE_CMDS" != "n" ]; then + int ' Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8 + fi + bool ' Enable SCB paging' CONFIG_AIC7XXX_PAGE_ENABLE N + bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N + int ' Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 15 +fi dep_tristate '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 @@ -55,15 +65,17 @@ if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_NCR53C7xx" != "y" ]; then dep_tristate 'NCR53C8XX SCSI support' CONFIG_SCSI_NCR53C8XX $CONFIG_SCSI if [ "$CONFIG_SCSI_NCR53C8XX" != "n" ]; then + bool ' detect and read serial NVRAMs' CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT bool ' enable tagged command queueing' CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE - bool ' force normal IO' CONFIG_SCSI_NCR53C8XX_IOMAPPED - bool ' not allow targets to disconnect' CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT - bool ' force asynchronous transfer mode' CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS - bool ' force synchronous negotiation' CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO - fi - if [ "$CONFIG_SCSI_NCR53C8XX" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool ' disable master parity checking' CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK - bool ' disable scsi parity checking' CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK + bool ' use normal IO' CONFIG_SCSI_NCR53C8XX_IOMAPPED + int ' maximum number of queued commands' CONFIG_SCSI_NCR53C8XX_MAX_TAGS 4 + int ' synchronous transfers frequency in MHz' CONFIG_SCSI_NCR53C8XX_SYNC 5 + if [ "$CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE" != "y" ]; then + bool ' not allow targets to disconnect' CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT + fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' assume boards are SYMBIOS compatible' CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT + fi fi fi dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI @@ -73,6 +85,9 @@ dep_tristate 'Qlogic ISP SCSI support' CONFIG_SCSI_QLOGIC_ISP $CONFIG_SCSI fi dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE $CONFIG_SCSI +if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_AM53C974" != "y" ]; then + dep_tristate 'Tekram DC-390(T) SCSI support' CONFIG_SCSI_DC390T $CONFIG_SCSI +fi dep_tristate 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 $CONFIG_SCSI dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F $CONFIG_SCSI if [ "$CONFIG_SCSI_U14_34F" != "n" ]; then @@ -80,5 +95,6 @@ int ' maximum number of queued commands' CONFIG_SCSI_U14_34F_MAX_TAGS 8 fi dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR $CONFIG_SCSI +dep_tristate 'GDT SCSI Disk Array Controller support' CONFIG_SCSI_GDTH $CONFIG_SCSI #dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI endmenu diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/FlashPoint.c linux/drivers/scsi/FlashPoint.c --- v2.0.30/linux/drivers/scsi/FlashPoint.c Sat Mar 29 09:01:00 1997 +++ linux/drivers/scsi/FlashPoint.c Mon Aug 11 00:10:00 1997 @@ -31,6 +31,18 @@ #endif +/* + FlashPoint support is only available for the Intel x86 Architecture. +*/ + +#ifndef __i386__ + +#undef CONFIG_SCSI_OMIT_FLASHPOINT +#define CONFIG_SCSI_OMIT_FLASHPOINT + +#endif + + #ifndef CONFIG_SCSI_OMIT_FLASHPOINT diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- v2.0.30/linux/drivers/scsi/Makefile Fri Feb 28 15:14:18 1997 +++ linux/drivers/scsi/Makefile Thu Aug 14 10:31:20 1997 @@ -19,6 +19,7 @@ SCSI_SRCS = $(wildcard $(L_OBJS:%.o=%.c)) AHA152X = -DDEBUG_AHA152X -DAUTOCONF +GDTH = #-DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS .SUFFIXES: .SUFFIXES: .c .o .h .a @@ -177,6 +178,14 @@ endif endif +ifeq ($(CONFIG_SCSI_DC390T),y) +L_OBJS += tmscsim.o +else + ifeq ($(CONFIG_SCSI_DC390T),m) + M_OBJS += tmscsim.o + endif +endif + ifeq ($(CONFIG_SCSI_AM53C974),y) L_OBJS += AM53C974.o else @@ -225,6 +234,14 @@ endif endif +ifeq ($(CONFIG_SCSI_GDTH),y) +L_OBJS += gdth.o +else + ifeq ($(CONFIG_SCSI_GDTH),m) + M_OBJS += gdth.o + endif +endif + ifeq ($(CONFIG_SCSI_DEBUG),y) L_OBJS += scsi_debug.o else @@ -251,10 +268,8 @@ ifeq ($(CONFIG_SCSI_GENERIC_NCR5380),y) L_OBJS += g_NCR5380.o -EXTRA_CFLAGS = -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};" else ifeq ($(CONFIG_SCSI_GENERIC_NCR5380),m) - EXTRA_CFLAGS = -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};" M_OBJS += g_NCR5380.o endif endif @@ -348,6 +363,11 @@ endif endif + +ifeq ($(CONFIG_BLK_DEV_IDESCSI),y) +L_OBJS += ide-scsi.o +endif + include $(TOPDIR)/Rules.make BusLogic.o: BusLogic.c FlashPoint.c @@ -359,19 +379,17 @@ aha152x.o: aha152x.c $(CC) $(CFLAGS) $(AHA152X) -c aha152x.c -aic7xxx_asm: aic7xxx_asm.c - $(HOSTCC) -o $@ aic7xxx_asm.c +gdth.o: gdth.c gdth.h gdth_proc.c gdth_proc.h + $(CC) $(CFLAGS) $(GDTH) -c gdth.c -aic7xxx.c: aic7xxx_seq.h -aic7xxx_seq.h: aic7xxx_asm aic7xxx.seq - ./aic7xxx_asm -o $@ aic7xxx.seq +aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h + $(CC) $(CFLAGS) -c -o $@ aic7xxx.c seagate.o: seagate.c $(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -c seagate.c -# For debugging, use the -g flag -53c7,8xx.o : 53c7,8xx.c - $(CC) $(CFLAGS) -g -c 53c7,8xx.c +tmscsim.o : tmscsim.c + $(CC) $(CFLAGS) -c tmscsim.c 53c8xx_d.h 53c8xx_u.h : 53c7,8xx.scr script_asm.pl ln -sf 53c7,8xx.scr fake.c @@ -380,8 +398,8 @@ mv scriptu.h 53c8xx_u.h rm fake.c -ncr53c8xx.o : ncr53c8xx.c - $(CC) $(CFLAGS) -c ncr53c8xx.c +g_NCR5380.o: g_NCR5380.c + $(CC) $(CFLAGS) -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};" -c g_NCR5380.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.30/linux/drivers/scsi/README.BusLogic linux/drivers/scsi/README.BusLogic --- v2.0.30/linux/drivers/scsi/README.BusLogic Sat Mar 29 09:01:00 1997 +++ linux/drivers/scsi/README.BusLogic Mon Aug 11 00:10:00 1997 @@ -1,10 +1,10 @@ BusLogic MultiMaster and FlashPoint SCSI Driver for Linux - Version 2.0.9 for Linux 2.0 + Version 2.0.10 for Linux 2.0 PRODUCTION RELEASE - 29 March 1997 + 11 August 1997 Leonard N. Zubkoff Dandelion Digital @@ -284,6 +284,14 @@ so as to recognize the host adapters in the same order as they are enumerated by the host adapter's BIOS. +o Mega-Transfers/Second + + The driver reports on the synchronous transfer parameters negotiated between + the host adapter and target devices in units of "mega-transfers/second". For + wide devices, the unit of transfer is 16 bits if wide negotiation has been + successfully completed. Therefore, the total transfer rate to wide devices + will generally be twice the synchronous tranfer rate reported by the driver. + COMMAND LINE OPTIONS @@ -372,16 +380,17 @@ INSTALLATION -This distribution was prepared for Linux kernel version 2.0.29, but should be -compatible with 2.0.4 or any later 2.0 series kernel. +This distribution was prepared for Linux kernel version 2.0.30, but should be +compatible with 2.0.4 or any later 2.0 series kernel if BusLogic.patch is also +applied. 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.9.tar.gz + tar -xvzf BusLogic-2.0.10.tar.gz mv README.* LICENSE.* BusLogic.[ch] FlashPoint.c linux/drivers/scsi - patch -p < BusLogic.patch + patch -p < BusLogic.patch # Only for kernels prior to 2.0.30 cd linux make config make depend diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/README.in2000 linux/drivers/scsi/README.in2000 --- v2.0.30/linux/drivers/scsi/README.in2000 Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/README.in2000 Tue Aug 5 13:26:16 1997 @@ -0,0 +1,180 @@ + +UPDATE NEWS: version 1.31 - 6 Jul 97 + + Fixed a bug that caused incorrect SCSI status bytes to be + returned from commands sent to LUN's greater than 0. This + means that CDROM changers work now! Fixed a bug in the + handling of command-line arguments when loaded as a module. + Also put all the header data in in2000.h where it belongs. + There are no longer any differences between this driver in + the 2.1.xx source tree and the 2.0.xx tree, as of 2.0.31 + and 2.1.45 (or is it .46?) - this makes things much easier + for me... + +UPDATE NEWS: version 1.30 - 14 Oct 96 + + Fixed a bug in the code that sets the transfer direction + bit (DESTID_DPD in the WD_DESTINATION_ID register). There + are quite a few SCSI commands that do a write-to-device; + now we deal with all of them correctly. Thanks to Joerg + Dorchain for catching this one. + +UPDATE NEWS: version 1.29 - 24 Sep 96 + + The memory-mapped hardware on the card is now accessed via + the 'readb()' and 'readl()' macros - required by the new + memory management scheme in the 2.1.x kernel series. + As suggested by Andries Brouwer, 'bios_param()' no longer + forces an artificial 1023 track limit on drives. Also + removed some kludge-code left over from struggles with + older (buggy) compilers. + +UPDATE NEWS: version 1.28 - 07 May 96 + + Tightened up the "interrupts enabled/disabled" discipline + in 'in2000_queuecommand()' and maybe 1 or 2 other places. + I _think_ it may have been a little too lax, causing an + occasional crash during full moon. A fully functional + /proc interface is now in place - if you want to play + with it, start by doing 'cat /proc/scsi/in2000/0'. You + can also use it to change a few run-time parameters on + the fly, but it's mostly for debugging. The curious + should take a good look at 'in2000_proc_info()' in the + in2000.c file to get an understanding of what it's all + about; I figure that people who are really into it will + want to add features suited to their own needs... + Also, sync is now DISABLED by default. + +UPDATE NEWS: version 1.27 - 10 Apr 96 + + Fixed a well-hidden bug in the adaptive-disconnect code + that would show up every now and then during extreme + heavy loads involving 2 or more simultaneously active + devices. Thanks to Joe Mack for keeping my nose to the + grindstone on this one. + +UPDATE NEWS: version 1.26 - 07 Mar 96 + + 1.25 had a nasty bug that bit people with swap partitions + and tape drives. Also, in my attempt to guess my way + through Intel assembly language, I made an error in the + inline code for IO writes. Made a few other changes and + repairs - this version (fingers crossed) should work well. + +UPDATE NEWS: version 1.25 - 05 Mar 96 + + Kernel 1.3.70 interrupt mods added; old kernels still OK. + Big help from Bill Earnest and David Willmore on speed + testing and optimizing: I think there's a real improvement + in this area. + New! User-friendly command-line interface for LILO and + module loading - the old method is gone, so you'll need + to read the comments for 'setup_strings' near the top + of in2000.c. For people with CDROM's or other devices + that have a tough time with sync negotiation, you can + now selectively disable sync on individual devices - + search for the 'nosync' keyword in the command-line + comments. Some of you disable the BIOS on the card, which + caused the auto-detect function to fail; there is now a + command-line option to force detection of a ROM-less card. + +UPDATE NEWS: version 1.24a - 24 Feb 96 + + There was a bug in the synchronous transfer code. Only + a few people downloaded before I caught it - could have + been worse. + +UPDATE NEWS: version 1.24 - 23 Feb 96 + + Lots of good changes. Advice from Bill Earnest resulted + in much better detection of cards, more efficient usage + of the fifo, and (hopefully) faster data transfers. The + jury is still out on speed - I hope it's improved some. + One nifty new feature is a cool way of doing disconnect/ + reselect. The driver defaults to what I'm calling + 'adaptive disconnect' - meaning that each command is + evaluated individually as to whether or not it should be + run with the option to disconnect/reselect (if the device + chooses), or as a "SCSI-bus-hog". When several devices + are operating simultaneously, disconnects are usually an + advantage. In a single device system, or if only 1 device + is being accessed, transfers usually go faster if disconnects + are not allowed. + + + +The default arguments (you get these when you don't give an 'in2000' +command-line argument, or you give a blank argument) will cause +the driver to do adaptive disconnect, synchronous transfers, and a +minimum of debug messages. If you want to fool with the options, +search for 'setup_strings' near the top of the in2000.c file and +check the 'hostdata->args' section in in2000.h - but be warned! Not +everything is working yet (some things will never work, probably). +I believe that disabling disconnects (DIS_NEVER) will allow you +to choose a LEVEL2 value higher than 'L2_BASIC', but I haven't +spent a lot of time testing this. You might try 'ENABLE_CLUSTERING' +to see what happens: my tests showed little difference either way. +There's also a define called 'DEFAULT_SX_PER'; this sets the data +transfer speed for the asynchronous mode. I've put it at 500 ns +despite the fact that the card could handle settings of 376 or +252, because higher speeds may be a problem with poor quality +cables or improper termination; 500 ns is a compromise. You can +choose your own default through the command-line with the +'period' keyword. + + +------------------------------------------------ +*********** DIP switch settings ************** +------------------------------------------------ + + sw1-1 sw1-2 BIOS address (hex) + ----------------------------------------- + off off C8000 - CBFF0 + on off D8000 - DBFF0 + off on D0000 - D3FF0 + on on BIOS disabled + + sw1-3 sw1-4 IO port address (hex) + ------------------------------------ + off off 220 - 22F + on off 200 - 20F + off on 110 - 11F + on on 100 - 10F + + sw1-5 sw1-6 sw1-7 Interrupt + ------------------------------ + off off off 15 + off on off 14 + off off on 11 + off on on 10 + on - - disabled + + sw1-8 function depends on BIOS version. In earlier versions this + controlled synchronous data transfer support for MSDOS: + off = disabled + on = enabled + In later ROMs (starting with 01.3 in April 1994) sw1-8 controls + the "greater than 2 disk drive" feature that first appeared in + MSDOS 5.0 (ignored by Linux): + off = 2 drives maximum + on = 7 drives maximum + + sw1-9 Floppy controller + -------------------------- + off disabled + on enabled + +------------------------------------------------ + + I should mention that Drew Eckhardt's 'Generic NCR5380' sources + were my main inspiration, with lots of reference to the IN2000 + driver currently distributed in the kernel source. I also owe + much to a driver written by Hamish Macdonald for Linux-m68k(!). + And to Eric Wright for being an ALPHA guinea pig. And to Bill + Earnest for 2 tons of great input and information. And to David + Willmore for extensive 'bonnie' testing. And to Joe Mack for + continual testing and feedback. + + + John Shifflett jshiffle@netcom.com + diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/README.ncr53c8xx linux/drivers/scsi/README.ncr53c8xx --- v2.0.30/linux/drivers/scsi/README.ncr53c8xx Fri Sep 20 07:00:34 1996 +++ linux/drivers/scsi/README.ncr53c8xx Thu Aug 14 10:30:08 1997 @@ -1,10 +1,10 @@ -The linux NCR53C8XX driver README file +The Linux NCR53C8XX driver README file Written by Gerard Roudier 21 Rue Carnot 95170 DEUIL LA BARRE - FRANCE -12 June 1995 +19 June 1997 =============================================================================== 1. Introduction @@ -21,15 +21,31 @@ 8.4 Set order type for tagged command 8.5 Set debug mode 8.6 Clear profile counters + 8.7 Set flag (no_sync) + 8.8 Debug error recovery 9. Configuration parameters -10. Some constants and flags of the ncr53c8xx.h header files -11. Provided files -12. Installation procedure for Linux version 1 -13. Installation procedure for Linux version 2 -14. Control commands under linux-1.2.13 -15. Known problems - 15.1 Tagged commands with Iomega Jaz device - 15.2 Tagged command queueing cannot be disabled at run time +10. Boot setup commands + 10.1 Syntax + 10.2 Available arguments + 10.3 Advised boot setup commands + 10.4 PCI configuration fix-up boot option + 10.5 Serial NVRAM support boot option +11. Some constants and flags of the ncr53c8xx.h header file +12. Installation + 12.1 Provided files + 12.2 Installation procedure +13. Control commands under linux-1.2.13 +14. Known problems + 14.1 Tagged commands with Iomega Jaz device + 14.2 Device names change when another controller is added +15. SCSI problem troubleshooting +16. Synchonous transfer negotiation tables + 16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers + 16.2 Synchronous timings for fast SCSI-2 53C8XX controllers +17. Serial NVRAM support (by Richard Waltham) + 17.1 Features + 17.2 Symbios NVRAM layout + 17.3 Tekram NVRAM layout =============================================================================== @@ -43,21 +59,37 @@ Wolfgang Stanglmeier Stefan Esser -You can find technical information about the NCR 8xx family in the PCI-HOWTO -written by Michael Will and in the SCSI-HOWTO written by Drew Eckhardt. +You can find technical information about the NCR 8xx family in the +PCI-HOWTO written by Michael Will and in the SCSI-HOWTO written by +Drew Eckhardt. Information about new chips is available at SYMBIOS web server: - http://www.symbios.com -This short documentation only describes the features of the NCR53C8XX driver, -configuration parameters and control commands available through the proc SCSI -file system read / write operations. + http://www.symbios.com/ -This driver has been tested OK with linux/i386 and is currently untested -under linux/Alpha. If you intend to use this driver under linux/Alpha, just -try it first with read-only or mounted read-only devices. +SCSI standard documentations are available at SYMBIOS ftp server: -I am not a native speaker of English and there are probably lots of + ftp://ftp.symbios.com/ + +Usefull SCSI tools written by Eric Youngdale are available at tsx-11: + + ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/scsiinfo-X.Y.tar.gz + ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/scsidev-X.Y.tar.gz + +These tools are not ALPHA but quite clean and work quite well. +It is essential you have the 'scsiinfo' package. + +This short documentation only describes the features of the NCR53C8XX +driver, configuration parameters and control commands available +through the proc SCSI file system read / write operations. + +This driver has been tested OK with linux/i386 and Linux/Alpha. + +Latest driver version and patches are available at: + + ftp://linux.wauug.org/pub/roudier + +I am not a native speaker of English and there are probably lots of mistakes in this README file. Any help will be welcome. @@ -71,8 +103,8 @@ SCSI parity checking Master parity checking -"Wide negotiation" is supported for chips that allow it. -The following table shows some characteristics of NCR 8xx family chips: +"Wide negotiation" is supported for chips that allow it. The +following table shows some characteristics of NCR 8xx family chips: On board Supported by Tested with Chip SDMS BIOS Wide Ultra SCSI the driver the driver @@ -81,12 +113,12 @@ 810A N N N Y Y 815 Y N N Y Y 825 Y Y N Y Y -825A Y Y N Y Not yet -875 Y Y Y(1) Y Not yet - -(1) Ultra SCSI extensions will be supported in a future release of the - driver. +825A Y Y N Y Y +860 N N Y Y Y +875 Y Y Y Y Y +895 Y Y Y(1) Y not yet +(1) The 895 chip is supported 'on paper'. 3. Summary of other supported features. @@ -97,45 +129,58 @@ Debugging information: written to syslog (expert only) Scatter / gather Shared interrupt + Boot setup commands + Serial NVRAM: Symbios and Tekram formats 4. Memory mapped I/O versus normal I/O -Memory mapped I/O has less latency than normal I/O. -Since linux-1.3.x, memory mapped I/O is used rather than normal I/O. -Memory mapped I/O seems to work fine on most hardware configurations, but some -poorly designed motherboards may break this feature. - -During the initialization phase, the driver first tries to use memory mapped -I/O. If nothing seems wrong, it will use memory mapped I/O. -If a flaw is detected, it will use normal I/O. +Memory mapped I/O has less latency than normal I/O. Since +linux-1.3.x, memory mapped I/O is used rather than normal I/O. Memory +mapped I/O seems to work fine on most hardware configurations, but +some poorly designed motherboards may break this feature. -However, it's possible that memory mapped I/O does not work properly and the -driver has not detected the problem. - -The configuration option CONFIG_SCSI_NCR53C8XX_IOMAPPED forces the +The configuration option CONFIG_SCSI_NCR53C8XX_IOMAPPED forces the driver to use normal I/O in all cases. 5. Tagged command queueing -Some SCSI devices do not properly support tagged command queuing. -A safe configuration is to not enable tagged command queuing support at -boot-up, and to enable support of it with the control command "settags" -described further in this text. - -Once you are sure that all your devices properly support tagged command queuing, -you can enable it by default with the CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE -configuration option. +Some SCSI devices do not properly support tagged command queuing. A +safe configuration is to not enable tagged command queuing support at +boot-up, and to enable support of it with the control command +"settags" described further in this text. + +Once you are sure that all your devices properly support tagged +command queuing, you can enable it by default with the +CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE configuration option. + +The maximum number of simultaneous tagged commands queued to a device +is currently set to 4 by default. It is defined in the file +ncr53c8xx.h by SCSI_NCR_MAX_TAGS. This value is suitable for most SCSI +disks. With large SCSI disks (> 2GB, cache > 512KB average seek time +< 10 ms), 8 tagged commands may give better performance. + +In some special conditions, some SCSI disk firmwares may return a +QUEUE FULL status for a SCSI command. This behaviour is managed by the +driver by the following heuristic: + +- Each time a QUEUE FULL status is returned, tagged command queueing is + temporarily disabled. + +- Every 100 successfully completed SCSI commands, if allowed by the + current limit, the maximum number of queueable commands is + incremented and tagged command queueing is reenabled. 6. Parity checking -The driver supports SCSI parity checking and PCI bus master parity checking. -These features must be enabled in order to ensure safe data transfers. -However, some flawed devices or mother boards will have problems with -parity. You can disable parity by choosing first "CONFIG_EXPERIMENTAL". -Then, "make config" will allow to set the following configuration options: +The driver supports SCSI parity checking and PCI bus master parity +checking. These features must be enabled in order to ensure safe data +transfers. However, some flawed devices or mother boards will have +problems with parity. You can disable parity by choosing first +"CONFIG_EXPERIMENTAL". Then, "make config" will allow to set the +following configuration options: CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK (disable SCSI parity checking) CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK (disable master parity checking) @@ -145,16 +190,19 @@ Profiling information is available through the proc SCSI file system. The device associated with a host has the following pathname: + /proc/scsi/ncr53c8xx/N (N=0,1,2 ....) Generally, only 1 board is used on hardware configuration, and that device is: /proc/scsi/ncr53c8xx/0 -However, if the driver has been made as module, the number of the hosts is -incremented each time the driver is loaded. +However, if the driver has been made as module, the number of the +hosts is incremented each time the driver is loaded. In order to display profiling information, just enter: + cat /proc/scsi/ncr53c8xx/0 + and you will get something like the following text: ------------------------------------------------------- @@ -162,6 +210,7 @@ Chip NCR53C810, device id 0x1, revision id 0x2 IO port address 0x6000, IRQ number 10 Using memory mapped IO at virtual address 0x282c000 + Synchronous transfer period 25, max commands per lun 4 Profiling information: num_trans = 18014 num_kbytes = 671314 @@ -175,7 +224,7 @@ ms_post = 1320 ------------------------------------------------------- -General information is easy to understand. The device ID and the +General information is easy to understand. The device ID and the revision ID identify the SCSI chip as follows: Chip Device id Revision Id @@ -187,15 +236,18 @@ 860 0x6 825A 0x3 >= 0x10 875 0xf +895 0xc The profiling information is updated upon completion of SCSI commands. -A data structure is allocated and zeroed when the host adapter is -attached. So, if the driver is a module, the profile counters are cleared each -time the driver is loaded. -The "clearprof" command allows you to clear these counters at any time. +A data structure is allocated and zeroed when the host adapter is +attached. So, if the driver is a module, the profile counters are +cleared each time the driver is loaded. The "clearprof" command +allows you to clear these counters at any time. The following counters are available: -("num" prefix means "number of", "ms" means milli-seconds) + +("num" prefix means "number of", +"ms" means milli-seconds) num_trans Number of completed commands @@ -238,46 +290,43 @@ (time from SCSI status get to command completion call) Example above: 1.32 seconds spent for post processing -Due to the 1/100 second tick of the system clock, "ms_post" time may be -wrong. +Due to the 1/100 second tick of the system clock, "ms_post" time may +be wrong. -In the example above, we got 18038 interrupts "on the fly" and only 1673 script -breaks probably due to disconnections inside a segment of the scatter list. -This is an excellent result due to the fact that the driver tries to use small -data segments (512) for the scatter list. The CPU load of this rescatter process -is acceptable. Unlike other SCSI processors, NCR53C8XX controllers do not need -large data chunks in order to get better performance, and it seems that it -is just the opposite. -The scatter/gather algorithm of the middle SCSI driver is not optimal for -NCR SCSI processors and should be tunable according to host type. - -You can tune the "wished" segment size for the scatterlist by changing the -following "define" in the file ncr53c8xx.h. -Use only power of 2 greater than 512 (1024, 2048 or 4096). - -SCSI_NCR_SEGMENT_SIZE (default: 512) +In the example above, we got 18038 interrupts "on the fly" and only +1673 script breaks generally due to disconnections inside a segment +of the scatter list. 8. Control commands -Control commands can be sent to the driver with write operations to the -proc SCSI file system. The generic command syntax is the following: +Control commands can be sent to the driver with write operations to +the proc SCSI file system. The generic command syntax is the +following: echo " " >/proc/scsi/ncr53c8xx/0 (assumes controller number is 0) +Using "all" for "" parameter with the commands below will +apply to all targets of the SCSI chain (except the controller). + Available commands: -8.1 Set minimum synchronous period +8.1 Set minimum synchronous period factor - setsync + setsync target: target number - period: minimum synchronous period in nano-seconds. - Maximum speed = 1000/(4*period) MB/second + period: minimum synchronous period. + Maximum speed = 1000/(4*period factor) except for special + cases below. Specify a period of 255, to force asynchronous transfer mode. + 10 means 25 nano-seconds synchronous period + 11 means 30 nano-seconds synchronous period + 12 means 50 nano-seconds synchronous period + 8.2 Set wide size setwide @@ -292,7 +341,6 @@ target: target number tags: number of concurrent tagged commands must not be greater than SCSI_NCR_MAX_TAGS (default: 4) - must not be lower that 1 (see: known problems) 8.4 Set order type for tagged command @@ -321,23 +369,63 @@ nego: print information about SCSI negotiations phase: print information on script interruptions + Use "setdebug" with no argument to reset debug flags. + 8.6 Clear profile counters clearprof - The profile counters are automatically cleared when the amount of data - transfered reaches 1000 GB in order to avoid overflow. + The profile counters are automatically cleared when the amount of + data transfered reaches 1000 GB in order to avoid overflow. The "clearprof" command allows you to clear these counters at any time. +8.7 Set flag (no_sync) + + setflag + + target: target number + + For the moment, only one flag is available: + + no_sync: not allow target to disconnect. + + Do not specify any flag in order to reset the flag. For example: + - setflag 4 + will reset no_sync flag for target 4, so will allow it disconnections. + - setflag all + will allow disconnection for all devices on the SCSI bus. + + +8.8 Debug error recovery + + debug_error_recovery + + Available error type to trigger: + sge: SCSI gross error + abort: abort command from the middle-level driver + reset: reset command from the middle-level driver + parity: scsi parity detected in DATA IN phase + none: restore driver normal behaviour + + The code corresponding to this feature is normally not compiled. + Its purpose is driver testing only. In order to compile the code + that allows to trigger error recovery you must define at compile time + SCSI_NCR_DEBUG_ERROR_RECOVERY. + If you have compiled the driver with this option, nothing will happen + as long as you donnot use the control command 'debug_error_recovery' + with sge, abort, reset or parity as argument. + If you select an error type, it will be triggered by the driver every + 30 seconds. + 9. Configuration parameters -If the firmware of all your devices is perfect enough, all the features -supported by the driver can be enabled at start-up. -However, if only one has a flaw for some SCSI feature, you can disable the -support by the driver of this feature at linux start-up and enable this -feature after boot-up only for devices that support it safely. +If the firmware of all your devices is perfect enough, all the +features supported by the driver can be enabled at start-up. However, +if only one has a flaw for some SCSI feature, you can disable the +support by the driver of this feature at linux start-up and enable +this feature after boot-up only for devices that support it safely. CONFIG_SCSI_NCR53C8XX_IOMAPPED (default answer: n) Answer "y" if you suspect your mother board to not allow memory mapped I/O. @@ -347,9 +435,16 @@ Answer "y" if you are sure that all your SCSI devices that are able to accept tagged commands will proceed safely. -CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS (default answer: n) - This option forces asynchronous transfer mode for all SCSI devices. - +CONFIG_SCSI_NCR53C8XX_MAX_TAGS (default answer: 4) + This option allows you to specify the maximum number of tagged commands + that can be queued to a device. + +CONFIG_SCSI_NCR53C8XX_SYNC (default answer: 5) + This option allows you to specify the frequency in MHz the driver + will use at boot time for synchronous data transfer negotiations. + This frequency can be changed later with the "setsync" control command. + 0 means "asynchronous data transfers". + CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO (default answer: n) Force synchronous negotiation for all SCSI-2 devices. Some SCSI-2 devices do not report this feature in byte 7 of inquiry @@ -360,12 +455,331 @@ you can answer "y". Then, all SCSI devices will never disconnect the bus even while performing long SCSI operations. - -10. Some constants and flags of the ncr53c8xx.h header files - -Some of these are defined from the configuration parameters. -To change other "defines", you must edit the header file. -Do that only if you know what you are doing. +CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT + Genuine SYMBIOS boards use GPIO0 in output for controller LED and GPIO3 + bit as a flag indicating singled-ended/differential interface. + If all the boards of your system are genuine SYMBIOS boards or use + BIOS and drivers from SYMBIOS, you would want to enable this option. + This option must NOT be enabled if your system has at least one 53C8XX + based scsi board with a vendor-specific BIOS. + For example, Tekram DC-390/U, DC-390/W and DC-390/F scsi controllers + use a vendor-specific BIOS and are known to not use SYMBIOS compatible + GPIO wiring. So, this option must not be enabled if your system has + such a board installed. + +CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT + Enable support for reading the serial NVRAM data on Symbios and + some Symbios compatible cards, and Tekram DC390W/U/F cards. Useful for + systems with more than one Symbios compatible controller where at least + one has a serial NVRAM, or for a system with a mixture of Symbios and + Tekram cards. Enables setting the boot order of host adaptors + to something other than the default order or "reverse probe" order. + Also enables Symbios and Tekram cards to be distinguished so + CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT may be set in a system with a + mixture of Symbios and Tekram cards so the Symbios cards can make use of + the full range of Symbios features, differential, led pin, without + causing problems for the Tekram card(s). + +10. Boot setup commands + +10.1 Syntax + +Setup commands can be passed to the driver at boot time. +A boot setup command for the ncr53c8xx driver begins with the driver name +"ncr53c8xx=". The kernel syntax parser then expects an optionnal list of +integers separated with comma followed by an optionnal list of comma- +separated strings. Example of boot setup command under lilo prompt: + +lilo: linux root=/dev/hda2 ncr53c8xx=tags:4,sync:10,debug:0x200 + +- enable tagged commands, up to 4 tagged commands queued. +- set synchronous negotiation speed to 10 Mega-transfers / second. +- set DEBUG_NEGO flag. + +For the moment, the integer list of arguments is disgarded by the driver. +It will be used in the future in order to allow a per controller setup. + +Each string argument must be specified as "keyword:value". Only lower-case +characters and digits are allowed. + +10.2 Available arguments + +Master parity checking + mpar:y enabled + mpar:n disabled + +Scsi parity checking + spar:y enabled + spar:n disabled + +Scsi disconnections + disc:y enabled + disc:n disabled + +Special features + Only apply to 810A, 825A, 860 and 875 controllers. + Have no effect with normal 810 and 825. + specf:y enabled + specf:n disabled + +Ultra SCSI support + Only apply to 860 and 875 controllers. + Have no effect with other ones. + ultra:y enabled + ultra:n disabled + +Number of tagged commands + tags:0 (or tags:1 ) tagged command queuing disabled + tags:#tags (#tags > 1) tagged command queuing enabled + #tags will be truncated to the max queued commands configuration parameter. + If the driver is configured with a maximum of 4 queued commands, tags:4 is + the right argument to specify. + +Default synchronous period factor + sync:255 disabled (asynchronous transfer mode) + sync:#factor + #factor = 10 Ultra-2 SCSI 40 Mega-transfers / second + #factor = 11 Ultra-2 SCSI 33 Mega-transfers / second + #factor < 25 Ultra SCSI 20 Mega-transfers / second + #factor < 50 Fast SCSI-2 + + In all cases, the driver will use the minimum transfer period supported by + controllers according to NCR53C8XX chip type. + +Negotiate synchronous with all devices + (force sync nego) + fsn:y enabled + fsn:n disabled + +Verbosity level + verb:0 minimal + verb:1 normal + verb:2 too much + +Debug mode + debug:0 clear debug flags + debug:#x set debug flags + #x is an integer value combining the following power-of-2 values: + DEBUG_ALLOC 0x1 + DEBUG_PHASE 0x2 + DEBUG_POLL 0x4 + DEBUG_QUEUE 0x8 + DEBUG_RESULT 0x10 + DEBUG_SCATTER 0x20 + DEBUG_SCRIPT 0x40 + DEBUG_TINY 0x80 + DEBUG_TIMING 0x100 + DEBUG_NEGO 0x200 + DEBUG_TAGS 0x400 + DEBUG_FREEZE 0x800 + DEBUG_RESTART 0x1000 + + You can play safely with DEBUG_NEGO. However, some of these flags may + generate bunches of syslog messages. + +Burst max + burst:0 burst disabled + burst:255 get burst length from initial IO register settings. + burst:#x burst enabled (1<<#x burst transfers max) + #x is an integer value which is log base 2 of the burst transfers max. + The NCR53C875 and NCR53C825A support up to 128 burst transfers (#x = 7). + Other chips only support up to 16 (#x = 4). + This is a maximum value. The driver set the burst length according to chip + and revision ids. By default the driver uses the maximum value supported + by the chip. + +LED support + led:1 enable LED support + led:0 disable LED support + Donnot enable LED support if your scsi board does not use SDMS BIOS. + (See 'Configuration parameters') + +Max wide + wide:1 wide scsi enabled + wide:0 wide scsi disabled + Some scsi boards use a 875 (ultra wide) and only supply narrow connectors. + If you have connected a wide device with a 50 pins to 68 pins cable + converter, any accepted wide negotiation will break further data transfers. + In such a case, using "wide:0" in the bootup command will be helpfull. + +Differential mode + diff:0 never set up diff mode + diff:1 set up diff mode if BIOS set it + diff:2 always set up diff mode + diff:3 set diff mode if GPIO3 is not set + +IRQ mode + irqm:0 always open drain + irqm:1 same as initial settings (assumed BIOS settings) + irqm:2 always totem pole + +Reverse probe + revprob:n probe chip ids from the PCI configuration in this order: + 810, 815, 820, 860, 875, 885, 895, 896 + revprob:y probe chip ids in the reverse order. + +Fix up PCI configuration space + pcifix: