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 Mon Aug 4 12:10:37 1997 @@ -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 @@ -1125,9 +1125,9 @@ S: Germany 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 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 Mon Aug 11 13:37:24 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 @@ -589,10 +612,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 +971,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 @@ -1723,6 +1749,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 +2131,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 @@ -2417,19 +2466,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 +3853,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 +3867,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 +3959,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/MAINTAINERS linux/MAINTAINERS --- v2.0.30/linux/MAINTAINERS Tue Apr 8 08:47:45 1997 +++ linux/MAINTAINERS Tue Aug 5 10:44:26 1997 @@ -128,6 +128,12 @@ 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 + BUSLOGIC SCSI DRIVER P: Leonard N. Zubkoff M: Leonard N. Zubkoff @@ -164,6 +170,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 +218,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 @@ -318,7 +336,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/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 Mon Aug 4 17:33:59 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) @@ -119,6 +124,7 @@ # ISDN subsystem # # CONFIG_ISDN is not set +CONFIG_HISAX_EURO=y # # CD-ROM drivers (not for SCSI or IDE/ATAPI drives) @@ -129,7 +135,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 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/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 Mon Aug 4 15:03:06 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/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/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 Wed Aug 6 17:52:01 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); } 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 Mon Aug 4 17:34:00 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,7 +2444,8 @@ /* * 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 first = 1; @@ -2011,648 +2461,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); + 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 +3246,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 +3293,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 +3313,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 +3326,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 +3336,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 +3365,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< 1512 effectively disables this feature. */ +static const rx_copybreak = 200; + +#include +#ifdef MODULE +#include #include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif #include #include @@ -39,6 +61,7 @@ #include #include #include +#include /* For NR_IRQS only. */ #include #include @@ -46,27 +69,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 +139,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 +185,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 +221,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 +253,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 +271,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 +309,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 +325,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 +370,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 +390,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 +437,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 +478,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 +633,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 +658,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 +673,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 +717,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 +741,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 +761,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 +790,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 +801,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 +851,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 +907,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 +952,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 +998,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 +1157,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 +1168,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 +1291,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 +1351,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 +1369,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 +1401,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 +1420,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 +1457,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 +1576,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 +1617,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 +1680,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 +1714,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 Mon Aug 11 13:37:24 1997 @@ -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 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 Mon Aug 11 13:37:24 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 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 Mon Aug 11 13:37:24 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); @@ -110,6 +111,9 @@ #endif #if defined(CONFIG_ULTRA) && ultra_probe(dev) +#endif +#if defined(CONFIG_ULTRA32) + && ultra32_probe(dev) #endif #if defined(CONFIG_SMC9194) && smc_init(dev) 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/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/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 Mon Aug 4 17:13:17 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,12 +62,21 @@ /* Do we have a non std. amount of memory? (in units of 256 byte pages) */ /* #define PACKETBUF_MEMSIZE 0x40 */ -/* ---- No user-serviceable parts below ---- */ - /* A zero-terminated list of I/O addresses to be probed. */ static unsigned int netcard_portlist[] = { 0x300, 0x280, 0x320, 0x340, 0x360, 0}; +#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. */ static struct { const char *name8, *name16; unsigned char SAprefix[4];} @@ -80,10 +90,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 +113,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); @@ -154,42 +169,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 +184,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 +338,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 +389,8 @@ } - if (pci_irq_line) { + if (pci_irq_line) dev->irq = pci_irq_line; - } if (dev->irq < 2) { autoirq_setup(0); @@ -713,17 +740,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 Sun Aug 3 14:02:34 1997 @@ -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/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 Mon Aug 11 13:37:24 1997 @@ -0,0 +1,414 @@ +/* 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 +#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 5 09:15:42 1997 @@ -105,6 +105,7 @@ DEVICE( SI, SI_5511, "85C5511"), DEVICE( SI, SI_5513, "85C5513"), 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 +120,7 @@ DEVICE( BUSLOGIC, BUSLOGIC_MULTIMASTER, "MultiMaster"), DEVICE( BUSLOGIC, BUSLOGIC_FLASHPOINT, "FlashPoint"), DEVICE( OAK, OAK_OTI107, "OTI107"), + DEVICE( WINBOND2, WINBOND2_89C940,"NE2000-PCI"), DEVICE( PROMISE, PROMISE_5300, "DC5030"), DEVICE( N9, N9_I128, "Imagine 128"), DEVICE( N9, N9_I128_2, "Imagine 128v2"), @@ -199,6 +201,8 @@ DEVICE( ZEITNET, ZEITNET_1225, "1225"), DEVICE( SPECIALIX, SPECIALIX_XIO, "XIO/SIO host"), DEVICE( SPECIALIX, SPECIALIX_RIO, "RIO host"), + 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"), @@ -241,6 +245,7 @@ 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_82371AB, "82371AB 430TX PIIX4"), DEVICE( INTEL, INTEL_P6, "Orion P6"), DEVICE( ADAPTEC, ADAPTEC_7850, "AIC-7850"), DEVICE( ADAPTEC, ADAPTEC_7855, "AIC-7855"), @@ -481,6 +486,7 @@ 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_PROMISE: return "Promise Technology"; case PCI_VENDOR_ID_N9: return "Number Nine"; case PCI_VENDOR_ID_UMC: return "UMC"; @@ -519,6 +525,7 @@ 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_SYMPHONY: return "Symphony"; 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/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 Mon Aug 4 17:15:07 1997 @@ -18,7 +18,14 @@ 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 + int ' Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8 + 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 @@ -80,5 +87,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 Mon Aug 11 13:38:56 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 @@ -225,6 +226,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 +260,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 +355,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,20 +371,15 @@ 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 - 53c8xx_d.h 53c8xx_u.h : 53c7,8xx.scr script_asm.pl ln -sf 53c7,8xx.scr fake.c $(CPP) -traditional -DCHIP=810 fake.c | grep -v '^#' | perl script_asm.pl @@ -380,8 +387,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/aic7xxx/aic7xxx.reg linux/drivers/scsi/aic7xxx/aic7xxx.reg --- v2.0.30/linux/drivers/scsi/aic7xxx/aic7xxx.reg Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/aic7xxx/aic7xxx.reg Thu Jul 31 12:37:17 1997 @@ -0,0 +1,1120 @@ +/* + * Aic7xxx register and scratch ram definitions. + * + * Copyright (c) 1994-1997 Justin Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Where this Software is combined with software released under the terms of + * the GNU Public License ("GPL") and the terms of the GPL would require the + * combined work to also be released under the terms of the GPL, the terms + * and conditions of this License will apply in addition to those of the + * GPL with the exception of any terms or conditions of this License that + * conflict with, or are expressly prohibited by, the GPL. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $Id: aic7xxx.reg,v 1.4 1997/06/27 19:38:39 gibbs Exp $ + */ + +/* + * This file is processed by the aic7xxx_asm utility for use in assembling + * firmware for the aic7xxx family of SCSI host adapters as well as to generate + * a C header file for use in the kernel portion of the Aic7xxx driver. + * + * All page numbers refer to the Adaptec AIC-7770 Data Book available from + * Adaptec's Technical Documents Department 1-800-934-2766 + */ + +/* + * SCSI Sequence Control (p. 3-11). + * Each bit, when set starts a specific SCSI sequence on the bus + */ +register SCSISEQ { + address 0x000 + access_mode RW + bit TEMODE 0x80 + bit ENSELO 0x40 + bit ENSELI 0x20 + bit ENRSELI 0x10 + bit ENAUTOATNO 0x08 + bit ENAUTOATNI 0x04 + bit ENAUTOATNP 0x02 + bit SCSIRSTO 0x01 +} + +/* + * SCSI Transfer Control 0 Register (pp. 3-13). + * Controls the SCSI module data path. + */ +register SXFRCTL0 { + address 0x001 + access_mode RW + bit DFON 0x80 + bit DFPEXP 0x40 + bit FAST20 0x20 + bit CLRSTCNT 0x10 + bit SPIOEN 0x08 + bit SCAMEN 0x04 + bit CLRCHN 0x02 +} + +/* + * SCSI Transfer Control 1 Register (pp. 3-14,15). + * Controls the SCSI module data path. + */ +register SXFRCTL1 { + address 0x002 + access_mode RW + bit BITBUCKET 0x80 + bit SWRAPEN 0x40 + bit ENSPCHK 0x20 + mask STIMESEL 0x18 + bit ENSTIMER 0x04 + bit ACTNEGEN 0x02 + bit STPWEN 0x01 /* Powered Termination */ +} + +/* + * SCSI Control Signal Read Register (p. 3-15). + * Reads the actual state of the SCSI bus pins + */ +register SCSISIGI { + address 0x003 + access_mode RO + bit CDI 0x80 + bit IOI 0x40 + bit MSGI 0x20 + bit ATNI 0x10 + bit SELI 0x08 + bit BSYI 0x04 + bit REQI 0x02 + bit ACKI 0x01 +/* + * Possible phases in SCSISIGI + */ + mask PHASE_MASK CDI|IOI|MSGI + mask P_DATAOUT 0x00 + mask P_DATAIN IOI + mask P_COMMAND CDI + mask P_MESGOUT CDI|MSGI + mask P_STATUS CDI|IOI + mask P_MESGIN CDI|IOI|MSGI +} + +/* + * SCSI Control Signal Write Register (p. 3-16). + * Writing to this register modifies the control signals on the bus. Only + * those signals that are allowed in the current mode (Initiator/Target) are + * asserted. + */ +register SCSISIGO { + address 0x003 + access_mode WO + bit CDO 0x80 + bit IOO 0x40 + bit MSGO 0x20 + bit ATNO 0x10 + bit SELO 0x08 + bit BSYO 0x04 + bit REQO 0x02 + bit ACKO 0x01 +/* + * Possible phases to write into SCSISIG0 + */ + mask PHASE_MASK CDI|IOI|MSGI + mask P_DATAOUT 0x00 + mask P_DATAIN IOI + mask P_COMMAND CDI + mask P_MESGOUT CDI|MSGI + mask P_STATUS CDI|IOI + mask P_MESGIN CDI|IOI|MSGI +} + +/* + * SCSI Rate Control (p. 3-17). + * Contents of this register determine the Synchronous SCSI data transfer + * rate and the maximum synchronous Req/Ack offset. An offset of 0 in the + * SOFS (3:0) bits disables synchronous data transfers. Any offset value + * greater than 0 enables synchronous transfers. + */ +register SCSIRATE { + address 0x004 + access_mode RW + bit WIDEXFER 0x80 /* Wide transfer control */ + mask SXFR 0x70 /* Sync transfer rate */ + mask SOFS 0x0f /* Sync offset */ +} + +/* + * SCSI ID (p. 3-18). + * Contains the ID of the board and the current target on the + * selected channel. + */ +register SCSIID { + address 0x005 + access_mode RW + mask TID 0xf0 /* Target ID mask */ + mask OID 0x0f /* Our ID mask */ +} + +/* + * SCSI Latched Data (p. 3-19). + * Read/Write latches used to transfer data on the SCSI bus during + * Automatic or Manual PIO mode. SCSIDATH can be used for the + * upper byte of a 16bit wide asynchronouse data phase transfer. + */ +register SCSIDATL { + address 0x006 + access_mode RW +} + +register SCSIDATH { + address 0x007 + access_mode RW +} + +/* + * SCSI Transfer Count (pp. 3-19,20) + * These registers count down the number of bytes transferred + * across the SCSI bus. The counter is decremented only once + * the data has been safely transferred. SDONE in SSTAT0 is + * set when STCNT goes to 0 + */ +register STCNT { + address 0x008 + size 3 + access_mode RW +} + +/* + * Clear SCSI Interrupt 0 (p. 3-20) + * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0. + */ +register CLRSINT0 { + address 0x00b + access_mode WO + bit CLRSELDO 0x40 + bit CLRSELDI 0x20 + bit CLRSELINGO 0x10 + bit CLRSWRAP 0x08 + bit CLRSPIORDY 0x02 +} + +/* + * SCSI Status 0 (p. 3-21) + * Contains one set of SCSI Interrupt codes + * These are most likely of interest to the sequencer + */ +register SSTAT0 { + address 0x00b + access_mode RO + bit TARGET 0x80 /* Board acting as target */ + bit SELDO 0x40 /* Selection Done */ + bit SELDI 0x20 /* Board has been selected */ + bit SELINGO 0x10 /* Selection In Progress */ + bit SWRAP 0x08 /* 24bit counter wrap */ + bit SDONE 0x04 /* STCNT = 0x000000 */ + bit SPIORDY 0x02 /* SCSI PIO Ready */ + bit DMADONE 0x01 /* DMA transfer completed */ +} + +/* + * Clear SCSI Interrupt 1 (p. 3-23) + * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1. + */ +register CLRSINT1 { + address 0x00c + access_mode WO + bit CLRSELTIMEO 0x80 + bit CLRATNO 0x40 + bit CLRSCSIRSTI 0x20 + bit CLRBUSFREE 0x08 + bit CLRSCSIPERR 0x04 + bit CLRPHASECHG 0x02 + bit CLRREQINIT 0x01 +} + +/* + * SCSI Status 1 (p. 3-24) + */ +register SSTAT1 { + address 0x00c + access_mode RO + bit SELTO 0x80 + bit ATNTARG 0x40 + bit SCSIRSTI 0x20 + bit PHASEMIS 0x10 + bit BUSFREE 0x08 + bit SCSIPERR 0x04 + bit PHASECHG 0x02 + bit REQINIT 0x01 +} + +/* + * SCSI Status 2 (pp. 3-25,26) + */ +register SSTAT2 { + address 0x00d + access_mode RO + bit OVERRUN 0x80 + mask SFCNT 0x1f +} + +/* + * SCSI Status 3 (p. 3-26) + */ +register SSTAT3 { + address 0x00e + access_mode RO + mask SCSICNT 0xf0 + mask OFFCNT 0x0f +} + +/* + * SCSI Test Control (p. 3-27) + */ +register SCSITEST { + address 0x00f + access_mode RW + bit RQAKCNT 0x04 + bit CNTRTEST 0x02 + bit CMODE 0x01 +} + +/* + * SCSI Interrupt Mode 1 (p. 3-28) + * Setting any bit will enable the corresponding function + * in SIMODE0 to interrupt via the IRQ pin. + */ +register SIMODE0 { + address 0x010 + access_mode RW + bit ENSELDO 0x40 + bit ENSELDI 0x20 + bit ENSELINGO 0x10 + bit ENSWRAP 0x08 + bit ENSDONE 0x04 + bit ENSPIORDY 0x02 + bit ENDMADONE 0x01 +} + +/* + * SCSI Interrupt Mode 1 (pp. 3-28,29) + * Setting any bit will enable the corresponding function + * in SIMODE1 to interrupt via the IRQ pin. + */ +register SIMODE1 { + address 0x011 + access_mode RW + bit ENSELTIMO 0x80 + bit ENATNTARG 0x40 + bit ENSCSIRST 0x20 + bit ENPHASEMIS 0x10 + bit ENBUSFREE 0x08 + bit ENSCSIPERR 0x04 + bit ENPHASECHG 0x02 + bit ENREQINIT 0x01 +} + +/* + * SCSI Data Bus (High) (p. 3-29) + * This register reads data on the SCSI Data bus directly. + */ +register SCSIBUSL { + address 0x012 + access_mode RO +} + +register SCSIBUSH { + address 0x013 + access_mode RO +} + +/* + * SCSI/Host Address (p. 3-30) + * These registers hold the host address for the byte about to be + * transferred on the SCSI bus. They are counted up in the same + * manner as STCNT is counted down. SHADDR should always be used + * to determine the address of the last byte transferred since HADDR + * can be skewed by write ahead. + */ +register SHADDR { + address 0x014 + size 4 + access_mode RO +} + +/* + * Selection Timeout Timer (p. 3-30) + */ +register SELTIMER { + address 0x018 + access_mode RW + bit STAGE6 0x20 + bit STAGE5 0x10 + bit STAGE4 0x08 + bit STAGE3 0x04 + bit STAGE2 0x02 + bit STAGE1 0x01 +} + +/* + * Selection/Reselection ID (p. 3-31) + * Upper four bits are the device id. The ONEBIT is set when the re/selecting + * device did not set its own ID. + */ +register SELID { + address 0x019 + access_mode RW + mask SELID_MASK 0xf0 + bit ONEBIT 0x08 +} + +/* + * SCSI Block Control (p. 3-32) + * Controls Bus type and channel selection. In a twin channel configuration + * addresses 0x00-0x1e are gated to the appropriate channel based on this + * register. SELWIDE allows for the coexistence of 8bit and 16bit devices + * on a wide bus. + */ +register SBLKCTL { + address 0x01f + access_mode RW + bit DIAGLEDEN 0x80 /* Aic78X0 only */ + bit DIAGLEDON 0x40 /* Aic78X0 only */ + bit AUTOFLUSHDIS 0x20 + bit SELBUSB 0x08 + bit SELWIDE 0x02 +} + +/* + * Sequencer Control (p. 3-33) + * Error detection mode and speed configuration + */ +register SEQCTL { + address 0x060 + access_mode RW + bit PERRORDIS 0x80 + bit PAUSEDIS 0x40 + bit FAILDIS 0x20 + bit FASTMODE 0x10 + bit BRKADRINTEN 0x08 + bit STEP 0x04 + bit SEQRESET 0x02 + bit LOADRAM 0x01 +} + +/* + * Sequencer RAM Data (p. 3-34) + * Single byte window into the Scratch Ram area starting at the address + * specified by SEQADDR0 and SEQADDR1. To write a full word, simply write + * four bytes in sucessesion. The SEQADDRs will increment after the most + * significant byte is written + */ +register SEQRAM { + address 0x061 + access_mode RW +} + +/* + * Sequencer Address Registers (p. 3-35) + * Only the first bit of SEQADDR1 holds addressing information + */ +register SEQADDR0 { + address 0x062 + access_mode RW +} + +register SEQADDR1 { + address 0x063 + access_mode RW + mask SEQADDR1_MASK 0x01 +} + +/* + * Accumulator + * We cheat by passing arguments in the Accumulator up to the kernel driver + */ +register ACCUM { + address 0x064 + access_mode RW + accumulator +} + +register SINDEX { + address 0x065 + access_mode RW + sindex +} + +register DINDEX { + address 0x066 + access_mode RW +} + +register ALLONES { + address 0x069 + access_mode RO + allones +} + +register ALLZEROS { + address 0x06a + access_mode RO + allzeros +} + +register NONE { + address 0x06a + access_mode WO + none +} + +register FLAGS { + address 0x06b + access_mode RO + bit ZERO 0x02 + bit CARRY 0x01 +} + +register SINDIR { + address 0x06c + access_mode RO +} + +register DINDIR { + address 0x06d + access_mode WO +} + +register FUNCTION1 { + address 0x06e + access_mode RW +} + +register STACK { + address 0x06f + access_mode RO +} + +/* + * Board Control (p. 3-43) + */ +register BCTL { + address 0x084 + access_mode RW + bit ACE 0x08 + bit ENABLE 0x01 +} + +/* + * On the aic78X0 chips, Board Control is replaced by the DSCommand + * register (p. 4-64) + */ +register DSCOMMAND { + address 0x084 + access_mode RW + bit CACHETHEN 0x80 /* Cache Threshold enable */ + bit DPARCKEN 0x40 /* Data Parity Check Enable */ + bit MPARCKEN 0x20 /* Memory Parity Check Enable */ + bit EXTREQLCK 0x10 /* External Request Lock */ +} + +/* + * Bus On/Off Time (p. 3-44) + */ +register BUSTIME { + address 0x085 + access_mode RW + mask BOFF 0xf0 + mask BON 0x0f +} + +/* + * Bus Speed (p. 3-45) + */ +register BUSSPD { + address 0x086 + access_mode RW + mask DFTHRSH 0xc0 + mask STBOFF 0x38 + mask STBON 0x07 + mask DFTHRSH_100 0xc0 +} + +/* + * Host Control (p. 3-47) R/W + * Overall host control of the device. + */ +register HCNTRL { + address 0x087 + access_mode RW + bit POWRDN 0x40 + bit SWINT 0x10 + bit IRQMS 0x08 + bit PAUSE 0x04 + bit INTEN 0x02 + bit CHIPRST 0x01 + bit CHIPRSTACK 0x01 +} + +/* + * Host Address (p. 3-48) + * This register contains the address of the byte about + * to be transferred across the host bus. + */ +register HADDR { + address 0x088 + size 4 + access_mode RW +} + +register HCNT { + address 0x08c + size 3 + access_mode RW +} + +/* + * SCB Pointer (p. 3-49) + * Gate one of the four SCBs into the SCBARRAY window. + */ +register SCBPTR { + address 0x090 + access_mode RW +} + +/* + * Interrupt Status (p. 3-50) + * Status for system interrupts + */ +register INTSTAT { + address 0x091 + access_mode RW + bit BRKADRINT 0x08 + bit SCSIINT 0x04 + bit CMDCMPLT 0x02 + bit SEQINT 0x01 + mask BAD_PHASE SEQINT /* unknown scsi bus phase */ + mask SEND_REJECT 0x10|SEQINT /* sending a message reject */ + mask NO_IDENT 0x20|SEQINT /* no IDENTIFY after reconnect*/ + mask NO_MATCH 0x30|SEQINT /* no cmd match for reconnect */ + mask EXTENDED_MSG 0x40|SEQINT /* Extended message received */ + mask NO_MATCH_BUSY 0x50|SEQINT /* Couldn't find BUSY SCB */ + mask REJECT_MSG 0x60|SEQINT /* Reject message received */ + mask BAD_STATUS 0x70|SEQINT /* Bad status from target */ + mask RESIDUAL 0x80|SEQINT /* Residual byte count != 0 */ + mask ABORT_CMDCMPLT 0x91 /* + * Command tagged for abort + * completed successfully. + */ + mask AWAITING_MSG 0xa0|SEQINT /* + * Kernel requested to specify + * a message to this target + * (command was null), so tell + * it that it can fill the + * message buffer. + */ + mask MSG_BUFFER_BUSY 0xc0|SEQINT /* + * Sequencer wants to use the + * message buffer, but it + * already contains a message + */ + mask MSGIN_PHASEMIS 0xd0|SEQINT /* + * Target changed phase on us + * when we were expecting + * another msgin byte. + */ + mask DATA_OVERRUN 0xe0|SEQINT /* + * Target attempted to write + * beyond the bounds of its + * command. + */ + + mask SEQINT_MASK 0xf0|SEQINT /* SEQINT Status Codes */ + mask INT_PEND (BRKADRINT|SEQINT|SCSIINT|CMDCMPLT) +} + +/* + * Hard Error (p. 3-53) + * Reporting of catastrophic errors. You usually cannot recover from + * these without a full board reset. + */ +register ERROR { + address 0x092 + access_mode RO + bit PARERR 0x08 + bit ILLOPCODE 0x04 + bit ILLSADDR 0x02 + bit ILLHADDR 0x01 +} + +/* + * Clear Interrupt Status (p. 3-52) + */ +register CLRINT { + address 0x092 + access_mode WO + bit CLRBRKADRINT 0x08 + bit CLRSCSIINT 0x04 + bit CLRCMDINT 0x02 + bit CLRSEQINT 0x01 +} + +register DFCNTRL { + address 0x093 + access_mode RW + bit WIDEODD 0x40 + bit SCSIEN 0x20 + bit SDMAEN 0x10 + bit SDMAENACK 0x10 + bit HDMAEN 0x08 + bit HDMAENACK 0x08 + bit DIRECTION 0x04 + bit FIFOFLUSH 0x02 + bit FIFORESET 0x01 +} + +register DFSTATUS { + address 0x094 + access_mode RO + bit DWORDEMP 0x20 + bit MREQPEND 0x10 + bit HDONE 0x08 + bit DFTHRESH 0x04 + bit FIFOFULL 0x02 + bit FIFOEMP 0x01 +} + +register DFDAT { + address 0x099 + access_mode RW +} + +/* + * SCB Auto Increment (p. 3-59) + * Byte offset into the SCB Array and an optional bit to allow auto + * incrementing of the address during download and upload operations + */ +register SCBCNT { + address 0x09a + access_mode RW + bit SCBAUTO 0x80 + mask SCBCNT_MASK 0x1f +} + +/* + * Queue In FIFO (p. 3-60) + * Input queue for queued SCBs (commands that the seqencer has yet to start) + */ +register QINFIFO { + address 0x09b + access_mode RW +} + +/* + * Queue In Count (p. 3-60) + * Number of queued SCBs + */ +register QINCNT { + address 0x09c + access_mode RO +} + +/* + * Queue Out FIFO (p. 3-61) + * Queue of SCBs that have completed and await the host + */ +register QOUTFIFO { + address 0x09d + access_mode WO +} + +/* + * Queue Out Count (p. 3-61) + * Number of queued SCBs in the Out FIFO + */ +register QOUTCNT { + address 0x09e + access_mode RO +} + +/* + * SCB Definition (p. 5-4) + */ +scb { + address 0x0a0 + SCB_CONTROL { + size 1 + bit MK_MESSAGE 0x80 + bit DISCENB 0x40 + bit TAG_ENB 0x20 + bit MUST_DMAUP_SCB 0x10 + bit ABORT_SCB 0x08 + bit DISCONNECTED 0x04 + mask SCB_TAG_TYPE 0x03 + } + SCB_TCL { + size 1 + bit SELBUSB 0x08 + mask TID 0xf0 + mask LID 0x07 + } + SCB_TARGET_STATUS { + size 1 + } + SCB_SGCOUNT { + size 1 + } + SCB_SGPTR { + size 4 + } + SCB_RESID_SGCNT { + size 1 + } + SCB_RESID_DCNT { + size 3 + } + SCB_DATAPTR { + size 4 + } + SCB_DATACNT { + size 3 + } + SCB_LINKED_NEXT { + size 1 + } + SCB_CMDPTR { + size 4 + } + SCB_CMDLEN { + size 1 + } + SCB_TAG { + size 1 + } + SCB_NEXT { + size 1 + } + SCB_PREV { + size 1 + } + SCB_BUSYTARGETS { + size 4 + } +} + +const SG_SIZEOF 0x08 /* sizeof(struct ahc_dma) */ + +/* --------------------- AHA-2840-only definitions -------------------- */ + +register SEECTL_2840 { + address 0x0c0 + access_mode RW + bit CS_2840 0x04 + bit CK_2840 0x02 + bit DO_2840 0x01 +} + +register STATUS_2840 { + address 0x0c1 + access_mode RW + bit EEPROM_TF 0x80 + mask BIOS_SEL 0x60 + mask ADSEL 0x1e + bit DI_2840 0x01 +} + +/* --------------------- AIC-7870-only definitions -------------------- */ + +register DSPCISTATUS { + address 0x086 +} + +register BRDCTL { + address 0x01d + bit BRDDAT7 0x80 + bit BRDDAT6 0x40 + bit BRDDAT5 0x20 + bit BRDSTB 0x10 + bit BRDCS 0x08 + bit BRDRW 0x04 + bit BRDCTL1 0x02 + bit BRDCTL0 0x01 +} + +/* + * Serial EEPROM Control (p. 4-92 in 7870 Databook) + * Controls the reading and writing of an external serial 1-bit + * EEPROM Device. In order to access the serial EEPROM, you must + * first set the SEEMS bit that generates a request to the memory + * port for access to the serial EEPROM device. When the memory + * port is not busy servicing another request, it reconfigures + * to allow access to the serial EEPROM. When this happens, SEERDY + * gets set high to verify that the memory port access has been + * granted. + * + * After successful arbitration for the memory port, the SEECS bit of + * the SEECTL register is connected to the chip select. The SEECK, + * SEEDO, and SEEDI are connected to the clock, data out, and data in + * lines respectively. The SEERDY bit of SEECTL is useful in that it + * gives us an 800 nsec timer. After a write to the SEECTL register, + * the SEERDY goes high 800 nsec later. The one exception to this is + * when we first request access to the memory port. The SEERDY goes + * high to signify that access has been granted and, for this case, has + * no implied timing. + * + * See 93cx6.c for detailed information on the protocol necessary to + * read the serial EEPROM. + */ +register SEECTL { + address 0x01e + bit EXTARBACK 0x80 + bit EXTARBREQ 0x40 + bit SEEMS 0x20 + bit SEERDY 0x10 + bit SEECS 0x08 + bit SEECK 0x04 + bit SEEDO 0x02 + bit SEEDI 0x01 +} +/* ---------------------- Scratch RAM Offsets ------------------------- */ +/* These offsets are either to values that are initialized by the board's + * BIOS or are specified by the sequencer code. + * + * The host adapter card (at least the BIOS) uses 20-2f for SCSI + * device information, 32-33 and 5a-5f as well. As it turns out, the + * BIOS trashes 20-2f, writing the synchronous negotiation results + * on top of the BIOS values, so we re-use those for our per-target + * scratchspace (actually a value that can be copied directly into + * SCSIRATE). The kernel driver will enable synchronous negotiation + * for all targets that have a value other than 0 in the lower four + * bits of the target scratch space. This should work regardless of + * whether the bios has been installed. + */ + +scratch_ram { + address 0x020 + + /* + * 1 byte per target starting at this address for configuration values + */ + TARG_SCRATCH { + size 16 + } + ULTRA_ENB { + size 2 + } + /* + * Bit vector of targets that have disconnection disabled. + */ + DISC_DSB { + size 2 + } + /* + * Length of pending message + */ + MSG_LEN { + size 1 + } + /* We reserve 8bytes to store outgoing messages */ + MSG_OUT { + size 8 + } + /* Parameters for DMA Logic */ + DMAPARAMS { + size 1 + bit WIDEODD 0x40 + bit SCSIEN 0x20 + bit SDMAEN 0x10 + bit SDMAENACK 0x10 + bit HDMAEN 0x08 + bit HDMAENACK 0x08 + bit DIRECTION 0x04 + bit FIFOFLUSH 0x02 + bit FIFORESET 0x01 + } + /* + * Number of SCBs supported by + * this card. + */ + SCBCOUNT { + size 1 + } + /* + * Two's complement of SCBCOUNT + */ + COMP_SCBCOUNT { + size 1 + } + /* + * Mask of bits to test against + * when looking at the Queue Count + * registers. Works around a bug + * on aic7850 chips. + */ + QCNTMASK { + size 1 + } + SEQ_FLAGS { + size 1 + bit RESELECTED 0x80 + bit IDENTIFY_SEEN 0x40 + bit TAGGED_SCB 0x20 + bit DPHASE 0x10 + bit PAGESCBS 0x04 + bit WIDE_BUS 0x02 + bit TWIN_BUS 0x01 + } + /* + * Temporary storage for the + * target/channel/lun of a + * reconnecting target + */ + SAVED_TCL { + size 1 + } + SG_COUNT { + size 1 + } + /* working value of SG pointer */ + SG_NEXT { + size 4 + } + /* + * head of list of SCBs awaiting + * selection + */ + WAITING_SCBH { + size 1 + } + SAVED_LINKPTR { + size 1 + } + SAVED_SCBPTR { + size 1 + } + /* + * The sequencer will stick the frist byte of any rejected message here + * so we can see what is getting thrown away. + */ + REJBYTE { + size 1 + } + /* + * The last bus phase as seen by the sequencer. + */ + LASTPHASE { + size 1 + bit CDI 0x80 + bit IOI 0x40 + bit MSGI 0x20 + mask PHASE_MASK CDI|IOI|MSGI + mask P_DATAOUT 0x00 + mask P_DATAIN IOI + mask P_COMMAND CDI + mask P_MESGOUT CDI|MSGI + mask P_STATUS CDI|IOI + mask P_MESGIN CDI|IOI|MSGI + mask P_BUSFREE 0x01 + } + MSGIN_EXT_LEN { + size 1 + } + MSGIN_EXT_OPCODE { + size 1 + } + /* + * location 3, stores the last + * byte of an extended message if + * it passes the two bytes of space + * we allow now. This byte isn't + * used for anything, it just makes + * the code shorter for tossing + * extra bytes. + */ + MSGIN_EXT_BYTES { + size 3 + } + /* + * head of list of SCBs that are + * disconnected. Used for SCB + * paging. + */ + DISCONNECTED_SCBH { + size 1 + } + /* + * head of list of SCBs that are + * not in use. Used for SCB paging. + */ + FREE_SCBH { + size 1 + } + HSCB_ADDR { + size 4 + } + CUR_SCBID { + size 1 + } + ARG_1 { + size 1 + mask SEND_MSG 0x80 + mask SEND_SENSE 0x40 + mask SEND_REJ 0x20 + alias RETURN_1 + } + /* + * These are reserved registers in the card's scratch ram. Some of + * the values are specified in the AHA2742 technical reference manual + * and are initialized by the BIOS at boot time. + */ + SCSICONF { + address 0x05a + size 1 + bit RESET_SCSI 0x40 + } + HOSTCONF { + address 0x05d + size 1 + } + HA_274_BIOSCTRL { + address 0x05f + size 1 + mask BIOSMODE 0x30 + mask BIOSDISABLED 0x30 + bit CHANNEL_B_PRIMARY 0x08 + } +} + +const SCB_LIST_NULL 0xff + + +/* WDTR Message values */ +const BUS_8_BIT 0x00 +const BUS_16_BIT 0x01 +const BUS_32_BIT 0x02 +const MAX_OFFSET_8BIT 0x0f +const MAX_OFFSET_16BIT 0x08 diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx/aic7xxx.seq linux/drivers/scsi/aic7xxx/aic7xxx.seq --- v2.0.30/linux/drivers/scsi/aic7xxx/aic7xxx.seq Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/aic7xxx/aic7xxx.seq Thu Jul 31 12:37:17 1997 @@ -0,0 +1,1147 @@ +/* + * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD. + * + * Copyright (c) 1994-1997 Justin Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Where this Software is combined with software released under the terms of + * the GNU Public License ("GPL") and the terms of the GPL would require the + * combined work to also be released under the terms of the GPL, the terms + * and conditions of this License will apply in addition to those of the + * GPL with the exception of any terms or conditions of this License that + * conflict with, or are expressly prohibited by, the GPL. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $Id: aic7xxx.seq,v 1.74 1997/06/27 19:38:42 gibbs Exp $ + */ + +#include +#include + +/* + * A few words on the waiting SCB list: + * After starting the selection hardware, we check for reconnecting targets + * as well as for our selection to complete just in case the reselection wins + * bus arbitration. The problem with this is that we must keep track of the + * SCB that we've already pulled from the QINFIFO and started the selection + * on just in case the reselection wins so that we can retry the selection at + * a later time. This problem cannot be resolved by holding a single entry + * in scratch ram since a reconnecting target can request sense and this will + * create yet another SCB waiting for selection. The solution used here is to + * use byte 27 of the SCB as a psuedo-next pointer and to thread a list + * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes, + * SCB_LIST_NULL is 0xff which is out of range. An entry is also added to + * this list everytime a request sense occurs or after completing a non-tagged + * command for which a second SCB has been queued. The sequencer will + * automatically consume the entries. + */ + +/* + * We assume that the kernel driver may reset us at any time, even in the + * middle of a DMA, so clear DFCNTRL too. + */ +reset: + clr SCSISIGO; /* De-assert BSY */ + /* Always allow reselection */ + mvi SCSISEQ, ENRSELI|ENAUTOATNP; + call clear_target_state; +poll_for_work: + test SSTAT0,SELDO jnz select; + test SSTAT0,SELDI jnz reselect; + test SCSISEQ, ENSELO jnz poll_for_work; +.if ( TWIN_CHANNEL ) + /* + * Twin channel devices cannot handle things like SELTO + * interrupts on the "background" channel. So, if we + * are selecting, keep polling the current channel util + * either a selection or reselection occurs. + */ + xor SBLKCTL,SELBUSB; /* Toggle to the other bus */ + test SSTAT0,SELDO jnz select; + test SSTAT0,SELDI jnz reselect; + test SCSISEQ, ENSELO jnz poll_for_work; + xor SBLKCTL,SELBUSB; /* Toggle back */ +.endif + cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting; +test_queue: + /* Has the driver posted any work for us? */ + mov A, QCNTMASK; + test QINCNT,A jz poll_for_work; + +/* + * We have at least one queued SCB now and we don't have any + * SCBs in the list of SCBs awaiting selection. If we have + * any SCBs available for use, pull the tag from the QINFIFO + * and get to work on it. + */ +.if ( SCB_PAGING ) + mov ALLZEROS call get_free_or_disc_scb; + cmp SINDEX, SCB_LIST_NULL je poll_for_work; +.endif +dequeue_scb: + mov CUR_SCBID,QINFIFO; +.if !( SCB_PAGING ) + /* In the non-paging case, the SCBID == hardware SCB index */ + mov SCBPTR, CUR_SCBID; +.endif +dma_queued_scb: +/* + * DMA the SCB from host ram into the current SCB location. + */ + mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; + mov CUR_SCBID call dma_scb; + +/* + * See if there is not already an active SCB for this target. This code + * locks out on a per target basis instead of target/lun. Although this + * is not ideal for devices that have multiple luns active at the same + * time, it is faster than looping through all SCB's looking for active + * commands. We also don't have enough spare SCB space for us to store the + * SCBID of the currently busy transaction for each target/lun making it + * impossible to link up the SCBs. + */ +test_busy: + test SCB_CONTROL, TAG_ENB|ABORT_SCB jnz start_scb; + mvi SEQCTL, PAUSEDIS|FASTMODE; + mov SAVED_SCBPTR, SCBPTR; + mov SCB_TCL call index_untagged_scb; + mov ARG_1, SINDIR; /* + * ARG_1 should + * now have the SCB ID of + * any active, non-tagged, + * command for this target. + */ + cmp ARG_1, SCB_LIST_NULL je make_busy; +.if ( SCB_PAGING ) + /* + * Put this SCB back onto the free list. It + * may be necessary to satisfy the search for + * the active SCB. + */ + mov SCBPTR, SAVED_SCBPTR; + call add_scb_to_free_list; + /* Find the active SCB */ + mov ALLZEROS call findSCB; + /* + * If we couldn't find it, tell the kernel. This should + * never happen. + */ + cmp SINDEX, SCB_LIST_NULL jne paged_busy_link; + mvi INTSTAT, NO_MATCH_BUSY; +paged_busy_link: + /* Link us in */ + mov SCB_LINKED_NEXT, CUR_SCBID; + /* Put it back on the disconnected list */ + call add_scb_to_disc_list; + mvi SEQCTL, FASTMODE; + jmp poll_for_work; +.else +simple_busy_link: + mov SCBPTR, ARG_1; + mov SCB_LINKED_NEXT, CUR_SCBID; + mvi SEQCTL, FASTMODE; + jmp poll_for_work; +.endif +make_busy: + mov DINDIR, CUR_SCBID; + mov SCBPTR, SAVED_SCBPTR; + mvi SEQCTL, FASTMODE; + +start_scb: + /* + * Place us on the waiting list in case our selection + * doesn't win during bus arbitration. + */ + mov SCB_NEXT,WAITING_SCBH; + mov WAITING_SCBH, SCBPTR; +start_waiting: + /* + * Pull the first entry off of the waiting SCB list + * We don't have to "test_busy" because only transactions that + * have passed that test can be in the WAITING_SCB list. + */ + mov SCBPTR, WAITING_SCBH; + call start_selection; + jmp poll_for_work; + +start_selection: +.if ( TWIN_CHANNEL ) + and SINDEX,~SELBUSB,SBLKCTL;/* Clear the channel select bit */ + and A,SELBUSB,SCB_TCL; /* Get new channel bit */ + or SINDEX,A; + mov SBLKCTL,SINDEX; /* select channel */ +.endif +initialize_scsiid: + and A, TID, SCB_TCL; /* Get target ID */ + and SCSIID, OID; /* Clear old target */ + or SCSIID, A; + mvi SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret; +/* + * Reselection has been initiated by a target. Make a note that we've been + * reselected, but haven't seen an IDENTIFY message from the target yet. + */ +reselect: + clr MSG_LEN; /* Don't have anything in the mesg buffer */ + mvi CLRSINT0, CLRSELDI; + /* XXX test for and handle ONE BIT condition */ + and SAVED_TCL, SELID_MASK, SELID; + or SEQ_FLAGS,RESELECTED; + jmp select2; + +/* + * After the selection, remove this SCB from the "waiting SCB" + * list. This is achieved by simply moving our "next" pointer into + * WAITING_SCBH. Our next pointer will be set to null the next time this + * SCB is used, so don't bother with it now. + */ +select: + /* Turn off the selection hardware */ + mvi SCSISEQ, ENRSELI|ENAUTOATNP; /* + * ATN on parity errors + * for "in" phases + */ + mvi CLRSINT0, CLRSELDO; + mov SCBPTR, WAITING_SCBH; + mov WAITING_SCBH,SCB_NEXT; + mov SAVED_TCL, SCB_TCL; +/* + * As soon as we get a successful selection, the target should go + * into the message out phase since we have ATN asserted. Prepare + * the message to send. + * + * Messages are stored in scratch RAM starting with a length byte + * followed by the message itself. + */ + +mk_identify: + and MSG_OUT,0x7,SCB_TCL; /* lun */ + and A,DISCENB,SCB_CONTROL; /* mask off disconnect privledge */ + or MSG_OUT,A; /* or in disconnect privledge */ + or MSG_OUT,MSG_IDENTIFYFLAG; + mvi MSG_LEN, 1; + +/* + * Send a tag message if TAG_ENB is set in the SCB control block. + * Use SCB_TAG (the position in the kernel's SCB array) as the tag value. + */ +mk_tag: + test SCB_CONTROL,TAG_ENB jz mk_message; + and MSG_OUT[1],TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL; + mov MSG_OUT[2],SCB_TAG; + add MSG_LEN,2; /* update message length */ + +/* + * Interrupt the driver, and allow it to tweak the message buffer + * if it asks. + */ +mk_message: + test SCB_CONTROL,MK_MESSAGE jz select2; + mvi INTSTAT,AWAITING_MSG; + +select2: + mvi CLRSINT1,CLRBUSFREE; + or SIMODE1, ENBUSFREE; /* + * We aren't expecting a + * bus free, so interrupt + * the kernel driver if it + * happens. + */ +/* + * Initialize Ultra mode setting and clear the SCSI channel. + */ + or SXFRCTL0, CLRSTCNT|SPIOEN|CLRCHN; +.if ( ULTRA ) +ultra: + mvi SINDEX, ULTRA_ENB+1; + test SAVED_TCL, 0x80 jnz ultra_2; /* Target ID > 7 */ + dec SINDEX; +ultra_2: + mov FUNCTION1,SAVED_TCL; + mov A,FUNCTION1; + test SINDIR, A jz ndx_dtr; + or SXFRCTL0, FAST20; +.endif + +/* + * Initialize SCSIRATE with the appropriate value for this target. + * The SCSIRATE settings for each target are stored in an array + * based at TARG_SCRATCH. + */ +ndx_dtr: + shr A,4,SAVED_TCL; + test SBLKCTL,SELBUSB jz ndx_dtr_2; + or SAVED_TCL, SELBUSB; /* Add the channel bit while we're here */ + or A,0x08; /* Channel B entries add 8 */ +ndx_dtr_2: + add SINDEX,TARG_SCRATCH,A; + mov SCSIRATE,SINDIR; + + +/* + * Main loop for information transfer phases. If BSY is false, then + * we have a bus free condition, expected or not. Otherwise, wait + * for the target to assert REQ before checking MSG, C/D and I/O + * for the bus phase. + * + */ +ITloop: + test SSTAT1,REQINIT jz ITloop; + test SSTAT1, SCSIPERR jnz ITloop; + + and A,PHASE_MASK,SCSISIGI; + mov LASTPHASE,A; + mov SCSISIGO,A; + + cmp ALLZEROS,A je p_dataout; + cmp A,P_DATAIN je p_datain; + cmp A,P_COMMAND je p_command; + cmp A,P_MESGOUT je p_mesgout; + cmp A,P_STATUS je p_status; + cmp A,P_MESGIN je p_mesgin; + + mvi INTSTAT,BAD_PHASE; /* unknown phase - signal driver */ + jmp ITloop; /* Try reading the bus again. */ + +await_busfree: + and SIMODE1, ~ENBUSFREE; + call clear_target_state; + mov NONE, SCSIDATL; /* Ack the last byte */ + test SSTAT1,REQINIT|BUSFREE jz .; + test SSTAT1, BUSFREE jnz poll_for_work; + mvi INTSTAT, BAD_PHASE; + +clear_target_state: + clr DFCNTRL; + clr SCSIRATE; /* + * We don't know the target we will + * connect to, so default to narrow + * transfers to avoid parity problems. + */ + and SXFRCTL0, ~FAST20; + mvi LASTPHASE, P_BUSFREE; + /* clear target specific flags */ + and SEQ_FLAGS,~(RESELECTED|IDENTIFY_SEEN|TAGGED_SCB|DPHASE) ret; + +p_dataout: + mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET; + jmp data_phase_init; + +/* + * If we re-enter the data phase after going through another phase, the + * STCNT may have been cleared, so restore it from the residual field. + */ +data_phase_reinit: + mvi DINDEX, STCNT; + mvi SCB_RESID_DCNT call bcopy_3; + jmp data_phase_loop; + +p_datain: + mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET; +data_phase_init: + call assert; /* + * Ensure entering a data + * phase is okay - seen identify, etc. + */ + + test SEQ_FLAGS, DPHASE jnz data_phase_reinit; + + /* + * Initialize the DMA address and counter from the SCB. + * Also set SG_COUNT and SG_NEXT in memory since we cannot + * modify the values in the SCB itself until we see a + * save data pointers message. + */ + mvi DINDEX, HADDR; + mvi SCB_DATAPTR call bcopy_7; + + call set_stcnt_from_hcnt; + + mov SG_COUNT,SCB_SGCOUNT; + + mvi DINDEX, SG_NEXT; + mvi SCB_SGPTR call bcopy_4; + +data_phase_loop: +/* Guard against overruns */ + test SG_COUNT, 0xff jnz data_phase_inbounds; +/* + * Turn on 'Bit Bucket' mode, set the transfer count to + * 16meg and let the target run until it changes phase. + * When the transfer completes, notify the host that we + * had an overrun. + */ + or SXFRCTL1,BITBUCKET; + mvi HCNT[0], 0xff; + mvi HCNT[1], 0xff; + mvi HCNT[2], 0xff; + call set_stcnt_from_hcnt; + +data_phase_inbounds: +/* If we are the last SG block, ensure wideodd is off. */ + cmp SG_COUNT,0x01 jne data_phase_wideodd; + and DMAPARAMS, ~WIDEODD; +data_phase_wideodd: + mov DMAPARAMS call dma; + +/* Go tell the host about any overruns */ + test SXFRCTL1,BITBUCKET jnz data_phase_overrun; + +/* Exit if we had an underrun. dma clears SINDEX in this case. */ + test SINDEX,0xff jz data_phase_finish; + +/* + * Advance the scatter-gather pointers if needed + */ +sg_advance: + dec SG_COUNT; /* one less segment to go */ + + test SG_COUNT, 0xff jz data_phase_finish; /* Are we done? */ + + clr A; /* add sizeof(struct scatter) */ + add SG_NEXT[0],SG_SIZEOF; + adc SG_NEXT[1],A; + +/* + * Load a struct scatter and set up the data address and length. + * If the working value of the SG count is nonzero, then + * we need to load a new set of values. + * + * This, like all DMA's, assumes little-endian host data storage. + */ +sg_load: + mvi DINDEX, HADDR; + mvi SG_NEXT call bcopy_4; + + mvi HCNT[0],SG_SIZEOF; + clr HCNT[1]; + clr HCNT[2]; + + or DFCNTRL, HDMAEN|DIRECTION|FIFORESET; + + call dma_finish; + +/* + * Copy data from FIFO into SCB data pointer and data count. This assumes + * that the SG segments are of the form: + * + * struct ahc_dma_seg { + * u_int32_t addr; four bytes, little-endian order + * u_int32_t len; four bytes, little endian order + * }; + */ + mvi HADDR call dfdat_in_7; + +/* Load STCNT as well. It is a mirror of HCNT */ + call set_stcnt_from_hcnt; + test SSTAT1,PHASEMIS jz data_phase_loop; + +data_phase_finish: +/* + * After a DMA finishes, save the SG and STCNT residuals back into the SCB + * We use STCNT instead of HCNT, since it's a reflection of how many bytes + * were transferred on the SCSI (as opposed to the host) bus. + */ + mov SCB_RESID_DCNT[0],STCNT[0]; + mov SCB_RESID_DCNT[1],STCNT[1]; + mov SCB_RESID_DCNT[2],STCNT[2]; + mov SCB_RESID_SGCNT, SG_COUNT; + + /* We have seen a data phase */ + or SEQ_FLAGS, DPHASE; + + jmp ITloop; + +data_phase_overrun: +/* + * Turn off BITBUCKET mode and notify the host + */ + and SXFRCTL1, ~BITBUCKET; + mvi INTSTAT,DATA_OVERRUN; + jmp ITloop; + +/* + * Command phase. Set up the DMA registers and let 'er rip. + */ +p_command: + call assert; + +/* + * Load HADDR and HCNT. + */ + mvi DINDEX, HADDR; + mvi SCB_CMDPTR call bcopy_5; + clr HCNT[1]; + clr HCNT[2]; + + call set_stcnt_from_hcnt; + + mvi (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma; + jmp ITloop; + +/* + * Status phase. Wait for the data byte to appear, then read it + * and store it into the SCB. + */ +p_status: + call assert; + + mov SCB_TARGET_STATUS, SCSIDATL; + jmp ITloop; + +/* + * Message out phase. If there is not an active message, but the target + * took us into this phase anyway, build a no-op message and send it. + */ +p_mesgout: + test MSG_LEN, 0xff jnz p_mesgout_start; + mvi MSG_NOOP call mk_mesg; /* build NOP message */ +p_mesgout_start: +/* + * Set up automatic PIO transfer from MSG_OUT. Bit 3 in + * SXFRCTL0 (SPIOEN) is already on. + */ + mvi SINDEX,MSG_OUT; + mov DINDEX,MSG_LEN; + +/* + * When target asks for a byte, drop ATN if it's the last one in + * the message. Otherwise, keep going until the message is exhausted. + * ATN must be dropped *at least* 90ns before we ack the last byte, so + * the code is aranged to execute two instructions before the byte is + * transferred to give a good margin of safety + * + * Keep an eye out for a phase change, in case the target issues + * a MESSAGE REJECT. + */ +p_mesgout_loop: + test SSTAT1, REQINIT jz p_mesgout_loop; + test SSTAT1, SCSIPERR jnz p_mesgout_loop; + and LASTPHASE, PHASE_MASK, SCSISIGI; + cmp LASTPHASE, P_MESGOUT jne p_mesgout_done; +p_mesgout_testretry: + test DINDEX,0xff jnz p_mesgout_dropatn; + or SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */ + jmp p_mesgout_start; +/* + * If the next bus phase after ATN drops is a message out, it means + * that the target is requesting that the last message(s) be resent. + */ +p_mesgout_dropatn: + cmp DINDEX,1 jne p_mesgout_outb; /* last byte? */ + mvi CLRSINT1,CLRATNO; /* drop ATN */ +p_mesgout_outb: + dec DINDEX; + mov SCSIDATL,SINDIR; + jmp p_mesgout_loop; + +p_mesgout_done: + mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */ + clr MSG_LEN; /* no active msg */ + jmp ITloop; + +/* + * Message in phase. Bytes are read using Automatic PIO mode. + */ +p_mesgin: + mvi ACCUM call inb_first; /* read the 1st message byte */ + mov REJBYTE,A; /* save it for the driver */ + + test A,MSG_IDENTIFYFLAG jnz mesgin_identify; + cmp A,MSG_DISCONNECT je mesgin_disconnect; + cmp A,MSG_SAVEDATAPOINTER je mesgin_sdptrs; + cmp ALLZEROS,A je mesgin_complete; + cmp A,MSG_RESTOREPOINTERS je mesgin_rdptrs; + cmp A,MSG_EXTENDED je mesgin_extended; + cmp A,MSG_MESSAGE_REJECT je mesgin_reject; + cmp A,MSG_NOOP je mesgin_done; + +rej_mesgin: +/* + * We have no idea what this message in is, so we issue a message reject + * and hope for the best. In any case, rejection should be a rare + * occurrence - signal the driver when it happens. + */ + mvi INTSTAT,SEND_REJECT; /* let driver know */ + + mvi MSG_MESSAGE_REJECT call mk_mesg; + +mesgin_done: + mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ + jmp ITloop; + + +mesgin_complete: +/* + * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO, + * and trigger a completion interrupt. Before doing so, check to see if there + * is a residual or the status byte is something other than NO_ERROR (0). In + * either of these conditions, we upload the SCB back to the host so it can + * process this information. In the case of a non zero status byte, we + * additionally interrupt the kernel driver synchronously, allowing it to + * decide if sense should be retrieved. If the kernel driver wishes to request + * sense, it will fill the kernel SCB with a request sense command and set + * RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE we redownload + * the SCB, and process it as the next command by adding it to the waiting list. + * If the kernel driver does not wish to request sense, it need only clear + * RETURN_1, and the command is allowed to complete normally. We don't bother + * to post to the QOUTFIFO in the error cases since it would require extra + * work in the kernel driver to ensure that the entry was removed before the + * command complete code tried processing it. + */ + +/* + * First check for residuals + */ + test SCB_RESID_SGCNT,0xff jnz upload_scb; + test SCB_TARGET_STATUS,0xff jz status_ok; /* Good Status? */ +upload_scb: + mvi DMAPARAMS, FIFORESET; + mov SCB_TAG call dma_scb; +check_status: + test SCB_TARGET_STATUS,0xff jz status_ok; /* Just a residual? */ + mvi INTSTAT,BAD_STATUS; /* let driver know */ + cmp RETURN_1, SEND_SENSE jne status_ok; + /* This SCB becomes the next to execute as it will retrieve sense */ + mov SCB_LINKED_NEXT, SCB_TAG; + jmp dma_next_scb; + +status_ok: +/* First, mark this target as free. */ + test SCB_CONTROL,TAG_ENB jnz complete; /* + * Tagged commands + * don't busy the + * target. + */ + mov SAVED_SCBPTR, SCBPTR; + mov SAVED_LINKPTR, SCB_LINKED_NEXT; + mov SCB_TCL call index_untagged_scb; + mov DINDIR, SAVED_LINKPTR; + mov SCBPTR, SAVED_SCBPTR; + +complete: + /* Post the SCB and issue an interrupt */ + mov QOUTFIFO,SCB_TAG; + mvi INTSTAT,CMDCMPLT; + test SCB_CONTROL, ABORT_SCB jz dma_next_scb; + mvi INTSTAT, ABORT_CMDCMPLT; + +dma_next_scb: + cmp SCB_LINKED_NEXT, SCB_LIST_NULL je add_to_free_list; +.if !( SCB_PAGING ) + /* Only DMA on top of ourselves if we are the SCB to download */ + mov A, SCB_LINKED_NEXT; + cmp SCB_TAG, A je dma_next_scb2; + call add_scb_to_free_list; + mov SCBPTR, A; + jmp add_to_waiting_list; +.endif +dma_next_scb2: + mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; + mov SCB_LINKED_NEXT call dma_scb; +add_to_waiting_list: + mov SCB_NEXT,WAITING_SCBH; + mov WAITING_SCBH, SCBPTR; + /* + * Prepare our selection hardware before the busfree so we have a + * high probability of winning arbitration. + */ + call start_selection; + jmp await_busfree; +add_to_free_list: + call add_scb_to_free_list; + jmp await_busfree; + +/* + * Is it an extended message? Copy the message to our message buffer and + * notify the host. The host will tell us whether to reject this message, + * respond to it with the message that the host placed in our message buffer, + * or simply to do nothing. + */ +mesgin_extended: + mvi MSGIN_EXT_LEN call inb_next; + mov A, MSGIN_EXT_LEN; +mesgin_extended_loop: + mov DINDEX call inb_next; + dec A; + cmp DINDEX, MSGIN_EXT_BYTES+3 jne mesgin_extended_loop_test; + dec DINDEX; /* dump by repeatedly filling the last byte */ +mesgin_extended_loop_test: + test A, 0xFF jnz mesgin_extended_loop; +mesgin_extended_intr: + mvi INTSTAT,EXTENDED_MSG; /* let driver know */ + cmp RETURN_1,SEND_REJ je rej_mesgin; + cmp RETURN_1,SEND_MSG jne mesgin_done; +/* The kernel has setup a message to be sent */ + or SCSISIGO,ATNO,LASTPHASE; /* turn on ATNO */ + jmp mesgin_done; + +/* + * Is it a disconnect message? Set a flag in the SCB to remind us + * and await the bus going free. + */ +mesgin_disconnect: + or SCB_CONTROL,DISCONNECTED; +.if ( SCB_PAGING ) + call add_scb_to_disc_list; +.endif + jmp await_busfree; + +/* + * Save data pointers message: + * Copying RAM values back to SCB, for Save Data Pointers message, but + * only if we've actually been into a data phase to change them. This + * protects against bogus data in scratch ram and the residual counts + * since they are only initialized when we go into data_in or data_out. + */ +mesgin_sdptrs: + test SEQ_FLAGS, DPHASE jz mesgin_done; + mov SCB_SGCOUNT,SG_COUNT; + + /* The SCB SGPTR becomes the next one we'll download */ + mvi DINDEX, SCB_SGPTR; + mvi SG_NEXT call bcopy_4; + + /* The SCB DATAPTR0 becomes the current SHADDR */ + mvi DINDEX, SCB_DATAPTR; + mvi SHADDR call bcopy_4; + +/* + * Use the residual number since STCNT is corrupted by any message transfer. + */ + mvi SCB_RESID_DCNT call bcopy_3; + + jmp mesgin_done; + +/* + * Restore pointers message? Data pointers are recopied from the + * SCB anytime we enter a data phase for the first time, so all + * we need to do is clear the DPHASE flag and let the data phase + * code do the rest. + */ +mesgin_rdptrs: + and SEQ_FLAGS, ~DPHASE; /* + * We'll reload them + * the next time through + * the dataphase. + */ + jmp mesgin_done; + +/* + * Identify message? For a reconnecting target, this tells us the lun + * that the reconnection is for - find the correct SCB and switch to it, + * clearing the "disconnected" bit so we don't "find" it by accident later. + */ +mesgin_identify: + test A,0x78 jnz rej_mesgin; /*!DiscPriv|!LUNTAR|!Reserved*/ + and A,0x07; /* lun in lower three bits */ + or SAVED_TCL,A; /* SAVED_TCL should be complete now */ + mov SAVED_TCL call index_untagged_scb; + mov ARG_1, SINDIR; +.if ( SCB_PAGING ) + cmp ARG_1,SCB_LIST_NULL jne use_findSCB; +.else + cmp ARG_1,SCB_LIST_NULL je snoop_tag; + /* Directly index the SCB */ + mov SCBPTR,ARG_1; + test SCB_CONTROL,DISCONNECTED jz not_found; + jmp setup_SCB; +.endif +/* + * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message. + * If we get one, we use the tag returned to find the proper + * SCB. With SCB paging, this requires using findSCB for both tagged + * and non-tagged transactions since the SCB may exist in any slot. + * If we're not using SCB paging, we can use the tag as the direct + * index to the SCB. + */ +snoop_tag: + mov NONE,SCSIDATL; /* ACK Identify MSG */ +snoop_tag_loop: + test SSTAT1,REQINIT jz snoop_tag_loop; + test SSTAT1, SCSIPERR jnz snoop_tag_loop; + and LASTPHASE, PHASE_MASK, SCSISIGI; + cmp LASTPHASE, P_MESGIN jne not_found; + cmp SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found; +get_tag: + or SEQ_FLAGS, TAGGED_SCB; + mvi ARG_1 call inb_next; /* tag value */ +/* + * See if the tag is in range. The tag is < SCBCOUNT if we add + * the complement of SCBCOUNT to the incomming tag and there is + * no carry. + */ + mov A,COMP_SCBCOUNT; + add SINDEX,A,ARG_1; + jc not_found; + +.if ! ( SCB_PAGING ) +index_by_tag: + mov SCBPTR,ARG_1; + mov A, SAVED_TCL; + cmp SCB_TCL,A jne not_found; + test SCB_CONTROL,TAG_ENB jz not_found; + test SCB_CONTROL,DISCONNECTED jz not_found; +.else +/* + * Ensure that the SCB the tag points to is for an SCB transaction + * to the reconnecting target. + */ +use_findSCB: + mov ALLZEROS call findSCB; /* Have to search */ + cmp SINDEX, SCB_LIST_NULL je not_found; +.endif +setup_SCB: + and SCB_CONTROL,~DISCONNECTED; + or SEQ_FLAGS,IDENTIFY_SEEN; /* make note of IDENTIFY */ + jmp mesgin_done; + +not_found: + mvi INTSTAT, NO_MATCH; + mvi MSG_BUS_DEV_RESET call mk_mesg; + jmp mesgin_done; + +/* + * Message reject? Let the kernel driver handle this. If we have an + * outstanding WDTR or SDTR negotiation, assume that it's a response from + * the target selecting 8bit or asynchronous transfer, otherwise just ignore + * it since we have no clue what it pertains to. + */ +mesgin_reject: + mvi INTSTAT, REJECT_MSG; + jmp mesgin_done; + +/* + * [ ADD MORE MESSAGE HANDLING HERE ] + */ + +/* + * Locking the driver out, build a one-byte message passed in SINDEX + * if there is no active message already. SINDEX is returned intact. + */ +mk_mesg: + mvi SEQCTL, PAUSEDIS|FASTMODE; + test MSG_LEN,0xff jz mk_mesg1; /* Should always succeed */ + + /* + * Hmmm. For some reason the mesg buffer is in use. + * Tell the driver. It should look at SINDEX to find + * out what we wanted to use the buffer for and resolve + * the conflict. + */ + mvi SEQCTL,FASTMODE; + mvi INTSTAT,MSG_BUFFER_BUSY; + +mk_mesg1: + or SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */ + mvi MSG_LEN,1; /* length = 1 */ + mov MSG_OUT,SINDEX; /* 1-byte message */ + mvi SEQCTL,FASTMODE ret; + +/* + * Functions to read data in Automatic PIO mode. + * + * According to Adaptec's documentation, an ACK is not sent on input from + * the target until SCSIDATL is read from. So we wait until SCSIDATL is + * latched (the usual way), then read the data byte directly off the bus + * using SCSIBUSL. When we have pulled the ATN line, or we just want to + * acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI + * spec guarantees that the target will hold the data byte on the bus until + * we send our ACK. + * + * The assumption here is that these are called in a particular sequence, + * and that REQ is already set when inb_first is called. inb_{first,next} + * use the same calling convention as inb. + */ + +inb_next: + mov NONE,SCSIDATL; /*dummy read from latch to ACK*/ +inb_next_wait: + /* + * If there is a parity error, wait for the kernel to + * see the interrupt and prepare our message response + * before continuing. + */ + test SSTAT1, REQINIT jz inb_next_wait; + test SSTAT1, SCSIPERR jnz inb_next_wait; + and LASTPHASE, PHASE_MASK, SCSISIGI; + cmp LASTPHASE, P_MESGIN jne mesgin_phasemis; +inb_first: + mov DINDEX,SINDEX; + mov DINDIR,SCSIBUSL ret; /*read byte directly from bus*/ +inb_last: + mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/ + +mesgin_phasemis: +/* + * We expected to receive another byte, but the target changed phase + */ + mvi INTSTAT, MSGIN_PHASEMIS; + jmp ITloop; + +/* + * DMA data transfer. HADDR and HCNT must be loaded first, and + * SINDEX should contain the value to load DFCNTRL with - 0x3d for + * host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared + * during initialization. + */ +dma: + mov DFCNTRL,SINDEX; +dma_loop: + test SSTAT0,DMADONE jnz dma_dmadone; + test SSTAT1,PHASEMIS jz dma_loop; /* ie. underrun */ +dma_phasemis: + test SSTAT0,SDONE jnz dma_checkfifo; + mov SINDEX,ALLZEROS; /* Notify caller of phasemiss */ + +/* + * We will be "done" DMAing when the transfer count goes to zero, or + * the target changes the phase (in light of this, it makes sense that + * the DMA circuitry doesn't ACK when PHASEMIS is active). If we are + * doing a SCSI->Host transfer, the data FIFO should be flushed auto- + * magically on STCNT=0 or a phase change, so just wait for FIFO empty + * status. + */ +dma_checkfifo: + test DFCNTRL,DIRECTION jnz dma_fifoempty; +dma_fifoflush: + test DFSTATUS,FIFOEMP jz dma_fifoflush; + +dma_fifoempty: + /* Don't clobber an inprogress host data transfer */ + test DFSTATUS, MREQPEND jnz dma_fifoempty; +/* + * Now shut the DMA enables off and make sure that the DMA enables are + * actually off first lest we get an ILLSADDR. + */ +dma_dmadone: + and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN); +dma_halt: + test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt; +return: + ret; + +/* + * Assert that if we've been reselected, then we've seen an IDENTIFY + * message. + */ +assert: + test SEQ_FLAGS,RESELECTED jz return; /* reselected? */ + test SEQ_FLAGS,IDENTIFY_SEEN jnz return; /* seen IDENTIFY? */ + + mvi INTSTAT,NO_IDENT ret; /* no - tell the kernel */ + +.if ( SCB_PAGING ) +/* + * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL) + * or by the SCBIDn ARG_1. The search begins at the SCB index passed in + * via SINDEX. If the SCB cannot be found, SINDEX will be SCB_LIST_NULL, + * otherwise, SCBPTR is set to the proper SCB. + */ +findSCB: + mov SCBPTR,SINDEX; /* switch to next SCB */ + mov A, ARG_1; /* Tag passed in ARG_1 */ + cmp SCB_TAG,A jne findSCB_loop; + test SCB_CONTROL,DISCONNECTED jnz foundSCB;/*should be disconnected*/ +findSCB_loop: + inc SINDEX; + mov A,SCBCOUNT; + cmp SINDEX,A jne findSCB; +/* + * We didn't find it. If we're paging, pull an SCB and DMA down the + * one we want. If we aren't paging or the SCB we dma down has the + * abort flag set, return not found. + */ + mov ALLZEROS call get_free_or_disc_scb; + mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; + mov ARG_1 call dma_scb; + test SCB_RESID_SGCNT, 0xff jz . + 2; + or SCB_CONTROL, MUST_DMAUP_SCB; + test SCB_CONTROL, ABORT_SCB jz return; +find_error: + mvi SINDEX, SCB_LIST_NULL ret; +foundSCB: + test SCB_CONTROL, ABORT_SCB jnz find_error; +rem_scb_from_disc_list: +/* Remove this SCB from the disconnection list */ + cmp SCB_NEXT,SCB_LIST_NULL je unlink_prev; + mov SAVED_LINKPTR, SCB_PREV; + mov SCBPTR, SCB_NEXT; + mov SCB_PREV, SAVED_LINKPTR; + mov SCBPTR, SINDEX; +unlink_prev: + cmp SCB_PREV,SCB_LIST_NULL je rHead;/* At the head of the list */ + mov SAVED_LINKPTR, SCB_NEXT; + mov SCBPTR, SCB_PREV; + mov SCB_NEXT, SAVED_LINKPTR; + mov SCBPTR, SINDEX ret; +rHead: + mov DISCONNECTED_SCBH,SCB_NEXT ret; +.else + ret; +.endif + +set_stcnt_from_hcnt: + mov STCNT[0], HCNT[0]; + mov STCNT[1], HCNT[1]; + mov STCNT[2], HCNT[2] ret; + +bcopy_7: + mov DINDIR, SINDIR; + mov DINDIR, SINDIR; +bcopy_5: + mov DINDIR, SINDIR; +bcopy_4: + mov DINDIR, SINDIR; +bcopy_3: + mov DINDIR, SINDIR; + mov DINDIR, SINDIR; + mov DINDIR, SINDIR ret; + +dma_scb: + /* + * SCB index is in SINDEX. Determine the physical address in + * the host where this SCB is located and load HADDR with it. + */ + shr DINDEX, 3, SINDEX; + shl A, 5, SINDEX; + add HADDR[0], A, HSCB_ADDR[0]; + mov A, DINDEX; + adc HADDR[1], A, HSCB_ADDR[1]; + clr A; + adc HADDR[2], A, HSCB_ADDR[2]; + adc HADDR[3], A, HSCB_ADDR[3]; + /* Setup Count */ + mvi HCNT[0], 28; + clr HCNT[1]; + clr HCNT[2]; + mov DFCNTRL, DMAPARAMS; + test DMAPARAMS, DIRECTION jnz dma_scb_fromhost; + /* Fill it with the SCB data */ +copy_scb_tofifo: + mvi SINDEX, SCB_CONTROL; + add A, 28, SINDEX; +copy_scb_tofifo_loop: + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + cmp SINDEX, A jne copy_scb_tofifo_loop; + or DFCNTRL, HDMAEN|FIFOFLUSH; +dma_scb_fromhost: + call dma_finish; + /* If we were putting the SCB, we are done */ + test DMAPARAMS, DIRECTION jz return; + mvi SCB_CONTROL call dfdat_in_7; + call dfdat_in_7_continued; + call dfdat_in_7_continued; + jmp dfdat_in_7_continued; +dfdat_in_7: + mov DINDEX,SINDEX; +dfdat_in_7_continued: + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT ret; + +/* + * Wait for DMA from host memory to data FIFO to complete, then disable + * DMA and wait for it to acknowledge that it's off. + */ +dma_finish: + test DFSTATUS,HDONE jz dma_finish; + /* Turn off DMA */ + and DFCNTRL, ~HDMAEN; + test DFCNTRL, HDMAEN jnz .; + ret; + +index_untagged_scb: + mov DINDEX, SINDEX; + shr DINDEX, 4; + and DINDEX, 0x03; /* Bottom two bits of tid */ + add DINDEX, SCB_BUSYTARGETS; + shr A, 6, SINDEX; /* Target ID divided by 4 */ + test SINDEX, SELBUSB jz index_untagged_scb2; + add A, 2; /* Add 2 positions */ +index_untagged_scb2: + mov SCBPTR, A; /* + * Select the SCB with this + * target's information. + */ + mov SINDEX, DINDEX ret; + +add_scb_to_free_list: + mov SCB_NEXT, FREE_SCBH; + mvi SCB_TAG, SCB_LIST_NULL; + mov FREE_SCBH, SCBPTR ret; + +.if ( SCB_PAGING ) +get_free_or_disc_scb: + cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb; + cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb; +return_error: + mvi SINDEX, SCB_LIST_NULL ret; +dequeue_disc_scb: + mov SCBPTR, DISCONNECTED_SCBH; +/* + * If we have a residual, then we are in the middle of some I/O + * and we have to send this SCB back up to the kernel so that the + * saved data pointers and residual information isn't lost. + */ + test SCB_CONTROL, MUST_DMAUP_SCB jz . + 3; + and SCB_CONTROL, ~MUST_DMAUP_SCB; + jmp dma_up_scb; + test SCB_RESID_SGCNT,0xff jnz dma_up_scb; + cmp SCB_LINKED_NEXT, SCB_LIST_NULL je unlink_disc_scb; +dma_up_scb: + mvi DMAPARAMS, FIFORESET; + mov SCB_TAG call dma_scb; +unlink_disc_scb: + /* jmp instead of call since we want to return anyway */ + mov SCBPTR jmp rem_scb_from_disc_list; +dequeue_free_scb: + mov SCBPTR, FREE_SCBH; + mov FREE_SCBH, SCB_NEXT ret; + +add_scb_to_disc_list: +/* + * Link this SCB into the DISCONNECTED list. This list holds the + * candidates for paging out an SCB if one is needed for a new command. + * Modifying the disconnected list is a critical(pause dissabled) section. + */ + mvi SCB_PREV, SCB_LIST_NULL; + mov SCB_NEXT, DISCONNECTED_SCBH; + mov DISCONNECTED_SCBH, SCBPTR; + cmp SCB_NEXT,SCB_LIST_NULL je return; + mov SCBPTR,SCB_NEXT; + mov SCB_PREV,DISCONNECTED_SCBH; + mov SCBPTR,DISCONNECTED_SCBH ret; +.endif diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx/scsi_message.h linux/drivers/scsi/aic7xxx/scsi_message.h --- v2.0.30/linux/drivers/scsi/aic7xxx/scsi_message.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/aic7xxx/scsi_message.h Thu Jul 31 12:37:17 1997 @@ -0,0 +1,41 @@ +/* + * SCSI messages definitions. + */ + +/* Messages (1 byte) */ /* I/T (M)andatory or (O)ptional */ +#define MSG_CMDCOMPLETE 0x00 /* M/M */ +#define MSG_EXTENDED 0x01 /* O/O */ +#define MSG_SAVEDATAPOINTER 0x02 /* O/O */ +#define MSG_RESTOREPOINTERS 0x03 /* O/O */ +#define MSG_DISCONNECT 0x04 /* O/O */ +#define MSG_INITIATOR_DET_ERR 0x05 /* M/M */ +#define MSG_ABORT 0x06 /* O/M */ +#define MSG_MESSAGE_REJECT 0x07 /* M/M */ +#define MSG_NOOP 0x08 /* M/M */ +#define MSG_PARITY_ERROR 0x09 /* M/M */ +#define MSG_LINK_CMD_COMPLETE 0x0a /* O/O */ +#define MSG_LINK_CMD_COMPLETEF 0x0b /* O/O */ +#define MSG_BUS_DEV_RESET 0x0c /* O/M */ +#define MSG_ABORT_TAG 0x0d /* O/O */ +#define MSG_CLEAR_QUEUE 0x0e /* O/O */ +#define MSG_INIT_RECOVERY 0x0f /* O/O */ +#define MSG_REL_RECOVERY 0x10 /* O/O */ +#define MSG_TERM_IO_PROC 0x11 /* O/O */ + +/* Messages (2 byte) */ +#define MSG_SIMPLE_Q_TAG 0x20 /* O/O */ +#define MSG_HEAD_OF_Q_TAG 0x21 /* O/O */ +#define MSG_ORDERED_Q_TAG 0x22 /* O/O */ +#define MSG_IGN_WIDE_RESIDUE 0x23 /* O/O */ + +/* Identify message */ /* M/M */ +#define MSG_IDENTIFYFLAG 0x80 +#define MSG_IDENTIFY(lun, disc) (((disc) ? 0xc0 : MSG_IDENTIFYFLAG) | (lun)) +#define MSG_ISIDENTIFY(m) ((m) & MSG_IDENTIFYFLAG) + +/* Extended messages (opcode and length) */ +#define MSG_EXT_SDTR 0x01 +#define MSG_EXT_SDTR_LEN 0x03 + +#define MSG_EXT_WDTR 0x03 +#define MSG_EXT_WDTR_LEN 0x02 diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx/sequencer.h linux/drivers/scsi/aic7xxx/sequencer.h --- v2.0.30/linux/drivers/scsi/aic7xxx/sequencer.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/aic7xxx/sequencer.h Thu Jul 31 12:37:17 1997 @@ -0,0 +1,102 @@ +/* + * Instruction formats for the sequencer program downloaded to + * Aic7xxx SCSI host adapters + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Where this Software is combined with software released under the terms of + * the GNU Public License ("GPL") and the terms of the GPL would require the + * combined work to also be released under the terms of the GPL, the terms + * and conditions of this License will apply in addition to those of the + * GPL with the exception of any terms or conditions of this License that + * conflict with, or are expressly prohibited by, the GPL. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $Id: sequencer.h,v 1.2 1997/06/27 19:38:52 gibbs Exp $ + */ + +#if defined(__KERNEL__) +typedef unsigned char u_int8_t; +#endif + +struct ins_format1 { + u_int8_t immediate; + u_int8_t source; + u_int8_t destination; + u_int8_t opcode_ret; +}; + +struct ins_format2 { + u_int8_t shift_control; + u_int8_t source; + u_int8_t destination; + u_int8_t opcode_ret; +#define RETURN_BIT 0x01 +}; + +struct ins_format3 { + u_int8_t immediate; + u_int8_t source; + u_int8_t address; + u_int8_t opcode_addr; +#define ADDR_HIGH_BIT 0x01 +}; + +struct instruction { + union { + struct ins_format1 format1; + struct ins_format2 format2; + struct ins_format3 format3; + u_int8_t bytes[4]; + } format; + u_int srcline; + struct symbol *patch_label; + struct { + struct instruction *stqe_next; /* next element */ + } links; +}; + +#define AIC_OP_OR 0x0 +#define AIC_OP_AND 0x1 +#define AIC_OP_XOR 0x2 +#define AIC_OP_ADD 0x3 +#define AIC_OP_ADC 0x4 +#define AIC_OP_ROL 0x5 + +#define AIC_OP_JMP 0x8 +#define AIC_OP_JC 0x9 +#define AIC_OP_JNC 0xa +#define AIC_OP_CALL 0xb +#define AIC_OP_JNE 0xc +#define AIC_OP_JNZ 0xd +#define AIC_OP_JE 0xe +#define AIC_OP_JZ 0xf + +/* Pseudo Ops */ +#define AIC_OP_SHL 0x10 +#define AIC_OP_SHR 0x20 +#define AIC_OP_ROR 0x30 diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c --- v2.0.30/linux/drivers/scsi/aic7xxx.c Tue Apr 8 08:47:46 1997 +++ linux/drivers/scsi/aic7xxx.c Thu Jul 31 12:37:17 1997 @@ -1,5 +1,3 @@ -#define EXPERIMENTAL_FLAGS 0 - /*+M************************************************************************* * Adaptec AIC7xxx device driver for Linux. * @@ -29,21 +27,73 @@ * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the * ANSI SCSI-2 specification (draft 10c), ... * - * ---------------------------------------------------------------- - * Modified to include support for wide and twin bus adapters, - * DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes, + * -------------------------------------------------------------------------- + * + * Modifications by Daniel M. Eischen (deischen@iworks.InterWorks.org): + * + * Substantially modified to include support for wide and twin bus + * adapters, DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes, * SCB paging, and other rework of the code. * - * Parts of this driver are based on the FreeBSD driver by Justin - * T. Gibbs. + * Parts of this driver were also based on the FreeBSD driver by + * Justin T. Gibbs. His copyright follows: + * + * -------------------------------------------------------------------------- + * Copyright (c) 1994-1997 Justin Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Where this Software is combined with software released under the terms of + * the GNU Public License ("GPL") and the terms of the GPL would require the + * combined work to also be released under the terms of the GPL, the terms + * and conditions of this License will apply in addition to those of the + * GPL with the exception of any terms or conditions of this License that + * conflict with, or are expressly prohibited by, the GPL. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $Id: aic7xxx.c,v 1.119 1997/06/27 19:39:18 gibbs Exp $ + *--------------------------------------------------------------------------- + * + * Thanks also go to (in alphabetical order) the following: + * + * Rory Bolt - Sequencer bug fixes + * Jay Estabrook - Initial DEC Alpha support + * Doug Ledford - Much needed abort/reset bug fixes + * Kai Makisara - DMAing of SCBs * * A Boot time option was also added for not resetting the scsi bus. * - * Form: aic7xxx=extended,no_reset + * Form: aic7xxx=extended + * aic7xxx=no_reset + * aic7xxx=ultra + * aic7xxx=irq_trigger:[0,1] # 0 edge, 1 level + * aic7xxx=verbose * - * -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 07/07/96 + * Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97 * - * $Id: aic7xxx.c,v 4.0 1996/10/13 08:23:42 deang Exp $ + * $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $ *-M*************************************************************************/ #ifdef MODULE @@ -67,7 +117,11 @@ #include "scsi.h" #include "hosts.h" #include "aic7xxx.h" + +#include "aic7xxx/sequencer.h" +#include "aic7xxx/scsi_message.h" #include "aic7xxx_reg.h" +#include "aic7xxx_seq.h" #include #include /* for kmalloc() */ @@ -81,14 +135,18 @@ struct proc_dir_entry proc_scsi_aic7xxx = { PROC_SCSI_AIC7XXX, 7, "aic7xxx", - S_IFDIR | S_IRUGO | S_IXUGO, 2 + S_IFDIR | S_IRUGO | S_IXUGO, 2, + 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -#define AIC7XXX_C_VERSION "$Revision: 4.0 $" +#define AIC7XXX_C_VERSION "$Revision: 4.1 $" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) -#define MIN(a,b) ((a < b) ? a : b) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) #define ALL_TARGETS -1 +#define ALL_CHANNELS '\0' +#define ALL_LUNS -1 #ifndef TRUE # define TRUE 1 #endif @@ -107,11 +165,8 @@ * support because all PCI dependent code is bracketed with * "#ifdef CONFIG_PCI ... #endif CONFIG_PCI". * - * o Twin bus support - this has been tested and does work. - * - * o DMAing of SCBs - thanks to Kai Makisara, this now works. - * This define is now taken out and DMAing of SCBs is always - * performed (8/12/95 - DE). + * o Twin bus support - this has been tested and does work. It is + * not an option anymore. * * o Tagged queueing - this driver is capable of tagged queueing * but I am unsure as to how well the higher level driver implements @@ -140,16 +195,16 @@ * LUN using its own heuristic based on the number of available * SCBs. * - * o 3985 support - The 3985 adapter is much like the 3940, but - * has three 7870 controllers as opposed to two for the 3940. - * It will get probed and recognized as three different adapters, - * but all three controllers can share the same external bank of - * 255 SCBs. If you enable AIC7XXX_SHARE_SCBS, then the driver - * will attempt to share the common bank of SCBs between the three - * controllers of the 3985. This is experimental and hasn't - * been tested. By default, we do not share the bank of SCBs, - * and force the controllers to use their own internal bank of - * 16 SCBs. Please let us know if sharing the SCB array works. + * o 3985 support - The 3985 adapter is much like the 3940, but has + * three 7870 controllers as opposed to two for the 3940. It will + * be probed and recognized as three different adapters, but all + * three controllers can share the same external bank of 255 SCBs. + * If you enable AIC7XXX_USE_EXT_SCBRAM, then the driver will attempt + * to use and share the common bank of SCBs between the three + * controllers of the 3985. This is experimental and hasn't been + * been tested. By default, we do not use external SCB RAM, and + * force the controllers to use their own internal bank of 16 SCBs. + * Please let us know if using the external SCB array works. * * o SCB paging support - SCB paging is enabled by defining * AIC7XXX_PAGE_ENABLE. Support for this was taken from the @@ -162,43 +217,54 @@ * Note that sharing of IRQs is not an option any longer. Linux supports * it so we support it. * - * Daniel M. Eischen, deischen@iworks.InterWorks.org, 06/30/96 + * Daniel M. Eischen, deischen@iworks.InterWorks.org, 01/26/96 */ -/* Uncomment this for testing twin bus support. */ -#define AIC7XXX_TWIN_SUPPORT - /* Uncomment this for tagged queueing. */ -/* #define AIC7XXX_TAGGED_QUEUEING */ +#ifdef CONFIG_AIC7XXX_TAGGED_QUEUEING +#define AIC7XXX_TAGGED_QUEUEING +#endif /* * You can try raising me if tagged queueing is enabled, or lowering * me if you only have 4 SCBs. */ -/* #define AIC7XXX_CMDS_PER_LUN 8 */ +#ifdef CONFIG_AIC7XXX_CMDS_PER_LUN +#define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN +#endif /* Set this to the delay in seconds after SCSI bus reset. */ +#ifdef CONFIG_AIC7XXX_RESET_DELAY +#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY +#else #define AIC7XXX_RESET_DELAY 15 +#endif /* - * Uncomment the following define for collection of SCSI transfer statistics - * for the /proc filesystem. + * Control collection of SCSI transfer statistics for the /proc filesystem. * * NOTE: Do NOT enable this when running on kernels version 1.2.x and below. * NOTE: This does affect performance since it has to maintain statistics. */ -/* #define AIC7XXX_PROC_STATS */ +#ifdef CONFIG_AIC7XXX_PROC_STATS +#define AIC7XXX_PROC_STATS +#endif /* - * Uncomment the following to enable SCB paging. + * Enable SCB paging. */ -/* #define AIC7XXX_PAGE_ENABLE */ +#ifdef CONFIG_AIC7XXX_PAGE_ENABLE +#define AIC7XXX_PAGE_ENABLE +#endif /* - * Uncomment the following to enable sharing of the external bank - * of 255 SCBs for the 3985. + * Uncomment the following to enable use of the external bank + * of 255 SCBs. For 3985 adapters, this will also enable sharing + * of the SCB array across all three controllers. */ -#define AIC7XXX_SHARE_SCBS +#ifdef CONFIG_AIC7XXX_USE_EXT_SCBRAM +#define AIC7XXX_USE_EXT_SCBRAM +#endif /* * For debugging the abort/reset code. @@ -211,6 +277,87 @@ #define AIC7XXX_DEBUG /* + * Set this for defining the number of tagged commands on a device + * by device, and controller by controller basis. The first set + * of tagged commands will be used for the first detected aic7xxx + * controller, the second set will be used for the second detected + * aic7xxx controller, and so on. These values will *only* be used + * for targets that are tagged queueing capable; these values will + * be ignored in all other cases. The tag_commands is an array of + * 16 to allow for wide and twin adapters. Twin adapters will use + * indexes 0-7 for channel 0, and indexes 8-15 for channel 1. + * + * *** Determining commands per LUN *** + * + * When AIC7XXX_CMDS_PER_LUN is not defined, the driver will use its + * own algorithm to determine the commands/LUN. If SCB paging is + * enabled, the commands/LUN is 8. When SCB paging is not enabled, + * then commands/LUN is 8 for adapters with 16 or more hardware SCBs + * and 4 commands/LUN for adapters with 3 or 4 SCBs. + * + */ +/* #define AIC7XXX_TAGGED_QUEUEING_BY_DEVICE */ + +#ifdef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE +typedef struct +{ + char tag_commands[16]; /* Allow for wide/twin channel adapters. */ +} adapter_tag_info_t; + +/* + * Make a define that will tell the driver to use it's own algorithm + * for determining commands/LUN (see Determining commands per LUN + * above). + */ +#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + +/* + * Modify this as you see fit for your system. By setting tag_commands + * to 0, the driver will use it's own algorithm for determining the + * number of commands to use (see above). When -1, the driver will + * not enable tagged queueing for that particular device. When positive + * (> 0) the values in the array are used for the queue_depth. Note + * that the maximum value for an entry is 127. + * + * In this example, the first line will enable tagged queueing for all + * the devices on the first probed aic7xxx adapter and tells the driver + * to use it's own algorithm for determining commands/LUN. + * + * The second line enables tagged queueing with 4 commands/LUN for IDs + * (1, 2-11, 13-15), disables tagged queueing for ID 12, and tells the + * driver to use its own algorithm for ID 1. + * + * The third line is the same as the first line. + * + * The fourth line disables tagged queueing for devices 0 and 3. It + * enables tagged queueing for the other IDs, with 16 commands/LUN + * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for + * IDs 2, 5-7, and 9-15. + */ +adapter_tag_info_t aic7xxx_tag_info[] = +{ + {DEFAULT_TAG_COMMANDS}, + {{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, -1, 4, 4, 4}}, + {DEFAULT_TAG_COMMANDS}, + {{-1, 16, 4, -1, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}} +}; +#endif + +/* + * Don't define this unless you have problems with the driver + * interrupt handler. The old method would register the drivers + * interrupt handler as a "fast" type interrupt handler that would + * lock out other interrupts. Since this driver can spend a lot + * of time in the interrupt handler, this is _not_ a good idea. + * It also conflicts with some of the more common ethernet drivers + * that don't use fast interrupts. Currently, Linux does not allow + * IRQ sharing unless both drivers can agree on the type of interrupt + * handler. + */ +/* #define AIC7XXX_OLD_ISR_TYPE */ + + +/* * Controller type and options */ typedef enum { @@ -232,14 +379,15 @@ AIC_7882, /* PCI aic7882 on 3940 Ultra */ AIC_7883, /* PCI aic7883 on 3985 Ultra */ AIC_7884 /* PCI aic7884 on 294x Ultra Differential */ -} aha_type; +} aha_chip_type; typedef enum { AIC_777x, /* AIC-7770 based */ - AIC_785x, /* AIC-7850 based */ + AIC_785x, /* AIC-7850 based (3 SCBs)*/ + AIC_786x, /* AIC-7860 based (7850 ultra) */ AIC_787x, /* AIC-7870 based */ - AIC_788x /* AIC-7880 based */ -} aha_chip_type; + AIC_788x /* AIC-7880 based (ultra) */ +} aha_chip_class_type; typedef enum { AIC_SINGLE, /* Single Channel */ @@ -269,24 +417,24 @@ * Don't forget to change this when changing the types! */ static const char *board_names[] = { - "", /* AIC_NONE */ - "AIC-7770", /* AIC_7770 */ - "AHA-2740", /* AIC_7771 */ - "AHA-2840", /* AIC_284x */ - "AIC-7850", /* AIC_7850 */ - "AIC-7855", /* AIC_7855 */ - "AIC-7850 Ultra", /* AIC_7860 */ - "AHA-2940A Ultra", /* AIC_7861 */ - "AIC-7870", /* AIC_7870 */ - "AHA-2940", /* AIC_7871 */ - "AHA-3940", /* AIC_7872 */ - "AHA-3985", /* AIC_7873 */ - "AHA-2940 Differential", /* AIC_7874 */ - "AIC-7880 Ultra", /* AIC_7880 */ - "AHA-2940 Ultra", /* AIC_7881 */ - "AHA-3940 Ultra", /* AIC_7882 */ - "AHA-3985 Ultra", /* AIC_7883 */ - "AHA-2940 Ultra Differential" /* AIC_7884 */ + "AIC-7xxx Unknown", /* AIC_NONE */ + "Adaptec AIC-7770 SCSI host adapter", /* AIC_7770 */ + "Adaptec AHA-274X SCSI host adapter", /* AIC_7771 */ + "Adaptec AHA-284X SCSI host adapter", /* AIC_284x */ + "Adaptec AIC-7850 SCSI host adapter", /* AIC_7850 */ + "Adaptec AIC-7855 SCSI host adapter", /* AIC_7855 */ + "Adaptec AIC-7860 Ultra SCSI host adapter", /* AIC_7860 */ + "Adaptec AHA-2940A Ultra SCSI host adapter", /* AIC_7861 */ + "Adaptec AIC-7870 SCSI host adapter", /* AIC_7870 */ + "Adaptec AHA-294X SCSI host adapter", /* AIC_7871 */ + "Adaptec AHA-394X SCSI host adapter", /* AIC_7872 */ + "Adaptec AHA-398X SCSI host adapter", /* AIC_7873 */ + "Adaptec AHA-2944 SCSI host adapter", /* AIC_7874 */ + "Adaptec AIC-7880 Ultra SCSI host adapter", /* AIC_7880 */ + "Adaptec AHA-294X Ultra SCSI host adapter", /* AIC_7881 */ + "Adaptec AHA-394X Ultra SCSI host adapter", /* AIC_7882 */ + "Adaptec AHA-398X Ultra SCSI host adapter", /* AIC_7883 */ + "Adaptec AHA-2944 Ultra SCSI host adapter" /* AIC_7884 */ }; /* @@ -310,12 +458,17 @@ */ #define DID_RETRY_COMMAND DID_ERROR +#define HSCSIID 0x07 +#define HWSCSIID 0x0F +#define SCSI_RESET 0x040 + /* * EISA/VL-bus stuff */ #define MINSLOT 1 #define MAXSLOT 15 #define SLOTBASE(x) ((x) << 12) +#define BASE_TO_SLOT(x) ((x) >> 12) /* * Standard EISA Host ID regs (Offset from slot base) @@ -334,14 +487,6 @@ #define INTDEF 0x5C /* Interrupt Definition Register */ /* - * Some defines for the HCNTRL register. - */ -#define REQ_PAUSE IRQMS | INTEN | PAUSE -#define UNPAUSE_274X IRQMS | INTEN -#define UNPAUSE_284X INTEN -#define UNPAUSE_294X IRQMS | INTEN - -/* * AIC-78X0 PCI registers */ #define CLASS_PROGIF_REVID 0x08 @@ -377,7 +522,7 @@ * each word, while the C56 and C66 (4096 bits) use 8 bits to * address each word. */ -typedef enum {c46 = 6, c56_66 = 8} seeprom_chip_type; +typedef enum {C46 = 6, C56_66 = 8} seeprom_chip_type; /* * @@ -417,15 +562,15 @@ /* * Host Adapter Control Bits */ -/* UNUSED 0x0001 */ +#define CFAUTOTERM 0x0001 /* Perform Auto termination */ #define CFULTRAEN 0x0002 /* Ultra SCSI speed enable (Ultra cards) */ #define CF284XSELTO 0x0003 /* Selection timeout (284x cards) */ #define CF284XFIFO 0x000C /* FIFO Threshold (284x cards) */ -#define CFSTERM 0x0004 /* SCSI low byte termination (non-wide cards) */ +#define CFSTERM 0x0004 /* SCSI low byte termination */ #define CFWSTERM 0x0008 /* SCSI high byte termination (wide card) */ #define CFSPARITY 0x0010 /* SCSI parity */ #define CF284XSTERM 0x0020 /* SCSI low byte termination (284x cards) */ -#define CFRESETB 0x0040 /* reset SCSI bus at IC initialization */ +#define CFRESETB 0x0040 /* reset SCSI bus at boot */ /* UNUSED 0xFF80 */ unsigned short adapter_control; /* word 17 */ @@ -448,35 +593,17 @@ unsigned short checksum; /* word 31 */ }; - -#define SCSI_RESET 0x040 - -/* - * Pause the sequencer and wait for it to actually stop - this - * is important since the sequencer can disable pausing for critical - * sections. - */ -#define PAUSE_SEQUENCER(p) \ - outb(p->pause, HCNTRL + p->base); \ - while ((inb(HCNTRL + p->base) & PAUSE) == 0) \ - ; \ - -/* - * Unpause the sequencer. Unremarkable, yet done often enough to - * warrant an easy way to do it. - */ -#define UNPAUSE_SEQUENCER(p) \ - outb(p->unpause, HCNTRL + p->base) - -/* - * Restart the sequencer program from address zero - */ -#define RESTART_SEQUENCER(p) \ - do { \ - outb(SEQRESET | FASTMODE, SEQCTL + p->base); \ - } while (inb(SEQADDR0 + p->base) != 0 && \ - inb(SEQADDR1 + p->base) != 0); \ - UNPAUSE_SEQUENCER(p); +#define SELBUS_MASK 0x0a +#define SELNARROW 0x00 +#define SELBUSB 0x08 +#define SINGLE_BUS 0x00 + +#define SCB_TARGET(scb) \ + (((scb)->hscb->target_channel_lun & TID) >> 4) +#define SCB_LUN(scb) \ + ((scb)->hscb->target_channel_lun & LID) +#define SCB_IS_SCSIBUS_B(scb) \ + (((scb)->hscb->target_channel_lun & SELBUSB) != 0) /* * If an error occurs during a data transfer phase, run the command @@ -540,12 +667,6 @@ static int aic7xxx_spurious_count; /* - * The driver keeps up to four scb structures per card in memory. Only the - * first 25 bytes of the structure are valid for the hardware, the rest used - * for driver level bookkeeping. - */ - -/* * As of Linux 2.1, the mid-level SCSI code uses virtual addresses * in the scatter-gather lists. We need to convert the virtual * addresses to physical addresses. @@ -558,20 +679,28 @@ /* * Maximum number of SG segments these cards can support. */ -#define MAX_SG 256 +#define AIC7XXX_MAX_SG 27 -struct aic7xxx_scb { +/* + * The maximum number of SCBs we could have for ANY type + * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE + * SEQUENCER CODE IF THIS IS MODIFIED! + */ +#define AIC7XXX_MAXSCB 255 + + +struct aic7xxx_hwscb { /* ------------ Begin hardware supported fields ---------------- */ /* 0*/ unsigned char control; /* 1*/ unsigned char target_channel_lun; /* 4/1/3 bits */ /* 2*/ unsigned char target_status; /* 3*/ unsigned char SG_segment_count; -/* 4*/ unsigned char SG_list_pointer[4] __attribute__ ((packed)); +/* 4*/ unsigned int SG_list_pointer; /* 8*/ unsigned char residual_SG_segment_count; -/* 9*/ unsigned char residual_data_count[3] __attribute__ ((packed)); -/*12*/ unsigned char data_pointer[4] __attribute__ ((packed)); -/*16*/ unsigned int data_count __attribute__ ((packed)); /* must be 32 bits */ -/*20*/ unsigned char SCSI_cmd_pointer[4] __attribute__ ((packed)); +/* 9*/ unsigned char residual_data_count[3]; +/*12*/ unsigned int data_pointer; +/*16*/ unsigned int data_count; +/*20*/ unsigned int SCSI_cmd_pointer; /*24*/ unsigned char SCSI_cmd_length; /*25*/ u_char tag; /* Index into our kernel SCB array. * Also used as the tag for tagged I/O @@ -579,29 +708,47 @@ #define SCB_PIO_TRANSFER_SIZE 26 /* amount we need to upload/download * via PIO to initialize a transaction. */ -/*26*/ u_char next; /* Used to thread SCBs awaiting selection +/*26*/ unsigned char next; /* Used to thread SCBs awaiting selection * or disconnected down in the sequencer. */ - /*-----------------end of hardware supported fields----------------*/ - Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */ - struct aic7xxx_scb *q_next; /* next scb in queue */ -#define SCB_FREE 0x00 -#define SCB_ACTIVE 0x01 -#define SCB_ABORTED 0x02 -#define SCB_DEVICE_RESET 0x04 -#define SCB_IMMED 0x08 -#define SCB_SENSE 0x10 -#define SCB_QUEUED_FOR_DONE 0x40 -#define SCB_PAGED_OUT 0x80 -#define SCB_WAITINGQ 0x100 -#define SCB_ASSIGNEDQ 0x200 -#define SCB_SENTORDEREDTAG 0x400 -#define SCB_IN_PROGRESS (SCB_ACTIVE | SCB_PAGED_OUT | \ - SCB_WAITINGQ | SCB_ASSIGNEDQ) - int state; /* current state of scb */ - unsigned int position; /* Position in scb array */ - struct hw_scatterlist sg_list[MAX_SG]; /* SG list in adapter format */ - unsigned char sense_cmd[6]; /* Allocate 6 characters for sense command */ +/*27*/ unsigned char prev; +/*28*/ unsigned int pad; /* + * Unused by the kernel, but we require + * the padding so that the array of + * hardware SCBs is alligned on 32 byte + * boundaries so the sequencer can index + */ +}; + +typedef enum { + SCB_FREE = 0x0000, + SCB_ACTIVE = 0x0001, + SCB_ABORTED = 0x0002, + SCB_DEVICE_RESET = 0x0004, + SCB_SENSE = 0x0008, + SCB_TIMEDOUT = 0x0010, + SCB_QUEUED_FOR_DONE = 0x0020, + SCB_RECOVERY_SCB = 0x0040, + SCB_WAITINGQ = 0x0080, + SCB_ASSIGNEDQ = 0x0100, + SCB_SENTORDEREDTAG = 0x0200, + SCB_MSGOUT_SDTR = 0x0400, + SCB_MSGOUT_WDTR = 0x0800, + SCB_ABORT = 0x1000, + SCB_QUEUED_ABORT = 0x2000 +} scb_flag_type; + +struct aic7xxx_scb { + struct aic7xxx_hwscb *hscb; /* corresponding hardware scb */ + Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */ + struct aic7xxx_scb *q_next; /* next scb in queue */ + scb_flag_type flags; /* current state of scb */ + struct hw_scatterlist *sg_list; /* SG list in adapter format */ + unsigned char sg_count; + unsigned char sense_cmd[6]; /* + * Allocate 6 characters for + * sense command. + */ }; /* @@ -626,20 +773,17 @@ generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 }; typedef struct { + struct aic7xxx_hwscb *hscbs; scb_queue_type free_scbs; /* * SCBs assigned to free slot on * card (no paging required) */ - int numscbs; /* current number of scbs */ - int activescbs; /* active scbs */ -} scb_usage_type; - -/* - * The maximum number of SCBs we could have for ANY type - * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE - * SEQUENCER CODE IF THIS IS MODIFIED! - */ -#define AIC7XXX_MAXSCB 255 + unsigned char numscbs; /* current number of scbs */ + unsigned char maxhscbs; /* hardware scbs */ + unsigned char maxscbs; /* max scbs including pageable scbs */ + struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB]; + unsigned int reserve[100]; +} scb_data_type; /* * Define a structure used for each host adapter, only one per IRQ. @@ -647,17 +791,26 @@ struct aic7xxx_host { struct Scsi_Host *host; /* pointer to scsi host */ int host_no; /* SCSI host number */ + int instance; /* aic7xxx instance number */ + int scsi_id; /* host adapter SCSI ID */ + int scsi_id_b; /* channel B for twin adapters */ + int irq; /* IRQ for this adapter */ int base; /* card base address */ - int maxhscbs; /* hardware SCBs */ - int maxscbs; /* max SCBs (including pageable) */ -#define A_SCANNED 0x0001 -#define B_SCANNED 0x0002 -#define EXTENDED_TRANSLATION 0x0004 -#define HAVE_SEEPROM 0x0008 -#define ULTRA_ENABLED 0x0010 -#define PAGE_ENABLED 0x0020 -#define IN_ISR 0x0040 -#define USE_DEFAULTS 0x0080 + unsigned int mbase; /* I/O memory address */ + volatile unsigned char *maddr; /* memory mapped address */ +#define A_SCANNED 0x0001 +#define B_SCANNED 0x0002 +#define EXTENDED_TRANSLATION 0x0004 +#define FLAGS_CHANNEL_B_PRIMARY 0x0008 +#define MULTI_CHANNEL 0x0010 +#define ULTRA_ENABLED 0x0020 +#define PAGE_ENABLED 0x0040 +#define USE_DEFAULTS 0x0080 +#define BIOS_ENABLED 0x0100 +#define IN_ISR 0x0200 +#define IN_TIMEOUT 0x0400 +#define SHARED_SCBDATA 0x0800 +#define HAVE_SEEPROM 0x1000 unsigned int flags; unsigned int isr_count; /* Interrupt count */ unsigned short needsdtr_copy; /* default config */ @@ -668,36 +821,22 @@ unsigned short wdtr_pending; unsigned short orderedtag; unsigned short discenable; /* Targets allowed to disconnect */ - aha_type type; /* card type */ - aha_chip_type chip_type; /* chip base type */ + aha_chip_type chip_type; /* card type */ + aha_chip_class_type chip_class; aha_bus_type bus_type; /* normal/twin/wide bus */ - char * mbase; /* I/O memory address */ - unsigned char chan_num; /* for 3940/3985, channel number */ + unsigned char chan_num; /* for 39xx, channel number */ unsigned char unpause; /* unpause value for HCNTRL */ unsigned char pause; /* pause value for HCNTRL */ unsigned char qcntmask; - struct seeprom_config seeprom; + unsigned char qfullcount; + unsigned char curqincnt; struct Scsi_Host *next; /* allow for multiple IRQs */ - struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB]; /* active commands */ - struct aic7xxx_scb *pagedout_ntscbs[16]; /* - * paged-out, non-tagged scbs - * indexed by target. - */ - scb_queue_type page_scbs; /* - * SCBs that will require paging - * before use (no assigned slot) - */ + unsigned char activescbs; /* active scbs */ scb_queue_type waiting_scbs; /* - * SCBs waiting to be paged and - * started. - */ - scb_queue_type assigned_scbs; /* - * SCBs that were waiting but have - * have now been assigned a slot - * by aic7xxx_free_scb + * SCBs waiting for space in + * the QINFIFO. */ - scb_usage_type scb_usage; - scb_usage_type *scb_link; + scb_data_type *scb_data; struct aic7xxx_cmd_queue { Scsi_Cmnd *head; @@ -709,6 +848,7 @@ #define BUS_DEVICE_RESET_PENDING 0x02 int flags; int commands_sent; + int active_cmds; } device_status[16]; #ifdef AIC7XXX_PROC_STATS /* @@ -734,34 +874,10 @@ #endif /* AIC7XXX_PROC_STATS */ }; -struct aic7xxx_host_config { - int irq; /* IRQ number */ - int mbase; /* memory base address*/ - int base; /* I/O base address*/ - int maxhscbs; /* hardware SCBs */ - int maxscbs; /* max SCBs (including pageable) */ - int unpause; /* unpause value for HCNTRL */ - int pause; /* pause value for HCNTRL */ - int scsi_id; /* host SCSI ID */ - int scsi_id_b; /* host SCSI ID B channel for twin cards */ - unsigned int flags; /* used the same as struct aic7xxx_host flags */ - int chan_num; /* for 3940/3985, channel number */ - unsigned char busrtime; /* bus release time */ - unsigned char bus_speed; /* bus speed */ - unsigned char qcntmask; - aha_type type; /* card type */ - aha_chip_type chip_type; /* chip base type */ - aha_bus_type bus_type; /* normal/twin/wide bus */ - aha_status_type bios; /* BIOS is enabled/disabled */ - aha_status_type parity; /* bus parity enabled/disabled */ - aha_status_type low_term; /* bus termination low byte */ - aha_status_type high_term; /* bus termination high byte (wide cards only) */ -}; - /* * Valid SCSIRATE values. (p. 3-17) - * Provides a mapping of transfer periods in ns to the proper value to - * stick in the scsiscfr reg to use that transfer rate. + * Provides a mapping of transfer periods in ns/4 to the proper value to + * stick in the SCSIRATE reg to use that transfer rate. */ static struct { short period; @@ -770,17 +886,17 @@ short rate; const char *english; } aic7xxx_syncrates[] = { - { 50, 0x100, "20.0" }, - { 62, 0x110, "16.0" }, - { 75, 0x120, "13.4" }, - { 100, 0x000, "10.0" }, - { 125, 0x010, "8.0" }, - { 150, 0x020, "6.67" }, - { 175, 0x030, "5.7" }, - { 200, 0x040, "5.0" }, - { 225, 0x050, "4.4" }, - { 250, 0x060, "4.0" }, - { 275, 0x070, "3.6" } + { 12, 0x100, "20.0" }, + { 15, 0x110, "16.0" }, + { 18, 0x120, "13.4" }, + { 25, 0x000, "10.0" }, + { 31, 0x010, "8.0" }, + { 37, 0x020, "6.67" }, + { 43, 0x030, "5.7" }, + { 50, 0x040, "5.0" }, + { 56, 0x050, "4.4" }, + { 62, 0x060, "4.0" }, + { 68, 0x070, "3.6" } }; static int num_aic7xxx_syncrates = @@ -789,166 +905,51 @@ #ifdef CONFIG_PCI static int number_of_3940s = 0; static int number_of_3985s = 0; -#ifdef AIC7XXX_SHARE_SCBS -static scb_usage_type *shared_3985_scbs = NULL; -#endif -#endif CONFIG_PCI +#endif /* CONFIG_PCI */ #ifdef AIC7XXX_DEBUG -static void -debug_config(struct aic7xxx_host_config *p) -{ - int scsi_conf; - unsigned char brelease; - unsigned char dfthresh; - - static int DFT[] = { 0, 50, 75, 100 }; - static int SST[] = { 256, 128, 64, 32 }; - static const char *BUSW[] = { "", "-TWIN", "-WIDE" }; - - scsi_conf = inb(SCSICONF + p->base); - - /* - * Scale the Data FIFO Threshhold and the Bus Release Time; they are - * stored in formats compatible for writing to sequencer registers. - */ - dfthresh = p->bus_speed >> 6; - - if (p->chip_type == AIC_777x) - { - brelease = p->busrtime >> 2; - } - else - { - brelease = p->busrtime; - } - if (brelease == 0) - { - brelease = 2; - } - - switch (p->type) - { - case AIC_7770: - case AIC_7771: - printk("%s%s AT EISA SLOT %d:\n", board_names[p->type], BUSW[p->bus_type], - p->base >> 12); - break; - - case AIC_284x: - printk("%s%s AT VLB SLOT %d:\n", board_names[p->type], BUSW[p->bus_type], - p->base >> 12); - break; - - case AIC_7850: - case AIC_7855: - case AIC_7860: - case AIC_7861: - case AIC_7870: - case AIC_7871: - case AIC_7872: - case AIC_7873: - case AIC_7874: - case AIC_7880: - case AIC_7881: - case AIC_7882: - case AIC_7883: - case AIC_7884: - printk("%s%s (PCI-bus), I/O 0x%x, Mem 0x%x:\n", board_names[p->type], - BUSW[p->bus_type], p->base, p->mbase); - break; - - default: - panic("aic7xxx: (debug_config) internal error.\n"); - } - - printk(" irq %d\n" - " bus release time %d bclks\n" - " data fifo threshold %d%%\n", - p->irq, - brelease, - DFT[dfthresh]); - - printk(" SCSI CHANNEL A:\n" - " scsi id %d\n" - " scsi selection timeout %d ms\n" - " scsi bus reset at power-on %sabled\n", - (p->bus_type & AIC_WIDE) ? (scsi_conf & 0x0f) : (scsi_conf & 0x07), - SST[(scsi_conf >> 3) & 0x03], - (scsi_conf & 0x40) ? "en" : "dis"); - - if ((p->chip_type == AIC_777x) && (p->parity == AIC_UNKNOWN)) - { - /* - * Set the parity for 7770 based cards. - */ - p->parity = (scsi_conf & 0x20) ? AIC_ENABLED : AIC_DISABLED; - } - if (p->parity != AIC_UNKNOWN) - { - printk(" scsi bus parity %sabled\n", - (p->parity == AIC_ENABLED) ? "en" : "dis"); - } - - if ((p->type == AIC_7770) || (p->type == AIC_7771)) - { - p->low_term = (scsi_conf & 0x80) ? AIC_ENABLED : AIC_DISABLED; - } - if (p->low_term != AIC_UNKNOWN) - { - printk(" scsi bus termination (low byte) %sabled\n", - (p->low_term == AIC_ENABLED) ? "en" : "dis"); - } - if ((p->bus_type == AIC_WIDE) && (p->high_term != AIC_UNKNOWN)) - { - printk(" scsi bus termination (high byte) %sabled\n", - (p->high_term == AIC_ENABLED) ? "en" : "dis"); - } -} - #if 0 static void debug_scb(struct aic7xxx_scb *scb) { - printk("control 0x%x, tcl 0x%x, sg_count %d, sg_ptr 0x%x, cmdp 0x%x, cmdlen %d\n", - scb->control, scb->target_channel_lun, scb->SG_segment_count, - (scb->SG_list_pointer[3] << 24) | (scb->SG_list_pointer[2] << 16) | - (scb->SG_list_pointer[1] << 8) | scb->SG_list_pointer[0], - (scb->SCSI_cmd_pointer[3] << 24) | (scb->SCSI_cmd_pointer[2] << 16) | - (scb->SCSI_cmd_pointer[1] << 8) | scb->SCSI_cmd_pointer[0], - scb->SCSI_cmd_length); - printk("reserved 0x%x, target status 0x%x, resid SG count %d, resid data count %d\n", - (scb->RESERVED[1] << 8) | scb->RESERVED[0], scb->target_status, - scb->residual_SG_segment_count, - ((scb->residual_data_count[2] << 16) | - (scb->residual_data_count[1] << 8) | - (scb->residual_data_count[0])); - printk("data ptr 0x%x, data count %d, next waiting %d\n", - (scb->data_pointer[3] << 24) | (scb->data_pointer[2] << 16) | - (scb->data_pointer[1] << 8) | scb->data_pointer[0], - scb->data_count, scb->next_waiting); - printk("next ptr 0x%lx, Scsi Cmnd 0x%lx, state 0x%x, position %d\n", - (unsigned long) scb->next, (unsigned long) scb->cmd, scb->state, - scb->position); + struct aic7xxx_hwscb *hscb = scb->hscb; + + printk("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n", + scb, + hscb->control, + hscb->target_channel_lun, + hscb->SCSI_cmd_length, + hscb->SCSI_cmd_pointer ); + printk(" datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n", + hscb->data_count, + hscb->data_pointer, + hscb->SG_segment_count, + hscb->SG_list_pointer); + printk(" sg_addr:%lx sg_len:%ld\n", + hscb->sg_list[0].address, + hscb->sg_list[0].length); } #endif #else -# define debug_config(x) # define debug_scb(x) #endif AIC7XXX_DEBUG -#define TCL_OF_SCB(x) (((x)->target_channel_lun >> 4) & 0xf), \ - (((x)->target_channel_lun >> 3) & 0x01), \ - ((x)->target_channel_lun & 0x07) +#define TCL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf), \ + (((scb->hscb)->target_channel_lun >> 3) & 0x01), \ + ((scb->hscb)->target_channel_lun & 0x07) -#define TARGET_INDEX(x) ((x)->target | ((x)->channel << 3)) +#define TC_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf), \ + (((scb->hscb)->target_channel_lun >> 3) & 0x01) + +#define CHAN_TO_INT(chan) ((chan) == 'A' ? 0 : 1) + +#define TARGET_INDEX(cmd) ((cmd)->target | ((cmd)->channel << 3)) /* * XXX - these options apply unilaterally to _all_ 274x/284x/294x - * cards in the system. This should be fixed, but then, - * does anyone really have more than one in a machine? + * cards in the system. This should be fixed. */ static unsigned int aic7xxx_extended = 0; /* extended translation on? */ static unsigned int aic7xxx_no_reset = 0; /* no resetting of SCSI bus */ @@ -958,6 +959,53 @@ * 1 use level triggered */ static int aic7xxx_enable_ultra = 0; /* enable ultra SCSI speeds */ +static int aic7xxx_verbose = 0; /* verbose messages */ + + +/**************************************************************************** + * + * These functions are not used yet, but when we do memory mapped + * IO, we'll use them then. + * + ***************************************************************************/ +static inline unsigned char +aic_inb(struct aic7xxx_host *p, long port) +{ + if (p->maddr != NULL) + return (p->maddr[port]); + else + return (inb(p->base + port)); +} + +static inline void +aic_outb(struct aic7xxx_host *p, unsigned char val, long port) +{ + if (p->maddr != NULL) + p->maddr[port] = val; + else + outb(val, p->base + port); +} + +static inline void +aic_outsb(struct aic7xxx_host *p, long port, unsigned char *valp, size_t size) +{ + if (p->maddr != NULL) + { + __asm __volatile(" + cld; + 1: lodsb; + movb %%al,(%0); + loop 1b" : + : + "r" ((p)->maddr + (port)), + "S" ((valp)), "c" ((size)) : + "%esi", "%ecx", "%eax"); + } + else + { + outsb(p->base + port, valp, size); + } +} /*+F************************************************************************* * Function: @@ -982,6 +1030,7 @@ { "no_reset", &aic7xxx_no_reset }, { "irq_trigger", &aic7xxx_irq_trigger }, { "ultra", &aic7xxx_enable_ultra }, + { "verbose", &aic7xxx_verbose }, { NULL, NULL } }; @@ -1007,162 +1056,343 @@ /*+F************************************************************************* * Function: - * aic7xxx_loadseq + * pause_sequencer * * Description: - * Load the sequencer code into the controller memory. + * Pause the sequencer and wait for it to actually stop - this + * is important since the sequencer can disable pausing for critical + * sections. *-F*************************************************************************/ -static void -aic7xxx_loadseq(int base) +static inline void +pause_sequencer(struct aic7xxx_host *p) { - static unsigned char seqprog[] = { - /* - * Each sequencer instruction is 29 bits - * long (fill in the excess with zeroes) - * and has to be loaded from least -> most - * significant byte, so this table has the - * byte ordering reversed. - */ -# include "aic7xxx_seq.h" - }; - - /* - * When the AIC-7770 is paused (as on chip reset), the - * sequencer address can be altered and a sequencer - * program can be loaded by writing it, byte by byte, to - * the sequencer RAM port - the Adaptec documentation - * recommends using REP OUTSB to do this, hence the inline - * assembly. Since the address autoincrements as we load - * the program, reset it back to zero afterward. Disable - * sequencer RAM parity error detection while loading, and - * make sure the LOADRAM bit is enabled for loading. - */ - outb(PERRORDIS | SEQRESET | LOADRAM, SEQCTL + base); - - outsb(SEQRAM + base, seqprog, sizeof(seqprog)); - - /* - * WARNING! This is a magic sequence! After extensive - * experimentation, it seems that you MUST turn off the - * LOADRAM bit before you play with SEQADDR again, else - * you will end up with parity errors being flagged on - * your sequencer program. (You would also think that - * turning off LOADRAM and setting SEQRESET to reset the - * address to zero would work, but you need to do it twice - * for it to take effect on the address. Timing problem?) - */ - do { - /* - * Actually, reset it until - * the address shows up as - * zero just to be safe.. - */ - outb(SEQRESET | FASTMODE, SEQCTL + base); - } while ((inb(SEQADDR0 + base) != 0) && (inb(SEQADDR1 + base) != 0)); + outb(p->pause, p->base + HCNTRL); + while ((inb(p->base + HCNTRL) & PAUSE) == 0) + { + ; + } } /*+F************************************************************************* * Function: - * aic7xxx_delay + * unpause_sequencer * * Description: - * Delay for specified amount of time. + * Unpause the sequencer. Unremarkable, yet done often enough to + * warrant an easy way to do it. *-F*************************************************************************/ -static void -aic7xxx_delay(int seconds) +static inline void +unpause_sequencer(struct aic7xxx_host *p, int unpause_always) { - unsigned long i; - - i = jiffies + (seconds * HZ); /* compute time to stop */ - - while (jiffies < i) + if (unpause_always || + ((inb(p->base + INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0)) { - ; /* Do nothing! */ + outb(p->unpause, p->base + HCNTRL); } } /*+F************************************************************************* * Function: - * rcs_version + * restart_sequencer * * Description: - * Return a string containing just the RCS version number from either - * an Id or Revision RCS clause. + * Restart the sequencer program from address zero. This assumes + * that the sequencer is already paused. *-F*************************************************************************/ -const char * -rcs_version(const char *version_info) +static inline void +restart_sequencer(struct aic7xxx_host *p) { - static char buf[10]; - char *bp, *ep; + /* Set the sequencer address to 0. */ + outb(0, p->base + SEQADDR0); + outb(0, p->base + SEQADDR1); - bp = NULL; - strcpy(buf, "????"); - if (!strncmp(version_info, "$Id: ", 5)) - { - if ((bp = strchr(version_info, ' ')) != NULL) - { - bp++; - if ((bp = strchr(bp, ' ')) != NULL) - { - bp++; - } - } - } - else + /* + * Reset and unpause the sequencer. The reset is suppose to + * start the sequencer running, but we do an unpause to make + * sure. + */ + outb(SEQRESET | FASTMODE, p->base + SEQCTL); + + unpause_sequencer(p, /*unpause_always*/ TRUE); +} + + +/*+F************************************************************************* + * Function: + * aic7xxx_next_patch + * + * Description: + * Find the next patch to download. + *-F*************************************************************************/ +static struct patch * +aic7xxx_next_patch(struct patch *cur_patch, int options, int instrptr) +{ + while (cur_patch != NULL) { - if (!strncmp(version_info, "$Revision: ", 11)) + if ((((cur_patch->options & options) != 0) && (cur_patch->negative == FALSE)) + || (((cur_patch->options & options) == 0) && (cur_patch->negative == TRUE)) + || (instrptr >= cur_patch->end)) { - if ((bp = strchr(version_info, ' ')) != NULL) + /* + * Either we want to keep this section of code, or we have consumed + * this patch. Skip to the next patch. + */ + cur_patch++; + if (cur_patch->options == 0) { - bp++; + /* Out of patches. */ + cur_patch = NULL; } } - } - - if (bp != NULL) - { - if ((ep = strchr(bp, ' ')) != NULL) + else { - register int len = ep - bp; - - strncpy(buf, bp, len); - buf[len] = '\0'; + /* Found an OK patch. */ + break; } } - - return buf; + return (cur_patch); } + /*+F************************************************************************* * Function: - * aic7xxx_info + * aic7xxx_download_instr * * Description: - * Return a string describing the driver. + * Find the next patch to download. *-F*************************************************************************/ -const char * -aic7xxx_info(struct Scsi_Host *notused) +static void +aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr) { - static char buffer[128]; + unsigned char opcode; + struct ins_format3 *instr; - strcpy(buffer, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) "); - strcat(buffer, rcs_version(AIC7XXX_C_VERSION)); - strcat(buffer, "/"); - strcat(buffer, rcs_version(AIC7XXX_H_VERSION)); - strcat(buffer, "/"); - strcat(buffer, rcs_version(AIC7XXX_SEQ_VER)); + instr = (struct ins_format3 *) &seqprog[instrptr * 4]; + /* Pull the opcode */ + opcode = instr->opcode_addr >> 1; + switch (opcode) + { + case AIC_OP_JMP: + case AIC_OP_JC: + case AIC_OP_JNC: + case AIC_OP_CALL: + case AIC_OP_JNE: + case AIC_OP_JNZ: + case AIC_OP_JE: + case AIC_OP_JZ: + { + int address_offset; + struct ins_format3 new_instr; + unsigned int address; + struct patch *patch; + int i; + + address_offset = 0; + new_instr = *instr; /* Strucure copy */ + address = new_instr.address; + address |= (new_instr.opcode_addr & ADDR_HIGH_BIT) << 8; + for (i = 0; i < NUMBER(patches); i++) + { + patch = &patches[i]; + if ((((patch->options & options) == 0) && (patch->negative == FALSE)) || + (((patch->options & options) != 0) && (patch->negative == TRUE))) + { + if (address >= patch->end) + { + address_offset += patch->end - patch->begin; + } + } + } + address -= address_offset; + new_instr.address = address &0xFF; + new_instr.opcode_addr &= ~ADDR_HIGH_BIT; + new_instr.opcode_addr |= (address >> 8) & ADDR_HIGH_BIT; + outsb(p->base + SEQRAM, &new_instr.immediate, 4); + break; + } - return buffer; -} + case AIC_OP_OR: + case AIC_OP_AND: + case AIC_OP_XOR: + case AIC_OP_ADD: + case AIC_OP_ADC: + case AIC_OP_ROL: + outsb(p->base + SEQRAM, &instr->immediate, 4); + break; + + default: + panic("aic7xxx: Unknown opcode encountered in sequencer program."); + break; + } +} + + +/*+F************************************************************************* + * Function: + * aic7xxx_loadseq + * + * Description: + * Load the sequencer code into the controller memory. + *-F*************************************************************************/ +static void +aic7xxx_loadseq(struct aic7xxx_host *p) +{ + int options; + struct patch *cur_patch; + int i; + int downloaded; + + if (aic7xxx_verbose) + { + printk(KERN_INFO "aic7xxx: Downloading sequencer code..."); + } + options = 1; /* Code for all options. */ + downloaded = 0; + if ((p->flags & ULTRA_ENABLED) != 0) + options |= ULTRA; + if (p->bus_type == AIC_TWIN) + options |= TWIN_CHANNEL; + if (p->scb_data->maxscbs > p->scb_data->maxhscbs) + options |= SCB_PAGING; + + cur_patch = patches; + outb(PERRORDIS | LOADRAM, p->base + SEQCTL); + outb(0, p->base + SEQADDR0); + outb(0, p->base + SEQADDR1); + + for (i = 0; i < sizeof(seqprog) / 4; i++) + { + cur_patch = aic7xxx_next_patch(cur_patch, options, i); + if (cur_patch && (cur_patch->begin <= i) && (cur_patch->end > i)) + { + /* Skip this instruction for this configuration. */ + continue; + } + aic7xxx_download_instr(p, options, i); + downloaded++; + } + + outb(FASTMODE, p->base + SEQCTL); + outb(0, p->base + SEQADDR0); + outb(0, p->base + SEQADDR1); + + if (aic7xxx_verbose) + { + printk(" %d instructions downloaded\n", downloaded); + } +} + +/*+F************************************************************************* + * Function: + * aic7xxx_delay + * + * Description: + * Delay for specified amount of time. We use udelay because the timer + * interrupt is not guaranteed to be enabled. This will cause an + * infinite loop since jiffies (clock ticks) is not updated. + *-F*************************************************************************/ +static void +aic7xxx_delay(int seconds) +{ + int i; + + /* + * Call udelay() for 1 millisecond inside a loop for + * the requested amount of seconds. + */ + for (i=0; i < seconds*1000; i++) + { + udelay(1000); /* Delay for 1 millisecond. */ + } +} + +/*+F************************************************************************* + * Function: + * rcs_version + * + * Description: + * Return a string containing just the RCS version number from either + * an Id or Revision RCS clause. + *-F*************************************************************************/ +const char * +rcs_version(const char *version_info) +{ + static char buf[10]; + char *bp, *ep; + + bp = NULL; + strcpy(buf, "????"); + if (!strncmp(version_info, "$Id: ", 5)) + { + if ((bp = strchr(version_info, ' ')) != NULL) + { + bp++; + if ((bp = strchr(bp, ' ')) != NULL) + { + bp++; + } + } + } + else + { + if (!strncmp(version_info, "$Revision: ", 11)) + { + if ((bp = strchr(version_info, ' ')) != NULL) + { + bp++; + } + } + } + + if (bp != NULL) + { + if ((ep = strchr(bp, ' ')) != NULL) + { + register int len = ep - bp; + + strncpy(buf, bp, len); + buf[len] = '\0'; + } + } + + return buf; +} + +/*+F************************************************************************* + * Function: + * aic7xxx_info + * + * Description: + * Return a string describing the driver. + *-F*************************************************************************/ +const char * +aic7xxx_info(struct Scsi_Host *notused) +{ + static char buffer[128]; + + strcpy(buffer, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) "); + strcat(buffer, rcs_version(AIC7XXX_C_VERSION)); + strcat(buffer, "/"); + strcat(buffer, rcs_version(AIC7XXX_H_VERSION)); +#if 0 + strcat(buffer, "/"); + strcat(buffer, rcs_version(AIC7XXX_SEQ_VER)); +#endif + + return buffer; +} /*+F************************************************************************* * Function: * aic7xxx_length * * Description: - * How much data should be transferred for this SCSI command? Stop - * at segment sg_last if it's a scatter-gather command so we can - * compute underflow easily. + * How much data should be transferred for this SCSI command? Assume + * all segments are to be transferred except for the last sg_last + * segments. This will allow us to compute underflow easily. To + * calculate the total length of the command, use sg_last = 0. To + * calculate the length of all but the last 2 SG segments, use + * sg_last = 2. *-F*************************************************************************/ static unsigned aic7xxx_length(Scsi_Cmnd *cmd, int sg_last) @@ -1176,7 +1406,7 @@ if (cmd->use_sg) { - for (i = length = 0; (i < cmd->use_sg) && (i < segments); i++) + for (i = length = 0; i < segments; i++) { length += sg[i].length; } @@ -1198,9 +1428,9 @@ *-F*************************************************************************/ static void aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate, - short period, unsigned char offset, int target, char channel) + unsigned char *period, unsigned char *offset, int target, char channel) { - int i; + int i = num_aic7xxx_syncrates; unsigned long ultra_enb_addr; unsigned char ultra_enb, sxfrctl0; @@ -1208,11 +1438,11 @@ * If the offset is 0, then the device is requesting asynchronous * transfers. */ - if (offset != 0) + if ((*period >= aic7xxx_syncrates[i].period) && *offset != 0) { for (i = 0; i < num_aic7xxx_syncrates; i++) { - if ((aic7xxx_syncrates[i].period - period) >= 0) + if (*period <= aic7xxx_syncrates[i].period) { /* * Watch out for Ultra speeds when ultra is not enabled and @@ -1228,99 +1458,57 @@ */ continue; } - *scsirate = (aic7xxx_syncrates[i].rate) | (offset & 0x0F); + *scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F); + *period = aic7xxx_syncrates[i].period; - /* - * Ensure Ultra mode is set properly for this target. - */ - ultra_enb_addr = ULTRA_ENB; - if ((channel == 'B') || (target > 7)) + if (aic7xxx_verbose) { - ultra_enb_addr++; + printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, " + "offset %d.\n", p->host_no, target, channel, + aic7xxx_syncrates[i].english, *offset); } - ultra_enb = inb(p->base + ultra_enb_addr); - sxfrctl0 = inb(p->base + SXFRCTL0); - if (aic7xxx_syncrates[i].rate & ULTRA_SXFR) - { - ultra_enb |= 0x01 << (target & 0x07); - sxfrctl0 |= ULTRAEN; - } - else - { - ultra_enb &= ~(0x01 << (target & 0x07)); - sxfrctl0 &= ~ULTRAEN; - } - outb(ultra_enb, p->base + ultra_enb_addr); - outb(sxfrctl0, p->base + SXFRCTL0); - - printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, " - "offset %d.\n", p->host_no, target, channel, - aic7xxx_syncrates[i].english, offset); - return; + break; } } } - /* - * Default to asynchronous transfer - */ - *scsirate = 0; - printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n", - p->host_no, target, channel); -} - -/*+F************************************************************************* - * Function: - * aic7xxx_putscb - * - * Description: - * Transfer a SCB to the controller. - *-F*************************************************************************/ -static inline void -aic7xxx_putscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) -{ - int base = p->base; - - outb(SCBAUTO, SCBCNT + base); + if (i >= num_aic7xxx_syncrates) + { + /* + * Use asynchronous transfers. + */ + *scsirate = 0; + *period = 0; + *offset = 0; + if (aic7xxx_verbose) + { + printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n", + p->host_no, target, channel); + } + } /* - * By turning on the SCB auto increment, any reference - * to the SCB I/O space postincrements the SCB address - * we're looking at. So turn this on and dump the relevant - * portion of the SCB to the card. - * - * We can do 16bit transfers on all but 284x. + * Ensure Ultra mode is set properly for this target. */ - if (p->type == AIC_284x) + ultra_enb_addr = ULTRA_ENB; + if ((channel == 'B') || (target > 7)) + { + ultra_enb_addr++; + } + ultra_enb = inb(p->base + ultra_enb_addr); + sxfrctl0 = inb(p->base + SXFRCTL0); + if ((*scsirate != 0) && (aic7xxx_syncrates[i].rate & ULTRA_SXFR)) { - outsb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE); + ultra_enb |= 0x01 << (target & 0x07); + sxfrctl0 |= FAST20; } else { - outsl(SCBARRAY + base, scb, (SCB_PIO_TRANSFER_SIZE + 3) / 4); + ultra_enb &= ~(0x01 << (target & 0x07)); + sxfrctl0 &= ~FAST20; } - - outb(0, SCBCNT + base); -} - -/*+F************************************************************************* - * Function: - * aic7xxx_getscb - * - * Description: - * Get a SCB from the controller. - *-F*************************************************************************/ -static inline void -aic7xxx_getscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) -{ - int base = p->base; - - /* - * This is almost identical to aic7xxx_putscb(). - */ - outb(SCBAUTO, SCBCNT + base); - insb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE); - outb(0, SCBCNT + base); + outb(ultra_enb, p->base + ultra_enb_addr); + outb(sxfrctl0, p->base + SXFRCTL0); } /*+F************************************************************************* @@ -1374,6 +1562,47 @@ /*+F************************************************************************* * Function: + * scbq_remove + * + * Description: + * Removes an SCB from the list. + * + *-F*************************************************************************/ +static inline void +scbq_remove(scb_queue_type *queue, struct aic7xxx_scb *scb) +{ + if (queue->head == scb) + { + /* At beginning of queue, remove from head. */ + scbq_remove_head(queue); + } + else + { + struct aic7xxx_scb *curscb = queue->head; + + /* + * Search until the next scb is the one we're looking for, or + * we run out of queue. + */ + while ((curscb != NULL) && (curscb->q_next != scb)) + { + curscb = curscb->q_next; + } + if (curscb != NULL) + { + /* Found it. */ + curscb->q_next = scb->q_next; + if (scb->q_next == NULL) + { + /* Update the tail when removing the tail. */ + queue->tail = curscb; + } + } + } +} + +/*+F************************************************************************* + * Function: * scbq_insert_tail * * Description: @@ -1403,23 +1632,87 @@ * to be reset and all devices on that channel must be aborted. *-F*************************************************************************/ static int -aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel) +aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel, + int lun, unsigned char tag) { - int targ = (scb->target_channel_lun >> 4) & 0x0F; - char chan = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A'; + int targ = (scb->hscb->target_channel_lun >> 4) & 0x0F; + char chan = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A'; + int slun = scb->hscb->target_channel_lun & 0x07; + int match; #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (match_scb) comparing target/channel %d/%c to scb %d/%c\n", - target, channel, targ, chan); + printk("scsi%d: (targ %d/chan %c) matching scb to (targ %d/chan %c)\n", + scb->cmd->device->host->host_no, target, channel, targ, chan); #endif - if (target == ALL_TARGETS) + match = ((chan == channel) || (channel == ALL_CHANNELS)); + if (match != 0) + match = ((targ == target) || (target == ALL_TARGETS)); + if (match != 0) + match = ((lun == slun) || (lun == ALL_LUNS)); + if (match != 0) + match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL)); + + return (match); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_add_curscb_to_free_list + * + * Description: + * Adds the current scb (in SCBPTR) to the list of free SCBs. + *-F*************************************************************************/ +static void +aic7xxx_add_curscb_to_free_list(struct aic7xxx_host *p) +{ + /* + * Invalidate the tag so that aic7xxx_find_scb doesn't think + * it's active + */ + outb(SCB_LIST_NULL, p->base + SCB_TAG); + + outb(inb(p->base + FREE_SCBH), p->base + SCB_NEXT); + outb(inb(p->base + SCBPTR), p->base + FREE_SCBH); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_rem_scb_from_disc_list + * + * Description: + * Removes the current SCB from the disconnected list and adds it + * to the free list. + *-F*************************************************************************/ +static unsigned char +aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr) +{ + unsigned char next; + unsigned char prev; + + outb(scbptr, p->base + SCBPTR); + next = inb(p->base + SCB_NEXT); + prev = inb(p->base + SCB_PREV); + + outb(0, p->base + SCB_CONTROL); + + aic7xxx_add_curscb_to_free_list(p); + + if (prev != SCB_LIST_NULL) { - return (chan == channel); + outb(prev, p->base + SCBPTR); + outb(next, p->base + SCB_NEXT); } else { - return ((chan == channel) && (targ == target)); + outb(next, p->base + DISCONNECTED_SCBH); } + + if (next != SCB_LIST_NULL) + { + outb(next, p->base + SCBPTR); + outb(prev, p->base + SCB_PREV); + } + return next; } /*+F************************************************************************* @@ -1427,51 +1720,93 @@ * aic7xxx_busy_target * * Description: - * Set the specified target active. + * Set the specified target busy. *-F*************************************************************************/ static void -aic7xxx_busy_target(unsigned char target, char channel, int base) +aic7xxx_busy_target(struct aic7xxx_host *p, unsigned char target, + char channel, unsigned char scbid) +{ + unsigned char active_scb; + unsigned char info_scb; + unsigned int scb_offset; + + info_scb = target / 4; + if (channel == 'B') + info_scb = info_scb + 2; + + active_scb = inb(p->base + SCBPTR); + outb(info_scb, p->base + SCBPTR); + scb_offset = SCB_BUSYTARGETS + (target & 0x03); + outb(scbid, p->base + scb_offset); + outb(active_scb, p->base + SCBPTR); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_index_busy_target + * + * Description: + * Returns the index of the busy target, and optionally sets the + * target inactive. + *-F*************************************************************************/ +static unsigned char +aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char target, + char channel, int unbusy) { - unsigned char active; - unsigned long active_port = ACTIVE_A + base; + unsigned char active_scb; + unsigned char info_scb; + unsigned char busy_scbid; + unsigned int scb_offset; + + info_scb = target / 4; + if (channel == 'B') + info_scb = info_scb + 2; - if ((target > 0x07) || (channel == 'B')) + active_scb = inb(p->base + SCBPTR); + outb(info_scb, p->base + SCBPTR); + scb_offset = SCB_BUSYTARGETS + (target & 0x03); + busy_scbid = inb(p->base + scb_offset); + if (unbusy) { - /* - * targets on the Second channel or above id 7 store info in byte two - * of ACTIVE - */ - active_port++; + outb(SCB_LIST_NULL, p->base + scb_offset); } - active = inb(active_port); - active |= (0x01 << (target & 0x07)); - outb(active, active_port); + outb(active_scb, p->base + SCBPTR); + return (busy_scbid); } /*+F************************************************************************* * Function: - * aic7xxx_unbusy_target + * aic7xxx_find_scb * * Description: - * Set the specified target inactive. + * Look through the SCB array of the card and attempt to find the + * hardware SCB that corresponds to the passed in SCB. Return + * SCB_LIST_NULL if unsuccessful. This routine assumes that the + * card is already paused. *-F*************************************************************************/ -static void -aic7xxx_unbusy_target(unsigned char target, char channel, int base) +static unsigned char +aic7xxx_find_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) { - unsigned char active; - unsigned long active_port = ACTIVE_A + base; + unsigned char saved_scbptr; + unsigned char curindex; - if ((target > 0x07) || (channel == 'B')) + saved_scbptr = inb(p->base + SCBPTR); + curindex = 0; + for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++) { - /* - * targets on the Second channel or above id 7 store info in byte two - * of ACTIVE - */ - active_port++; + outb(curindex, p->base + SCBPTR); + if (inb(p->base + SCB_TAG) == scb->hscb->tag) + { + break; + } } - active = inb(active_port); - active &= ~(0x01 << (target & 0x07)); - outb(active, active_port); + outb(saved_scbptr, p->base + SCBPTR); + if (curindex >= p->scb_data->maxhscbs) + { + curindex = SCB_LIST_NULL; + } + + return (curindex); } /*+F************************************************************************* @@ -1479,68 +1814,60 @@ * aic7xxx_allocate_scb * * Description: - * Get a free SCB either from one already assigned to a hardware - * slot, or one that will require an SCB to be paged out before - * use. If there are none, attempt to allocate a new one. + * Get an SCB from the free list or by allocating a new one. *-F*************************************************************************/ static struct aic7xxx_scb * aic7xxx_allocate_scb(struct aic7xxx_host *p) { - struct aic7xxx_scb *scbp = NULL; - int maxscbs; + struct aic7xxx_scb *scbp = NULL; + struct aic7xxx_hwscb *hscbp = NULL; +#ifdef AGRESSIVE + long processor_flags; + + save_flags(processor_flags); + cli(); +#endif - scbp = p->scb_link->free_scbs.head; + scbp = p->scb_data->free_scbs.head; if (scbp != NULL) { - scbq_remove_head(&p->scb_link->free_scbs); + scbq_remove_head(&p->scb_data->free_scbs); } else { - /* - * This should always be NULL if paging is not enabled. - */ - scbp = p->page_scbs.head; - if (scbp != NULL) - { - scbq_remove_head(&p->page_scbs); - } - else + if (p->scb_data->numscbs < p->scb_data->maxscbs) { - /* - * Set limit the SCB allocation to the maximum number of - * hardware SCBs if paging is not enabled; otherwise use - * the maximum (255). - */ - if (p->flags & PAGE_ENABLED) - maxscbs = p->maxscbs; - else - maxscbs = p->maxhscbs; - if (p->scb_link->numscbs < maxscbs) - { - int scb_index = p->scb_link->numscbs; - int scb_size = sizeof(struct aic7xxx_scb); - - p->scb_array[scb_index] = kmalloc(scb_size, GFP_ATOMIC | GFP_DMA); - scbp = (p->scb_array[scb_index]); - if (scbp != NULL) - { - memset(scbp, 0, sizeof(*scbp)); - scbp->tag = scb_index; - if (scb_index < p->maxhscbs) - scbp->position = scb_index; - else - scbp->position = SCB_LIST_NULL; - p->scb_link->numscbs++; - } + int scb_index = p->scb_data->numscbs; + int scb_size = sizeof(struct aic7xxx_scb) + + sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG; + + scbp = kmalloc(scb_size, GFP_ATOMIC); + if (scbp != NULL) + { + memset(scbp, 0, sizeof(struct aic7xxx_scb)); + hscbp = &p->scb_data->hscbs[scb_index]; + scbp->hscb = hscbp; + scbp->sg_list = (struct hw_scatterlist *) &scbp[1]; + memset(hscbp, 0, sizeof(struct aic7xxx_hwscb)); + hscbp->tag = scb_index; + p->scb_data->numscbs++; + /* + * Place in the scb array; never is removed + */ + p->scb_data->scb_array[scb_index] = scbp; } } } +#ifdef AIC7XXX_DEBUG if (scbp != NULL) { -#ifdef AIC7XXX_DEBUG - p->scb_link->activescbs++; -#endif + p->activescbs++; } +#endif + +#ifdef AGRESSIVE + restore_flags(processor_flags); +#endif return (scbp); } @@ -1580,6 +1907,7 @@ cmd = p->completeq.head; p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble; cmd->host_scribble = NULL; + p->device_status[TARGET_INDEX(cmd)].active_cmds--; cmd->scsi_done(cmd); } p->completeq.tail = NULL; @@ -1590,53 +1918,29 @@ * aic7xxx_free_scb * * Description: - * Free the scb and update the page, waiting, free scb lists. + * Free the scb and insert into the free scb list. *-F*************************************************************************/ static void aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) { - struct aic7xxx_scb *wscb; + struct aic7xxx_hwscb *hscb; + long flags; + + hscb = scb->hscb; + save_flags(flags); + cli(); - scb->state = SCB_FREE; + scb->flags = SCB_FREE; scb->cmd = NULL; - scb->control = 0; - scb->state = 0; + hscb->control = 0; + hscb->target_status = 0; - if (scb->position == SCB_LIST_NULL) - { - scbq_insert_head(&p->page_scbs, scb); - } - else - { - /* - * If there are any SCBS on the waiting queue, assign the slot of this - * "freed" SCB to the first one. We'll run the waiting queues after - * all command completes for a particular interrupt are completed or - * when we start another command. - */ - wscb = p->waiting_scbs.head; - if (wscb != NULL) - { - scbq_remove_head(&p->waiting_scbs); - wscb->position = scb->position; - scbq_insert_tail(&p->assigned_scbs, wscb); - wscb->state = (wscb->state & ~SCB_WAITINGQ) | SCB_ASSIGNEDQ; - - /* - * The "freed" SCB will need to be assigned a slot before being - * used, so put it in the page_scbs queue. - */ - scb->position = SCB_LIST_NULL; - scbq_insert_head(&p->page_scbs, scb); - } - else - { - scbq_insert_head(&p->scb_link->free_scbs, scb); - } + scbq_insert_head(&p->scb_data->free_scbs, scb); #ifdef AIC7XXX_DEBUG - p->scb_link->activescbs--; /* For debugging purposes. */ + p->activescbs--; /* For debugging purposes. */ #endif - } + + restore_flags(flags); } /*+F************************************************************************* @@ -1651,68 +1955,113 @@ { Scsi_Cmnd *cmd = scb->cmd; + if (scb->flags & SCB_RECOVERY_SCB) + { + p->flags &= ~IN_TIMEOUT; + } + if (cmd->result == DID_OK) + { + if (scb->flags & SCB_ABORTED) + { + cmd->result = (DID_RESET << 16); + } + } + if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0) + { + unsigned short mask; + + mask = 0x01 << TARGET_INDEX(scb->cmd); + if (scb->flags & SCB_MSGOUT_WDTR) + { + p->wdtr_pending &= ~mask; + } + if (scb->flags & SCB_MSGOUT_SDTR) + { + p->sdtr_pending &= ~mask; + } + } aic7xxx_free_scb(p, scb); aic7xxx_queue_cmd_complete(p, cmd); +#ifdef AIC7XXX_PROC_STATS + { + int actual; + + /* + * XXX: we should actually know how much actually transferred + * XXX: for each command, but apparently that's too difficult. + */ + actual = aic7xxx_length(cmd, 0); + if (!(scb->flags & (SCB_ABORTED | SCB_SENSE)) && (actual > 0) + && (aic7xxx_error(cmd) == 0)) + { + struct aic7xxx_xferstats *sp; + long *ptr; + int x; + + sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07]; + sp->xfers++; + + if (cmd->request.cmd == WRITE) + { + sp->w_total++; + sp->w_total512 += (actual >> 9); + ptr = sp->w_bins; + } + else + { + sp->r_total++; + sp->r_total512 += (actual >> 9); + ptr = sp->r_bins; + } + for (x = 9; x <= 17; x++) + { + if (actual < (1 << x)) + { + ptr[x - 9]++; + break; + } + } + if (x > 17) + { + ptr[x - 9]++; + } + } + } +#endif /* AIC7XXX_PROC_STATS */ } /*+F************************************************************************* * Function: - * aic7xxx_done_aborted_scbs + * aic7xxx_run_done_queue * * Description: - * Calls the scsi_done() for the Scsi_Cmnd of each scb in the - * aborted list, and adds each scb to the free list. + * Calls the aic7xxx_done() for the Scsi_Cmnd of each scb in the + * aborted list, and adds each scb to the free list. If complete + * is TRUE, we also process the commands complete list. *-F*************************************************************************/ static void -aic7xxx_done_aborted_scbs(struct aic7xxx_host *p) +aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete) { - Scsi_Cmnd *cmd; struct aic7xxx_scb *scb; int i; - for (i = 0; i < p->scb_link->numscbs; i++) + for (i = 0; i < p->scb_data->numscbs; i++) { - scb = (p->scb_array[i]); - if (scb->state & SCB_QUEUED_FOR_DONE) + scb = p->scb_data->scb_array[i]; + if (scb->flags & SCB_QUEUED_FOR_DONE) { #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (done_aborted_scbs) Aborting scb %d, TCL=%d/%d/%d\n", - scb->position, TCL_OF_SCB(scb)); + printk("(scsi%d:%d:%d) Aborting scb %d\n", + p->host_no, TC_OF_SCB(scb), scb->hscb->tag); #endif - /* - * Process the command after marking the scb as free - * and adding it to the free list. - */ - cmd = scb->cmd; - p->device_status[TARGET_INDEX(cmd)].flags = 0; - aic7xxx_free_scb(p, scb); - cmd->scsi_done(cmd); /* call the done function */ + aic7xxx_done(p, scb); } } -} - -/*+F************************************************************************* - * Function: - * aic7xxx_add_waiting_scb - * - * Description: - * Add this SCB to the head of the "waiting for selection" list. - *-F*************************************************************************/ -static void -aic7xxx_add_waiting_scb(u_long base, struct aic7xxx_scb *scb) -{ - unsigned char next; - unsigned char curscb; - - curscb = inb(SCBPTR + base); - next = inb(WAITING_SCBH + base); - - outb(scb->position, SCBPTR + base); - outb(next, SCB_NEXT + base); - outb(scb->position, WAITING_SCBH + base); - - outb(curscb, SCBPTR + base); + if (complete) + { + aic7xxx_done_cmds_complete(p); + } } /*+F************************************************************************* @@ -1725,26 +2074,23 @@ *-F*************************************************************************/ static unsigned char aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb, - unsigned char prev) + unsigned char scbpos, unsigned char prev) { unsigned char curscb, next; - int target = (scb->target_channel_lun >> 4) & 0x0F; - char channel = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A'; - int base = p->base; /* * Select the SCB we want to abort and pull the next pointer out of it. */ - curscb = inb(SCBPTR + base); - outb(scb->position, SCBPTR + base); - next = inb(SCB_NEXT + base); + curscb = inb(p->base + SCBPTR); + outb(scbpos, p->base + SCBPTR); + next = inb(p->base + SCB_NEXT); /* * Clear the necessary fields */ - outb(0, SCB_CONTROL + base); - outb(SCB_LIST_NULL, SCB_NEXT + base); - aic7xxx_unbusy_target(target, channel, base); + outb(0, p->base + SCB_CONTROL); + + aic7xxx_add_curscb_to_free_list(p); /* * Update the waiting list @@ -1754,22 +2100,23 @@ /* * First in the list */ - outb(next, WAITING_SCBH + base); + outb(next, p->base + WAITING_SCBH); } else { /* * Select the scb that pointed to us and update its next pointer. */ - outb(prev, SCBPTR + base); - outb(next, SCB_NEXT + base); + outb(prev, p->base + SCBPTR); + outb(next, p->base + SCB_NEXT); } /* * Point us back at the original scb position and inform the SCSI * system that the command has been aborted. */ - outb(curscb, SCBPTR + base); - scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE; + outb(curscb, p->base + SCBPTR); + scb->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE; + scb->flags &= ~SCB_ACTIVE; scb->cmd->result = (DID_RESET << 16); return (next); @@ -1777,6 +2124,75 @@ /*+F************************************************************************* * Function: + * aic7xxx_search_qinfifo + * + * Description: + * Search the queue-in FIFO for matching SCBs and conditionally + * requeue. Returns the number of matching SCBs. + *-F*************************************************************************/ +static int +aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, char channel, + int lun, unsigned char tag, int flags, int requeue) +{ + unsigned char saved_queue[AIC7XXX_MAXSCB]; + int queued = inb(p->base + QINCNT) & p->qcntmask; + int i; + int found; + struct aic7xxx_scb *scbp; + scb_queue_type removed_scbs; + + found = 0; + scbq_init (&removed_scbs); + for (i = 0; i < (queued - found); i++) + { + saved_queue[i] = inb(p->base + QINFIFO); + scbp = p->scb_data->scb_array[saved_queue[i]]; + if (aic7xxx_match_scb(scbp, target, channel, lun, tag)) + { + /* + * We found an scb that needs to be removed. + */ + if (requeue) + { + scbq_insert_head(&removed_scbs, scbp); + } + else + { + scbp->flags = flags; + scbp->flags &= ~SCB_ACTIVE; + /* + * XXX - Don't know what error to use here. + */ + aic7xxx_error(scbp->cmd) = DID_RESET; + } + i--; + found++; + } + } + /* Now put the saved scbs back. */ + for (queued = 0; queued < i; queued++) + outb(saved_queue[queued], p->base + QINFIFO); + + if (requeue) + { + scbp = removed_scbs.head; + while (scbp != NULL) + { + scbq_remove_head(&removed_scbs); + /* + * XXX - Shouldn't we be adding this to the free list? + */ + scbq_insert_head(&p->waiting_scbs, scbp); + scbp->flags |= SCB_WAITINGQ; + scbp = removed_scbs.head; + } + } + + return (found); +} + +/*+F************************************************************************* + * Function: * aic7xxx_reset_device * * Description: @@ -1784,131 +2200,280 @@ * all active and queued scbs for that target/channel. *-F*************************************************************************/ static int -aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel) +aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel, + int lun, unsigned char tag) { - int base = p->base; - struct aic7xxx_scb *scb; + struct aic7xxx_scb *scbp; unsigned char active_scb; int i = 0; - int found = 0; + int found; /* * Restore this when we're done */ - active_scb = inb(SCBPTR + base); + active_scb = inb(p->base + SCBPTR); #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset_device) target/channel %d/%c, active_scb %d\n", - target, channel, active_scb); + printk("(scsi%d:%d:%d) Reset device, active_scb %d\n", + p->host_no, target, CHAN_TO_INT(channel), active_scb); #endif + /* - * Search the QINFIFO. + * Deal with the busy target and linked next issues. */ { - int saved_queue[AIC7XXX_MAXSCB]; - int queued = inb(QINCNT + base) & p->qcntmask; + int min_target, max_target; + unsigned char busy_scbid; - for (i = 0; i < (queued - found); i++) + /* Make all targets 'relative' to bus A. */ + if (target == ALL_TARGETS) { - saved_queue[i] = inb(QINFIFO + base); - outb(saved_queue[i], SCBPTR + base); - scb = (p->scb_array[inb(SCB_TAG + base)]); - if (aic7xxx_match_scb(scb, target, channel)) + switch (channel) { - /* - * We found an scb that needs to be aborted. - */ -#ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset_device) aborting SCB %d, TCL=%d/%d/%d\n", - saved_queue[i], TCL_OF_SCB(scb)); -#endif - scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE; - scb->cmd->result = (DID_RESET << 16); - outb(0, SCB_CONTROL + base); - i--; - found++; + case 'A': + min_target = 0; + max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15; + break; + case 'B': + min_target = 8; + max_target = 15; + break; + case ALL_CHANNELS: + default: + min_target = 0; + max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15; + break; } } - /* - * Now put the saved scbs back. - */ - for (queued = 0; queued < i; queued++) + else + { + min_target = target + channel == 'B' ? 8 : 0; + max_target = min_target; + } + + for (i = min_target; i <= max_target; i++) { - outb(saved_queue[queued], QINFIFO + base); + busy_scbid = aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/FALSE); + if (busy_scbid < p->scb_data->numscbs) + { + struct aic7xxx_scb *busy_scb; + struct aic7xxx_scb *next_scb; + unsigned char next_scbid; + + busy_scb = p->scb_data->scb_array[busy_scbid]; + + next_scbid = busy_scb->hscb->data_count >> 24; + + if (next_scbid == SCB_LIST_NULL) + { + busy_scbid = aic7xxx_find_scb(p, busy_scb); + + if (busy_scbid != SCB_LIST_NULL) + { + outb(busy_scbid, p->base + SCBPTR); + next_scbid = inb(p->base + SCB_LINKED_NEXT); + } + } + + if (aic7xxx_match_scb(busy_scb, target, channel, lun, tag)) + { + aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/TRUE); + } + + if (next_scbid != SCB_LIST_NULL) + { + next_scb = p->scb_data->scb_array[next_scbid]; + if (aic7xxx_match_scb(next_scb, target, channel, lun, tag)) + { + continue; + } + /* Requeue for later processing */ + scbq_insert_head(&p->waiting_scbs, next_scb); + next_scb->flags |= SCB_WAITINGQ; + } + } } } + found = aic7xxx_search_qinfifo(p, target, channel, lun, tag, + SCB_ABORTED | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE); + /* * Search waiting for selection list. */ { - unsigned char next, prev; + unsigned char next, prev, scb_index; - next = inb(WAITING_SCBH + base); /* Start at head of list. */ + next = inb(p->base + WAITING_SCBH); /* Start at head of list. */ prev = SCB_LIST_NULL; while (next != SCB_LIST_NULL) { - outb(next, SCBPTR + base); - scb = (p->scb_array[inb(SCB_TAG + base)]); - /* - * Select the SCB. - */ - if (aic7xxx_match_scb(scb, target, channel)) + outb(next, p->base + SCBPTR); + scb_index = inb(p->base + SCB_TAG); + if (scb_index >= p->scb_data->numscbs) + { + panic("aic7xxx: Waiting List inconsistency; SCB index=%d, numscbs=%d\n", + scb_index, p->scb_data->numscbs); + } + scbp = p->scb_data->scb_array[scb_index]; + if (aic7xxx_match_scb(scbp, target, channel, lun, tag)) { - next = aic7xxx_abort_waiting_scb(p, scb, prev); + unsigned char linked_next; + + next = aic7xxx_abort_waiting_scb(p, scbp, next, prev); + linked_next = inb(p->base + SCB_LINKED_NEXT); + if (linked_next != SCB_LIST_NULL) + { + struct aic7xxx_scb *next_scb; + /* + * Requeue the waiting SCB via the waiting list. + */ + next_scb = p->scb_data->scb_array[linked_next]; + if (! aic7xxx_match_scb(next_scb, target, channel, lun, tag)) + { + scbq_insert_head(&p->waiting_scbs, next_scb); + next_scb->flags |= SCB_WAITINGQ; + } + } found++; } else { prev = next; - next = inb(SCB_NEXT + base); + next = inb(p->base + SCB_NEXT); } } } /* - * Go through the entire SCB array now and look for commands for - * for this target that are active. These are other (most likely - * tagged) commands that were disconnected when the reset occurred. + * Go through disconnected list and remove any entries we have queued + * for completion, zeroing their control byte too. */ - for (i = 0; i < p->scb_link->numscbs; i++) { - scb = (p->scb_array[i]); - if ((scb->state & SCB_ACTIVE) && aic7xxx_match_scb(scb, target, channel)) + unsigned char next, prev, scb_index; + + next = inb(p->base + DISCONNECTED_SCBH); + prev = SCB_LIST_NULL; + + while (next != SCB_LIST_NULL) { - /* - * Ensure the target is "free" - */ - aic7xxx_unbusy_target(target, channel, base); - if (! (scb->state & SCB_PAGED_OUT)) + outb(next, p->base + SCBPTR); + scb_index = inb(p->base + SCB_TAG); + if (scb_index > p->scb_data->numscbs) { - outb(scb->position, SCBPTR + base); - outb(0, SCB_CONTROL + base); + panic("aic7xxx: Disconnected List inconsistency, SCB index = %d, " + "num scbs = %d.\n", scb_index, p->scb_data->numscbs); } - scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE; - scb->cmd->result = (DID_RESET << 16); + scbp = p->scb_data->scb_array[scb_index]; + if (aic7xxx_match_scb(scbp, target, channel, lun, tag)) + { + next = aic7xxx_rem_scb_from_disc_list(p, next); + } + else + { + prev = next; + next = inb(p->base + SCB_NEXT); + } + } + } + + /* + * Go through the hardware SCB array looking for commands that + * were active but not on any list. + */ + for (i = 0; i < p->scb_data->maxhscbs; i++) + { + unsigned char scbid; + + outb(i, p->base + SCBPTR); + scbid = inb(p->base + SCB_TAG); + if (scbid < p->scb_data->numscbs) + { + scbp = p->scb_data->scb_array[scbid]; + if (aic7xxx_match_scb(scbp, target, channel, lun, tag)) + { + aic7xxx_add_curscb_to_free_list(p); + } + } + } + + /* + * Go through the entire SCB array now and look for commands for + * for this target that are stillactive. These are other (most likely + * tagged) commands that were disconnected when the reset occurred. + */ + for (i = 0; i < p->scb_data->numscbs; i++) + { + scbp = p->scb_data->scb_array[i]; + if (((scbp->flags & SCB_ACTIVE) != 0) && + aic7xxx_match_scb(scbp, target, channel, lun, tag)) + { + scbp->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE; + scbp->flags &= ~SCB_ACTIVE; + aic7xxx_error(scbp->cmd) = DID_RESET; + found++; + + if ((scbp->flags & SCB_WAITINGQ) != 0) + { + scbq_remove(&p->waiting_scbs, scbp); + scbp->flags &= ~SCB_WAITINGQ; + } } } - outb(active_scb, SCBPTR + base); + outb(active_scb, p->base + SCBPTR); return (found); } /*+F************************************************************************* * Function: + * aic7xxx_clear_intstat + * + * Description: + * Clears the interrupt status. + *-F*************************************************************************/ +static void +aic7xxx_clear_intstat(struct aic7xxx_host *p) +{ + /* Clear any interrupt conditions this may have caused. */ + outb(CLRSELDO | CLRSELDI | CLRSELINGO, p->base + CLRSINT0); + outb(CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR | + CLRPHASECHG | CLRREQINIT, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); +} + +/*+F************************************************************************* + * Function: * aic7xxx_reset_current_bus * * Description: * Reset the current SCSI bus. *-F*************************************************************************/ static void -aic7xxx_reset_current_bus(int base) +aic7xxx_reset_current_bus(struct aic7xxx_host *p) { - outb(SCSIRSTO, SCSISEQ + base); + unsigned char scsiseq; + + /* Disable reset interrupts. */ + outb(inb(p->base + SIMODE1) & ~ENSCSIRST, p->base + SIMODE1); + + /* Turn on the bus reset. */ + scsiseq = inb(p->base + SCSISEQ); + outb(scsiseq | SCSIRSTO, p->base + SCSISEQ); + + udelay(1000); + + /* Turn off the bus reset. */ + outb(scsiseq & ~SCSIRSTO, p->base + SCSISEQ); + + aic7xxx_clear_intstat(p); + + /* Re-enable reset interrupts. */ + outb(inb(p->base + SIMODE1) | ENSCSIRST, p->base + SIMODE1); + udelay(1000); - outb(0, SCSISEQ + base); } /*+F************************************************************************* @@ -1921,25 +2486,24 @@ static int aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset) { - int base = p->base; - unsigned char sblkctl; - char cur_channel; unsigned long offset, offset_max; int found; + unsigned char sblkctl; + char cur_channel; + pause_sequencer(p); /* - * Clean up all the state information for the - * pending transactions on this bus. + * Clean up all the state information for the pending transactions + * on this bus. */ - found = aic7xxx_reset_device(p, ALL_TARGETS, channel); + found = aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL); if (channel == 'B') { p->needsdtr |= (p->needsdtr_copy & 0xFF00); p->sdtr_pending &= 0x00FF; - outb(0, ACTIVE_B + base); - offset = TARG_SCRATCH + base + 8; - offset_max = TARG_SCRATCH + base + 16; + offset = TARG_SCRATCH + 8; + offset_max = TARG_SCRATCH + 16; } else { @@ -1949,132 +2513,100 @@ p->needwdtr = p->needwdtr_copy; p->sdtr_pending = 0x0; p->wdtr_pending = 0x0; - outb(0, ACTIVE_A + base); - outb(0, ACTIVE_B + base); - offset = TARG_SCRATCH + base; - offset_max = TARG_SCRATCH + base + 16; + offset = TARG_SCRATCH; + offset_max = TARG_SCRATCH + 16; } else { + /* Channel A */ p->needsdtr |= (p->needsdtr_copy & 0x00FF); p->sdtr_pending &= 0xFF00; - outb(0, ACTIVE_A + base); - offset = TARG_SCRATCH + base; - offset_max = TARG_SCRATCH + base + 8; + offset = TARG_SCRATCH; + offset_max = TARG_SCRATCH + 8; } } + while (offset < offset_max) { /* - * Revert to async/narrow transfers - * until we renegotiate. + * Revert to async/narrow transfers until we renegotiate. */ u_char targ_scratch; - targ_scratch = inb(offset); + + targ_scratch = inb(p->base + offset); targ_scratch &= SXFR; - outb(targ_scratch, offset); + outb(targ_scratch, p->base + offset); offset++; } /* * Reset the bus and unpause/restart the controller */ - - /* - * Case 1: Command for another bus is active - */ - sblkctl = inb(SBLKCTL + base); + sblkctl = inb(p->base + SBLKCTL); cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A'; if (cur_channel != channel) { + /* + * Case 1: Command for another bus is active + */ #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset_channel) Stealthily resetting channel %c\n", - channel); + printk("scsi%d: Stealthily resetting channel %c\n", + p->host_no, channel); #endif /* - * Stealthily reset the other bus without upsetting the current bus + * Stealthily reset the other bus without upsetting the current bus. */ - outb(sblkctl ^ SELBUSB, SBLKCTL + base); + outb(sblkctl ^ SELBUSB, p->base + SBLKCTL); + outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1); if (initiate_reset) { - aic7xxx_reset_current_bus(base); + aic7xxx_reset_current_bus(p); + /* + * Cause the mid-level SCSI code to delay any further + * queueing by the bus settle time for us. + */ + p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ)); } - outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base); - outb(CLRSCSIINT, CLRINT + base); - outb(sblkctl, SBLKCTL + base); - - UNPAUSE_SEQUENCER(p); + outb(0, p->base + SCSISEQ); + aic7xxx_clear_intstat(p); + outb(sblkctl, p->base + SBLKCTL); + unpause_sequencer(p, /* unpause_always */ FALSE); } - /* - * Case 2: A command from this bus is active or we're idle - */ else { + /* + * Case 2: A command from this bus is active or we're idle. + */ #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset_channel) Resetting current channel %c\n", - channel); + printk("scsi%d: Resetting current channel %c\n", + p->host_no, channel); #endif + outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1); if (initiate_reset) { - aic7xxx_reset_current_bus(base); + aic7xxx_reset_current_bus(p); + /* + * Cause the mid-level SCSI code to delay any further + * queueing by the bus settle time for us. + */ +#if 0 + p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ)); +#endif } - outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base); - outb(CLRSCSIINT, CLRINT + base); - RESTART_SEQUENCER(p); + outb(0, p->base + SCSISEQ); + aic7xxx_clear_intstat(p); + restart_sequencer(p); #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset_channel) Channel reset, sequencer restarted\n"); + printk("scsi%d: Channel reset, sequencer restarted\n", p->host_no); #endif } /* - * Cause the mid-level SCSI code to delay any further - * queueing by the bus settle time for us. - */ - p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ)); - - /* * Now loop through all the SCBs that have been marked for abortion, * and call the scsi_done routines. */ - aic7xxx_done_aborted_scbs(p); - return found; -} - -/*+F************************************************************************* - * Function: - * aic7xxx_page_scb - * - * Description: - * Swap in_scbp for out_scbp down in the cards SCB array. - * We assume that the SCB for out_scbp is already selected in SCBPTR. - * - *-F*************************************************************************/ -static inline void -aic7xxx_page_scb(struct aic7xxx_host *p, struct aic7xxx_scb *out_scbp, - struct aic7xxx_scb *in_scbp) -{ - int index; - - /* Page-out */ -#if 0 -printk("aic7xxx: Paging out target %d SCB and paging in target %d SCB\n", - out_scbp->cmd->target, in_scbp->cmd->target); -#endif - aic7xxx_getscb(p, out_scbp); - out_scbp->state |= SCB_PAGED_OUT; - if (!(out_scbp->control & TAG_ENB)) - { - /* Stick in non-tagged array */ - index = (out_scbp->target_channel_lun >> 4) | - (out_scbp->target_channel_lun & SELBUSB); - p->pagedout_ntscbs[index] = out_scbp; - } - - /* Page-in */ - in_scbp->position = out_scbp->position; - out_scbp->position = SCB_LIST_NULL; - aic7xxx_putscb(p, in_scbp); - in_scbp->state &= ~SCB_PAGED_OUT; + aic7xxx_run_done_queue(p, /*complete*/ TRUE); + return (found); } /*+F************************************************************************* @@ -2082,1159 +2614,1326 @@ * aic7xxx_run_waiting_queues * * Description: - * Scan the assigned_scbs and waiting_scbs queues. For scbs in the - * assigned_scbs queue, we download and start them. For scbs in the - * waiting_scbs queue, we page in as many as we can being careful - * not to cause a deadlock for a reconnecting target. - * + * Scan the awaiting_scbs queue downloading and starting as many + * scbs as we can. *-F*************************************************************************/ static inline void aic7xxx_run_waiting_queues(struct aic7xxx_host *p) { struct aic7xxx_scb *scb; - u_char cur_scb, intstat; - u_long base = p->base; - long flags; - if ((p->assigned_scbs.head == NULL) && (p->waiting_scbs.head == NULL)) + if (p->waiting_scbs.head == NULL) return; - save_flags(flags); - cli(); - - PAUSE_SEQUENCER(p); - cur_scb = inb(SCBPTR + base); - intstat = inb(INTSTAT + base); - + pause_sequencer(p); /* * First handle SCBs that are waiting but have been assigned a slot. */ - scb = p->assigned_scbs.head; - while (scb != NULL) - { - scbq_remove_head(&(p->assigned_scbs)); - outb(scb->position, SCBPTR + base); - aic7xxx_putscb(p, scb); - /* Mark this as an active command. */ - scb->state = (scb->state & ~SCB_ASSIGNEDQ) | SCB_ACTIVE; - outb(scb->position, QINFIFO + base); - scb = p->assigned_scbs.head; - } - - /* Now deal with SCBs that require paging. */ scb = p->waiting_scbs.head; - if (scb != NULL) + while (scb != NULL) { - u_char disc_scb = inb(DISCONNECTED_SCBH + base); - u_char active = inb(FLAGS + base) & (SELECTED | IDENTIFY_SEEN); - int count = 0; - u_char next_scb; - - while (scb != NULL) + if (p->curqincnt >= p->qfullcount) { - /* Attempt to page this SCB in */ - if (disc_scb == SCB_LIST_NULL) - break; - - /* - * Advance disc_scb to the next one in the list. - */ - outb(disc_scb, SCBPTR + base); - next_scb = inb(SCB_NEXT + base); - - /* - * We have to be careful about when we allow an SCB to be paged out. - * There must always be at least one slot availible for a reconnecting - * target in case it references an SCB that has been paged out. Our - * heuristic is that either the disconnected list has at least two - * entries in it or there is one entry and the sequencer is activily - * working on an SCB which implies that it will either complete or - * disconnect before another reconnection can occur. - */ - if ((next_scb != SCB_LIST_NULL) || active) + p->curqincnt = inb(p->base + QINCNT) & p->qcntmask; + if (p->curqincnt >= p->qfullcount) { - u_char out_scbi; - struct aic7xxx_scb *out_scbp; - - scbq_remove_head(&(p->waiting_scbs)); - - /* - * Find the in-core SCB for the one we're paging out. - */ - out_scbi = inb(SCB_TAG + base); - out_scbp = (p->scb_array[out_scbi]); - - /* Do the page out and mark the paged in SCB as active. */ - aic7xxx_page_scb(p, out_scbp, scb); - - /* Mark this as an active command. */ - scb->state = (scb->state & ~SCB_WAITINGQ) | SCB_ACTIVE; - - /* Queue the command */ - outb(scb->position, QINFIFO + base); - count++; - - /* Advance to the next disconnected SCB */ - disc_scb = next_scb; - scb = p->waiting_scbs.head; + break; } - else - scb = NULL; } - if (count) + /* + * We have some space. + */ + scbq_remove_head(&(p->waiting_scbs)); + scb->flags &= ~SCB_WAITINGQ; + + outb(scb->hscb->tag, p->base + QINFIFO); + + if ((p->flags & PAGE_ENABLED) != 0) { - /* - * Update the head of the disconnected list. + /* + * We only care about this statistic when paging + * since it's impossible to overflow the qinfifo + * in the non-paging case. */ - outb(disc_scb, DISCONNECTED_SCBH + base); - if (disc_scb != SCB_LIST_NULL) - { - outb(disc_scb, SCBPTR + base); - outb(SCB_LIST_NULL, SCB_PREV + base); - } + p->curqincnt++; } + scb = p->waiting_scbs.head; } - /* Restore old position */ - outb(cur_scb, SCBPTR + base); - /* - * Guard against unpausing the sequencer if there is an interrupt - * waiting to happen. - */ - if (!(intstat & (BRKADRINT | SEQINT | SCSIINT))) - { - UNPAUSE_SEQUENCER(p); - } + unpause_sequencer(p, FALSE); +} - restore_flags(flags); +/*+F************************************************************************* + * Function: + * aic7xxx_construct_sdtr + * + * Description: + * Constucts a synchronous data transfer message in the message + * buffer on the sequencer. + *-F*************************************************************************/ +static void +aic7xxx_construct_sdtr(struct aic7xxx_host *p, int start_byte, + unsigned char period, unsigned char offset) +{ + outb(MSG_EXTENDED, p->base + MSG_OUT + start_byte); + outb(MSG_EXT_SDTR_LEN, p->base + MSG_OUT + 1 + start_byte); + outb(MSG_EXT_SDTR, p->base + MSG_OUT + 2 + start_byte); + outb(period, p->base + MSG_OUT + 3 + start_byte); + outb(offset, p->base + MSG_OUT + 4 + start_byte); + outb(start_byte + 5, p->base + MSG_LEN); } /*+F************************************************************************* * Function: - * aic7xxx_isr + * aic7xxx_construct_wdtr * * Description: - * SCSI controller interrupt handler. + * Constucts a wide data transfer message in the message buffer + * on the sequencer. + *-F*************************************************************************/ +static void +aic7xxx_construct_wdtr(struct aic7xxx_host *p, int start_byte, + unsigned char bus_width) +{ + outb(MSG_EXTENDED, p->base + MSG_OUT + start_byte); + outb(MSG_EXT_WDTR_LEN, p->base + MSG_OUT + 1 + start_byte); + outb(MSG_EXT_WDTR, p->base + MSG_OUT + 2 + start_byte); + outb(bus_width, p->base + MSG_OUT + 3 + start_byte); + outb(start_byte + 4, p->base + MSG_LEN); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_calc_residual * - * NOTE: Since we declared this using SA_INTERRUPT, interrupts should - * be disabled all through this function unless we say otherwise. + * Description: + * Calculate the residual data not yet transferred. *-F*************************************************************************/ static void -aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) +aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb) { - int base, intstat, actual, scb_index, run_aborted_queue = FALSE; - struct aic7xxx_host *p; - struct aic7xxx_scb *scb = NULL; - short transfer; - unsigned char ha_flags, scsi_id, bus_width; - unsigned char offset, rate, scratch, scratch_offset; - unsigned char max_offset, rej_byte; - unsigned short target_mask; - char channel; - unsigned int addr; /* must be 32 bits */ + struct aic7xxx_hwscb *hscb; Scsi_Cmnd *cmd; + int actual; - p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata; + cmd = scb->cmd; + hscb = scb->hscb; /* - * Search for the host with a pending interrupt. If we can't find - * one, then we've encountered a spurious interrupt. + * Don't destroy valid residual information with + * residual coming from a check sense operation. */ - while ((p != NULL) && !(inb(INTSTAT + p->base) & INT_PEND)) + if (((scb->hscb->control & DISCONNECTED) == 0) && + (scb->flags & SCB_SENSE) == 0) { - if (p->next == NULL) - { - p = NULL; - } - else + /* + * We had an underflow. At this time, there's only + * one other driver that bothers to check for this, + * and cmd->underflow seems to be set rather half- + * heartedly in the higher-level SCSI code. + */ + actual = aic7xxx_length(cmd, hscb->residual_SG_segment_count); + + actual -= (hscb->residual_data_count[2] << 16) | + (hscb->residual_data_count[1] << 8) | + hscb->residual_data_count[0]; + + if (actual < cmd->underflow) { - p = (struct aic7xxx_host *) p->next->hostdata; + printk(KERN_WARNING "(scsi%d:%d:%d) Underflow - " + "Wanted at least %u, got %u, residual SG count %d.\n", + p->host_no, TC_OF_SCB(scb), cmd->underflow, actual, + hscb->residual_SG_segment_count); + aic7xxx_error(cmd) = DID_RETRY_COMMAND; + aic7xxx_status(cmd) = hscb->target_status; } } - if (p == NULL) - return; - /* - * Keep track of interrupts for /proc/scsi + * Clean out the residual information in the SCB for the + * next consumer. */ - p->isr_count++; + hscb->residual_data_count[2] = 0; + hscb->residual_data_count[1] = 0; + hscb->residual_data_count[0] = 0; + hscb->residual_SG_segment_count = 0; +} - if (!(p->flags & A_SCANNED) && (p->isr_count == 1)) +/*+F************************************************************************* + * Function: + * aic7xxx_handle_device_reset + * + * Description: + * Interrupt handler for sequencer interrupts (SEQINT). + *-F*************************************************************************/ +static void +aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, char channel) +{ + unsigned short targ_mask; + unsigned char targ_scratch; + int scratch_offset = target; + int found; + + if (channel == 'B') { - /* - * We must only have one card at this IRQ and it must have been - * added to the board data before the spurious interrupt occurred. - * It is sufficient that we check isr_count and not the spurious - * interrupt count. - */ - printk("aic7xxx: (aic7xxx_isr) Encountered spurious interrupt.\n"); - return; + scratch_offset += 8; } - - base = p->base; + targ_mask = (0x01 << scratch_offset); /* - * Handle all the interrupt sources - especially for SCSI - * interrupts, we won't get a second chance at them. + * Go back to async/narrow transfers and renegotiate. */ - intstat = inb(INTSTAT + base); + p->needsdtr |= p->needsdtr_copy & targ_mask; + p->needwdtr |= p->needwdtr_copy & targ_mask; + p->sdtr_pending &= ~targ_mask; + p->wdtr_pending &= ~targ_mask; + targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset); + targ_scratch &= SXFR; + outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset); + found = aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL); + printk(KERN_WARNING "(scsi%d:%d:%d) Bus Device Reset delivered, " + "%d SCBs aborted.\n", p->host_no, target, CHAN_TO_INT(channel), found); + aic7xxx_run_done_queue(p, /*complete*/ TRUE); +} - /* - * Indicate that we're in the interrupt handler. - */ - p->flags |= IN_ISR; +/*+F************************************************************************* + * Function: + * aic7xxx_handle_seqint + * + * Description: + * Interrupt handler for sequencer interrupts (SEQINT). + *-F*************************************************************************/ +static void +aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) +{ + struct aic7xxx_scb *scb; + unsigned short target_mask; + unsigned char target, scratch_offset; + char channel; - if (intstat & BRKADRINT) + if ((inb(p->base + SEQ_FLAGS) & RESELECTED) != 0) { - int i; - unsigned char errno = inb(ERROR + base); - - printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno); - for (i = 0; i < NUMBER(hard_error); i++) - { - if (errno & hard_error[i].errno) - { - printk(KERN_ERR " %s\n", hard_error[i].errmesg); - } - } - panic("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no, - inb(ERROR + base), (inb(SEQADDR1 + base) << 8) | inb(SEQADDR0 + base)); + target = (inb(p->base + SELID) >> 4) & 0x0F; } - - if (intstat & SEQINT) + else { - /* - * Although the sequencer is paused immediately on - * a SEQINT, an interrupt for a SCSIINT condition will - * unpaused the sequencer before this point. - */ - PAUSE_SEQUENCER(p); + target = (inb(p->base + SCSIID) >> 4) & 0x0F; + } + scratch_offset = target; + channel = 'A'; + if (inb(p->base + SBLKCTL) & SELBUSB) + { + channel = 'B'; + scratch_offset += 8; + } + target_mask = (0x01 << scratch_offset); - scsi_id = (inb(SCSIID + base) >> 4) & 0x0F; - scratch_offset = scsi_id; - channel = 'A'; - if (inb(SBLKCTL + base) & SELBUSB) - { - channel = 'B'; - scratch_offset += 8; - } - target_mask = (0x01 << scratch_offset); + switch (intstat & SEQINT_MASK) + { + case NO_MATCH: + { + /* + * This could be for a normal abort request. Figure out + * which SCB we were trying to find and only give an error + * if we didn't ask for this to happen. + */ + unsigned char scb_index; + unsigned char busy_scbid; + unsigned char arg1; + + busy_scbid = aic7xxx_index_busy_target(p, target, channel, + /*unbusy*/ FALSE); + arg1 = inb(p->base + ARG_1); - switch (intstat & SEQINT_MASK) - { - case NO_MATCH: - if (p->flags & PAGE_ENABLED) + if (arg1 == SCB_LIST_NULL) { - /* SCB Page-in request */ - struct aic7xxx_scb *outscb; - u_char arg_1 = inb(ARG_1 + base); - int use_disconnected = FALSE; - - /* - * The sequencer expects this value upon return. Assume - * we will find the paged out SCB and set the value now. - * If we don't, and one of the methods used to acquire an - * SCB calls aic7xxx_done(), we will end up in our queue - * routine and unpause the sequencer without giving it the - * correct return value, which causes a hang. - */ - outb(SCB_PAGEDIN, RETURN_1 + base); - if (arg_1 == SCB_LIST_NULL) - { - /* Non-tagged command */ - int index = scsi_id; - if (channel == 'B') - { - index |= SELBUSB; - } - scb = p->pagedout_ntscbs[index]; - } - else - scb = (p->scb_array[arg_1]); + /* untagged request */ + scb_index = busy_scbid; + } + else + { + scb_index = arg1; + } - if (!(scb->state & SCB_PAGED_OUT)) + if (scb_index < p->scb_data->numscbs) + { + scb = p->scb_data->scb_array[scb_index]; + if (scb->hscb->control & ABORT_SCB) { - printk(KERN_WARNING "scsi%d: No active paged-out SCB for reconnecting " - "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n", - p->host_no, scsi_id, channel, inb(SAVED_TCL + base)); - aic7xxx_unbusy_target(scsi_id, channel, base); - outb(CLRSELTIMEO, CLRSINT1 + base); - outb(0, RETURN_1 + base); + /* + * We expected this. Let the busfree handler take care + * of this when we the abort is finially sent. Set + * IDENTIFY_SEEN so that the busfree handler knows that + * there is an SCB to cleanup. + */ + outb(inb(p->base + SEQ_FLAGS) | IDENTIFY_SEEN, p->base + SEQ_FLAGS); + printk(KERN_INFO "(scsi%d:%d:%d) reconnect SCB abort successful\n", + p->host_no, TC_OF_SCB(scb)); break; } + } + printk(KERN_WARNING "(scsi%d:%d:%d) No active SCB for reconnecting " + "target - Issuing BUS DEVICE RESET.\n", + p->host_no, target, CHAN_TO_INT(channel)); + + printk(KERN_WARNING " SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n", + inb(p->base + SAVED_TCL), arg1, + (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0)); + aic7xxx_handle_device_reset(p, target, channel); + } + break; - /* - * Now to pick the SCB to page out. Either take a free SCB, an - * assigned SCB, an SCB that just completed, or the first one - * on the disconnected SCB list. - */ - if (p->scb_link->free_scbs.head != NULL) - { - outscb = p->scb_link->free_scbs.head; - scbq_remove_head(&p->scb_link->free_scbs); - scb->position = outscb->position; - outscb->position = SCB_LIST_NULL; - scbq_insert_head(&p->page_scbs, outscb); - outb(scb->position, SCBPTR + base); - aic7xxx_putscb(p, scb); - scb->state &= ~SCB_PAGED_OUT; - } - else if (p->assigned_scbs.head != NULL) - { - outscb = p->assigned_scbs.head; - scbq_remove_head(&p->assigned_scbs); - scb->position = outscb->position; - outscb->position = SCB_LIST_NULL; - scbq_insert_head(&p->waiting_scbs, outscb); - outscb->state = (outscb->state & ~SCB_ASSIGNEDQ) | SCB_WAITINGQ; - outb(scb->position, SCBPTR + base); - aic7xxx_putscb(p, scb); - scb->state &= ~SCB_PAGED_OUT; - } - else if (intstat & CMDCMPLT) + case NO_MATCH_BUSY: + { + /* + * XXX - Leave this as a panic for the time being since it + * indicates a bug in the timeout code for this to happen. + */ + unsigned char scb_index; + + scb_index = inb(p->base + CUR_SCBID); + scb = p->scb_data->scb_array[scb_index]; + + panic("scsi%d: Target %d, channel %c, Target busy link failure, " + "but busy SCB exists!\n", + p->host_no, target, channel); + } + break; + + case SEND_REJECT: + { + unsigned char rej_byte; + + rej_byte = inb(p->base + REJBYTE); + printk(KERN_WARNING "(scsi%d:%d:%d) Rejecting unknown message (0x%x) " + "received from target, SEQ_FLAGS=0x%x\n", + p->host_no, target, CHAN_TO_INT(channel), rej_byte, + inb(p->base + SEQ_FLAGS)); + } + break; + + case NO_IDENT: + { + /* + * The reconnecting target either did not send an identify + * message, or did, but we didn't find and SCB to match and + * before it could respond to our ATN/abort, it hit a dataphase. + * The only safe thing to do is to blow it away with a bus + * reset. + */ + int found; + + printk(KERN_WARNING "(scsi%d:%d:%d): Target did not send an IDENTIFY " + "message; LASTPHASE 0x%x, SAVED_TCL 0x%x\n", + p->host_no, target, CHAN_TO_INT(channel), + inb(p->base + LASTPHASE), inb(p->base + SAVED_TCL)); + + found = aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE); + + printk(KERN_WARNING "scsi%d: Issued channel %c bus reset; " + "%d SCBs aborted\n", p->host_no, channel, found); + } + break; + + case BAD_PHASE: + if (inb(p->base + LASTPHASE) == P_BUSFREE) + { + printk(KERN_WARNING "(scsi%d:%d:%d): Missed busfree.\n", + p->host_no, CHAN_TO_INT(channel), target); + restart_sequencer(p); + } + else + { + printk(KERN_WARNING "(scsi%d:%d:%d): Unknown scsi bus phase, attempting " + "to continue\n", p->host_no, CHAN_TO_INT(channel), target); + } + break; + + case EXTENDED_MSG: + { + unsigned char message_length; + unsigned char message_code; + unsigned char scb_index; + + message_length = inb(p->base + MSGIN_EXT_LEN); + message_code = inb(p->base + MSGIN_EXT_OPCODE); + scb_index = inb(p->base + SCB_TAG); + scb = p->scb_data->scb_array[scb_index]; + + switch (message_code) + { + case MSG_EXT_SDTR: { - int scb_index; + unsigned char period; + unsigned char offset; + unsigned char saved_offset; + unsigned char targ_scratch; + unsigned char max_offset; + unsigned char rate; - outb(CLRCMDINT, CLRINT + base); - scb_index = inb(QOUTFIFO + base); - if (!(inb(QOUTCNT + base) & p->qcntmask)) + if (message_length != MSG_EXT_SDTR_LEN) { - intstat &= ~CMDCMPLT; - } - outscb = (p->scb_array[scb_index]); - if (!(outscb->state & SCB_ACTIVE)) - { - printk(KERN_WARNING "scsi%d: No command for completed SCB %d " - "during NO_MATCH interrupt\n", scb_index, p->host_no); - use_disconnected = TRUE; + outb(SEND_REJ, p->base + RETURN_1); + break; } + + period = inb(p->base + MSGIN_EXT_BYTES); + saved_offset = inb(p->base + MSGIN_EXT_BYTES + 1); + targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset); + + if (targ_scratch & WIDEXFER) + max_offset = MAX_OFFSET_16BIT; else + max_offset = MAX_OFFSET_8BIT; + offset = MIN(saved_offset, max_offset); + + aic7xxx_scsirate(p, &rate, &period, &offset, target, channel); + + /* + * Preserve the WideXfer flag. + */ + targ_scratch = rate | (targ_scratch & WIDEXFER); + + /* + * Update both the target scratch area and current SCSIRATE. + */ + outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset); + outb(targ_scratch, p->base + SCSIRATE); + + /* + * See if we initiated Sync Negotiation and didn't have + * have to fall down to async transfers. + */ + if ((scb->flags & SCB_MSGOUT_SDTR) != 0) { - scb->position = outscb->position; - outscb->position = SCB_LIST_NULL; - outb(scb->position, SCBPTR + base); - aic7xxx_putscb(p, scb); - scb->state &= ~SCB_PAGED_OUT; - outscb->cmd->result |= (aic7xxx_error(outscb->cmd) << 16); - if ((outscb->cmd->flags & WAS_SENSE) && - !(outscb->cmd->flags & ASKED_FOR_SENSE)) + /* We started it. */ + if (saved_offset == offset) { - /* - * Got sense information. - */ - outscb->cmd->flags &= ASKED_FOR_SENSE; + /* + * Don't send an SDTR back to the target. + */ + outb(0, p->base + RETURN_1); + } + else + { + /* We went too low - force async. */ + outb(SEND_REJ, p->base + RETURN_1); } - p->device_status[TARGET_INDEX(outscb->cmd)].flags - |= DEVICE_SUCCESS; - aic7xxx_done(p, outscb); } + else + { + /* + * Send our own SDTR in reply. + * + * We want to see this message as we don't expect a target + * to send us a SDTR request first. + */ + printk(KERN_WARNING "scsi%d: Sending SDTR!!\n", p->host_no); + aic7xxx_construct_sdtr(p, /* start byte */ 0, period, offset); + outb(SEND_MSG, p->base + RETURN_1); + } + /* + * Clear the flags. + */ + p->needsdtr &= ~target_mask; + break; } - else - { - use_disconnected = TRUE; - } - if (use_disconnected) + + case MSG_EXT_WDTR: { - u_char tag; - u_char next; - u_char disc_scb = inb(DISCONNECTED_SCBH + base); - if (disc_scb != SCB_LIST_NULL) + unsigned char scratch, bus_width; + + if (message_length != MSG_EXT_WDTR_LEN) { - outb(disc_scb, SCBPTR + base); - tag = inb(SCB_TAG + base); - outscb = (p->scb_array[tag]); - next = inb(SCB_NEXT + base); - if (next != SCB_LIST_NULL) - { - outb(next, SCBPTR + base); - outb(SCB_LIST_NULL, SCB_PREV + base); - outb(disc_scb, SCBPTR + base); - } - outb(next, DISCONNECTED_SCBH + base); - aic7xxx_page_scb(p, outscb, scb); - } - else if (inb(QINCNT + base) & p->qcntmask) + outb(SEND_REJ, p->base + RETURN_1); + break; + } + + bus_width = inb(p->base + MSGIN_EXT_BYTES); + scratch = inb(p->base + TARG_SCRATCH + scratch_offset); + + if ((scb->flags & SCB_MSGOUT_WDTR) != 0) { - /* Pull one of our queued commands as a last resort. */ - disc_scb = inb(QINFIFO + base); - outb(disc_scb, SCBPTR + base); - tag = inb(SCB_TAG + base); - outscb = (p->scb_array[tag]); - if ((outscb->control & 0x23) != TAG_ENB) + /* + * Don't send an WDTR back to the target, since we asked first. + */ + outb(0, p->base + RETURN_1); + switch (bus_width) { - /* - * This is not a simple tagged command so its position - * in the queue matters. Take the command at the end of - * the queue instead. - */ - int i; - int saved_queue[AIC7XXX_MAXSCB]; - int queued = inb(QINCNT + base) & p->qcntmask; - - /* Count the command we removed already */ - saved_queue[0] = disc_scb; - queued++; - - /* Empty the input queue. */ - for (i = 1; i < queued; i++) - { - saved_queue[i] = inb(QINFIFO + base); - } - - /* Put everyone back but the last entry. */ - queued--; - for (i = 0; i < queued; i++) - { - outb(saved_queue[i], QINFIFO + base); - } - - outb(saved_queue[queued], SCBPTR + base); - tag = inb(SCB_TAG + base); - outscb = (p->scb_array[tag]); + case BUS_8_BIT: + scratch &= 0x7F; + break; + + case BUS_16_BIT: + if (aic7xxx_verbose) + { + printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 " + "bit transfers.\n", p->host_no, target, channel); + } + scratch |= WIDEXFER; + break; + + case BUS_32_BIT: + outb(SEND_REJ, p->base + RETURN_1); + /* No verbose here! We want to see this condition. */ + printk(KERN_WARNING "scsi%d: Target %d, channel %c, " + "requesting 32 bit transfers, rejecting...\n", + p->host_no, target, channel); + break; + + default: + break; } - scb->position = outscb->position; - outscb->position = SCB_LIST_NULL; - scbq_insert_head(&p->waiting_scbs, outscb); - outscb->state |= SCB_WAITINGQ; - aic7xxx_putscb(p, scb); - scb->state &= ~SCB_PAGED_OUT; } else { - printk(KERN_WARNING "scsi%d: Page-in request with no candidates " - "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n", - p->host_no, scsi_id, channel, inb(SAVED_TCL + base)); - aic7xxx_unbusy_target(scsi_id, channel, base); - outb(CLRSELTIMEO, CLRSINT1 + base); - outb(0, RETURN_1 + base); + /* + * Send our own WDTR in reply. + */ + switch (bus_width) + { + case BUS_8_BIT: + scratch &= 0x7F; + break; + + case BUS_32_BIT: + case BUS_16_BIT: + if (p->bus_type == AIC_WIDE) + { + printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 " + "bit transfers.\n", p->host_no, target, channel); + bus_width = BUS_16_BIT; + scratch |= WIDEXFER; + } + else + { + bus_width = BUS_8_BIT; + scratch &= 0x7F; /* XXX - FreeBSD doesn't do this. */ + } + break; + + default: + break; + } + aic7xxx_construct_wdtr(p, /* start byte */ 0, bus_width); + outb(SEND_MSG, p->base + RETURN_1); } - } - } - else - { - printk(KERN_WARNING "scsi%d: No active SCB for reconnecting " - "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n", - p->host_no, scsi_id, channel, inb(SAVED_TCL + base)); - aic7xxx_unbusy_target(scsi_id, channel, base); - outb(0, SCB_CONTROL + base); - outb(CLRSELTIMEO, CLRSINT1 + base); - outb(0, RETURN_1 + base); - } - break; - - case BAD_PHASE: - panic("scsi%d: Unknown scsi bus phase.\n", p->host_no); - break; - - case SEND_REJECT: - rej_byte = inb(REJBYTE + base); - if ((rej_byte & 0xF0) == 0x20) - { - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - printk(KERN_WARNING "scsi%d: Tagged message received without identify." - "Disabling tagged commands for target %d channel %c.\n", - p->host_no, scsi_id, channel); - scb->cmd->device->tagged_supported = 0; - scb->cmd->device->tagged_queue = 0; - } - else - { - printk(KERN_WARNING "scsi%d: Rejecting unknown message (0x%x) received " - "from target %d channel %c.\n", - p->host_no, rej_byte, scsi_id, channel); - } - break; - - case NO_IDENT: - panic("scsi%d: Target %d, channel %c, did not send an IDENTIFY " - "message. SAVED_TCL 0x%x.\n", - p->host_no, scsi_id, channel, inb(SAVED_TCL + base)); - break; + p->needwdtr &= ~target_mask; + outb(scratch, p->base + TARG_SCRATCH + scratch_offset); + outb(scratch, p->base + SCSIRATE); + break; + } /* case MSG_EXT_WDTR */ - case SDTR_MSG: - /* - * Help the sequencer to translate the negotiated - * transfer rate. Transfer is 1/4 the period - * in ns as is returned by the sync negotiation - * message. So, we must multiply by four. - */ - transfer = (inb(ARG_1 + base) << 2); - offset = inb(ACCUM + base); - scratch = inb(TARG_SCRATCH + base + scratch_offset); + default: + /* + * Unknown extended message - reject it. + */ + outb(SEND_REJ, p->base + RETURN_1); + break; + } /* switch (message_code) */ + } /* case EXTENDED_MSG */ + break; + + case REJECT_MSG: + { /* - * The maximum offset for a wide device is 0x08; for a - * 8-bit bus device the maximum offset is 0x0F. + * What we care about here is if we had an outstanding SDTR + * or WDTR message for this target. If we did, this is a + * signal that the target is refusing negotiation. */ - if (scratch & WIDEXFER) + unsigned char targ_scratch; + unsigned char scb_index; + + scb_index = inb(p->base + SCB_TAG); + scb = p->scb_data->scb_array[scb_index]; + targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset); + + if ((scb->flags & SCB_MSGOUT_WDTR) != 0) { - max_offset = 0x08; + /* + * note 8bit xfers and clear flag + */ + targ_scratch &= 0x7F; + p->needwdtr &= ~target_mask; + printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE " + "negotiation; using 8 bit transfers.\n", + p->host_no, target, channel); } else { - max_offset = 0x0F; - } - aic7xxx_scsirate(p, &rate, transfer, MIN(offset, max_offset), - scsi_id, channel); - /* - * Preserve the wide transfer flag. - */ - scratch = rate | (scratch & WIDEXFER); - outb(scratch, TARG_SCRATCH + base + scratch_offset); - outb(scratch, SCSIRATE + base); - if ((scratch & 0x0F) == 0) - { - /* - * One of two things happened. Either the device requested - * asynchronous data transfers, or it requested a synchronous - * data transfer rate that was so low that asynchronous - * transfers are faster (not to mention the controller won't - * support them). In both cases the synchronous data transfer - * rate and the offset are set to 0 indicating asynchronous - * transfers. - * - * If the device requested an asynchronous transfer, then - * accept the request. If the device is being forced to - * asynchronous data transfers and this is the first time - * we've seen the request, accept the request. If we've - * already seen the request, then attempt to force - * asynchronous data transfers by rejecting the message. - */ - if ((offset == 0) || (p->sdtr_pending & target_mask)) + if ((scb->flags & SCB_MSGOUT_SDTR) != 0) { /* - * Device requested asynchronous transfers or we're - * forcing asynchronous transfers for the first time. + * note asynch xfers and clear flag */ - outb(0, RETURN_1 + base); - } - else - { - /* - * The first time in forcing asynchronous transfers - * failed, so we try sending a reject message. - */ - outb(SEND_REJ, RETURN_1 + base); + targ_scratch &= 0xF0; + p->needsdtr &= ~target_mask; + printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing " + "synchronous negotiation; using asynchronous transfers.\n", + p->host_no, target, channel); } + /* + * Otherwise, we ignore it. + */ } - else - { - /* - * See if we initiated Sync Negotiation - */ - if (p->sdtr_pending & target_mask) - { - /* - * Don't send an SDTR back to the target. - */ - outb(0, RETURN_1 + base); - } - else - { - /* - * Send our own SDTR in reply. - */ - printk("aic7xxx: Sending SDTR!!\n"); - outb(SEND_SDTR, RETURN_1 + base); - } - } - /* - * Clear the flags. - */ - p->needsdtr &= ~target_mask; - p->sdtr_pending &= ~target_mask; - break; - - case WDTR_MSG: - { - bus_width = inb(ARG_1 + base); - printk(KERN_INFO "scsi%d: Received MSG_WDTR, Target %d, channel %c " - "needwdtr(0x%x).\n", p->host_no, scsi_id, channel, p->needwdtr); - scratch = inb(TARG_SCRATCH + base + scratch_offset); - - if (p->wdtr_pending & target_mask) - { - /* - * Don't send an WDTR back to the target, since we asked first. - */ - outb(0, RETURN_1 + base); - switch (bus_width) - { - case BUS_8_BIT: - scratch &= 0x7F; - break; - - case BUS_16_BIT: - printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit " - "transfers.\n", p->host_no, scsi_id, channel); - scratch |= 0x80; - break; - - case BUS_32_BIT: - outb(SEND_REJ, RETURN_1 + base); - printk(KERN_INFO "scsi%d: Target %d, channel %c, requesting 32 bit " - "transfers, rejecting...\n", p->host_no, scsi_id, channel); - break; - } - } - else - { - /* - * Send our own WDTR in reply. - */ - printk(KERN_INFO "scsi%d: Will send WDTR!!\n", p->host_no); - switch (bus_width) - { - case BUS_8_BIT: - scratch &= 0x7F; - break; - - case BUS_32_BIT: - /* - * Negotiate 16 bits. - */ - bus_width = BUS_16_BIT; - /* Yes, we mean to fall thru here. */ - - case BUS_16_BIT: - printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit " - "transfers.\n", p->host_no, scsi_id, channel); - scratch |= 0x80; - break; - } - outb(bus_width | SEND_WDTR, RETURN_1 + base); - } - p->needwdtr &= ~target_mask; - p->wdtr_pending &= ~target_mask; - outb(scratch, TARG_SCRATCH + base + scratch_offset); - outb(scratch, SCSIRATE + base); - break; + outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset); + outb(targ_scratch, p->base + SCSIRATE); } + break; - case REJECT_MSG: + case BAD_STATUS: { - /* - * What we care about here is if we had an - * outstanding SDTR or WDTR message for this - * target. If we did, this is a signal that - * the target is refusing negotiation. + unsigned char scb_index; + struct aic7xxx_hwscb *hscb; + Scsi_Cmnd *cmd; + + /* The sequencer will notify us when a command has an error that + * would be of interest to the kernel. This allows us to leave + * the sequencer running in the common case of command completes + * without error. The sequencer will have DMA'd the SCB back + * up to us, so we can reference the drivers SCB array. */ + scb_index = inb(p->base + SCB_TAG); + scb = p->scb_data->scb_array[scb_index]; + hscb = scb->hscb; - scratch = inb(TARG_SCRATCH + base + scratch_offset); - - if (p->wdtr_pending & target_mask) - { - /* - * note 8bit xfers and clear flag - */ - scratch &= 0x7F; - p->needwdtr &= ~target_mask; - p->wdtr_pending &= ~target_mask; - printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE " - "negotiation; using 8 bit transfers.\n", - p->host_no, scsi_id, channel); - } - else - { - if (p->sdtr_pending & target_mask) - { - /* - * note asynch xfers and clear flag - */ - scratch &= 0xF0; - p->needsdtr &= ~target_mask; - p->sdtr_pending &= ~target_mask; - printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing " - "synchronous negotiation; using asynchronous transfers.\n", - p->host_no, scsi_id, channel); - } - /* - * Otherwise, we ignore it. - */ - } - outb(scratch, TARG_SCRATCH + base + scratch_offset); - outb(scratch, SCSIRATE + base); - break; - } - - case BAD_STATUS: - /* The sequencer will notify us when a command has an error that - * would be of interest to the kernel. This allows us to leave - * the sequencerrunning in the common case of command completes - * without error. - */ - - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - outb(0, RETURN_1 + base); /* CHECK_CONDITION may change this */ - if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) + /* + * Set the default return value to 0 indicating not to send + * sense. The sense code will change this if needed and this + * reduces code duplication. + */ + outb(0, p->base + RETURN_1); + if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) { - printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " - "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no, - intstat, scb_index, scb->state, (unsigned long) scb->cmd); + printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " + "SEQINT 0x%x, scb %d, flags 0x%x, cmd 0x%x.\n", p->host_no, + intstat, scb_index, scb->flags, (unsigned int) scb->cmd); } else { - cmd = scb->cmd; - scb->target_status = inb(SCB_TARGET_STATUS + base); - aic7xxx_status(cmd) = scb->target_status; + cmd = scb->cmd; + hscb->target_status = inb(p->base + SCB_TARGET_STATUS); + aic7xxx_status(cmd) = hscb->target_status; - cmd->result |= scb->target_status; + cmd->result |= hscb->target_status; - switch (status_byte(scb->target_status)) - { - case GOOD: - printk(KERN_WARNING "aic7xxx: Interrupted for status of GOOD???\n"); - break; - - case CHECK_CONDITION: - if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE)) - { - unsigned char tcl; - unsigned int req_buf; /* must be 32 bits */ + switch (status_byte(hscb->target_status)) + { + case GOOD: + printk(KERN_WARNING "(scsi%d:%d:%d) Interrupted for status of " + "GOOD???\n", p->host_no, TC_OF_SCB(scb)); + break; - tcl = scb->target_channel_lun; + case CHECK_CONDITION: + if ((aic7xxx_error(cmd) == 0) && !(scb->flags & SCB_SENSE)) + { + unsigned int addr; /* must be 32 bits */ + /* + * XXX - How do we save the residual (if there is one). + */ + aic7xxx_calculate_residual(p, scb); + + /* + * Send a sense command to the requesting target. + * XXX - revisit this and get rid of the memcopys. + */ + memcpy((void *) scb->sense_cmd, (void *) generic_sense, + sizeof(generic_sense)); + + scb->sense_cmd[1] = (cmd->lun << 5); + scb->sense_cmd[4] = sizeof(cmd->sense_buffer); + + scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer); + scb->sg_list[0].length = sizeof(cmd->sense_buffer); + cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]); - /* - * Send a sense command to the requesting target. + /* + * XXX - We should allow disconnection, but can't as it + * might allow overlapped tagged commands. */ - cmd->flags |= WAS_SENSE; - memcpy((void *) scb->sense_cmd, (void *) generic_sense, - sizeof(generic_sense)); - - scb->sense_cmd[1] = (cmd->lun << 5); - scb->sense_cmd[4] = sizeof(cmd->sense_buffer); - - scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer); - scb->sg_list[0].length = sizeof(cmd->sense_buffer); - req_buf = VIRT_TO_BUS(&scb->sg_list[0]); - cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]); - - scb->control = scb->control & DISCENB; - scb->target_channel_lun = tcl; - addr = VIRT_TO_BUS(scb->sense_cmd); - scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]); - memcpy(scb->SCSI_cmd_pointer, &addr, - sizeof(scb->SCSI_cmd_pointer)); - scb->SG_segment_count = 1; - memcpy(scb->SG_list_pointer, &req_buf, - sizeof(scb->SG_list_pointer)); - scb->data_count = scb->sg_list[0].length; - memcpy(scb->data_pointer, &(scb->sg_list[0].address), - sizeof(scb->data_pointer)); + /* hscb->control &= DISCENB; */ + hscb->control = 0; + hscb->target_status = 0; + hscb->SG_segment_count = 1; + + addr = VIRT_TO_BUS(&scb->sg_list[0]); + memcpy(&hscb->SG_list_pointer, &addr, + sizeof(hscb->SG_list_pointer)); + + memcpy(&hscb->data_pointer, &(scb->sg_list[0].address), + sizeof(hscb->data_pointer)); + /* Maintain SCB_LINKED_NEXT */ + hscb->data_count &= 0xFF000000; + hscb->data_count |= scb->sg_list[0].length; + + addr = VIRT_TO_BUS(scb->sense_cmd); + memcpy(&hscb->SCSI_cmd_pointer, &addr, + sizeof(hscb->SCSI_cmd_pointer)); + hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]); - aic7xxx_putscb(p, scb); + scb->sg_count = hscb->SG_segment_count; + scb->flags |= SCB_SENSE; /* - * Ensure that the target is "BUSY" so we don't get overlapping - * commands if we happen to be doing tagged I/O. + * Ensure the target is busy since this will be an + * an untagged request. */ - aic7xxx_busy_target(scsi_id, channel, base); + aic7xxx_busy_target(p, target, channel, hscb->tag); + outb(SEND_SENSE, p->base + RETURN_1); + } /* first time sense, no errors */ + else + { + if (aic7xxx_error(cmd) == 0) + { + aic7xxx_error(cmd) = DID_RETRY_COMMAND; + } + } + break; - aic7xxx_add_waiting_scb(base, scb); - outb(SEND_SENSE, RETURN_1 + base); - } /* first time sense, no errors */ - else + case QUEUE_FULL: +#ifdef NOT_YET + if (scb->hscb->control & TAG_ENB) + { + if (cmd->device->queue_depth > 2) + { + cmd->device->queue_depth--; /* Not correct */ + printk(KERN_WARNING "(scsi%d:%d:%d) Tagged queue depth " + "reduced to %d\n", p->host_no, + TC_OF_SCB(scb), cmd->device->queue_depth); + } + /* + * XXX - Requeue this unconditionally? + */ + + /* + * We'd like to be able to give the SCB some more time + * (untimeout, then timeout). + */ + break; + } +#endif + printk(KERN_WARNING "(scsi%d:%d:%d) Queue full received; " + "queue depth %d, active %d\n", p->host_no, + TC_OF_SCB(scb), cmd->device->queue_depth, + p->device_status[TARGET_INDEX(cmd)].active_cmds); + + /* Else treat this as if it was a BUSY condition. */ + scb->hscb->target_status = (BUSY << 1) | + (scb->hscb->target_status & 0x01); + /* Fall through to the BUSY case. */ + + case BUSY: + printk(KERN_WARNING "(scsi%d:%d:%d) Target busy\n", + p->host_no, TC_OF_SCB(scb)); + if (!aic7xxx_error(cmd)) { - cmd->flags &= ~ASKED_FOR_SENSE; - if (aic7xxx_error(cmd) == 0) - { - aic7xxx_error(cmd) = DID_RETRY_COMMAND; - } + /* + * The mid-level SCSI code should be fixed to + * retry the command at a later time instead of + * trying right away. + */ + aic7xxx_error(cmd) = DID_BUS_BUSY | (SUGGEST_RETRY << 8); } - break; + udelay(1000); /* A small pause (1ms) to help the drive */ + break; - case BUSY: - printk(KERN_WARNING "scsi%d: Target busy, TCL=0x%x.\n", - p->host_no, scb->target_channel_lun); - if (!aic7xxx_error(cmd)) - { - /* The error code here used to be DID_BUS_BUSY, - * but after extensive testing, it has been determined - * that a DID_BUS_BUSY return is a waste of time. If - * the problem is something that will go away, then it - * will, if it isn't, then you don't want the endless - * looping that you get with a DID_BUS_BUSY. Better - * to be on the safe side and specify an error condition - * that will eventually lead to a reset or abort of some - * sort instead of an endless loop. - */ - aic7xxx_error(cmd) = DID_RETRY_COMMAND; - } - break; - - case QUEUE_FULL: - printk(KERN_WARNING "scsi%d: Queue full.\n", p->host_no); - scb->state |= SCB_ASSIGNEDQ; - scbq_insert_tail(&p->assigned_scbs, scb); - break; - - default: - printk(KERN_WARNING "scsi%d: Unexpected target status 0x%x.\n", - p->host_no, scb->target_status); - if (!aic7xxx_error(cmd)) - { - aic7xxx_error(cmd) = DID_RETRY_COMMAND; - } - break; - } /* end switch */ + default: + printk(KERN_WARNING "(scsi%d:%d:%d) Unexpected target " + "status 0x%x.\n", p->host_no, + TC_OF_SCB(scb), scb->hscb->target_status); + if (!aic7xxx_error(cmd)) + { + aic7xxx_error(cmd) = DID_RETRY_COMMAND; + } + break; + } /* end switch */ } /* end else of */ - break; - - case RESIDUAL: - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) - { - printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " - "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no, - intstat, scb_index, scb->state, (unsigned long) scb->cmd); - } - else - { - cmd = scb->cmd; - /* - * Don't destroy valid residual information with - * residual coming from a check sense operation. - */ - if (!(cmd->flags & WAS_SENSE)) - { - /* - * We had an underflow. At this time, there's only - * one other driver that bothers to check for this, - * and cmd->underflow seems to be set rather half- - * heartedly in the higher-level SCSI code. - */ - actual = aic7xxx_length(cmd, scb->residual_SG_segment_count); - - actual -= (inb(SCB_RESID_DCNT2 + base) << 16) | - (inb(SCB_RESID_DCNT1 + base) << 8) | - inb(SCB_RESID_DCNT0 + base); - - if (actual < cmd->underflow) - { - printk(KERN_WARNING "scsi%d: Target %d underflow - " - "Wanted at least %u, got %u, residual SG count %d.\n", - p->host_no, cmd->target, cmd->underflow, actual, - inb(SCB_RESID_SGCNT + base)); - aic7xxx_error(cmd) = DID_RETRY_COMMAND; - aic7xxx_status(cmd) = scb->target_status; - } - } - } - break; + } + break; - case ABORT_TAG: - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) - { - printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " - "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx\n", p->host_no, - intstat, scb_index, scb->state, (unsigned long) scb->cmd); - } - else - { - cmd = scb->cmd; - /* - * We didn't receive a valid tag back from the target - * on a reconnect. - */ - printk("scsi%d: Invalid tag received on target %d, channel %c, " - "lun %d - Sending ABORT_TAG.\n", p->host_no, - scsi_id, channel, cmd->lun & 0x07); + case AWAITING_MSG: + { + unsigned char scb_index; + unsigned char message_offset; - cmd->result = (DID_RETRY_COMMAND << 16); - aic7xxx_done(p, scb); - } - break; + scb_index = inb(p->base + SCB_TAG); + scb = p->scb_data->scb_array[scb_index]; - case AWAITING_MSG: - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) + /* + * This SCB had a MK_MESSAGE set in its control byte informing + * the sequencer that we wanted to send a special message to + * this target. + */ + message_offset = inb(p->base + MSG_LEN); + if (scb->flags & SCB_DEVICE_RESET) { - printk(KERN_WARNING "scsi%d: Referenced SCB not valid during " - "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no, - intstat, scb_index, scb->state, (unsigned long) scb->cmd); + outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT); + outb(1, p->base + MSG_LEN); + printk(KERN_INFO "(scsi%d:%d:%d) Bus device reset sent\n", + p->host_no, TC_OF_SCB(scb)); } - else + else if (scb->flags & SCB_ABORT) + { + if ((scb->hscb->control & TAG_ENB) != 0) + { + outb(MSG_ABORT_TAG, p->base + MSG_OUT + message_offset); + } + else + { + outb(MSG_ABORT, p->base + MSG_OUT + message_offset); + } + outb(message_offset + 1, p->base + MSG_LEN); + printk(KERN_WARNING "(scsi%d:%d:%d): Abort message sent.\n", + p->host_no, TC_OF_SCB(scb)); + } + else if (scb->flags & SCB_MSGOUT_WDTR) { - /* - * This SCB had a zero length command, informing the sequencer - * that we wanted to send a special message to this target. - * We only do this for BUS_DEVICE_RESET messages currently. - */ - if (scb->state & SCB_DEVICE_RESET) - { -#ifdef AIC7XXX_DEBUG_ABORT - printk ("aic7xxx: (isr) sending bus device reset to target %d\n", - scsi_id); -#endif - outb(MSG_BUS_DEVICE_RESET, MSG0 + base); - outb(1, MSG_LEN + base); - } - else - { - panic("scsi%d: AWAITING_SCB for an SCB that does " - "not have a waiting message.\n", p->host_no); - } - } - break; - - case IMMEDDONE: - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); -#ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: received IMMEDDONE for target %d, scb %d, state %d\n", - scsi_id, scb_index, scb->state); -#endif - if (scb->state & SCB_DEVICE_RESET) + aic7xxx_construct_wdtr(p, message_offset, BUS_16_BIT); + } + else if (scb->flags & SCB_MSGOUT_SDTR) { - int found; + unsigned char target_scratch; + unsigned short ultra_enable; + int i, sxfr; /* - * Go back to async/narrow transfers and renegotiate. + * Pull the user defined setting from scratch RAM. */ - aic7xxx_unbusy_target(scsi_id, channel, base); - p->needsdtr |= (p->needsdtr_copy & target_mask); - p->needwdtr |= (p->needwdtr_copy & target_mask); - p->sdtr_pending &= ~target_mask; - p->wdtr_pending &= ~target_mask; - scratch = inb(TARG_SCRATCH + base + scratch_offset); - scratch &= SXFR; - outb(scratch, TARG_SCRATCH + base + scratch_offset); - found = aic7xxx_reset_device(p, (int) scsi_id, channel); - printk(KERN_INFO "scsi%d: Bus Device Reset delivered, %d SCBs " - "aborted.\n", p->host_no, found); - /* Indicate that we want to call aic7xxx_done_aborted_scbs() */ - run_aborted_queue = TRUE; + target_scratch = inb(p->base + TARG_SCRATCH + scratch_offset); + sxfr = target_scratch & SXFR; + ultra_enable = inb(p->base + ULTRA_ENB) | + (inb(p->base + ULTRA_ENB + 1) << 8); + if (ultra_enable & target_mask) + { + sxfr |= 0x100; + } + for (i = 0; i < num_aic7xxx_syncrates; i++) + { + if (sxfr == aic7xxx_syncrates[i].rate) + break; + } + aic7xxx_construct_sdtr(p, message_offset, + aic7xxx_syncrates[i].period, + target_scratch & WIDEXFER ? + MAX_OFFSET_16BIT : MAX_OFFSET_8BIT); } - else + else { - panic("scsi%d: Immediate complete for unknown operation.\n", - p->host_no); - } - break; + panic("aic7xxx: AWAITING_MSG for an SCB that does " + "not have a waiting message."); + } + } + break; - case DATA_OVERRUN: + case DATA_OVERRUN: { - unsigned int overrun; - - scb = (p->scb_array[inb(base + SCB_TAG)]); - overrun = inb(base + STCNT0) | (inb(base + STCNT1) << 8) | - (inb(base + STCNT2) << 16); - overrun =0x00FFFFFF - overrun; - printk(KERN_WARNING "scsi%d: data overrun of %d bytes detected; forcing " - "a retry.\n", p->host_no, overrun); - aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND; - break; + unsigned char scb_index = inb(p->base + SCB_TAG); + unsigned char lastphase = inb(p->base + LASTPHASE); + unsigned int i, overrun; + + scb = (p->scb_data->scb_array[scb_index]); + overrun = inb(p->base + STCNT) | (inb(p->base + STCNT + 1) << 8) | + (inb(p->base + STCNT + 2) << 16); + overrun = 0x00FFFFFF - overrun; + printk(KERN_WARNING "(scsi%d:%d:%d) Data overrun of %d bytes detected " + "in %s phase, tag %d; forcing a retry.\n", + p->host_no, TC_OF_SCB(scb), overrun, + lastphase == P_DATAIN ? "Data-In" : "Data-Out", + scb->hscb->tag); + printk(KERN_WARNING "%s seen Data Phase. Length = %d, NumSGs = %d.\n", + inb(p->base + SEQ_FLAGS) & DPHASE ? "Have" : "Haven't", + aic7xxx_length(scb->cmd, 0), scb->sg_count); + for (i = 0; i < scb->sg_count; i++) + { + printk(KERN_INFO " sg[%d] - Addr 0x%x : Length %d\n", + i, scb->sg_list[i].address, scb->sg_list[i].length); + } + /* + * XXX - What do we really want to do on an overrun? The + * mid-level SCSI code should handle this, but for now, + * we'll just indicate that the command should retried. + */ + aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND; } + break; -#if AIC7XXX_NOT_YET - /* XXX Fill these in later */ - case MESG_BUFFER_BUSY: - break; - case MSGIN_PHASEMIS: - break; -#endif +/* #if AIC7XXX_NOT_YET */ + /* XXX Fill these in later */ + case MSG_BUFFER_BUSY: + printk("aic7xxx: Message buffer busy.\n"); + break; + case MSGIN_PHASEMIS: + printk("aic7xxx: Message-in phasemis.\n"); + break; +/*#endif */ + + case ABORT_CMDCMPLT: + /* This interrupt serves to pause the sequencer until we can clean + * up the QOUTFIFO allowing us to handle any abort SCBs that may + * completed yet still have an SCB in the QINFIFO or waiting for + * selection queue. By the time we get here, we should have + * already cleaned up the queues, so all we need to do is unpause + * the sequencer. + */ + break; + + default: /* unknown */ + printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n", + p->host_no, intstat, inb(p->base + SCSISIGI)); + break; + } + + /* + * Clear the sequencer interrupt and unpause the sequencer. + */ + outb(CLRSEQINT, p->base + CLRINT); + unpause_sequencer(p, /* unpause always */ TRUE); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_handle_scsiint + * + * Description: + * Interrupt handler for SCSI interrupts (SCSIINT). + *-F*************************************************************************/ +static void +aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) +{ + unsigned char scb_index; + unsigned char status; + struct aic7xxx_scb *scb; + + scb_index = inb(p->base + SCB_TAG); + status = inb(p->base + SSTAT1); - default: /* unknown */ - printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n", - p->host_no, intstat, inb(SCSISIGI + base)); - break; + if (scb_index < p->scb_data->numscbs) + { + scb = p->scb_data->scb_array[scb_index]; + if ((scb->flags & SCB_ACTIVE) == 0) + { + scb = NULL; } + } + else + { + scb = NULL; + } + if ((status & SCSIRSTI) != 0) + { + char channel; + + channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A'; + + printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n", + p->host_no, channel); /* - * Clear the sequencer interrupt and unpause the sequencer. + * Go through and abort all commands for the channel, but do not + * reset the channel again. */ - outb(CLRSEQINT, CLRINT + base); - UNPAUSE_SEQUENCER(p); + aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE); + scb = NULL; } - - if (intstat & SCSIINT) + else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) ) { - int status = inb(SSTAT1 + base); - scsi_id = (inb(SCSIID + base) >> 4) & 0x0F; - channel = 'A'; - if (inb(SBLKCTL + base) & SELBUSB) - { - channel = 'B'; + /* + * First look at what phase we were last in. If it's message-out, + * chances are pretty good that the bus free was in response to + * one of our abort requests. + */ + unsigned char lastphase = inb(p->base + LASTPHASE); + unsigned char target = (inb(p->base + SAVED_TCL) >> 4) & 0x0F; + char channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A'; + int printerror = TRUE; + + outb(0, p->base + SCSISEQ); + if (lastphase == P_MESGOUT) + { + unsigned char sindex; + unsigned char message; + + sindex = inb(p->base + SINDEX); + message = inb(p->base + sindex - 1); + + if (message == MSG_ABORT) + { + printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort completed.\n", + p->host_no, TC_OF_SCB(scb), scb->hscb->tag); + aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), SCB_LIST_NULL); + aic7xxx_run_done_queue(p, /* complete */ TRUE); + scb = NULL; + printerror = 0; + } + else if (message == MSG_ABORT_TAG) + { + printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort Tag completed.\n", + p->host_no, TC_OF_SCB(scb), scb->hscb->tag); + aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), scb->hscb->tag); + aic7xxx_run_done_queue(p, /* complete */ TRUE); + scb = NULL; + printerror = 0; + } + else if (message == MSG_BUS_DEV_RESET) + { + aic7xxx_handle_device_reset(p, target, channel); + scb = NULL; + printerror = 0; + } } + if (printerror != 0) + { + if (scb != NULL) + { + unsigned char tag; - scb_index = inb(SCB_TAG + base); - scb = (p->scb_array[scb_index]); - if (status & SCSIRSTI) + if ((scb->hscb->control & TAG_ENB) != 0) + { + tag = scb->hscb->tag; + } + else + { + tag = SCB_LIST_NULL; + } + aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), tag); + } + else + { + aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL); + } + printk(KERN_WARNING "scsi%d: Unexpected busfree, LASTPHASE = 0x%x, " + "SEQADDR = 0x%x\n", p->host_no, lastphase, + (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0)); + } + outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1); + outb(CLRBUSFREE, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); + restart_sequencer(p); + } + else if ((status & SELTO) != 0) + { + unsigned char scbptr; + unsigned char nextscb; + Scsi_Cmnd *cmd; + + scbptr = inb(p->base + WAITING_SCBH); + outb(scbptr, p->base + SCBPTR); + scb_index = inb(p->base + SCB_TAG); + + scb = NULL; + if (scb_index < p->scb_data->numscbs) { - PAUSE_SEQUENCER(p); - printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n", - p->host_no, channel); - /* - * Go through and abort all commands for the channel, but do not - * reset the channel again. - */ - aic7xxx_reset_channel(p, channel, FALSE); - run_aborted_queue = TRUE; + scb = p->scb_data->scb_array[scb_index]; + if ((scb->flags & SCB_ACTIVE) == 0) + { + scb = NULL; + } + } + if (scb == NULL) + { + printk(KERN_WARNING "scsi%d: Referenced SCB %d not valid during SELTO.\n", + p->host_no, scb_index); + printk(KERN_WARNING " SCSISEQ = 0x%x SEQADDR = 0x%x SSTAT0 = 0x%x " + "SSTAT1 = 0x%x\n", inb(p->base + SCSISEQ), + inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8), + inb(p->base + SSTAT0), inb(p->base + SSTAT1)); } - else if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) + else { - printk(KERN_WARNING "scsi%d: SCSIINT - No command for SCB.\n", p->host_no); /* - * Turn off the interrupt and set status to zero, so that it - * falls through the rest of the SCSIINT code. + * XXX - If we queued an abort tag, go clean up the disconnected list. + */ + cmd = scb->cmd; + cmd->result = (DID_TIME_OUT << 16); + + /* + * Clear an pending messages for the timed out + * target and mark the target as free. + */ + outb(0, p->base + MSG_LEN); + aic7xxx_index_busy_target(p, cmd->target, + cmd->channel ? 'B': 'A', /*unbusy*/ TRUE); + outb(0, p->base + SCB_CONTROL); + + /* + * Shift the waiting for selection queue forward + */ + nextscb = inb(p->base + SCB_NEXT); + outb(nextscb, p->base + WAITING_SCBH); + + /* + * Put this SCB back on the free list. */ - outb(status, CLRSINT1 + base); - UNPAUSE_SEQUENCER(p); - outb(CLRSCSIINT, CLRINT + base); + aic7xxx_add_curscb_to_free_list(p); + } + /* + * Stop the selection. + */ + outb(0, p->base + SCSISEQ); + outb(CLRSELTIMEO | CLRBUSFREE, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); + restart_sequencer(p); + } + else if (scb == NULL) + { + printk(KERN_WARNING "scsi%d: aic7xxx_isr - referenced scb not valid " + "during scsiint 0x%x scb(%d)\n" + " SIMODE0 0x%x, SIMODE1 0x%x, SSTAT0 0x%x, SEQADDR 0x%x\n", + p->host_no, status, scb_index, inb(p->base + SIMODE0), + inb(p->base + SIMODE1), inb(p->base + SSTAT0), + (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0)); + /* + * Turn off the interrupt and set status to zero, so that it + * falls through the rest of the SCSIINT code. + */ + outb(status, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); + unpause_sequencer(p, /* unpause always */ TRUE); + scb = NULL; + } + else if (status & SCSIPERR) + { + /* + * Determine the bus phase and queue an appropriate message. + */ + char *phase; + Scsi_Cmnd *cmd; + unsigned char mesg_out = MSG_NOOP; + unsigned char lastphase = inb(p->base + LASTPHASE); + + cmd = scb->cmd; + switch (lastphase) + { + case P_DATAOUT: + phase = "Data-Out"; + break; + case P_DATAIN: + phase = "Data-In"; + mesg_out = MSG_INITIATOR_DET_ERR; + break; + case P_COMMAND: + phase = "Command"; + break; + case P_MESGOUT: + phase = "Message-Out"; + break; + case P_STATUS: + phase = "Status"; + mesg_out = MSG_INITIATOR_DET_ERR; + break; + case P_MESGIN: + phase = "Message-In"; + mesg_out = MSG_PARITY_ERROR; + break; + default: + phase = "unknown"; + break; + } + + /* + * A parity error has occurred during a data + * transfer phase. Flag it and continue. + */ + printk(KERN_WARNING "(scsi%d:%d:%d) Parity error during phase %s.\n", + p->host_no, TC_OF_SCB(scb), phase); + + /* + * We've set the hardware to assert ATN if we get a parity + * error on "in" phases, so all we need to do is stuff the + * message buffer with the appropriate message. "In" phases + * have set mesg_out to something other than MSG_NOP. + */ + if (mesg_out != MSG_NOOP) + { + outb(mesg_out, p->base + MSG_OUT); + outb(1, p->base + MSG_LEN); scb = NULL; } - else if (status & SCSIPERR) + else { - char *phase; - unsigned char mesg_out = MSG_NOP; - unsigned char lastphase = inb(LASTPHASE + base); + /* + * Should we allow the target to make this decision for us? + */ + cmd->result = DID_RETRY_COMMAND << 16; + } + outb(CLRSCSIPERR, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); + unpause_sequencer(p, /* unpause_always */ TRUE); + } + else + { + /* + * We don't know what's going on. Turn off the + * interrupt source and try to continue. + */ + printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status); + outb(status, p->base + CLRSINT1); + outb(CLRSCSIINT, p->base + CLRINT); + unpause_sequencer(p, /* unpause always */ TRUE); + scb = NULL; + } + if (scb != NULL) + { + aic7xxx_done(p, scb); + aic7xxx_done_cmds_complete(p); + } +} - cmd = scb->cmd; - switch (lastphase) - { - case P_DATAOUT: - phase = "Data-Out"; - break; - case P_DATAIN: - phase = "Data-In"; - mesg_out = MSG_INITIATOR_DET_ERROR; - break; - case P_COMMAND: - phase = "Command"; - break; - case P_MESGOUT: - phase = "Message-Out"; - break; - case P_STATUS: - phase = "Status"; - mesg_out = MSG_INITIATOR_DET_ERROR; - break; - case P_MESGIN: - phase = "Message-In"; - mesg_out = MSG_MSG_PARITY_ERROR; - break; - default: - phase = "unknown"; - break; - } +/*+F************************************************************************* + * Function: + * aic7xxx_isr + * + * Description: + * SCSI controller interrupt handler. + * + * NOTE: Since we declared this using SA_INTERRUPT, interrupts should + * be disabled all through this function unless we say otherwise. + *-F*************************************************************************/ +static void +aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct aic7xxx_host *p; + unsigned char intstat; + unsigned long flags; - /* - * A parity error has occurred during a data - * transfer phase. Flag it and continue. - */ - printk(KERN_WARNING "scsi%d: Parity error during phase %s on target %d, " - "channel %d, lun %d.\n", p->host_no, phase, - cmd->target, cmd->channel & 0x01, cmd->lun & 0x07); + p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata; - /* - * We've set the hardware to assert ATN if we get a parity - * error on "in" phases, so all we need to do is stuff the - * message buffer with the appropriate message. In phases - * have set mesg_out to something other than MSG_NOP. - */ - if (mesg_out != MSG_NOP) - { - outb(mesg_out, MSG0 + base); - outb(1, MSG_LEN + base); - cmd->result = DID_PARITY << 16; - } - else - { - /* - * Should we allow the target to make this decision for us? - */ - cmd->result = DID_RETRY_COMMAND << 16; - } - aic7xxx_done(p, scb); + /* + * Search for the host with a pending interrupt. If we can't find + * one, then we've encountered a spurious interrupt. + */ + while ((p != NULL) && !(inb(p->base + INTSTAT) & INT_PEND)) + { + if (p->next == NULL) + { + p = NULL; } - else if (status & SELTO) + else { - unsigned char waiting; + p = (struct aic7xxx_host *) p->next->hostdata; + } + } - cmd = scb->cmd; + if (p == NULL) + return; - cmd->result = (DID_TIME_OUT << 16); - /* - * Clear an pending messages for the timed out - * target and mark the target as free. - */ - ha_flags = inb(FLAGS + base); - outb(0, MSG_LEN + base); - aic7xxx_unbusy_target(scsi_id, channel, base); - /* - * Stop the selection. - */ - outb(0, SCSISEQ + base); - outb(0, SCB_CONTROL + base); - outb(CLRSELTIMEO, CLRSINT1 + base); - outb(CLRSCSIINT, CLRINT + base); + /* + * Handle all the interrupt sources - especially for SCSI + * interrupts, we won't get a second chance at them. + */ + intstat = inb(p->base + INTSTAT); - /* - * Shift the waiting for selection queue forward - */ - waiting = inb(WAITING_SCBH + base); - outb(waiting, SCBPTR + base); - waiting = inb(SCB_NEXT + base); - outb(waiting, WAITING_SCBH + base); + /* + * Keep track of interrupts for /proc/scsi + */ + p->isr_count++; - RESTART_SEQUENCER(p); - aic7xxx_done(p, scb); - } - else if (!(status & BUSFREE)) + if (!(p->flags & A_SCANNED) && (p->isr_count == 1)) + { + /* + * We must only have one card at this IRQ and it must have been + * added to the board data before the spurious interrupt occurred. + * It is sufficient that we check isr_count and not the spurious + * interrupt count. + */ + printk("scsi%d: Encountered spurious interrupt.\n", p->host_no); + if (intstat) { - /* - * We don't know what's going on. Turn off the - * interrupt source and try to continue. - */ - printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status); - outb(status, CLRSINT1 + base); - UNPAUSE_SEQUENCER(p); - outb(CLRSCSIINT, CLRINT + base); + /* Try clearing all interrupts. */ + outb(CLRBRKADRINT | CLRSCSIINT | CLRCMDINT | CLRSEQINT, p->base + CLRINT); } + return; + } + + if (p->flags & IN_ISR) + { + printk(KERN_WARNING "scsi%d: Warning!! Interrupt routine called reentrantly!\n", + p->host_no); + return; } - if (run_aborted_queue) - aic7xxx_done_aborted_scbs(p); + /* + * Indicate that we're in the interrupt handler. + */ + save_flags(flags); + cli(); + p->flags |= IN_ISR; if (intstat & CMDCMPLT) { - int complete; + struct aic7xxx_scb *scb = NULL; + Scsi_Cmnd *cmd; + unsigned char qoutcnt; + unsigned char scb_index; + int i, interrupts_cleared = 0; /* * The sequencer will continue running when it * issues this interrupt. There may be >1 commands * finished, so loop until we've processed them all. */ - do { - complete = inb(QOUTFIFO + base); + qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask; - scb = (p->scb_array[complete]); - if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL)) - { - printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d.\n" - " QOUTCNT %d, QINCNT %d, SCB state 0x%x, cmd 0x%lx, " - "pos(%d).\n", p->host_no, complete, inb(QOUTCNT + base), - inb(QINCNT + base), scb->state, (unsigned long) scb->cmd, - scb->position); - outb(CLRCMDINT, CLRINT + base); - continue; - } - cmd = scb->cmd; - cmd->result |= (aic7xxx_error(cmd) << 16); - if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE)) +#if 1 + if (qoutcnt >= p->qfullcount - 1) + printk(KERN_WARNING "aic7xxx: Command complete near Qfull count, " + "qoutcnt = %d.\n", qoutcnt); +#endif + while (qoutcnt > 0) + { + for (i = 0; i < qoutcnt; i++) { - /* - * Got sense information. - */ - cmd->flags &= ASKED_FOR_SENSE; + scb_index = inb(p->base + QOUTFIFO); + scb = p->scb_data->scb_array[scb_index]; + if (scb == NULL) + { + printk(KERN_WARNING "scsi%d: CMDCMPLT with invalid SCB index %d, " + "QOUTCNT %d, QINCNT %d\n", p->host_no, scb_index, + inb(p->base + QOUTCNT), inb(p->base + QINCNT)); + continue; + } + else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) + { + printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d, " + "QOUTCNT %d, QINCNT %d, SCB flags 0x%x, cmd 0x%lx\n", + p->host_no, scb_index, inb(p->base + QOUTCNT), + inb(p->base + QINCNT), scb->flags, (unsigned long) scb->cmd); + continue; + } + cmd = scb->cmd; + if (scb->hscb->residual_SG_segment_count != 0) + { + aic7xxx_calculate_residual(p, scb); + } + if ((scb->flags & SCB_QUEUED_ABORT) != 0) + { + /* + * Have to clean up any possible entries in the + * waiting queue and the QINFIFO. + */ + int target; + char channel; + int lun; + unsigned char tag; + + tag = SCB_LIST_NULL; + target = cmd->target; + lun = cmd->lun; + channel = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A'; + if (scb->hscb->control & TAG_ENB) + { + tag = scb->hscb->tag; + } + aic7xxx_reset_device(p, target, channel, lun, tag); + /* + * Run the done queue, but don't complete the commands; we + * do this once at the end of the loop. + */ + aic7xxx_run_done_queue(p, /*complete*/ FALSE); + } + cmd->result |= (aic7xxx_error(cmd) << 16); + p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS; + aic7xxx_done(p, scb); } - p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS; - /* * Clear interrupt status before checking the output queue again. * This eliminates a race condition whereby a command could @@ -3242,56 +3941,152 @@ * so notification of the command being complete never made it * back up to the kernel. */ - outb(CLRCMDINT, CLRINT + base); - aic7xxx_done(p, scb); + outb(CLRCMDINT, p->base + CLRINT); + interrupts_cleared++; + qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask; + } -#ifdef AIC7XXX_PROC_STATS - /* - * XXX: we should actually know how much actually transferred - * XXX: for each command, but apparently that's too difficult. - */ - actual = aic7xxx_length(cmd, 0); - if (!(cmd->flags & WAS_SENSE) && (actual > 0)) + if (interrupts_cleared == 0) + { + outb(CLRCMDINT, p->base + CLRINT); + } + + aic7xxx_done_cmds_complete(p); + } + + if (intstat & BRKADRINT) + { + int i; + unsigned char errno = inb(p->base + ERROR); + + printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno); + for (i = 0; i < NUMBER(hard_error); i++) + { + if (errno & hard_error[i].errno) { - struct aic7xxx_xferstats *sp; - long *ptr; - int x; + printk(KERN_ERR " %s\n", hard_error[i].errmesg); + } + } + printk("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no, + inb(p->base + ERROR), + (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0)); + aic7xxx_reset_device(p, ALL_TARGETS, ALL_CHANNELS, ALL_LUNS, SCB_LIST_NULL); + aic7xxx_run_done_queue(p, /*complete*/ TRUE); + } + + if (intstat & SEQINT) + { + aic7xxx_handle_seqint(p, intstat); + } + + if (intstat & SCSIINT) + { + aic7xxx_handle_scsiint(p, intstat); + } + + if (p->waiting_scbs.head != NULL) + { + aic7xxx_run_waiting_queues(p); + } + + p->flags &= ~IN_ISR; + restore_flags(flags); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_device_queue_depth + * + * Description: + * Determines the queue depth for a given device. There are two ways + * a queue depth can be obtained for a tagged queueing device. One + * way is the default queue depth which is determined by whether + * AIC7XXX_CMDS_PER_LUN is defined. If it is defined, then it is used + * as the default queue depth. Otherwise, we use either 4 or 8 as the + * default queue depth (dependent on the number of hardware SCBs). + * The other way we determine queue depth is through the use of the + * aic7xxx_tag_info array which is enabled by defining + * AIC7XXX_TAGGED_QUEUEING_BY_DEVICE. This array can be initialized + * with queue depths for individual devices. It also allows tagged + * queueing to be [en|dis]abled for a specific adapter. + *-F*************************************************************************/ +static void +aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device) +{ + int default_depth = 2; + + device->queue_depth = default_depth; +#ifdef AIC7XXX_TAGGED_QUEUEING + if (device->tagged_supported) + { + unsigned short target_mask; + int tag_enabled = TRUE; - sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07]; - sp->xfers++; + target_mask = (1 << (device->id | (device->channel << 3))); + +#ifdef AIC7XXX_CMDS_PER_LUN + default_depth = AIC7XXX_CMDS_PER_LUN; +#else + if (p->scb_data->maxhscbs <= 4) + { + default_depth = 4; /* Not many SCBs to work with. */ + } + else + { + default_depth = 8; + } +#endif + + if (!(p->discenable & target_mask)) + { + printk(KERN_INFO "(scsi%d:%d:%d) Disconnection disabled, unable to " + "enable tagged queueing.\n", + p->host_no, device->id, device->channel); + } + else + { +#ifndef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE + device->queue_depth = default_depth; +#else + if (p->instance > NUMBER(aic7xxx_tag_info)) + { + device->queue_depth = default_depth; + } + else + { + unsigned char tindex; - if (cmd->request.cmd == WRITE) + tindex = device->id | (device->channel << 3); + if (aic7xxx_tag_info[p->instance].tag_commands[tindex] < 0) { - sp->w_total++; - sp->w_total512 += (actual >> 9); - ptr = sp->w_bins; + tag_enabled = FALSE; + device->queue_depth = 2; /* Tagged queueing is disabled. */ } - else + else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0) { - sp->r_total++; - sp->r_total512 += (actual >> 9); - ptr = sp->r_bins; + device->queue_depth = default_depth; } - for (x = 9; x <= 17; x++) + else { - if (actual < (1 << x)) - { - ptr[x - 9]++; - break; - } + device->queue_depth = + aic7xxx_tag_info[p->instance].tag_commands[tindex]; } - if (x > 17) + } +#endif + if ((device->tagged_queue == 0) && tag_enabled) + { + if (aic7xxx_verbose) { - ptr[x - 9]++; + printk(KERN_INFO "(scsi%d:%d:%d) Enabled tagged queuing, " + "queue depth %d.\n", p->host_no, + device->id, device->channel, device->queue_depth); } + device->tagged_queue = 1; + device->current_tag = SCB_LIST_NULL; } -#endif /* AIC7XXX_PROC_STATS */ - - } while (inb(QOUTCNT + base) & p->qcntmask); + } } - aic7xxx_done_cmds_complete(p); - p->flags &= ~IN_ISR; - aic7xxx_run_waiting_queues(p); +#endif } /*+F************************************************************************* @@ -3306,59 +4101,18 @@ * algorithm for determining the queue depth based on the maximum * SCBs for the controller. *-F*************************************************************************/ -static void aic7xxx_select_queue_depth(struct Scsi_Host *host, +static void +aic7xxx_select_queue_depth(struct Scsi_Host *host, Scsi_Device *scsi_devs) { - Scsi_Device *device = scsi_devs; - int tq_depth = 2; + Scsi_Device *device; struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata; -#ifdef AIC7XXX_CMDS_PER_LUN - tq_depth = AIC7XXX_CMDS_PER_LUN; -#else - { - if (p->maxhscbs <= 4) - { - tq_depth = 4; /* Not many SCBs to work with. */ - } - else - { - tq_depth = 8; - } - } -#endif - for (device = scsi_devs; device != NULL; device = device->next) { if (device->host == host) { - device->queue_depth = 2; -#ifdef AIC7XXX_TAGGED_QUEUEING - if (device->tagged_supported) - { - unsigned short target_mask = (1 << device->id) | device->channel; - - if (!(p->discenable & target_mask)) - { - printk(KERN_INFO "scsi%d: Disconnection disabled, unable to enable " - "tagged queueing for target %d, channel %d, LUN %d.\n", - host->host_no, device->id, device->channel, device->lun); - } - else - { - device->queue_depth = tq_depth; - if (device->tagged_queue == 0) - { - printk(KERN_INFO "scsi%d: Enabled tagged queuing for target %d, " - "channel %d, LUN %d, queue depth %d.\n", host->host_no, - device->id, device->channel, device->lun, - device->queue_depth); - device->tagged_queue = 1; - device->current_tag = SCB_LIST_NULL; - } - } - } -#endif + aic7xxx_device_queue_depth(p, device); } } } @@ -3385,7 +4139,7 @@ * The fourth byte's lowest bit seems to be an enabled/disabled * flag (rest of the bits are reserved?). *-F*************************************************************************/ -static aha_type +static aha_chip_type aic7xxx_probe(int slot, int base, aha_status_type *bios) { int i; @@ -3394,7 +4148,7 @@ static struct { int n; unsigned char signature[sizeof(buf)]; - aha_type type; + aha_chip_type type; int bios_disabled; } AIC7xxx[] = { { 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_7771, FALSE }, /* host adapter 274x */ @@ -3433,7 +4187,8 @@ return (AIC7xxx[i].type); } - printk("aic7xxx: Disabled at slot %d, ignored.\n", slot); + printk("aic7xxx: " + "disabled at slot %d, ignored.\n", slot); } } @@ -3460,10 +4215,9 @@ * useful in that it gives us an 800 nsec timer. After a read from the * SEECTL_2840 register the timing flag is cleared and goes high 800 nsec * later. - * *-F*************************************************************************/ static int -read_2840_seeprom(int base, struct seeprom_config *sc) +read_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc) { int i = 0, k = 0; unsigned char temp; @@ -3476,11 +4230,11 @@ struct seeprom_cmd seeprom_read = {3, {1, 1, 0}}; #define CLOCK_PULSE(p) \ - while ((inb(STATUS_2840 + base) & EEPROM_TF) == 0) \ + while ((inb(p->base + STATUS_2840) & EEPROM_TF) == 0) \ { \ ; /* Do nothing */ \ } \ - (void) inb(SEECTL_2840 + base); + (void) inb(p->base + SEECTL_2840); /* * Read the first 32 registers of the seeprom. For the 2840, @@ -3493,8 +4247,8 @@ /* * Send chip select for one clock cycle. */ - outb(CK_2840 | CS_2840, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(CK_2840 | CS_2840, p->base + SEECTL_2840); + CLOCK_PULSE(p); /* * Now we're ready to send the read command followed by the @@ -3503,11 +4257,11 @@ for (i = 0; i < seeprom_read.len; i++) { temp = CS_2840 | seeprom_read.bits[i]; - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); temp = temp ^ CK_2840; - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); } /* * Send the 6 bit address (MSB first, LSB last). @@ -3517,11 +4271,11 @@ temp = k; temp = (temp >> i) & 1; /* Mask out all but lower bit. */ temp = CS_2840 | temp; - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); temp = temp ^ CK_2840; - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); } /* @@ -3533,12 +4287,12 @@ for (i = 0; i <= 16; i++) { temp = CS_2840; - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); temp = temp ^ CK_2840; - seeprom[k] = (seeprom[k] << 1) | (inb(STATUS_2840 + base) & DI_2840); - outb(temp, SEECTL_2840 + base); - CLOCK_PULSE(base); + seeprom[k] = (seeprom[k] << 1) | (inb(p->base + STATUS_2840) & DI_2840); + outb(temp, p->base + SEECTL_2840); + CLOCK_PULSE(p); } /* * The serial EEPROM has a checksum in the last word. Keep a @@ -3554,12 +4308,12 @@ /* * Reset the chip select for the next command cycle. */ - outb(0, SEECTL_2840 + base); - CLOCK_PULSE(base); - outb(CK_2840, SEECTL_2840 + base); - CLOCK_PULSE(base); - outb(0, SEECTL_2840 + base); - CLOCK_PULSE(base); + outb(0, p->base + SEECTL_2840); + CLOCK_PULSE(p); + outb(CK_2840, p->base + SEECTL_2840); + CLOCK_PULSE(p); + outb(0, p->base + SEECTL_2840); + CLOCK_PULSE(p); } #if 0 @@ -3588,6 +4342,53 @@ /*+F************************************************************************* * Function: + * acquire_seeprom + * + * Description: + * Acquires access to the memory port on PCI controllers. + *-F*************************************************************************/ +static inline int +acquire_seeprom(struct aic7xxx_host *p) +{ + int wait; + + /* + * Request access of the memory port. When access is + * granted, SEERDY will go high. We use a 1 second + * timeout which should be near 1 second more than + * is needed. Reason: after the 7870 chip reset, there + * should be no contention. + */ + outb(SEEMS, p->base + SEECTL); + wait = 1000; /* 1000 msec = 1 second */ + while ((wait > 0) && ((inb(p->base + SEECTL) & SEERDY) == 0)) + { + wait--; + udelay(1000); /* 1 msec */ + } + if ((inb(p->base + SEECTL) & SEERDY) == 0) + { + outb(0, p->base + SEECTL); + return (0); + } + return (1); +} + +/*+F************************************************************************* + * Function: + * release_seeprom + * + * Description: + * Releases access to the memory port on PCI controllers. + *-F*************************************************************************/ +static inline void +release_seeprom(struct aic7xxx_host *p) +{ + outb(0, p->base + SEECTL); +} + +/*+F************************************************************************* + * Function: * read_seeprom * * Description: @@ -3625,7 +4426,7 @@ * first). The clock cycling from low to high initiates the next data * bit to be sent from the chip. * - * The 7870 interface to the 93C46 serial EEPROM is through the SEECTL + * The 78xx interface to the 93C46 serial EEPROM is through the SEECTL * register. After successful arbitration for the memory port, the * SEECS bit of the SEECTL register is connected to the chip select. * The SEECK, SEEDO, and SEEDI are connected to the clock, data out, @@ -3635,17 +4436,14 @@ * to this is when we first request access to the memory port. The * SEERDY goes high to signify that access has been granted and, for * this case, has no implied timing. - * *-F*************************************************************************/ static int -read_seeprom(int base, int offset, struct seeprom_config *sc, - seeprom_chip_type chip) +read_seeprom(struct aic7xxx_host *p, int offset, unsigned short *scarray, + unsigned int len, seeprom_chip_type chip) { int i = 0, k; - unsigned long timeout; unsigned char temp; unsigned short checksum = 0; - unsigned short *seeprom = (unsigned short *) sc; struct seeprom_cmd { unsigned char len; unsigned char bits[3]; @@ -3653,43 +4451,33 @@ struct seeprom_cmd seeprom_read = {3, {1, 1, 0}}; #define CLOCK_PULSE(p) \ - while ((inb(SEECTL + base) & SEERDY) == 0) \ + while ((inb(p->base + SEECTL) & SEERDY) == 0) \ { \ ; /* Do nothing */ \ } /* - * Request access of the memory port. When access is - * granted, SEERDY will go high. We use a 1 second - * timeout which should be near 1 second more than - * is needed. Reason: after the 7870 chip reset, there - * should be no contention. + * Request access of the memory port. */ - outb(SEEMS, SEECTL + base); - timeout = jiffies + 100; /* 1 second timeout */ - while ((jiffies < timeout) && ((inb(SEECTL + base) & SEERDY) == 0)) + if (acquire_seeprom(p) == 0) { - ; /* Do nothing! Wait for access to be granted. */ - } - if ((inb(SEECTL + base) & SEERDY) == 0) - { - outb(0, SEECTL + base); return (0); } /* - * Read the first 32 registers of the seeprom. For the 7870, - * the 93C46 SEEPROM is a 1024-bit device with 64 16-bit registers - * but only the first 32 are used by Adaptec BIOS. The loop - * will range from 0 to 31. + * Read 'len' registers of the seeprom. For the 7870, the 93C46 + * SEEPROM is a 1024-bit device with 64 16-bit registers but only + * the first 32 are used by Adaptec BIOS. Some adapters use the + * 93C56 SEEPROM which is a 2048-bit device. The loop will range + * from 0 to 'len' - 1. */ - for (k = 0; k < (sizeof(*sc) / 2); k++) + for (k = 0; k < len; k++) { /* * Send chip select for one clock cycle. */ - outb(SEEMS | SEECK | SEECS, SEECTL + base); - CLOCK_PULSE(base); + outb(SEEMS | SEECK | SEECS, p->base + SEECTL); + CLOCK_PULSE(p); /* * Now we're ready to send the read command followed by the @@ -3698,25 +4486,25 @@ for (i = 0; i < seeprom_read.len; i++) { temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1); - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); temp = temp ^ SEECK; - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); } /* - * Send the 6 bit address (MSB first, LSB last). + * Send the 6 or 8 bit address (MSB first, LSB last). */ for (i = ((int) chip - 1); i >= 0; i--) { temp = k + offset; temp = (temp >> i) & 1; /* Mask out all but lower bit. */ temp = SEEMS | SEECS | (temp << 1); - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); temp = temp ^ SEECK; - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); } /* @@ -3728,56 +4516,57 @@ for (i = 0; i <= 16; i++) { temp = SEEMS | SEECS; - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); temp = temp ^ SEECK; - seeprom[k] = (seeprom[k] << 1) | (inb(SEECTL + base) & SEEDI); - outb(temp, SEECTL + base); - CLOCK_PULSE(base); + scarray[k] = (scarray[k] << 1) | (inb(p->base + SEECTL) & SEEDI); + outb(temp, p->base + SEECTL); + CLOCK_PULSE(p); } /* - * The serial EEPROM has a checksum in the last word. Keep a - * running checksum for all words read except for the last - * word. We'll verify the checksum after all words have been - * read. + * The serial EEPROM should have a checksum in the last word. + * Keep a running checksum for all words read except for the + * last word. We'll verify the checksum after all words have + * been read. */ - if (k < (sizeof(*sc) / 2) - 1) + if (k < (len - 1)) { - checksum = checksum + seeprom[k]; + checksum = checksum + scarray[k]; } /* * Reset the chip select for the next command cycle. */ - outb(SEEMS, SEECTL + base); - CLOCK_PULSE(base); - outb(SEEMS | SEECK, SEECTL + base); - CLOCK_PULSE(base); - outb(SEEMS, SEECTL + base); - CLOCK_PULSE(base); + outb(SEEMS, p->base + SEECTL); + CLOCK_PULSE(p); + outb(SEEMS | SEECK, p->base + SEECTL); + CLOCK_PULSE(p); + outb(SEEMS, p->base + SEECTL); + CLOCK_PULSE(p); } /* * Release access to the memory port and the serial EEPROM. */ - outb(0, SEECTL + base); + release_seeprom(p); #if 0 - printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum); + printk("Computed checksum 0x%x, checksum read 0x%x\n", + checksum, scarray[len - 1]); printk("Serial EEPROM:"); - for (k = 0; k < (sizeof(*sc) / 2); k++) + for (k = 0; k < len; k++) { if (((k % 8) == 0) && (k != 0)) { printk("\n "); } - printk(" 0x%x", seeprom[k]); + printk(" 0x%x", scarray[k]); } printk("\n"); #endif - if (checksum != sc->checksum) + if (checksum != scarray[len - 1]) { return (0); } @@ -3788,563 +4577,452 @@ /*+F************************************************************************* * Function: - * detect_maxscb + * write_brdctl * * Description: - * Detects the maximum number of SCBs for the controller and returns - * the count and a mask in config (config->maxscbs, config->qcntmask). + * Writes a value to the BRDCTL register. *-F*************************************************************************/ -static void -detect_maxscb(struct aic7xxx_host_config *config) +static inline void +write_brdctl(struct aic7xxx_host *p, unsigned char value) { - unsigned char sblkctl_reg; - int base, i; - -#ifdef AIC7XXX_PAGE_ENABLE - config->flags |= PAGE_ENABLED; -#endif - base = config->base; - switch (config->type) - { - case AIC_7770: - case AIC_7771: - case AIC_284x: - /* - * Check for Rev C or E boards. Rev E boards can supposedly have - * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs. - * It's still not clear extactly what is different about the Rev E - * boards, but we think it allows 8 bit entries in the QOUTFIFO to - * support "paging" SCBs (more than 4 commands can be active at once). - * - * The Rev E boards have a read/write autoflush bit in the - * SBLKCTL register, while in the Rev C boards it is read only. - */ - sblkctl_reg = inb(SBLKCTL + base) ^ AUTOFLUSHDIS; - outb(sblkctl_reg, SBLKCTL + base); - if (inb(SBLKCTL + base) == sblkctl_reg) - { - /* - * We detected a Rev E board, we allow paging on this board. - */ - printk(KERN_INFO "aic7xxx: %s Rev E and subsequent.\n", - board_names[config->type]); - outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL + base); - } - else - { - /* Do not allow paging. */ - config->flags &= ~PAGE_ENABLED; - printk(KERN_INFO "aic7xxx: %s Rev C and previous.\n", - board_names[config->type]); - } - break; - - default: - break; - } - - /* - * Walk the SCBs to determine how many there are. - */ - i = 1; - outb(0, SCBPTR + base); - outb(0, SCBARRAY + base); - - while (i < AIC7XXX_MAXSCB) - { - outb(i, SCBPTR + base); - outb(i, SCBARRAY + base); - if (inb(SCBARRAY + base) != i) - break; - outb(0, SCBPTR + base); - if (inb(SCBARRAY + base) != 0) - break; - - outb(i, SCBPTR + base); /* Clear the control byte. */ - outb(0, SCBARRAY + base); - - config->qcntmask |= i; /* Update the count mask. */ - i++; - } - outb(i, SCBPTR + base); /* Ensure we clear the control bytes. */ - outb(0, SCBARRAY + base); - outb(0, SCBPTR + base); - outb(0, SCBARRAY + base); - - config->maxhscbs = i; - config->qcntmask |= i; - if ((config->flags & PAGE_ENABLED) && (config->maxhscbs < AIC7XXX_MAXSCB)) - { - config->maxscbs = AIC7XXX_MAXSCB; - } - else - { - config->flags &= ~PAGE_ENABLED; /* Disable paging if we have 255 SCBs!. */ - config->maxscbs = config->maxhscbs; - } - - printk(KERN_INFO "aic7xxx: Memory check yields %d SCBs", config->maxhscbs); - if (config->flags & PAGE_ENABLED) - printk(", %d page-enabled SCBs.\n", config->maxscbs); - else - printk(", paging not enabled.\n"); + unsigned char brdctl; + brdctl = BRDCS | BRDSTB; + outb(brdctl, p->base + BRDCTL); + brdctl |= value; + outb(brdctl, p->base + BRDCTL); + brdctl &= ~BRDSTB; + outb(brdctl, p->base + BRDCTL); + brdctl &= ~BRDCS; + outb(brdctl, p->base + BRDCTL); } /*+F************************************************************************* * Function: - * aic7xxx_register + * read_brdctl * * Description: - * Register a Adaptec aic7xxx chip SCSI controller with the kernel. + * Reads the BRDCTL register. *-F*************************************************************************/ -static int -aic7xxx_register(Scsi_Host_Template *template, - struct aic7xxx_host_config *config) +static inline unsigned char +read_brdctl(struct aic7xxx_host *p) { - int i; - unsigned char sblkctl, flags = 0; - int max_targets; - int found = 1; - unsigned int sram, base; - unsigned char target_settings; - unsigned char scsi_conf, host_conf; - unsigned short ultraenable = 0; - int have_seeprom = FALSE; - struct Scsi_Host *host; - struct aic7xxx_host *p; - struct seeprom_config sc; - - base = config->base; + outb(BRDRW | BRDCS, p->base + BRDCTL); + return (inb(p->base + BRDCTL)); +} - /* - * Lock out other contenders for our i/o space. - */ - request_region(base, MAXREG - MINREG, "aic7xxx"); +/*+F************************************************************************* + * Function: + * configure_termination + * + * Description: + * Configures the termination settings on PCI adapters that have + * SEEPROMs available. + *-F*************************************************************************/ +static void +configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1, + unsigned short adapter_control, unsigned char max_targ) +{ + unsigned char brdctl_int, brdctl_ext; + int internal50_present; + int internal68_present = 0; + int external_present = 0; + int eprom_present; + int high_on; + int low_on; + int old_verbose; + + if (acquire_seeprom(p)) + { + if (adapter_control & CFAUTOTERM) + { + old_verbose = aic7xxx_verbose; + printk(KERN_INFO "aic7xxx: Warning - detected auto-termination. Please " + "verify driver"); + printk(KERN_INFO " detected settings and use manual termination " + "if necessary."); - switch (config->type) - { - case AIC_7770: - case AIC_7771: - /* - * Use the boot-time option for the interrupt trigger type. If not - * supplied (-1), then we use BIOS settings to determine the interrupt - * trigger type (level or edge) and use this value for pausing and - * unpausing the sequencer. - */ - switch (aic7xxx_irq_trigger) - { - case 0: config->unpause = INTEN; /* Edge */ - break; - case 1: config->unpause = IRQMS | INTEN; /* Level */ - break; - case -1: - default: config->unpause = (inb(HCNTRL + base) & IRQMS) | INTEN; - break; - } - config->pause = config->unpause | PAUSE; + /* Configure auto termination. */ + outb(SEECS | SEEMS, p->base + SEECTL); /* - * For some 274x boards, we must clear the CHIPRST bit and pause - * the sequencer. For some reason, this makes the driver work. - * For 284x boards, we give it a CHIPRST just like the 294x boards. + * First read the status of our cables. Set the rom bank to + * 0 since the bank setting serves as a multiplexor for the + * cable detection logic. BRDDAT5 controls the bank switch. */ - outb(config->pause | CHIPRST, HCNTRL + base); - aic7xxx_delay(1); - if (inb(HCNTRL + base) & CHIPRST) - { - printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n"); - } - outb(config->pause, HCNTRL + base); + write_brdctl(p, 0); /* - * Just to be on the safe side with the 274x, we will re-read the irq - * since there was some issue about resetting the board. + * Now read the state of the internal connectors. The + * bits BRDDAT6 and BRDDAT7 are 0 when cables are present + * set when cables are not present (BRDDAT6 is INT50 and + * BRDDAT7 is INT68). */ - config->irq = inb(INTDEF + base) & 0x0F; - if ((config->type == AIC_7771) && - (inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED) - { - config->bios = AIC_DISABLED; - config->flags |= USE_DEFAULTS; - } - else + brdctl_int = read_brdctl(p); + internal50_present = (brdctl_int & BRDDAT6) ? 0 : 1; + if (max_targ > 8) { - host_conf = inb(HOSTCONF + base); - config->bus_speed = host_conf & DFTHRSH; - config->busrtime = (host_conf << 2) & BOFF; + internal68_present = (brdctl_int & BRDDAT7) ? 0 : 1; } /* - * Setup the FIFO threshold and the bus off time + * Set the rom bank to 1 and determine + * the other signals. */ - outb(config->bus_speed & DFTHRSH, BUSSPD + base); - outb(config->busrtime, BUSTIME + base); + write_brdctl(p, BRDDAT5); /* - * A reminder until this can be detected automatically. + * Now read the state of the external connectors. BRDDAT6 is + * 0 when an external cable is present, and BRDDAT7 (EPROMPS) is + * set when the eprom is present. */ - printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n", - (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis"); - break; - - case AIC_284x: - outb(CHIPRST, HCNTRL + base); - config->unpause = UNPAUSE_284X; - config->pause = REQ_PAUSE; /* DWG would like to be like the rest */ - aic7xxx_delay(1); - outb(config->pause, HCNTRL + base); - - config->parity = AIC_ENABLED; - config->irq = inb(INTDEF + base) & 0x0F; - host_conf = inb(HOSTCONF + base); - - printk(KERN_INFO "aic7xxx: Reading SEEPROM..."); - have_seeprom = read_2840_seeprom(base, &sc); - if (!have_seeprom) - { - printk("aic7xxx: Unable to read SEEPROM.\n"); - } - else + brdctl_ext = read_brdctl(p); + external_present = (brdctl_ext & BRDDAT6) ? 0 : 1; + eprom_present = brdctl_ext & BRDDAT7; + if (aic7xxx_verbose) { - printk("done.\n"); - config->flags |= HAVE_SEEPROM; - if (sc.bios_control & CF284XEXTEND) - config->flags |= EXTENDED_TRANSLATION; - if (!(sc.bios_control & CFBIOSEN)) + if (max_targ > 8) { - /* - * The BIOS is disabled; the values left over in scratch - * RAM are still valid. Do not use defaults as in the - * AIC-7770 case. - */ - config->bios = AIC_DISABLED; + printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Int-68 %s, " + "Ext-68 %s)\n", + internal50_present ? "YES" : "NO", + internal68_present ? "YES" : "NO", + external_present ? "YES" : "NO"); } else { - config->parity = (sc.adapter_control & CFSPARITY) ? - AIC_ENABLED : AIC_DISABLED; - config->low_term = (sc.adapter_control & CF284XSTERM) ? - AIC_ENABLED : AIC_DISABLED; - /* - * XXX - Adaptec *does* make 284x wide controllers, but the - * documents do not say where the high byte termination - * enable bit is located. - */ + printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n", + internal50_present ? "YES" : "NO", + external_present ? "YES" : "NO"); } + printk(KERN_INFO "aic7xxx: eprom %s present, brdctl_int=0x%x, " + "brdctl_ext=0x%x\n", + eprom_present ? "is" : "not", brdctl_int, brdctl_ext); } - host_conf = inb(HOSTCONF + base); - config->bus_speed = host_conf & DFTHRSH; - config->busrtime = (host_conf << 2) & BOFF; - - /* - * Setup the FIFO threshold and the bus off time - */ - outb(config->bus_speed & DFTHRSH, BUSSPD + base); - outb(config->busrtime, BUSTIME + base); - - printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n", - (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis"); - break; - - case AIC_7860: - case AIC_7861: - case AIC_7880: - case AIC_7881: - case AIC_7882: - case AIC_7883: - case AIC_7884: - /* - * Remember if Ultra was enabled in case there is no SEEPROM. - * Fall through to the rest of the AIC_78xx code. - */ - if ((inb(SXFRCTL0 + base) & ULTRAEN) || aic7xxx_enable_ultra) - config->flags |= ULTRA_ENABLED; - - case AIC_7850: - case AIC_7855: - case AIC_7870: - case AIC_7871: - case AIC_7872: - case AIC_7873: - case AIC_7874: /* - * Grab the SCSI ID before chip reset in case there is no SEEPROM. + * Now set the termination based on what we found. BRDDAT6 + * controls wide termination enable. */ - config->scsi_id = inb(SCSIID + base) & OID; - outb(CHIPRST, HCNTRL + base); - config->unpause = UNPAUSE_294X; - config->pause = config->unpause | PAUSE; - aic7xxx_delay(1); - outb(config->pause, HCNTRL + base); - - config->parity = AIC_ENABLED; + high_on = FALSE; + low_on = FALSE; + if ((max_targ > 8) && + ((external_present == 0) || (internal68_present == 0))) + { + high_on = TRUE; + } - printk(KERN_INFO "aic7xxx: Reading SEEPROM..."); - if ((config->type == AIC_7873) || (config->type == AIC_7883)) + if ((internal50_present + internal68_present + external_present) <= 1) { - have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2), - &sc, c56_66); + low_on = TRUE; } - else + + if (internal50_present && internal68_present && external_present) { - have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2), - &sc, c46); + printk(KERN_WARNING "aic7xxx: Illegal cable configuration!!\n" + " Only two connectors on the adapter may be " + "used at a time!\n"); } - if (!have_seeprom) + + if (high_on == TRUE) + write_brdctl(p, BRDDAT6); + else + write_brdctl(p, 0); + + if (low_on == TRUE) + *sxfrctl1 |= STPWEN; + + if (aic7xxx_verbose) { - for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++) - { - if (inb(sram) != 0x00) - break; - } - if (sram == base + TARG_SCRATCH) - { - for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++) - { - if (inb(sram) != 0xFF) - break; - } - } - if ((sram != base + 0x60) && (config->scsi_id != 0)) + if (max_targ > 8) { - config->flags &= ~USE_DEFAULTS; - printk("\naic7xxx: Unable to read SEEPROM; " - "using leftover BIOS values.\n"); + printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n", + low_on ? "ON" : "OFF", + high_on ? "ON" : "OFF"); } else { - printk("\n"); - printk(KERN_INFO "aic7xxx: Unable to read SEEPROM; using default " - "settings.\n"); - config->flags |= USE_DEFAULTS; - config->flags &= ~ULTRA_ENABLED; - config->scsi_id = 7; + printk(KERN_INFO "aic7xxx: Termination %s\n", low_on ? "ON" : "OFF"); } - scsi_conf = ENSPCHK | RESET_SCSI; + } + aic7xxx_verbose = old_verbose; + } + else + { + if (adapter_control & CFSTERM) + { + *sxfrctl1 |= STPWEN; + } + outb(SEEMS | SEECS, p->base + SEECTL); + /* + * Configure high byte termination. + */ + if (adapter_control & CFWSTERM) + { + write_brdctl(p, BRDDAT6); } else { - printk("done.\n"); - config->flags |= HAVE_SEEPROM; - if (!(sc.bios_control & CFBIOSEN)) - { - /* - * The BIOS is disabled; the values left over in scratch - * RAM are still valid. Do not use defaults as in the - * AIC-7770 case. - */ - config->bios = AIC_DISABLED; - scsi_conf = ENSPCHK | RESET_SCSI; - } - else - { - scsi_conf = 0; - if (sc.adapter_control & CFRESETB) - scsi_conf |= RESET_SCSI; - if (sc.adapter_control & CFSPARITY) - scsi_conf |= ENSPCHK; - if (sc.bios_control & CFEXTEND) - config->flags |= EXTENDED_TRANSLATION; - config->scsi_id = (sc.brtime_id & CFSCSIID); - config->parity = (sc.adapter_control & CFSPARITY) ? - AIC_ENABLED : AIC_DISABLED; - config->low_term = (sc.adapter_control & CFSTERM) ? - AIC_ENABLED : AIC_DISABLED; - config->high_term = (sc.adapter_control & CFWSTERM) ? - AIC_ENABLED : AIC_DISABLED; - config->busrtime = ((sc.brtime_id & CFBRTIME) >> 8); - if (((config->type == AIC_7880) || (config->type == AIC_7881) || - (config->type == AIC_7882) || (config->type == AIC_7883) || - (config->type == AIC_7884)) && (sc.adapter_control & CFULTRAEN)) - { - printk(KERN_INFO "aic7xxx: Enabling support for Ultra SCSI " - "speed.\n"); - config->flags |= ULTRA_ENABLED; - } - } + write_brdctl(p, 0); + } + if (aic7xxx_verbose) + { + printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n", + (adapter_control & CFSTERM) ? "ON" : "OFF", + (adapter_control & CFWSTERM) ? "ON" : "OFF"); } + } + release_seeprom(p); + } +} + +/*+F************************************************************************* + * Function: + * detect_maxscb + * + * Description: + * Detects the maximum number of SCBs for the controller and returns + * the count and a mask in p (p->maxscbs, p->qcntmask). + *-F*************************************************************************/ +static void +detect_maxscb(struct aic7xxx_host *p) +{ + int i; + unsigned char max_scbid = 255; - outb(scsi_conf | (config->scsi_id & 0x07), SCSICONF + base); - config->bus_speed = DFTHRSH_100; - outb(config->bus_speed, DSPCISTATUS + base); + /* + * It's possible that we've already done this for multichannel + * adapters. + */ + if (p->scb_data->maxhscbs == 0) + { + /* + * We haven't initialized the SCB settings yet. Walk the SCBs to + * determince how many there are. + */ + outb(0, p->base + FREE_SCBH); - /* - * In case we are a wide card... - */ - outb(config->scsi_id, SCSICONF + base + 1); + for (i = 0; i < AIC7XXX_MAXSCB; i++) + { + outb(i, p->base + SCBPTR); + outb(i, p->base + SCB_CONTROL); + if (inb(p->base + SCB_CONTROL) != i) + break; + outb(0, p->base + SCBPTR); + if (inb(p->base + SCB_CONTROL) != 0) + break; - printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n", - (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis"); - break; + outb(i, p->base + SCBPTR); + outb(0, p->base + SCB_CONTROL); /* Clear the control byte. */ + outb(i + 1, p->base + SCB_NEXT); /* Set the next pointer. */ + outb(SCB_LIST_NULL, p->base + SCB_TAG); /* Make the tag invalid. */ - default: - panic(KERN_WARNING "aic7xxx: (aic7xxx_register) Internal error.\n"); + /* Make the non-tagged targets not busy. */ + outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS); + outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 1); + outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 2); + outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 3); + } + + /* Make sure the last SCB terminates the free list. */ + outb(i - 1, p->base + SCBPTR); + outb(SCB_LIST_NULL, p->base + SCB_NEXT); + + /* Ensure we clear the first (0) SCBs control byte. */ + outb(0, p->base + SCBPTR); + outb(0, p->base + SCB_CONTROL); + + p->scb_data->maxhscbs = i; } - detect_maxscb(config); + if ((p->flags & PAGE_ENABLED) && (p->scb_data->maxhscbs < AIC7XXX_MAXSCB)) + { + /* Determine the number of valid bits in the FIFOs. */ + outb(max_scbid, p->base + QINFIFO); + max_scbid = inb(p->base + QINFIFO); + p->scb_data->maxscbs = MIN(AIC7XXX_MAXSCB, max_scbid + 1); + } + else + { + p->scb_data->maxscbs = p->scb_data->maxhscbs; + } + if (p->scb_data->maxscbs == p->scb_data->maxhscbs) + { + /* + * Disable paging if the QINFIFO doesn't allow more SCBs than + * we have in hardware. + */ + p->flags &= ~PAGE_ENABLED; + } - if (config->chip_type == AIC_777x) + /* + * Set the Queue Full Count. Some cards have more queue space than + * SCBs. + */ + switch (p->chip_class) { - if (config->pause & IRQMS) - { - printk(KERN_INFO "aic7xxx: Using level sensitive interrupts.\n"); - } - else - { - printk(KERN_INFO "aic7xxx: Using edge triggered interrupts.\n"); - } + case AIC_777x: + p->qfullcount = 4; + p->qcntmask = 0x07; + break; + case AIC_785x: + case AIC_786x: + p->qfullcount = 8; + p->qcntmask = 0x0f; + break; + case AIC_787x: + case AIC_788x: + if (p->scb_data->maxhscbs == AIC7XXX_MAXSCB) + { + p->qfullcount = AIC7XXX_MAXSCB; + p->qcntmask = 0xFF; + } + else + { + p->qfullcount = 16; + p->qcntmask = 0x1F; + } + break; } +} + +/*+F************************************************************************* + * Function: + * aic7xxx_register + * + * Description: + * Register a Adaptec aic7xxx chip SCSI controller with the kernel. + *-F*************************************************************************/ +static int +aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p) +{ + int i; + unsigned char sblkctl, flags = 0; + int max_targets; + int found = 1; + char channel_ids[] = {'A', 'B', 'C'}; + unsigned char target_settings; + unsigned char scsi_conf, sxfrctl1; + unsigned short ultraenable = 0; + struct Scsi_Host *host; + + /* + * Lock out other contenders for our i/o space. + */ + request_region(p->base, MAXREG - MINREG, "aic7xxx"); /* * Read the bus type from the SBLKCTL register. Set the FLAGS * register in the sequencer for twin and wide bus cards. */ - sblkctl = inb(SBLKCTL + base); - if (config->flags & PAGE_ENABLED) + sblkctl = inb(p->base + SBLKCTL); + if (p->flags & PAGE_ENABLED) flags = PAGESCBS; switch (sblkctl & SELBUS_MASK) { case SELNARROW: /* narrow/normal bus */ - config->scsi_id = inb(SCSICONF + base) & 0x07; - config->bus_type = AIC_SINGLE; - outb(flags | SINGLE_BUS, FLAGS + base); + p->scsi_id = inb(p->base + SCSICONF) & 0x07; + p->bus_type = AIC_SINGLE; + p->flags &= ~FLAGS_CHANNEL_B_PRIMARY; + if (p->flags & MULTI_CHANNEL) + { + printk(KERN_INFO "aic7xxx: Channel %c, SCSI ID %d, ", + channel_ids[p->chan_num], p->scsi_id); + } + else + { + printk (KERN_INFO "aic7xxx: Single Channel, SCSI ID %d, ", + p->scsi_id); + } + outb(flags | SINGLE_BUS, p->base + SEQ_FLAGS); break; case SELWIDE: /* Wide bus */ - config->scsi_id = inb(SCSICONF + base + 1) & 0x0F; - config->bus_type = AIC_WIDE; - printk("aic7xxx: Enabling wide channel of %s-Wide.\n", - board_names[config->type]); - outb(flags | WIDE_BUS, FLAGS + base); - break; - - case SELBUSB: /* Twin bus */ - config->scsi_id = inb(SCSICONF + base) & 0x07; -#ifdef AIC7XXX_TWIN_SUPPORT - config->scsi_id_b = inb(SCSICONF + base + 1) & 0x07; - config->bus_type = AIC_TWIN; - printk(KERN_INFO "aic7xxx: Enabled channel B of %s-Twin.\n", - board_names[config->type]); - outb(flags | TWIN_BUS, FLAGS + base); -#else - config->bus_type = AIC_SINGLE; - printk(KERN_INFO "aic7xxx: Channel B of %s-Twin will be ignored.\n", - board_names[config->type]); - outb(flags, FLAGS + base); -#endif + p->scsi_id = inb(p->base + SCSICONF + 1) & HWSCSIID; + p->bus_type = AIC_WIDE; + p->flags &= ~FLAGS_CHANNEL_B_PRIMARY; + if (p->flags & MULTI_CHANNEL) + { + printk(KERN_INFO "aic7xxx: Wide Channel %c, SCSI ID %d, ", + channel_ids[p->chan_num], p->scsi_id); + } + else + { + printk (KERN_INFO "aic7xxx: Wide Channel, SCSI ID %d, ", + p->scsi_id); + } + outb(flags | WIDE_BUS, p->base + SEQ_FLAGS); break; - default: - printk(KERN_WARNING "aic7xxx: Unsupported type 0x%x, please " - "mail deang@teleport.com\n", inb(SBLKCTL + base)); - outb(0, FLAGS + base); - return (0); - } - - /* - * For the 294x cards, clearing DIAGLEDEN and DIAGLEDON, will - * take the card out of diagnostic mode and make the host adapter - * LED follow bus activity (will not always be on). - */ - outb(sblkctl & ~(DIAGLEDEN | DIAGLEDON), SBLKCTL + base); - - /* - * The IRQ level in i/o port 4 maps directly onto the real - * IRQ number. If it's ok, register it with the kernel. - * - * NB. the Adaptec documentation says the IRQ number is only - * in the lower four bits; the ECU information shows the - * high bit being used as well. Which is correct? - * - * The PCI cards get their interrupt from PCI BIOS. - */ - if ((config->chip_type == AIC_777x) && ((config->irq < 9) || (config->irq > 15))) - { - printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ level, " - "ignoring.\n"); - return (0); + case SELBUSB: /* Twin bus */ + p->scsi_id = inb(p->base + SCSICONF) & HSCSIID; + p->scsi_id_b = inb(p->base + SCSICONF + 1) & HSCSIID; + p->bus_type = AIC_TWIN; + printk(KERN_INFO "aic7xxx: Twin Channel, A SCSI ID %d, B SCSI ID %d, ", + p->scsi_id, p->scsi_id_b); + outb(flags | TWIN_BUS, p->base + SEQ_FLAGS); + break; + + default: + printk(KERN_WARNING "aic7xxx: Unsupported type 0x%x, please " + "mail deang@teleport.com\n", inb(p->base + SBLKCTL)); + outb(0, p->base + SEQ_FLAGS); + return (0); } /* - * Print out debugging information before re-enabling - * the card - a lot of registers on it can't be read - * when the sequencer is active. - */ - debug_config(config); - - /* - * Register each "host" and fill in the returned Scsi_Host - * structure as best we can. Some of the parameters aren't - * really relevant for bus types beyond ISA, and none of the - * high-level SCSI code looks at it anyway. Why are the fields - * there? Also save the pointer so that we can find the - * information when an IRQ is triggered. + * Detect SCB parameters and initialize the SCB array. */ - host = scsi_register(template, sizeof(struct aic7xxx_host)); - host->can_queue = config->maxscbs; + detect_maxscb(p); + printk("%d/%d SCBs, QFull %d, QMask 0x%x\n", + p->scb_data->maxhscbs, p->scb_data->maxscbs, + p->qfullcount, p->qcntmask); + + host = p->host; + + host->can_queue = p->scb_data->maxscbs; host->cmd_per_lun = 2; + host->sg_tablesize = AIC7XXX_MAX_SG; host->select_queue_depths = aic7xxx_select_queue_depth; - host->this_id = config->scsi_id; - host->io_port = config->base; + host->this_id = p->scsi_id; + host->io_port = p->base; host->n_io_port = 0xFF; - host->base = (unsigned char *)config->mbase; - host->irq = config->irq; - if (config->bus_type == AIC_WIDE) + host->base = (unsigned char *) p->mbase; + host->irq = p->irq; + if (p->bus_type == AIC_WIDE) { host->max_id = 16; } - if (config->bus_type == AIC_TWIN) + if (p->bus_type == AIC_TWIN) { host->max_channel = 1; } - p = (struct aic7xxx_host *) host->hostdata; - p->host = host; - p->host_no = (int)host->host_no; + p->host_no = host->host_no; p->isr_count = 0; - p->base = base; - p->maxscbs = config->maxscbs; - p->maxhscbs = config->maxhscbs; - p->qcntmask = config->qcntmask; - p->mbase = (char *)config->mbase; - p->type = config->type; - p->chip_type = config->chip_type; - p->flags = config->flags; - p->chan_num = config->chan_num; - p->scb_link = &(p->scb_usage); -#if defined(CONFIG_PCI) && defined(AIC7XXX_SHARE_SCBS) - if ((p->chan_num == 0) && ((p->type == AIC_7873) | (p->type == AIC_7883))) - { - shared_3985_scbs = &(p->scb_usage); - p->scb_link = &(p->scb_usage); - } -#endif - p->scb_link->numscbs = 0; - p->bus_type = config->bus_type; - p->seeprom = sc; p->next = NULL; p->completeq.head = NULL; p->completeq.tail = NULL; - scbq_init(&p->scb_link->free_scbs); - scbq_init(&p->page_scbs); + scbq_init(&p->scb_data->free_scbs); scbq_init(&p->waiting_scbs); - scbq_init(&p->assigned_scbs); - p->unpause = config->unpause; - p->pause = config->pause; - - for (i = 0; i <= 15; i++) + for (i = 0; i <= NUMBER(p->device_status); i++) { p->device_status[i].commands_sent = 0; p->device_status[i].flags = 0; + p->device_status[i].active_cmds = 0; p->device_status[i].last_reset = 0; } - if (aic7xxx_boards[config->irq] == NULL) + if (aic7xxx_boards[p->irq] == NULL) { + int result; + int irq_flags = 0; + +#ifdef AIC7XXX_OLD_ISR_TYPE + irg_flags = SA_INTERRUPT; +#endif /* * Warning! This must be done before requesting the irq. It is * possible for some boards to raise an interrupt as soon as @@ -4352,17 +5030,26 @@ * kernel, an interrupt is triggered immediately. Therefore, we * must ensure the board data is correctly set before the request. */ - aic7xxx_boards[config->irq] = host; + aic7xxx_boards[p->irq] = host; /* - * Register IRQ with the kernel. + * Register IRQ with the kernel. Only allow sharing IRQs with + * PCI devices. */ - if (request_irq(config->irq, aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ, - "aic7xxx", NULL)) + if (p->chip_class == AIC_777x) + { + result = (request_irq(p->irq, aic7xxx_isr, irq_flags, "aic7xxx", NULL)); + } + else + { + result = (request_irq(p->irq, aic7xxx_isr, irq_flags | SA_SHIRQ, + "aic7xxx", NULL)); + } + if (result < 0) { printk(KERN_WARNING "aic7xxx: Couldn't register IRQ %d, ignoring.\n", - config->irq); - aic7xxx_boards[config->irq] = NULL; + p->irq); + aic7xxx_boards[p->irq] = NULL; return (0); } } @@ -4373,79 +5060,74 @@ * registered host adapter. Add this host adapter's Scsi_Host * to the beginning of the linked list of hosts at the same IRQ. */ - p->next = aic7xxx_boards[config->irq]; - aic7xxx_boards[config->irq] = host; - } - - /* - * Load the sequencer program, then re-enable the board - - * resetting the AIC-7770 disables it, leaving the lights - * on with nobody home. On the PCI bus you *may* be home, - * but then your mailing address is dynamically assigned - * so no one can find you anyway :-) - */ - printk(KERN_INFO "aic7xxx: Downloading sequencer code..."); - aic7xxx_loadseq(base); - - /* - * Set Fast Mode and Enable the board - */ - outb(FASTMODE, SEQCTL + base); - - if (p->chip_type == AIC_777x) - { - outb(ENABLE, BCTL + base); + p->next = aic7xxx_boards[p->irq]; + aic7xxx_boards[p->irq] = host; } - printk("done.\n"); - /* * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels */ if (p->bus_type == AIC_TWIN) { /* - * Select Channel B. + * The controller is gated to channel B after a chip reset; set + * bus B values first. */ - outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base); - - outb(config->scsi_id_b, SCSIID + base); - scsi_conf = inb(SCSICONF + base + 1) & (ENSPCHK | STIMESEL); - outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base); -#if EXPERIMENTAL_FLAGS - outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base); -#else - outb(ENSELTIMO, SIMODE1 + base); -#endif + outb(p->scsi_id_b, p->base + SCSIID); + scsi_conf = inb(p->base + SCSICONF + 1); + sxfrctl1 = inb(p->base + SXFRCTL1); + outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) | + ENSTIMER | ACTNEGEN, p->base + SXFRCTL1); + outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1); if (p->flags & ULTRA_ENABLED) { - outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base); + outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0); } else { - outb(DFON | SPIOEN, SXFRCTL0 + base); + outb(DFON | SPIOEN, p->base + SXFRCTL0); } - /* - * Select Channel A - */ - outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base); + if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0)) + { + /* Reset SCSI bus B. */ + if (aic7xxx_verbose) + printk(KERN_INFO "aic7xxx: Resetting channel B\n"); + + aic7xxx_reset_current_bus(p); + } + + /* Select channel A */ + outb(SELNARROW, p->base + SBLKCTL); } - outb(config->scsi_id, SCSIID + base); - scsi_conf = inb(SCSICONF + base) & (ENSPCHK | STIMESEL); - outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base); -#if EXPERIMENTAL_FLAGS - outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base); -#else - outb(ENSELTIMO, SIMODE1 + base); -#endif + + outb(p->scsi_id, p->base + SCSIID); + scsi_conf = inb(p->base + SCSICONF); + sxfrctl1 = inb(p->base + SXFRCTL1); + outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) | + ENSTIMER | ACTNEGEN, p->base + SXFRCTL1); + outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1); if (p->flags & ULTRA_ENABLED) { - outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base); + outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0); } else { - outb(DFON | SPIOEN, SXFRCTL0 + base); + outb(DFON | SPIOEN, p->base + SXFRCTL0); + } + + if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0)) + { + /* Reset SCSI bus A. */ + if (aic7xxx_verbose) + printk(KERN_INFO "aic7xxx: Resetting channel A\n"); + + aic7xxx_reset_current_bus(p); + + /* + * Delay for the reset delay. + */ + aic7xxx_delay(AIC7XXX_RESET_DELAY); } /* @@ -4472,67 +5154,47 @@ /* * Grab the disconnection disable table and invert it for our needs */ - if (have_seeprom) + if (p->flags & USE_DEFAULTS) { - p->discenable = 0x0; + printk(KERN_INFO "aic7xxx: Host adapter BIOS disabled. Using default SCSI " + "device parameters.\n"); + p->discenable = 0xFFFF; } else { - if (config->bios == AIC_DISABLED) - { - printk(KERN_INFO "aic7xxx : Host adapter BIOS disabled. Using default SCSI " - "device parameters.\n"); - p->discenable = 0xFFFF; - } - else - { - p->discenable = ~((inb(DISC_DSB + base + 1) << 8) | - inb(DISC_DSB + base)); - } + p->discenable = ~((inb(p->base + DISC_DSB + 1) << 8) | + inb(p->base + DISC_DSB)); } for (i = 0; i < max_targets; i++) { - if (config->flags & USE_DEFAULTS) + if (p->flags & USE_DEFAULTS) { - target_settings = 0; /* 10 MHz */ + target_settings = 0; /* 10 or 20 MHz depending on Ultra enable */ p->needsdtr_copy |= (0x01 << i); p->needwdtr_copy |= (0x01 << i); + if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x)) + ultraenable |= (0x01 << i); } else { - if (have_seeprom) + target_settings = inb(p->base + TARG_SCRATCH + i); + if (target_settings & 0x0F) { - target_settings = ((sc.device_flags[i] & CFXFER) << 4); - if (sc.device_flags[i] & CFSYNCH) - { - p->needsdtr_copy |= (0x01 << i); - } - if (sc.device_flags[i] & CFWIDEB) - { - p->needwdtr_copy |= (0x01 << i); - } - if (sc.device_flags[i] & CFDISC) - { - p->discenable |= (0x01 << i); - } + p->needsdtr_copy |= (0x01 << i); + /* + * Default to asynchronous transfers (0 offset) + */ + target_settings &= 0xF0; } - else + if (target_settings & 0x80) { - target_settings = inb(TARG_SCRATCH + base + i); - if (target_settings & 0x0F) - { - p->needsdtr_copy |= (0x01 << i); - /* - * Default to asynchronous transfers (0 offset) - */ - target_settings &= 0xF0; - } - if (target_settings & 0x80) - { - p->needwdtr_copy |= (0x01 << i); - target_settings &= 0x7F; - } + p->needwdtr_copy |= (0x01 << i); + /* + * Clear the wide flag. When wide negotiation is successful, + * we'll enable it. + */ + target_settings &= 0x7F; } if (p->flags & ULTRA_ENABLED) { @@ -4543,126 +5205,471 @@ case 0x20: ultraenable |= (0x01 << i); break; - case 0x40: + case 0x40: /* treat 10MHz as 10MHz without Ultra enabled */ target_settings &= ~(0x70); break; default: break; } } - } - outb(target_settings, (TARG_SCRATCH + base + i)); + } + outb(target_settings, p->base + TARG_SCRATCH + i); + } + + /* + * If we are not wide, forget WDTR. This makes the driver + * work on some cards that don't leave these fields cleared + * when BIOS is not installed. + */ + if (p->bus_type != AIC_WIDE) + { + p->needwdtr_copy = 0; + } + p->needsdtr = p->needsdtr_copy; + p->needwdtr = p->needwdtr_copy; + p->orderedtag = 0; + outb(ultraenable & 0xFF, p->base + ULTRA_ENB); + outb((ultraenable >> 8) & 0xFF, p->base + ULTRA_ENB + 1); + + /* + * Set the number of available hardware SCBs. + */ + outb(p->scb_data->maxhscbs, p->base + SCBCOUNT); + + /* + * 2s compliment of maximum tag value. + */ + i = p->scb_data->maxscbs; + outb(-i & 0xFF, p->base + COMP_SCBCOUNT); + + /* + * Allocate enough hardware scbs to handle the maximum number of + * concurrent transactions we can have. We have to make sure that + * the allocated memory is contiguous memory. The Linux kmalloc + * routine should only allocate contiguous memory, but note that + * this could be a problem if kmalloc() is changed. + */ + if (p->scb_data->hscbs == NULL) + { + size_t array_size; + unsigned int hscb_physaddr; + + array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb); + p->scb_data->hscbs = kmalloc(array_size, GFP_ATOMIC); + if (p->scb_data->hscbs == NULL) + { + printk("aic7xxx: Unable to allocate hardware SCB array; " + "failing detection.\n"); + release_region(p->base, MAXREG - MINREG); + /* + * Ensure that we only free the IRQ when there is _not_ another + * aic7xxx adapter sharing this IRQ. The adapters are always + * added to the beginning of the list, so we can grab the next + * pointer and place it back in the board array. + */ + if (p->next == NULL) + { + free_irq(p->irq, aic7xxx_isr); + } + aic7xxx_boards[p->irq] = p->next; + return(0); + } + /* At least the control byte of each SCB needs to be 0. */ + memset(p->scb_data->hscbs, 0, array_size); + + /* Tell the sequencer where it can find the hardware SCB array. */ + hscb_physaddr = VIRT_TO_BUS(p->scb_data->hscbs); + outb(hscb_physaddr & 0xFF, p->base + HSCB_ADDR); + outb((hscb_physaddr >> 8) & 0xFF, p->base + HSCB_ADDR + 1); + outb((hscb_physaddr >> 16) & 0xFF, p->base + HSCB_ADDR + 2); + outb((hscb_physaddr >> 24) & 0xFF, p->base + HSCB_ADDR + 3); + } + + /* + * QCount mask to deal with broken aic7850s that sporadically get + * garbage in the upper bits of their QCNT registers. + */ + outb(p->qcntmask, p->base + QCNTMASK); + + /* + * We don't have any waiting selections or disconnected SCBs. + */ + outb(SCB_LIST_NULL, p->base + WAITING_SCBH); + outb(SCB_LIST_NULL, p->base + DISCONNECTED_SCBH); + + /* + * Message out buffer starts empty + */ + outb(0, p->base + MSG_LEN); + + /* + * Load the sequencer program, then re-enable the board - + * resetting the AIC-7770 disables it, leaving the lights + * on with nobody home. On the PCI bus you *may* be home, + * but then your mailing address is dynamically assigned + * so no one can find you anyway :-) + */ + aic7xxx_loadseq(p); + + if (p->chip_class == AIC_777x) + { + outb(ENABLE, p->base + BCTL); /* Enable the boards BUS drivers. */ + } + + /* + * Unpause the sequencer before returning and enable + * interrupts - we shouldn't get any until the first + * command is sent to us by the high-level SCSI code. + */ + unpause_sequencer(p, /* unpause_always */ TRUE); + + return (found); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_chip_reset + * + * Description: + * Perform a chip reset on the aic7xxx SCSI controller. The controller + * is paused upon return. + *-F*************************************************************************/ +static void +aic7xxx_chip_reset(struct aic7xxx_host *p) +{ + unsigned char hcntrl; + int wait; + + /* Retain the IRQ type across the chip reset. */ + hcntrl = (inb(p->base + HCNTRL) & IRQMS) | INTEN; + + /* + * For some 274x boards, we must clear the CHIPRST bit and pause + * the sequencer. For some reason, this makes the driver work. + */ + outb(PAUSE | CHIPRST, p->base + HCNTRL); + + /* + * In the future, we may call this function as a last resort for + * error handling. Let's be nice and not do any unecessary delays. + */ + wait = 1000; /* 1 second (1000 * 1000 usec) */ + while ((wait > 0) && ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0)) + { + udelay(1000); /* 1 msec = 1000 usec */ + wait = wait - 1; + } + + if ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0) + { + printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n"); + } + + outb(hcntrl | PAUSE, p->base + HCNTRL); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_alloc + * + * Description: + * Allocate and initialize a host structure. Returns NULL upon error + * and a pointer to a aic7xxx_host struct upon success. + *-F*************************************************************************/ +static struct aic7xxx_host * +aic7xxx_alloc(Scsi_Host_Template *sht, unsigned int base, unsigned int mbase, + aha_chip_type chip_type, int flags, scb_data_type *scb_data) +{ + struct aic7xxx_host *p = NULL; + struct Scsi_Host *host; + + /* + * Allocate a storage area by registering us with the mid-level + * SCSI layer. + */ + host = scsi_register(sht, sizeof(struct aic7xxx_host)); + + if (host != NULL) + { + p = (struct aic7xxx_host *) host->hostdata; + memset(p, 0, sizeof(struct aic7xxx_host)); + p->host = host; + + if (scb_data != NULL) + { + /* + * We are sharing SCB data areas; use the SCB data pointer + * provided. + */ + p->scb_data = scb_data; + p->flags |= SHARED_SCBDATA; + } + else + { + /* + * We are not sharing SCB data; allocate one. + */ + p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC); + if (p->scb_data != NULL) + { + memset(p->scb_data, 0, sizeof(scb_data_type)); + scbq_init (&p->scb_data->free_scbs); + } + else + { + /* + * For some reason we don't have enough memory. Free the + * allocated memory for the aic7xxx_host struct, and return NULL. + */ + scsi_unregister(host); + p = NULL; + } + } + if (p != NULL) + { + p->host_no = host->host_no; + p->base = base; + p->mbase = mbase; + p->maddr = NULL; + p->flags = flags; + p->chip_type = chip_type; + p->unpause = (inb(p->base + HCNTRL) & IRQMS) | INTEN; + p->pause = p->unpause | PAUSE; + } + } + return (p); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_free + * + * Description: + * Frees and releases all resources associated with an instance of + * the driver (struct aic7xxx_host *). + *-F*************************************************************************/ +static void +aic7xxx_free (struct aic7xxx_host *p) +{ + int i; + + /* + * We should be careful in freeing the scb_data area. For those + * adapters sharing external SCB RAM(398x), there will be only one + * scb_data area allocated. The flag SHARED_SCBDATA indicates if + * one adapter is sharing anothers SCB RAM. + */ + if (!(p->flags & SHARED_SCBDATA)) + { + /* + * Free the allocated hardware SCB space. + */ + if (p->scb_data->hscbs != NULL) + { + kfree(p->scb_data->hscbs); + } + /* + * Free the driver SCBs. These were allocated on an as-need + * basis. + */ + for (i = 0; i < p->scb_data->numscbs; i++) + { + kfree(p->scb_data->scb_array[i]); + } + /* + * Free the hardware SCBs. + */ + if (p->scb_data->hscbs != NULL) + { + kfree(p->scb_data->hscbs); + } + + /* + * Free the SCB data area. + */ + kfree(p->scb_data); + } + /* + * Free the instance of the device structure. + */ + scsi_unregister(p->host); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_load_seeprom + * + * Description: + * Load the seeprom and configure adapter and target settings. + * Returns 1 if the load was successful and 0 otherwise. + *-F*************************************************************************/ +static int +load_seeprom (struct aic7xxx_host *p, unsigned char *sxfrctl1) +{ + int have_seeprom = 0; + int i, max_targets; + unsigned char target_settings, scsi_conf; + unsigned short scarray[128]; + struct seeprom_config *sc = (struct seeprom_config *) scarray; + + if (aic7xxx_verbose) + { + printk(KERN_INFO "aic7xxx: Loading serial EEPROM..."); + } + switch (p->chip_type) + { + case AIC_7770: /* None of these adapters have seeproms. */ + case AIC_7771: + case AIC_7850: + case AIC_7855: + break; + + case AIC_284x: + have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray); + break; + + case AIC_7861: + case AIC_7870: + case AIC_7871: + case AIC_7872: + case AIC_7874: + case AIC_7881: + case AIC_7882: + case AIC_7884: + have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2), + scarray, sizeof(*sc)/2, C46); + break; + + case AIC_7860: /* Motherboard Ultra controllers might have RAID port. */ + case AIC_7880: + have_seeprom = read_seeprom(p, 0, scarray, sizeof(*sc)/2, C46); + if (!have_seeprom) + { + have_seeprom = read_seeprom(p, 0, scarray, sizeof(scarray)/2, C56_66); + } + break; + + case AIC_7873: /* The 3985 adapters use the 93c56 serial EEPROM. */ + case AIC_7883: + have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2), + scarray, sizeof(scarray)/2, C56_66); + break; + + default: + break; } - /* - * If we are not wide, forget WDTR. This makes the driver - * work on some cards that don't leave these fields cleared - * when BIOS is not installed. - */ - if (p->bus_type != AIC_WIDE) + if (!have_seeprom) { - p->needwdtr_copy = 0; + if (aic7xxx_verbose) + { + printk("\naic7xxx: No SEEPROM available; using defaults.\n"); + } + p->flags |= USE_DEFAULTS; } - p->needsdtr = p->needsdtr_copy; - p->needwdtr = p->needwdtr_copy; - p->orderedtag = 0; -#if 0 - printk("NeedSdtr = 0x%x, 0x%x\n", p->needsdtr_copy, p->needsdtr); - printk("NeedWdtr = 0x%x, 0x%x\n", p->needwdtr_copy, p->needwdtr); -#endif - outb(ultraenable & 0xFF, ULTRA_ENB + base); - outb((ultraenable >> 8) & 0xFF, ULTRA_ENB + base + 1); + else + { + if (aic7xxx_verbose) + { + printk("done\n"); + } + p->flags |= HAVE_SEEPROM; - /* - * Set the number of available SCBs. - */ - outb(config->maxhscbs, SCBCOUNT + base); + /* + * Update the settings in sxfrctl1 to match the termination settings. + */ + *sxfrctl1 = 0; - /* - * 2s compliment of maximum tag value. - */ - i = p->maxscbs; - outb(-i & 0xFF, COMP_SCBCOUNT + base); + /* + * First process the settings that are different between the VLB + * and PCI adapter seeproms. + */ + if (p->chip_class == AIC_777x) + { + /* VLB adapter seeproms */ + if (sc->bios_control & CF284XEXTEND) + p->flags |= EXTENDED_TRANSLATION; - /* - * Set the QCNT (queue count) mask to deal with broken aic7850s that - * sporatically get garbage in the upper bits of their QCNT registers. - */ - outb(config->qcntmask, QCNTMASK + base); + if (sc->adapter_control & CF284XSTERM) + *sxfrctl1 |= STPWEN; + /* + * The 284x SEEPROM doesn't have a max targets field. We + * set it to 16 to make sure we take care of the 284x-wide + * adapters. For narrow adapters, going through the extra + * 8 target entries will not cause any harm since they will + * will not be used. + * + * XXX - We should probably break out the bus detection + * from the register function so we can use it here + * to tell us how many targets there really are. + */ + max_targets = 16; + } + else + { + /* PCI adapter seeproms */ + if (sc->bios_control & CFEXTEND) + p->flags |= EXTENDED_TRANSLATION; - /* - * Clear the active flags - no targets are busy. - */ - outb(0, ACTIVE_A + base); - outb(0, ACTIVE_B + base); + if (sc->adapter_control & CFSTERM) + *sxfrctl1 |= STPWEN; - /* - * We don't have any waiting selections or disconnected SCBs. - */ - outb(SCB_LIST_NULL, WAITING_SCBH + base); - outb(SCB_LIST_NULL, DISCONNECTED_SCBH + base); + /* Limit to 16 targets just in case. */ + max_targets = MIN(sc->max_targets & CFMAXTARG, 16); + } - /* - * Message out buffer starts empty - */ - outb(0, MSG_LEN + base); + for (i = 0; i < max_targets; i++) + { + target_settings = (sc->device_flags[i] & CFXFER) << 4; + if (sc->device_flags[i] & CFSYNCH) + target_settings |= SOFS; + if (sc->device_flags[i] & CFWIDEB) + target_settings |= WIDEXFER; + if (sc->device_flags[i] & CFDISC) + p->discenable |= (0x01 << i); + outb(target_settings, p->base + TARG_SCRATCH + i); + } + outb(~(p->discenable & 0xFF), p->base + DISC_DSB); + outb(~((p->discenable >> 8) & 0xFF), p->base + DISC_DSB + 1); - /* - * Reset the SCSI bus. Is this necessary? - * There may be problems for a warm boot without resetting - * the SCSI bus. Either BIOS settings in scratch RAM - * will not get reinitialized, or devices may stay at - * previous negotiated settings (SDTR and WDTR) while - * the driver will think that no negotiations have been - * performed. - * - * Some devices need a long time to "settle" after a SCSI - * bus reset. - */ - if (!aic7xxx_no_reset) - { - printk("aic7xxx: Resetting the SCSI bus..."); - if (p->bus_type == AIC_TWIN) + p->scsi_id = sc->brtime_id & CFSCSIID; + + scsi_conf = (p->scsi_id & 0x7); + if (sc->adapter_control & CFSPARITY) + scsi_conf |= ENSPCHK; + if (sc->adapter_control & CFRESETB) + scsi_conf |= RESET_SCSI; + + if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x)) { /* - * Select Channel B. + * We allow the operator to override ultra enable through + * the boot prompt. */ - outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base); + if (!(sc->adapter_control & CFULTRAEN) && (aic7xxx_enable_ultra == 0)) + { + /* Treat us as a non-ultra card */ + p->flags &= ~ULTRA_ENABLED; + } + } - outb(SCSIRSTO, SCSISEQ + base); - udelay(1000); - outb(0, SCSISEQ + base); - - /* Ensure we don't get a RSTI interrupt from this. */ - outb(CLRSCSIRSTI, CLRSINT1 + base); - outb(CLRSCSIINT, CLRINT + base); + /* Set the host ID */ + outb(scsi_conf, p->base + SCSICONF); + /* In case we are a wide card */ + outb(p->scsi_id, p->base + SCSICONF + 1); - /* - * Select Channel A. + if (p->chip_class != AIC_777x) + { + /* + * Update the settings in sxfrctl1 to match the termination + * settings. */ - outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base); + *sxfrctl1 = 0; + configure_termination(p, sxfrctl1, sc->adapter_control, + (unsigned char) sc->max_targets & CFMAXTARG); } - - outb(SCSIRSTO, SCSISEQ + base); - udelay(1000); - outb(0, SCSISEQ + base); - - /* Ensure we don't get a RSTI interrupt from this. */ - outb(CLRSCSIRSTI, CLRSINT1 + base); - outb(CLRSCSIINT, CLRINT + base); - - aic7xxx_delay(AIC7XXX_RESET_DELAY); - - printk("done.\n"); } - - /* - * Unpause the sequencer before returning and enable - * interrupts - we shouldn't get any until the first - * command is sent to us by the high-level SCSI code. - */ - UNPAUSE_SEQUENCER(p); - return (found); + return (have_seeprom); } /*+F************************************************************************* @@ -4671,17 +5678,24 @@ * * Description: * Try to detect and register an Adaptec 7770 or 7870 SCSI controller. + * + * XXX - This should really be called aic7xxx_probe(). A sequence of + * probe(), attach()/detach(), and init() makes more sense than + * one do-it-all function. This may be useful when (and if) the + * mid-level SCSI code is overhauled. *-F*************************************************************************/ int aic7xxx_detect(Scsi_Host_Template *template) { - int found = 0, slot, base; - unsigned char irq = 0; + int found = 0; + aha_status_type adapter_bios; + aha_chip_class_type chip_class; + aha_chip_type chip_type; + int slot, base; + int chan_num = 0; + unsigned char hcntrl, sxfrctl1, sblkctl, hostconf, irq = 0; int i; - struct aic7xxx_host_config config; - - template->proc_dir = &proc_scsi_aic7xxx; - config.chan_num = 0; + struct aic7xxx_host *p; /* * Since we may allow sharing of IRQs, it is imperative @@ -4695,6 +5709,10 @@ aic7xxx_boards[i] = NULL; } + template->proc_dir = &proc_scsi_aic7xxx; + template->name = aic7xxx_info(NULL); + template->sg_tablesize = AIC7XXX_MAX_SG; + /* * Initialize the spurious count to 0. */ @@ -4716,33 +5734,174 @@ continue; } - config.type = aic7xxx_probe(slot, HID0 + base, &(config.bios)); - if (config.type != AIC_NONE) + chip_type = aic7xxx_probe(slot, base + HID0, &(adapter_bios)); + if (chip_type != AIC_NONE) { + + switch (chip_type) + { + case AIC_7770: + case AIC_7771: + printk("aic7xxx: <%s> at EISA %d\n", + board_names[chip_type], slot); + break; + case AIC_284x: + printk("aic7xxx: <%s> at VLB %d\n", + board_names[chip_type], slot); + break; + default: + break; + } + /* * We found a card, allow 1 spurious interrupt. */ aic7xxx_spurious_count = 1; /* - * We "find" a AIC-7770 if we locate the card - * signature and we can set it up and register - * it with the kernel without incident. + * Pause the card preserving the IRQ type. Allow the operator + * to override the IRQ trigger. */ - config.chip_type = AIC_777x; - config.base = base; - config.mbase = 0; - config.irq = irq; - config.parity = AIC_ENABLED; - config.low_term = AIC_UNKNOWN; - config.high_term = AIC_UNKNOWN; - config.flags = 0; - if (aic7xxx_extended) - config.flags |= EXTENDED_TRANSLATION; - config.bus_speed = DFTHRSH_100; - config.busrtime = BOFF; - found += aic7xxx_register(template, &config); + if (aic7xxx_irq_trigger == 1) + hcntrl = IRQMS; /* Level */ + else if (aic7xxx_irq_trigger == 0) + hcntrl = 0; /* Edge */ + else + hcntrl = inb(base + HCNTRL) & IRQMS; /* Default */ + outb(hcntrl | PAUSE, base + HCNTRL); + + irq = inb(INTDEF + base) & 0x0F; + switch (irq) + { + case 9: + case 10: + case 11: + case 12: + case 14: + case 15: + break; + + default: + printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ " + "level, ignoring.\n"); + irq = 0; + break; + } + + if (irq != 0) + { + p = aic7xxx_alloc(template, base, 0, chip_type, 0, NULL); + if (p == NULL) + { + printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n"); + continue; + } + p->irq = irq & 0x0F; + p->chip_class = AIC_777x; +#ifdef AIC7XXX_PAGE_ENABLE + p->flags |= PAGE_ENABLED; +#endif + p->instance = found; + if (aic7xxx_extended) + { + p->flags |= EXTENDED_TRANSLATION; + } + aic7xxx_chip_reset(p); + + switch (p->chip_type) + { + case AIC_7770: + case AIC_7771: + { + unsigned char biosctrl = inb(p->base + HA_274_BIOSCTRL); + + /* + * Get the primary channel information. Right now we don't + * do anything with this, but someday we will be able to inform + * the mid-level SCSI code which channel is primary. + */ + if (biosctrl & CHANNEL_B_PRIMARY) + { + p->flags |= FLAGS_CHANNEL_B_PRIMARY; + } + + if ((biosctrl & BIOSMODE) == BIOSDISABLED) + { + p->flags |= USE_DEFAULTS; + } + break; + } + case AIC_284x: + if (!load_seeprom(p, &sxfrctl1)) + { + if (aic7xxx_verbose) + printk(KERN_INFO "aic7xxx: SEEPROM not available.\n"); + } + break; + + default: /* Won't get here. */ + break; + } + printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, IRQ %d (%s), ", + (p->flags & USE_DEFAULTS) ? "dis" : "en", p->base, p->irq, + (p->pause & IRQMS) ? "level sensitive" : "edge triggered"); + /* + * Check for Rev C or E boards. Rev E boards can supposedly have + * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs. + * It's still not clear extactly what is different about the Rev E + * boards, but we think it allows 8 bit entries in the QOUTFIFO to + * support "paging" SCBs (more than 4 commands can be active at once). + * + * The Rev E boards have a read/write autoflush bit in the + * SBLKCTL register, while in the Rev C boards it is read only. + */ + sblkctl = inb(p->base + SBLKCTL) ^ AUTOFLUSHDIS; + outb(sblkctl, p->base + SBLKCTL); + if (inb(p->base + SBLKCTL) == sblkctl) + { + /* + * We detected a Rev E board, we allow paging on this board. + */ + printk("Revision >= E\n"); + outb(sblkctl & ~AUTOFLUSHDIS, base + SBLKCTL); + } + else + { + /* Do not allow paging. */ + p->flags &= ~PAGE_ENABLED; + printk("Revision <= C\n"); + } + + if (aic7xxx_verbose) + printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n", + (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis"); + + /* + * Set the FIFO threshold and the bus off time. + */ + hostconf = inb(p->base + HOSTCONF); + outb(hostconf & DFTHRSH, p->base + BUSSPD); + outb((hostconf << 2) & BOFF, p->base + BUSTIME); + + /* + * Try to initialize the card and register it with the kernel. + */ + if (aic7xxx_register(template, p)) + { + /* + * We successfully found a board and registered it. + */ + found = found + 1; + } + else + { + /* + * Something went wrong; release and free all resources. + */ + aic7xxx_free(p); + } + } /* * Disallow spurious interrupts. */ @@ -4758,15 +5917,15 @@ { struct { - unsigned short vendor_id; - unsigned short device_id; - aha_type card_type; - aha_chip_type chip_type; + unsigned short vendor_id; + unsigned short device_id; + aha_chip_type chip_type; + aha_chip_class_type chip_class; } const aic7xxx_pci_devices[] = { {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AIC_7850, AIC_785x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AIC_7855, AIC_785x}, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_785x}, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_785x}, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_786x}, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_786x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AIC_7870, AIC_787x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AIC_7871, AIC_787x}, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AIC_7872, AIC_787x}, @@ -4779,14 +5938,14 @@ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AIC_7884, AIC_788x} }; - int error; + int error, flags; int done = 0; unsigned int iobase, mbase; unsigned short index = 0; unsigned char pci_bus, pci_device_fn; - unsigned int csize_lattime; - unsigned int class_revid; - unsigned int devconfig; + unsigned char ultra_enb = 0; + unsigned int devconfig, class_revid; + scb_data_type *shared_scb_data = NULL; char rev_id[] = {'B', 'C', 'D'}; for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++) @@ -4803,36 +5962,33 @@ } else /* Found an Adaptec PCI device. */ { - config.type = aic7xxx_pci_devices[i].card_type; - config.chip_type = aic7xxx_pci_devices[i].chip_type; - config.chan_num = 0; - config.bios = AIC_ENABLED; /* Assume bios is enabled. */ - config.flags = 0; - config.busrtime = 40; - switch (config.type) + chip_class = aic7xxx_pci_devices[i].chip_class; + chip_type = aic7xxx_pci_devices[i].chip_type; + chan_num = 0; + flags = 0; + switch (aic7xxx_pci_devices[i].chip_type) { case AIC_7850: case AIC_7855: - case AIC_7860: - case AIC_7861: - config.bios = AIC_DISABLED; - config.flags |= USE_DEFAULTS; - config.bus_speed = DFTHRSH_100; + flags |= USE_DEFAULTS; break; case AIC_7872: /* 3940 */ case AIC_7882: /* 3940-Ultra */ - config.chan_num = number_of_3940s & 0x1; /* Has 2 controllers */ + flags |= MULTI_CHANNEL; + chan_num = number_of_3940s & 0x1; /* Has 2 controllers */ number_of_3940s++; break; case AIC_7873: /* 3985 */ case AIC_7883: /* 3985-Ultra */ - config.chan_num = number_of_3985s; /* Has 3 controllers */ + chan_num = number_of_3985s; /* Has 3 controllers */ + flags |= MULTI_CHANNEL; number_of_3985s++; if (number_of_3985s == 3) { number_of_3985s = 0; + shared_scb_data = NULL; } break; @@ -4849,39 +6005,165 @@ PCI_INTERRUPT_LINE, &irq); error += pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &mbase); + error += pcibios_read_config_dword(pci_bus, pci_device_fn, + DEVCONFIG, &devconfig); + error += pcibios_read_config_dword(pci_bus, pci_device_fn, + CLASS_PROGIF_REVID, &class_revid); + + printk("aic7xxx: <%s> at PCI %d\n", + board_names[chip_type], PCI_SLOT(pci_device_fn)); /* - * The first bit of PCI_BASE_ADDRESS_0 is always set, so + * The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so * we mask it off. */ iobase &= PCI_BASE_ADDRESS_IO_MASK; + p = aic7xxx_alloc(template, iobase, mbase, chip_type, flags, + shared_scb_data); + + if (p == NULL) + { + printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n"); + continue; + } + + /* Remember to set the channel number, irq, and chip class. */ + p->chan_num = chan_num; + p->irq = irq; + p->chip_class = chip_class; +#ifdef AIC7XXX_PAGE_ENABLE + p->flags |= PAGE_ENABLED; +#endif + p->instance = found; + /* - * Read the PCI burst size and latency timer. + * Remember how the card was setup in case there is no seeprom. */ - error += pcibios_read_config_dword(pci_bus, pci_device_fn, - CSIZE_LATTIME, &csize_lattime); - printk(KERN_INFO "aic7xxx: BurstLen = %d DWDs, Latency Timer = %d " - "PCLKS\n", (int) (csize_lattime & CACHESIZE), - (csize_lattime >> 8) & 0x000000ff); + p->scsi_id = inb(p->base + SCSIID) & OID; + if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x)) + { + p->flags |= ULTRA_ENABLED; + ultra_enb = inb(p->base + SXFRCTL1) & FAST20; + } + sxfrctl1 = inb(p->base + SXFRCTL1) & STPWEN; - error += pcibios_read_config_dword(pci_bus, pci_device_fn, - CLASS_PROGIF_REVID, &class_revid); - if ((class_revid & DEVREVID) < 3) + aic7xxx_chip_reset(p); + +#ifdef AIC7XXX_USE_EXT_SCBRAM + if (devconfig & RAMPSM) + { + printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM " + "access.\n"); + /* + * XXX - Assume 9 bit SRAM and enable parity checking. + */ + devconfig |= EXTSCBPEN; + + /* + * XXX - Assume fast SRAM and only enable 2 cycle access if we + * are sharing the SRAM across multiple adapters (398x). + */ + if ((devconfig & MPORTMODE) == 0) + { + devconfig |= EXTSCBTIME; + } + devconfig &= ~SCBRAMSEL; + pcibios_write_config_dword(pci_bus, pci_device_fn, + DEVCONFIG, devconfig); + } +#endif + + if ((p->flags & USE_DEFAULTS) == 0) { - printk(KERN_INFO "aic7xxx: %s Rev %c.\n", board_names[config.type], - rev_id[class_revid & DEVREVID]); + load_seeprom(p, &sxfrctl1); } - error += pcibios_read_config_dword(pci_bus, pci_device_fn, - DEVCONFIG, &devconfig); - if (error) + /* + * Take the LED out of diagnostic mode + */ + sblkctl = inb(p->base + SBLKCTL); + outb((sblkctl & ~(DIAGLEDEN | DIAGLEDON)), p->base + SBLKCTL); + + /* + * We don't know where this is set in the SEEPROM or by the + * BIOS, so we default to 100%. + */ + outb(DFTHRSH_100, p->base + DSPCISTATUS); + + if (p->flags & USE_DEFAULTS) { - panic("aic7xxx: (aic7xxx_detect) Error %d reading PCI registers.\n", - error); + int j; + /* + * Default setup; should only be used if the adapter does + * not have a SEEPROM. + */ + /* + * Check the target scratch area to see if someone set us + * up already. We are previously set up if the scratch + * area contains something other than all zeroes and ones. + */ + for (j = TARG_SCRATCH; j < 0x60; j++) + { + if (inb(p->base + j) != 0x00) /* Check for all zeroes. */ + break; + } + if (j == TARG_SCRATCH) + { + for (j = TARG_SCRATCH; j < 0x60; j++) + { + if (inb(p->base + 1) != 0xFF) /* Check for all ones. */ + break; + } + } + if ((j != 0x60) && (p->scsi_id != 0)) + { + p->flags &= ~USE_DEFAULTS; + if (aic7xxx_verbose) + { + printk(KERN_INFO "aic7xxx: Using leftover BIOS values.\n"); + } + } + else + { + if (aic7xxx_verbose) + { + printk(KERN_INFO "aic7xxx: No BIOS found; using default " + "settings.\n"); + } + /* + * Assume only one connector and always turn on + * termination. + */ + sxfrctl1 = STPWEN; + p->scsi_id = 7; + } + outb((p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI, + p->base + SCSICONF); + /* In case we are a wide card. */ + outb(p->scsi_id, p->base + SCSICONF + 1); + if ((ultra_enb == 0) && ((p->flags & USE_DEFAULTS) == 0)) + { + /* + * If there wasn't a BIOS or the board wasn't in this mode + * to begin with, turn off Ultra. + */ + p->flags &= ~ULTRA_ENABLED; + } } - printk(KERN_INFO "aic7xxx: devconfig = 0x%x.\n", devconfig); + /* + * Print some additional information about the adapter. + */ + printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, " + "IO Mem 0x%x, IRQ %d", + (p->flags & USE_DEFAULTS) ? "dis" : "en", + p->base, p->mbase, p->irq); + if ((class_revid & DEVREVID) < 3) + { + printk(", Revision %c", rev_id[class_revid & DEVREVID]); + } + printk("\n"); /* * I don't think we need to bother with allowing @@ -4890,58 +6172,57 @@ */ aic7xxx_spurious_count = 1; - config.base = iobase; - config.mbase = mbase; - config.irq = irq; - config.parity = AIC_ENABLED; - config.low_term = AIC_UNKNOWN; - config.high_term = AIC_UNKNOWN; if (aic7xxx_extended) - config.flags |= EXTENDED_TRANSLATION; -#ifdef AIC7XXX_SHARE_SCBs - if (devconfig & RAMPSM) -#else - if ((devconfig & RAMPSM) && (config.type != AIC_7873) && - (config.type != AIC_7883)) -#endif + p->flags |= EXTENDED_TRANSLATION; + + if (aic7xxx_verbose) + printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n", + (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis"); + + /* + * Put our termination setting into sxfrctl1 now that the + * generic initialization is complete. + */ + sxfrctl1 |= inb(p->base + SXFRCTL1); + outb(sxfrctl1, p->base + SXFRCTL1); + + if (aic7xxx_register(template, p) == 0) + { + aic7xxx_free(p); + } + else { + found = found + 1; + +#ifdef AIC7XXX_USE_EXT_SCBRAM /* - * External SRAM present. The probe will walk the SCBs to see - * how much SRAM we have and set the number of SCBs accordingly. - * We have to turn off SCBRAMSEL to access the external SCB - * SRAM. - * - * It seems that early versions of the aic7870 didn't use these - * bits, hence the hack for the 3940 above. I would guess that - * recent 3940s using later aic7870 or aic7880 chips do actually - * set RAMPSM. + * Set the shared SCB data once we've successfully probed a + * 398x adapter. * - * The documentation isn't clear, but it sounds like the value - * written to devconfig must not have RAMPSM set. The second - * sixteen bits of the register are R/O anyway, so it shouldn't - * affect RAMPSM either way. + * Note that we can only do this if the use of external + * SCB RAM is enabled. */ - printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM " - "access.\n"); - devconfig &= ~(RAMPSM | SCBRAMSEL); - pcibios_write_config_dword(pci_bus, pci_device_fn, - DEVCONFIG, devconfig); + if ((p->chip_type == AIC_7873) || (p->chip_type == AIC_7883)) + { + if (shared_scb_data == NULL) + { + shared_scb_data = p->scb_data; + } + } +#endif } - found += aic7xxx_register(template, &config); + index++; /* * Disable spurious interrupts. */ aic7xxx_spurious_count = 0; - - index++; } /* Found an Adaptec PCI device. */ } } } #endif CONFIG_PCI - template->name = aic7xxx_info(NULL); return (found); } @@ -4957,45 +6238,45 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd, struct aic7xxx_scb *scb) { - unsigned int addr; /* must be 32 bits */ unsigned short mask; + struct aic7xxx_hwscb *hscb; mask = (0x01 << TARGET_INDEX(cmd)); + hscb = scb->hscb; + /* * Setup the control byte if we need negotiation and have not * already requested it. */ -#ifdef AIC7XXX_TAGGED_QUEUEING - if (cmd->device->tagged_queue) + if (p->discenable & mask) { - cmd->tag = scb->tag; - cmd->device->current_tag = scb->tag; - scb->control |= TAG_ENB; - p->device_status[TARGET_INDEX(cmd)].commands_sent++; - if (p->device_status[TARGET_INDEX(cmd)].commands_sent == 200) - { - scb->control |= 0x02; - p->device_status[TARGET_INDEX(cmd)].commands_sent = 0; - } -#if 0 - if (p->orderedtag & mask) + hscb->control |= DISCENB; +#ifdef AIC7XXX_TAGGED_QUEUEING + if (cmd->device->tagged_queue) { - scb->control |= 0x02; - p->orderedtag = p->orderedtag & ~mask; + cmd->tag = hscb->tag; + p->device_status[TARGET_INDEX(cmd)].commands_sent++; + if (p->device_status[TARGET_INDEX(cmd)].commands_sent < 75) + { + hscb->control |= MSG_SIMPLE_Q_TAG; + } + else + { + hscb->control |= MSG_ORDERED_Q_TAG; + p->device_status[TARGET_INDEX(cmd)].commands_sent = 0; + } } -#endif - } -#endif - if (p->discenable & mask) - { - scb->control |= DISCENB; +#endif /* Tagged queueing */ } + if ((p->needwdtr & mask) && !(p->wdtr_pending & mask)) { p->wdtr_pending |= mask; - scb->control |= NEEDWDTR; + hscb->control |= MK_MESSAGE; + scb->flags |= SCB_MSGOUT_WDTR; #if 0 - printk("aic7xxx: Sending WDTR request to target %d.\n", cmd->target); + printk("scsi%d: Sending WDTR request to target %d.\n", + p->host_no, cmd->target); #endif } else @@ -5003,19 +6284,20 @@ if ((p->needsdtr & mask) && !(p->sdtr_pending & mask)) { p->sdtr_pending |= mask; - scb->control |= NEEDSDTR; + hscb->control |= MK_MESSAGE; + scb->flags |= SCB_MSGOUT_SDTR; #if 0 - printk("aic7xxx: Sending SDTR request to target %d.\n", cmd->target); + printk("scsi%d: Sending SDTR request to target %d.\n", + p->host_no, cmd->target); #endif } } - #if 0 printk("aic7xxx: (build_scb) Target %d, cmd(0x%x) size(%u) wdtr(0x%x) " "mask(0x%x).\n", cmd->target, cmd->cmnd[0], cmd->cmd_len, p->needwdtr, mask); #endif - scb->target_channel_lun = ((cmd->target << 4) & 0xF0) | + hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) | ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07); /* @@ -5029,9 +6311,8 @@ * XXX - this relies on the host data being stored in a * little-endian format. */ - addr = VIRT_TO_BUS(cmd->cmnd); - scb->SCSI_cmd_length = cmd->cmd_len; - memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer)); + hscb->SCSI_cmd_length = cmd->cmd_len; + hscb->SCSI_cmd_pointer = VIRT_TO_BUS(cmd->cmnd); if (cmd->use_sg) { @@ -5051,15 +6332,16 @@ scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address); scb->sg_list[i].length = (unsigned int) sg[i].length; } - scb->SG_segment_count = cmd->use_sg; - addr = VIRT_TO_BUS(scb->sg_list); - memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer)); - memcpy(scb->data_pointer, &(scb->sg_list[0].address), - sizeof(scb->data_pointer)); - scb->data_count = scb->sg_list[0].length; + hscb->SG_list_pointer = VIRT_TO_BUS(scb->sg_list); + hscb->SG_segment_count = cmd->use_sg; + scb->sg_count = hscb->SG_segment_count; + + /* Copy the first SG into the data pointer area. */ + hscb->data_pointer = scb->sg_list[0].address; + hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24); #if 0 printk("aic7xxx: (build_scb) SG segs(%d), length(%u), sg[0].length(%d).\n", - cmd->use_sg, aic7xxx_length(cmd, 0), scb->data_count); + cmd->use_sg, aic7xxx_length(cmd, 0), hscb->data_count); #endif } else @@ -5068,28 +6350,23 @@ printk("aic7xxx: (build_scb) Creating scatterlist, addr(0x%lx) length(%d).\n", (unsigned long) cmd->request_buffer, cmd->request_bufflen); #endif - if (cmd->request_bufflen == 0) + if (cmd->request_bufflen) { - /* - * In case the higher level SCSI code ever tries to send a zero - * length command, ensure the SCB indicates no data. The driver - * will interpret a zero length command as a Bus Device Reset. - */ - scb->SG_segment_count = 0; - memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer)); - memset(scb->data_pointer, 0, sizeof(scb->data_pointer)); - scb->data_count = 0; + hscb->SG_segment_count = 1; + scb->sg_count = 1; + scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer); + scb->sg_list[0].length = cmd->request_bufflen; + hscb->SG_list_pointer = VIRT_TO_BUS(&scb->sg_list[0]); + hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24); + hscb->data_pointer = VIRT_TO_BUS(cmd->request_buffer); } else { - scb->SG_segment_count = 1; - scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer); - scb->sg_list[0].length = cmd->request_bufflen; - addr = VIRT_TO_BUS(&scb->sg_list[0]); - memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer)); - scb->data_count = scb->sg_list[0].length; - addr = VIRT_TO_BUS(cmd->request_buffer); - memcpy(scb->data_pointer, &addr, sizeof(scb->data_pointer)); + hscb->SG_segment_count = 0; + scb->sg_count = 0; + hscb->SG_list_pointer = 0; + hscb->data_pointer = 0; + hscb->data_count = SCB_LIST_NULL << 24; } } } @@ -5107,7 +6384,6 @@ long processor_flags; struct aic7xxx_host *p; struct aic7xxx_scb *scb; - u_char curscb, intstat; p = (struct aic7xxx_host *) cmd->host->hostdata; if (p->host != cmd->host) @@ -5139,34 +6415,21 @@ cmd->lun & 0x07); #endif - /* - * This is a critical section, since we don't want the interrupt - * routine mucking with the host data or the card. For this reason - * it is nice to know that this function can only be called in one - * of two ways from scsi.c First, as part of a routine queue command, - * in which case, the irq for our card is disabled before this - * function is called. This doesn't help us if there is more than - * one card using more than one IRQ in our system, therefore, we - * should disable all interrupts on these grounds alone. Second, - * this can be called as part of the scsi_done routine, in which case - * we are in the aic7xxx_isr routine already and interrupts are - * disabled, therefore we should saveflags first, then disable the - * interrupts, do our work, then restore the CPU flags. If it weren't - * for the possibility of more than one card using more than one IRQ - * in our system, we wouldn't have to touch the interrupt flags at all. - */ - save_flags(processor_flags); - cli(); - + if (p->device_status[TARGET_INDEX(cmd)].active_cmds + > cmd->device->queue_depth) + { + printk(KERN_WARNING "(scsi%d:%d:%d) Commands queued exceeds queue depth\n", + p->host_no, cmd->target, cmd->channel); + } scb = aic7xxx_allocate_scb(p); if (scb == NULL) { - panic("aic7xxx: (aic7xxx_free) Couldn't find a free SCB.\n"); + panic("aic7xxx: (aic7xxx_queue) Couldn't find a free SCB.\n"); } else { scb->cmd = cmd; - aic7xxx_position(cmd) = scb->tag; + aic7xxx_position(cmd) = scb->hscb->tag; #if 0 debug_scb(scb); #endif; @@ -5178,14 +6441,14 @@ aic7xxx_buildscb(p, cmd, scb); #if 0 - if (scb != (p->scb_array[scb->position])) + if (scb != (p->scb_data->scb_array[scb->hscb->tag])) { printk("aic7xxx: (queue) Address of SCB by position does not match SCB " "address.\n"); } printk("aic7xxx: (queue) SCB pos(%d) cmdptr(0x%x) state(%d) freescb(0x%x)\n", - scb->position, (unsigned int) scb->cmd, - scb->state, (unsigned int) p->free_scb); + scb->hscb->tag, (unsigned int) scb->cmd, + scb->flags, (unsigned int) p->free_scb); #endif /* @@ -5200,70 +6463,28 @@ cmd->host_scribble = NULL; memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); - if (scb->position != SCB_LIST_NULL) - { - /* We've got a valid slot, yeah! */ - if (p->flags & IN_ISR) - { - scbq_insert_tail(&p->assigned_scbs, scb); - scb->state |= SCB_ASSIGNEDQ; - } - else - { - /* - * Pause the sequencer so we can play with its registers - - * wait for it to acknowledge the pause. - * - * XXX - should the interrupts be left on while doing this? - */ - PAUSE_SEQUENCER(p); - intstat = inb(INTSTAT + p->base); - - /* - * Save the SCB pointer and put our own pointer in - this - * selects one of the four banks of SCB registers. Load - * the SCB, then write its pointer into the queue in FIFO - * and restore the saved SCB pointer. - */ - curscb = inb(SCBPTR + p->base); - outb(scb->position, SCBPTR + p->base); - aic7xxx_putscb(p, scb); - outb(curscb, SCBPTR + p->base); - outb(scb->position, QINFIFO + p->base); - scb->state |= SCB_ACTIVE; + scb->flags |= SCB_ACTIVE | SCB_WAITINGQ; - /* - * Guard against unpausing the sequencer if there is an interrupt - * waiting to happen. - */ - if (!(intstat & (BRKADRINT | SEQINT | SCSIINT))) - { - UNPAUSE_SEQUENCER(p); - } - } - } - else + save_flags(processor_flags); + cli(); + scbq_insert_tail(&p->waiting_scbs, scb); + if ((p->flags & (IN_ISR | IN_TIMEOUT)) == 0) { - scb->state |= SCB_WAITINGQ; - scbq_insert_tail(&p->waiting_scbs, scb); - if (!(p->flags & IN_ISR)) - { - aic7xxx_run_waiting_queues(p); - } + aic7xxx_run_waiting_queues(p); } + restore_flags(processor_flags); #if 0 printk("aic7xxx: (queue) After - cmd(0x%lx) scb->cmd(0x%lx) pos(%d).\n", - (long) cmd, (long) scb->cmd, scb->position); + (long) cmd, (long) scb->cmd, scb->hscb->tag); #endif; - restore_flags(processor_flags); } return (0); } /*+F************************************************************************* * Function: - * aic7xxx_abort_reset + * aic7xxx_bus_device_reset * * Description: * Abort or reset the current SCSI command(s). If the scb has not @@ -5275,204 +6496,257 @@ static int aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd) { - struct aic7xxx_scb *scb; + struct aic7xxx_scb *scb; + struct aic7xxx_hwscb *hscb; unsigned char bus_state; - int base, result = -1; + int result = -1; char channel; - scb = (p->scb_array[aic7xxx_position(cmd)]); - base = p->base; + scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]); + hscb = scb->hscb; + + /* + * Ensure that the card doesn't do anything behind our back. + * Also make sure that we didn't just miss an interrupt that + * could affect this abort/reset. + */ + pause_sequencer(p); + while (inb(p->base + INTSTAT) & INT_PEND); + { + aic7xxx_isr(p->irq, (void *) NULL, (void *) NULL); + pause_sequencer(p); + } + if ((cmd != scb->cmd) || ((scb->flags & SCB_ACTIVE) == 0)) + { + result = SCSI_RESET_NOT_RUNNING; + unpause_sequencer(p, /* unpause_always */ TRUE); + return(result); + } + + + printk(KERN_WARNING "(scsi%d:%d:%d) Abort_reset, scb flags 0x%x, ", + p->host_no, TC_OF_SCB(scb), scb->flags); + bus_state = inb(p->base + LASTPHASE); - channel = scb->target_channel_lun & SELBUSB ? 'B': 'A'; - if ((cmd == scb->cmd) && (scb->state & SCB_IN_PROGRESS)) + switch (bus_state) { + case P_DATAOUT: + printk("Data-Out phase, "); + break; + case P_DATAIN: + printk("Data-In phase, "); + break; + case P_COMMAND: + printk("Command phase, "); + break; + case P_MESGOUT: + printk("Message-Out phase, "); + break; + case P_STATUS: + printk("Status phase, "); + break; + case P_MESGIN: + printk("Message-In phase, "); + break; + default: + /* + * We're not in a valid phase, so assume we're idle. + */ + printk("while idle, LASTPHASE = 0x%x, ", bus_state); + break; + } + printk("SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 0x%x\n", + inb(p->base + SCSISIGI), + inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8), + inb(p->base + SSTAT0), inb(p->base + SSTAT1)); - if (scb->state & SCB_IN_PROGRESS) + channel = hscb->target_channel_lun & SELBUSB ? 'B': 'A'; + /* + * Determine our course of action. + */ + if (scb->flags & SCB_ABORT) + { + /* + * Been down this road before; do a full bus reset. + */ + scb->flags |= SCB_RECOVERY_SCB; + unpause_sequencer(p, /* unpause_always */ TRUE); + result = -1; + } +#if 0 + else if (hscb->control & TAG_ENB) { /* - * Ensure that the card doesn't do anything - * behind our back. + * We could be starving this command; try sending and ordered tag + * command to the target we come from. */ - PAUSE_SEQUENCER(p); + scb->flags |= SCB_SENTORDEREDTAG | SCB_RECOVERY_SCB; + p->orderedtag = p->orderedtag | 0xFF; + result = SCSI_RESET_PENDING; + unpause_sequencer(p, /* unpause_always */ TRUE); + printk(KERN_WARNING "scsi%d: Abort_reset, odered tag queued.\n", + p->host_no); + } +#endif + else + { + unsigned char active_scb_index, saved_scbptr; + struct aic7xxx_scb *active_scb; - printk(KERN_WARNING "aic7xxx: (abort_reset) scb state 0x%x, ", scb->state); - bus_state = inb(LASTPHASE + p->base); + /* + * Send an Abort Message: + * The target that is holding up the bus may not be the same as + * the one that triggered this timeout (different commands have + * different timeout lengths). Our strategy here is to queue an + * abort message to the timed out target if it is disconnected. + * Otherwise, if we have an active target we stuff the message buffer + * with an abort message and assert ATN in the hopes that the target + * will let go of the bus and go to the mesgout phase. If this + * fails, we'll get another timeout a few seconds later which will + * attempt a bus reset. + */ + saved_scbptr = inb(p->base + SCBPTR); + active_scb_index = inb(p->base + SCB_TAG); + active_scb = p->scb_data->scb_array[active_scb_index]; - switch (bus_state) + if (bus_state != P_BUSFREE) + { + if (active_scb_index >= p->scb_data->numscbs) { - case P_DATAOUT: - printk("Data-Out phase, "); - break; - case P_DATAIN: - printk("Data-In phase, "); - break; - case P_COMMAND: - printk("Command phase, "); - break; - case P_MESGOUT: - printk("Message-Out phase, "); - break; - case P_STATUS: - printk("Status phase, "); - break; - case P_MESGIN: - printk("Message-In phase, "); - break; - default: - printk("while idle, LASTPHASE = 0x%x, ", bus_state); - /* - * We're not in a valid phase, so assume we're idle. - */ - bus_state = 0; - break; + /* + * Perform a bus reset. + * + * XXX - We want to queue an abort for the timedout SCB + * instead. + */ + result = -1; + printk(KERN_WARNING "scsi%d: Invalid SCB ID %d is active, " + "SCB flags = 0x%x.\n", p->host_no, scb->hscb->tag, scb->flags); } - printk("SCSISIGI = 0x%x\n", inb(p->base + SCSISIGI)); - - /* - * First, determine if we want to do a bus reset or simply a bus device - * reset. If this is the first time that a transaction has timed out - * and the SCB is not paged out, just schedule a bus device reset. - * Otherwise, we reset the bus and abort all pending I/Os on that bus. - */ - if (!(scb->state & (SCB_ABORTED | SCB_PAGED_OUT))) + else { -#if 0 - if (scb->control & TAG_ENB) - { + /* Send the abort message to the active SCB. */ + outb(1, p->base + MSG_LEN); + if (active_scb->hscb->control & TAG_ENB) + { + outb(MSG_ABORT_TAG, p->base + MSG_OUT); + } + else + { + outb(MSG_ABORT, p->base + MSG_OUT); + } + outb(bus_state | ATNO, p->base + SCSISIGO); + printk(KERN_WARNING "scsi%d: abort message in message buffer\n", + p->host_no); + active_scb->flags |= SCB_ABORT | SCB_RECOVERY_SCB; + if (active_scb != scb) + { /* - * We could be starving this command; try sending and ordered tag - * command to the target we come from. + * XXX - We would like to increment the timeout on scb, but + * access to that routine is denied because it is hidden + * in scsi.c. If we were able to do this, it would give + * scb a new lease on life. */ - scb->state = scb->state | SCB_ABORTED | SCB_SENTORDEREDTAG; - p->orderedtag = p->orderedtag | 0xFF; result = SCSI_RESET_PENDING; - UNPAUSE_SEQUENCER(p); - printk(KERN_WARNING "aic7xxx: (abort_reset) Ordered tag queued.\n"); - } -#endif - unsigned char active_scb, control; - struct aic7xxx_scb *active_scbp; + aic7xxx_error(active_scb->cmd) = DID_RESET; + } + else + { + aic7xxx_error(scb->cmd) = DID_RESET; + result = SCSI_RESET_PENDING; + } + unpause_sequencer(p, /* unpause_always */ TRUE); + } + } + else + { + unsigned char hscb_index, linked_next; + int disconnected; - /* - * Send a Bus Device Reset Message: - * The target we select to send the message to may be entirely - * different than the target pointed to by the scb that timed - * out. If the command is in the QINFIFO or the waiting for - * selection list, its not tying up the bus and isn't responsible - * for the delay so we pick off the active command which should - * be the SCB selected by SCBPTR. If its disconnected or active, - * we device reset the target scbp points to. Although it may - * be that this target is not responsible for the delay, it may - * may also be that we're timing out on a command that just takes - * too much time, so we try the bus device reset there first. - */ - active_scb = inb(SCBPTR + base); - active_scbp = (p->scb_array[inb(SCB_TAG + base)]); - control = inb(SCB_CONTROL + base); + disconnected = FALSE; + hscb_index = aic7xxx_find_scb(p, scb); + if (hscb_index == SCB_LIST_NULL) + { + disconnected = TRUE; + linked_next = (scb->hscb->data_count >> 24) & 0xFF; + } + else + { + outb(hscb_index, p->base + SCBPTR); + if (inb(p->base + SCB_CONTROL) & DISCONNECTED) + { + disconnected = TRUE; + } + linked_next = inb(p->base + SCB_LINKED_NEXT); + } + if (disconnected) + { + /* + * Simply set the ABORT_SCB control bit and preserve the + * linked next pointer. + */ + scb->hscb->control |= ABORT_SCB | MK_MESSAGE; + scb->hscb->data_count &= ~0xFF000000; + scb->hscb->data_count |= linked_next << 24; + if ((p->flags & PAGE_ENABLED) == 0) + { + scb->hscb->control &= ~DISCONNECTED; + } + scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB; + if (hscb_index != SCB_LIST_NULL) + { + unsigned char scb_control; - /* - * Test to see if scbp is disconnected - */ - outb(scb->position, SCBPTR + base); - if (inb(SCB_CONTROL + base) & DISCONNECTED) - { -#ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (abort_scb) scb %d is disconnected; " - "bus device reset message queued.\n", scb->position); -#endif - if (p->flags & PAGE_ENABLED) - { - /* Pull this SCB out of the disconnected list. */ - u_char prev = inb(SCB_PREV + base); - u_char next = inb(SCB_NEXT + base); - if (prev == SCB_LIST_NULL) - { - /* Head of list */ - outb(next, DISCONNECTED_SCBH + base); - } - else - { - outb(prev, SCBPTR + base); - outb(next, SCB_NEXT + base); - if (next != SCB_LIST_NULL) - { - outb(next, SCBPTR + base); - outb(prev, SCB_PREV + base); - } - outb(scb->position, SCBPTR + base); - } - } - scb->state |= (SCB_DEVICE_RESET | SCB_ABORTED); - scb->control = scb->control & DISCENB; - scb->SCSI_cmd_length = 0; - scb->SG_segment_count = 0; - memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer)); - memset(scb->data_pointer, 0, sizeof(scb->data_pointer)); - scb->data_count = 0; - aic7xxx_putscb(p, scb); - aic7xxx_add_waiting_scb(base, scb); - outb(active_scb, SCBPTR + base); - result = SCSI_RESET_PENDING; - UNPAUSE_SEQUENCER(p); - } - else - { - /* - * Is the active SCB really active? - */ - if ((active_scbp->state & SCB_ACTIVE) && bus_state) - { - /* - * Load the message buffer and assert attention. - */ - active_scbp->state |= (SCB_DEVICE_RESET | SCB_ABORTED); - outb(1, MSG_LEN + base); - outb(MSG_BUS_DEVICE_RESET, MSG0 + base); - outb(bus_state | ATNO, SCSISIGO + base); -#ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (abort_scb) asserted ATN - " - "bus device reset in message buffer.\n"); -#endif - if (active_scbp != scb) - { - /* - * XXX - We would like to increment the timeout on scb, but - * access to that routine is denied because it is hidden - * in scsi.c. If we were able to do this, it would give - * scb a new lease on life. - */ - ; - } - aic7xxx_error(scb->cmd) = DID_RESET; - /* - * Restore the active SCB and unpause the sequencer. - */ - outb(active_scb, SCBPTR + base); - if (active_scbp != scb) - { - /* - * The mid-level SCSI code requested us to reset a command - * different from the one that we actually reset. Return - * a "not running" indication and hope that the SCSI code - * will Do the Right Thing (tm). - */ - result = SCSI_RESET_NOT_RUNNING; - } - else - { - result = SCSI_RESET_PENDING; - } - UNPAUSE_SEQUENCER(p); - } - } + scb_control = inb(p->base + SCB_CONTROL); + outb(scb_control | MK_MESSAGE| ABORT_SCB, p->base + SCB_CONTROL); + } + /* + * Actually requeue this SCB in case we can select the + * device before it reconnects. If the transaction we + * want to abort is not tagged, unbusy it first so that + * we don't get held back from sending the command. + */ + if ((scb->hscb->control & TAG_ENB) == 0) + { + unsigned char target; + int lun; + + target = scb->cmd->target; + lun = scb->cmd->lun; + aic7xxx_search_qinfifo(p, target, channel, lun, SCB_LIST_NULL, + 0, /* requeue */ TRUE); + } + printk(KERN_WARNING "(scsi%d:%d:%d) Queueing an Abort SCB.\n", + p->host_no, TC_OF_SCB(scb)); + scbq_insert_head(&p->waiting_scbs, scb); + scb->flags |= SCB_WAITINGQ; + outb(saved_scbptr, p->base + SCBPTR); + if ((p->flags & IN_ISR) == 0) + { + /* + * Processing the waiting queue may unpause us. + */ + aic7xxx_run_waiting_queues(p); + /* + * If we are using AAP, aic7xxx_run_waiting_queues() will not + * unpause us, so ensure we are unpaused. + */ + unpause_sequencer(p, /*unpause_always*/ FALSE); + } + else + { + unpause_sequencer(p, /*unpause_always*/ TRUE); + } + result = SCSI_RESET_PENDING; + } + else + { + scb->flags |= SCB_RECOVERY_SCB; + unpause_sequencer(p, /* unpause_always */ TRUE); + result = -1; } } } - /* Make sure the sequencer is unpaused upon return. */ - if (result == -1) - { - UNPAUSE_SEQUENCER(p); - } return (result); } @@ -5490,16 +6764,48 @@ struct aic7xxx_scb *scb = NULL; struct aic7xxx_host *p; int base, result; + unsigned long processor_flags; p = (struct aic7xxx_host *) cmd->host->hostdata; - scb = (p->scb_array[aic7xxx_position(cmd)]); + scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]); base = p->base; + save_flags(processor_flags); + cli(); + #ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (abort) Aborting scb %d, TCL %d/%d/%d\n", - scb->position, TCL_OF_SCB(scb)); + if (scb != NULL) + { + printk("(scsi%d:%d:%d) Aborting scb %d, flags 0x%x\n", + p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags); + } + else + { + printk("aic7xxx: Abort called with no SCB for cmd.\n"); + } #endif + if (p->flags & IN_TIMEOUT) + { + /* + * We've already started a recovery operation. + */ + if ((scb->flags & SCB_RECOVERY_SCB) == 0) + { + restore_flags(processor_flags); + return (SCSI_ABORT_PENDING); + } + else + { + /* + * This is the second time we've tried to abort the recovery + * SCB. We want the mid-level SCSI code to call the reset + * function to reset the SCSI bus. + */ + restore_flags(processor_flags); + return (SCSI_ABORT_NOT_RUNNING); + } + } if (cmd->serial_number != cmd->serial_number_at_timeout) { result = SCSI_ABORT_NOT_RUNNING; @@ -5508,14 +6814,34 @@ { result = SCSI_ABORT_NOT_RUNNING; } - else if ((scb->cmd != cmd) || (!(scb->state & SCB_IN_PROGRESS))) + else if ((scb->cmd != cmd) || (!(scb->flags & SCB_ACTIVE))) { result = SCSI_ABORT_NOT_RUNNING; } else { - result = SCSI_ABORT_SNOOZE; + /* + * XXX - Check use of IN_TIMEOUT to see if we're Doing the + * Right Thing with it. + */ + p->flags |= IN_TIMEOUT; + result = aic7xxx_bus_device_reset(p, scb->cmd); + switch (result) + { + case SCSI_RESET_NOT_RUNNING: + p->flags &= ~IN_TIMEOUT; + result = SCSI_ABORT_NOT_RUNNING; + break; + case SCSI_RESET_PENDING: + result = SCSI_ABORT_PENDING; + break; + default: + p->flags &= ~IN_TIMEOUT; + result = SCSI_ABORT_SNOOZE; + break; + } } + restore_flags(processor_flags); return (result); } @@ -5535,18 +6861,27 @@ { struct aic7xxx_scb *scb = NULL; struct aic7xxx_host *p; - int base, found, tindex, min_target, max_target, result = -1; + int base, found, tindex, min_target, max_target; + int result = -1; char channel = 'A'; unsigned long processor_flags; p = (struct aic7xxx_host *) cmd->host->hostdata; - scb = (p->scb_array[aic7xxx_position(cmd)]); + scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]); base = p->base; channel = cmd->channel ? 'B': 'A'; tindex = (cmd->channel << 4) | cmd->target; -#ifdef AIC7XXX_DEBUG_ABORT - printk("aic7xxx: (reset) target/channel %d/%d\n", cmd->target, cmd->channel); +#ifdef 0 /* AIC7XXX_DEBUG_ABORT */ + if (scb != NULL) + { + printk("(scsi%d:%d:%d) Reset called, scb %d, flags 0x%x\n", + p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags); + } + else + { + printk("aic7xxx: Reset called with no SCB for cmd.\n"); + } #endif /* @@ -5561,34 +6896,45 @@ if (scb->cmd != cmd) scb = NULL; - if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET)) - && (scb != NULL)) + if (p->flags & IN_TIMEOUT) { /* - * Attempt a bus device reset if commands have completed successfully - * since the last bus device reset, or it has been less than 100ms - * since the last reset. + * We've already started a recovery operation. */ - if ((p->flags & DEVICE_SUCCESS) || - ((jiffies - p->device_status[tindex].last_reset) < HZ/10)) + if ((scb->flags & SCB_RECOVERY_SCB) == 0) { - if (cmd->serial_number != cmd->serial_number_at_timeout) - { - result = SCSI_RESET_NOT_RUNNING; - } - else + restore_flags(processor_flags); + return (SCSI_RESET_PENDING); + } + } + else + { + if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET)) + && (scb != NULL)) + { + /* + * Attempt a bus device reset if commands have completed successfully + * since the last bus device reset, or it has been less than 100ms + * since the last reset. + */ + if ((p->flags & DEVICE_SUCCESS) || + ((jiffies - p->device_status[tindex].last_reset) < HZ/10)) { - if (scb == NULL) + if (cmd->serial_number != cmd->serial_number_at_timeout) + { + result = SCSI_RESET_NOT_RUNNING; + } + else if (scb == NULL) { result = SCSI_RESET_NOT_RUNNING; } else if (flags & SCSI_RESET_ASYNCHRONOUS) { - if (scb->state & SCB_ABORTED) + if (scb->flags & SCB_ABORTED) { result = SCSI_RESET_PENDING; } - else if (!(scb->state & SCB_IN_PROGRESS)) + else if (!(scb->flags & SCB_ACTIVE)) { result = SCSI_RESET_NOT_RUNNING; } @@ -5599,20 +6945,23 @@ if ((flags & SCSI_RESET_SYNCHRONOUS) && (p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING)) { - scb->state |= SCB_ABORTED; + scb->flags |= SCB_ABORTED; result = SCSI_RESET_PENDING; } else { + p->flags |= IN_TIMEOUT; result = aic7xxx_bus_device_reset(p, cmd); if (result == 0) + { + p->flags &= ~IN_TIMEOUT; result = SCSI_RESET_PENDING; + } } - } + } } } } - if (result == -1) { /* @@ -5625,11 +6974,11 @@ { result = SCSI_RESET_NOT_RUNNING; } - else if (!(scb->state & SCB_IN_PROGRESS)) + else if (!(scb->flags & SCB_ACTIVE)) { result = SCSI_RESET_NOT_RUNNING; } - else if ((scb->state & SCB_ABORTED) && + else if ((scb->flags & SCB_ABORTED) && (!(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING))) { result = SCSI_RESET_PENDING; @@ -5641,8 +6990,9 @@ /* * The reset channel function assumes that the sequencer is paused. */ - PAUSE_SEQUENCER(p); + pause_sequencer(p); found = aic7xxx_reset_channel(p, channel, TRUE); + p->flags = p->flags & ~IN_TIMEOUT; /* * If this is a synchronous reset and there is no SCB for this @@ -5688,8 +7038,10 @@ } result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET; + p->flags &= ~IN_TIMEOUT; } } + aic7xxx_run_waiting_queues(p); restore_flags(processor_flags); return (result); } diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx.h linux/drivers/scsi/aic7xxx.h --- v2.0.30/linux/drivers/scsi/aic7xxx.h Sat Aug 17 11:19:27 1996 +++ linux/drivers/scsi/aic7xxx.h Thu Jul 31 12:37:17 1997 @@ -40,13 +40,13 @@ aic7xxx_info, \ NULL, \ aic7xxx_queue, \ - aic7xxx_abort, \ + NULL, \ aic7xxx_reset, \ NULL, \ aic7xxx_biosparam, \ -1, /* max simultaneous cmds */\ -1, /* scsi id of host adapter */\ - SG_ALL, /* max scatter-gather cmds */\ + 0, /* max scatter-gather cmds */\ 2, /* cmds per lun (linked cmds) */\ 0, /* number of 7xxx's present */\ 0, /* no memory DMA restrictions */\ @@ -57,7 +57,6 @@ extern int aic7xxx_biosparam(Disk *, kdev_t, int[]); extern int aic7xxx_detect(Scsi_Host_Template *); extern int aic7xxx_command(Scsi_Cmnd *); -extern int aic7xxx_abort(Scsi_Cmnd *); extern int aic7xxx_reset(Scsi_Cmnd *, unsigned int); extern const char *aic7xxx_info(struct Scsi_Host *); diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx.seq linux/drivers/scsi/aic7xxx.seq --- v2.0.30/linux/drivers/scsi/aic7xxx.seq Sun Oct 13 03:42:28 1996 +++ linux/drivers/scsi/aic7xxx.seq Wed Dec 31 16:00:00 1969 @@ -1,1127 +0,0 @@ -/*+M************************************************************************* - * Adaptec AIC7xxx device driver for Linux and FreeBSD. - * - * Copyright (c) 1994 John Aycock - * The University of Calgary Department of Computer Science. - * - *Modifications/enhancements: - * Copyright (c) 1994, 1995, 1996 Justin Gibbs. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * FreeBSD, Twin, Wide, 2 command per target support, tagged queuing and other - * optimizations provided by Justin T. Gibbs (gibbs@FreeBSD.org) - * - * This version corresponds to version 1.42 of FreeBSDs aic7xxx.seq. - * - *-M*************************************************************************/ - -VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 4.0 1996/10/13 08:23:42 deang Exp $" - -#ifdef linux -#include "aic7xxx_reg.h" -#else -#if defined(__NetBSD__) -#include "../../../../dev/ic/aic7xxxreg.h" -#elif defined(__FreeBSD__) -#include "../../dev/aic7xxx/aic7xxx_reg.h" -#endif -#endif - -/* - * We can't just use ACCUM in the sequencer code because it - * must be treated specially by the assembler, and it currently - * looks for the symbol 'A'. This is the only register defined in - * the assembler's symbol space. - */ -A = ACCUM - -/* After starting the selection hardware, we check for reconnecting targets - * as well as for our selection to complete just in case the reselection wins - * bus arbitration. The problem with this is that we must keep track of the - * SCB that we've already pulled from the QINFIFO and started the selection - * on just in case the reselection wins so that we can retry the selection at - * a later time. This problem cannot be resolved by holding a single entry - * in scratch ram since a reconnecting target can request sense and this will - * create yet another SCB waiting for selection. The solution used here is to - * use byte 27 of the SCB as a pseudo-next pointer and to thread a list - * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB offsets, - * SCB_LIST_NULL is 0xff which is out of range. The kernel driver must - * add an entry to this list every time a request sense occurs. The sequencer - * will automatically consume the entries. - */ - -/* - * We assume that the kernel driver may reset us at any time, even in the - * middle of a DMA, so clear DFCNTRL too. - */ -reset: - clr DFCNTRL - clr SCSISIGO /* De-assert BSY */ -/* - * We jump to start after every bus free. - */ -start: - and FLAGS,0x0f /* clear target specific flags */ - mvi SCSISEQ,ENRSELI /* Always allow reselection */ - clr SCSIRATE /* - * We don't know the target we will - * connect to, so default to narrow - * transfers to avoid parity problems. - */ -poll_for_work: - /* - * Are we a twin channel device? - * For fairness, we check the other bus first, - * since we just finished a transaction on the - * current channel. - */ - test FLAGS,TWIN_BUS jz start2 - xor SBLKCTL,SELBUSB /* Toggle to the other bus */ - test SSTAT0,SELDI jnz reselect - xor SBLKCTL,SELBUSB /* Toggle to the original bus */ -start2: - test SSTAT0,SELDI jnz reselect - cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting - mov A, QCNTMASK - test QINCNT,A jz poll_for_work - -/* - * We have at least one queued SCB now and we don't have any - * SCBs in the list of SCBs awaiting selection. Set the SCB - * pointer from the FIFO so we see the right bank of SCB - * registers. - */ - mov SCBPTR,QINFIFO - -/* - * See if there is not already an active SCB for this target. This code - * locks out on a per target basis instead of target/lun. Although this - * is not ideal for devices that have multiple luns active at the same - * time, it is faster than looping through all SCB's looking for active - * commands. It may be beneficial to make findscb a more general procedure - * to see if the added cost of the search is negligible. This code also - * assumes that the kernel driver will clear the active flags on board - * initialization, board reset, and a target SELTO. Tagged commands - * don't set the active bits since you can queue more than one command - * at a time. We do, however, look to see if there are any non-tagged - * I/Os in progress, and requeue the command if there are. Tagged and - * non-tagged commands cannot be mixed to a single target. - */ - -test_busy: - mov FUNCTION1,SCB_TCL - mov A,FUNCTION1 - test SCB_TCL,0x88 jz test_a /* Id < 8 && A channel */ - - test ACTIVE_B,A jnz requeue - test SCB_CONTROL,TAG_ENB jnz start_scb - /* Mark the current target as busy */ - or ACTIVE_B,A - jmp start_scb - -/* Place the currently active SCB back on the queue for later processing */ -requeue: - mov QINFIFO, SCBPTR - jmp poll_for_work - -/* - * Pull the first entry off of the waiting for selection list - * We don't have to "test_busy" because only transactions that - * have passed that test can be in the waiting_scb list. - */ -start_waiting: - mov SCBPTR,WAITING_SCBH - jmp start_scb2 - -test_a: - test ACTIVE_A,A jnz requeue - test SCB_CONTROL,TAG_ENB jnz start_scb - /* Mark the current target as busy */ - or ACTIVE_A,A - -start_scb: - mov SCB_NEXT,WAITING_SCBH - mov WAITING_SCBH, SCBPTR -start_scb2: - and SINDEX,0xf7,SBLKCTL /* Clear the channel select bit */ - and A,0x08,SCB_TCL /* Get new channel bit */ - or SINDEX,A - mov SBLKCTL,SINDEX /* select channel */ - mov SCB_TCL call initialize_scsiid - -/* - * Enable selection phase as an initiator, and do automatic ATN - * after the selection. We do this now so that we can overlap the - * rest of our work to set up this target with the arbitration and - * selection bus phases. - */ -start_selection: - mvi SCSISEQ,0x58 /* ENSELO|ENAUTOATNO|ENRSELI */ - -/* - * As soon as we get a successful selection, the target should go - * into the message out phase since we have ATN asserted. Prepare - * the message to send. - * - * Messages are stored in scratch RAM starting with a length byte - * followed by the message itself. - */ - test SCB_CMDLEN,0xff jnz mk_identify /* 0 Length Command? */ - -/* - * The kernel has sent us an SCB with no command attached. This implies - * that the kernel wants to send a message of some sort to this target, - * so we interrupt the driver, allow it to fill the message buffer, and - * then go back into the arbitration loop - */ - mvi INTSTAT,AWAITING_MSG - jmp wait_for_selection - -mk_identify: - and A,DISCENB,SCB_CONTROL /* mask off disconnect privledge */ - - and MSG0,0x7,SCB_TCL /* lun */ - or MSG0,A /* or in disconnect privledge */ - or MSG0,MSG_IDENTIFY - mvi MSG_LEN, 1 - - test SCB_CONTROL,0xb0 jz !message /* WDTR, SDTR or TAG?? */ -/* - * Send a tag message if TAG_ENB is set in the SCB control block. - * Use SCB_TAG (the position in the kernel's SCB array) as the tag value. - */ - -mk_tag: - mvi DINDEX, MSG1 - test SCB_CONTROL,TAG_ENB jz mk_tag_done - and DINDIR,0x23,SCB_CONTROL - mov DINDIR,SCB_TAG - - add MSG_LEN,COMP_MSG0,DINDEX /* update message length */ - -mk_tag_done: - - test SCB_CONTROL,0x90 jz !message /* NEEDWDTR|NEEDSDTR */ - mov DINDEX call mk_dtr /* build DTR message if needed */ - -!message: -wait_for_selection: - test SSTAT0,SELDO jnz select - test SSTAT0,SELDI jz wait_for_selection - -/* - * Reselection has been initiated by a target. Make a note that we've been - * reselected, but haven't seen an IDENTIFY message from the target - * yet. - */ -reselect: - clr MSG_LEN /* Don't have anything in the mesg buffer */ - mov SELID call initialize_scsiid - or FLAGS,RESELECTED - jmp select2 - -/* - * After the selection, remove this SCB from the "waiting for selection" - * list. This is achieved by simply moving our "next" pointer into - * WAITING_SCBH. Our next pointer will be set to null the next time this - * SCB is used, so don't bother with it now. - */ -select: - mov WAITING_SCBH,SCB_NEXT - or FLAGS,SELECTED -select2: -/* - * Set CLRCHN here before the target has entered a data transfer mode - - * with synchronous SCSI, if you do it later, you blow away some - * data in the SCSI FIFO that the target has already sent to you. - */ - or SXFRCTL0,CLRCHN -/* - * Initialize SCSIRATE with the appropriate value for this target. - */ - call ndx_dtr - mov SCSIRATE,SINDIR - -/* - * Initialize Ultra mode setting. - */ - mov FUNCTION1,SCSIID - mov A,FUNCTION1 - and SINDEX,0xdf,SXFRCTL0 /* default to Ultra disabled */ - test SCSIID, 0x80 jnz ultra_b /* Target ID > 7 */ - test SBLKCTL, SELBUSB jnz ultra_b /* Second channel device */ - test ULTRA_ENB,A jz set_sxfrctl0 - or SINDEX, ULTRAEN jmp set_sxfrctl0 -ultra_b: - test ULTRA_ENB_B,A jz set_sxfrctl0 - or SINDEX, ULTRAEN - -set_sxfrctl0: - mov SXFRCTL0,SINDEX - - mvi SCSISEQ,ENAUTOATNP /* - * ATN on parity errors - * for "in" phases - */ - mvi CLRSINT1,CLRBUSFREE - mvi CLRSINT0,0x60 /* CLRSELDI|CLRSELDO */ -/* - * Main loop for information transfer phases. If BSY is false, then - * we have a bus free condition, expected or not. Otherwise, wait - * for the target to assert REQ before checking MSG, C/D and I/O - * for the bus phase. - * - */ -ITloop: - test SSTAT1,BUSFREE jnz p_busfree - test SSTAT1,REQINIT jz ITloop - - and A,PHASE_MASK,SCSISIGI - mov LASTPHASE,A - mov SCSISIGO,A - - cmp ALLZEROS,A je p_dataout - cmp A,P_DATAIN je p_datain - cmp A,P_COMMAND je p_command - cmp A,P_MESGOUT je p_mesgout - cmp A,P_STATUS je p_status - cmp A,P_MESGIN je p_mesgin - - mvi INTSTAT,BAD_PHASE /* unknown phase - signal driver */ - jmp ITloop /* Try reading the bus again. */ - -p_dataout: - mvi DMAPARAMS,0x7d /* - * WIDEODD|SCSIEN|SDMAEN|HDMAEN| - * DIRECTION|FIFORESET - */ - jmp data_phase_init - -/* - * If we re-enter the data phase after going through another phase, the - * STCNT may have been cleared, so restore it from the residual field. - */ -data_phase_reinit: - mov STCNT0,SCB_RESID_DCNT0 - mov STCNT1,SCB_RESID_DCNT1 - mov STCNT2,SCB_RESID_DCNT2 - jmp data_phase_loop - -p_datain: - mvi DMAPARAMS,0x79 /* - * WIDEODD|SCSIEN|SDMAEN|HDMAEN| - * !DIRECTION|FIFORESET - */ -data_phase_init: - call assert - - test FLAGS, DPHASE jnz data_phase_reinit - call sg_scb2ram - or FLAGS, DPHASE /* We have seen a data phase */ - -data_phase_loop: -/* Guard against overruns */ - test SG_COUNT, 0xff jnz data_phase_inbounds -/* - * Turn on 'Bit Bucket' mode, set the transfer count to - * 16meg and let the target run until it changes phase. - * When the transfer completes, notify the host that we - * had an overrun. - */ - or SXFRCTL1,BITBUCKET - mvi STCNT0,0xff - mvi STCNT1,0xff - mvi STCNT2,0xff - -data_phase_inbounds: -/* If we are the last SG block, don't set wideodd. */ - cmp SG_COUNT,0x01 jne data_phase_wideodd - and DMAPARAMS, 0xbf /* Turn off WIDEODD */ -data_phase_wideodd: - mov DMAPARAMS call dma - -/* Go tell the host about any overruns */ - test SXFRCTL1,BITBUCKET jnz data_phase_overrun - -/* Exit if we had an underrun */ - test SSTAT0,SDONE jz data_phase_finish /* underrun STCNT != 0 */ - -/* - * Advance the scatter-gather pointers if needed - */ -sg_advance: - dec SG_COUNT /* one less segment to go */ - - test SG_COUNT, 0xff jz data_phase_finish /* Are we done? */ - - clr A /* add sizeof(struct scatter) */ - add SG_NEXT0,SG_SIZEOF,SG_NEXT0 - adc SG_NEXT1,A,SG_NEXT1 - -/* - * Load a struct scatter and set up the data address and length. - * If the working value of the SG count is nonzero, then - * we need to load a new set of values. - * - * This, like all DMA's, assumes a little-endian host data storage. - */ -sg_load: - clr HCNT2 - clr HCNT1 - mvi HCNT0,SG_SIZEOF - - mov HADDR0,SG_NEXT0 - mov HADDR1,SG_NEXT1 - mov HADDR2,SG_NEXT2 - mov HADDR3,SG_NEXT3 - - or DFCNTRL,0xd /* HDMAEN|DIRECTION|FIFORESET */ - -/* - * Wait for DMA from host memory to data FIFO to complete, then disable - * DMA and wait for it to acknowledge that it's off. - */ -dma_finish: - test DFSTATUS,HDONE jz dma_finish - /* Turn off DMA preserving WIDEODD */ - and DFCNTRL,WIDEODD -dma_finish2: - test DFCNTRL,HDMAENACK jnz dma_finish2 - -/* - * Copy data from FIFO into SCB data pointer and data count. In - * both FreeBSD and Linux, the scatter list entry is 8 bytes. - * - * struct ahc_dma_seg { - * physaddr addr; four bytes, little-endian order - * long len; four bytes, little endian order - * }; - */ - - mov HADDR0,DFDAT - mov HADDR1,DFDAT - mov HADDR2,DFDAT - mov HADDR3,DFDAT - mov HCNT0,DFDAT - mov HCNT1,DFDAT - mov HCNT2,DFDAT - -/* Load STCNT as well. It is a mirror of HCNT */ - mov STCNT0,HCNT0 - mov STCNT1,HCNT1 - mov STCNT2,HCNT2 - test SSTAT1,PHASEMIS jz data_phase_loop - -data_phase_finish: -/* - * After a DMA finishes, save the SG and STCNT residuals back into the SCB - * We use STCNT instead of HCNT, since it's a reflection of how many bytes - * were transferred on the SCSI (as opposed to the host) bus. - */ - mov SCB_RESID_DCNT0,STCNT0 - mov SCB_RESID_DCNT1,STCNT1 - mov SCB_RESID_DCNT2,STCNT2 - mov SCB_RESID_SGCNT, SG_COUNT - jmp ITloop - -data_phase_overrun: -/* - * Turn off BITBUCKET mode and notify the host - */ - and SXFRCTL1,0x7f /* ~BITBUCKET */ - mvi INTSTAT,DATA_OVERRUN - jmp ITloop - -/* - * Command phase. Set up the DMA registers and let 'er rip. - */ -p_command: - call assert - -/* - * Load HADDR and HCNT. - */ - mov HADDR0, SCB_CMDPTR0 - mov HADDR1, SCB_CMDPTR1 - mov HADDR2, SCB_CMDPTR2 - mov HADDR3, SCB_CMDPTR3 - mov HCNT0, SCB_CMDLEN - clr HCNT1 - clr HCNT2 - - mov STCNT0, HCNT0 - mov STCNT1, HCNT1 - mov STCNT2, HCNT2 - - mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN| - # DIRECTION|FIFORESET - jmp ITloop - -/* - * Status phase. Wait for the data byte to appear, then read it - * and store it into the SCB. - */ -p_status: - mvi SCB_TARGET_STATUS call inb_first - jmp mesgin_done - -/* - * Message out phase. If there is not an active message, but the target - * took us into this phase anyway, build a no-op message and send it. - */ -p_mesgout: - test MSG_LEN, 0xff jnz p_mesgout_start - mvi MSG_NOP call mk_mesg /* build NOP message */ - -p_mesgout_start: -/* - * Set up automatic PIO transfer from MSG0. Bit 3 in - * SXFRCTL0 (SPIOEN) is already on. - */ - mvi SINDEX,MSG0 - mov DINDEX,MSG_LEN - -/* - * When target asks for a byte, drop ATN if it's the last one in - * the message. Otherwise, keep going until the message is exhausted. - * - * Keep an eye out for a phase change, in case the target issues - * a MESSAGE REJECT. - */ -p_mesgout_loop: - test SSTAT1,PHASEMIS jnz p_mesgout_phasemis - test SSTAT0,SPIORDY jz p_mesgout_loop - test SSTAT1,PHASEMIS jnz p_mesgout_phasemis - cmp DINDEX,1 jne p_mesgout_outb /* last byte? */ - mvi CLRSINT1,CLRATNO /* drop ATN */ -p_mesgout_outb: - dec DINDEX - or CLRSINT0, CLRSPIORDY - mov SCSIDATL,SINDIR - -p_mesgout4: - test DINDEX,0xff jnz p_mesgout_loop - -/* - * If the next bus phase after ATN drops is a message out, it means - * that the target is requesting that the last message(s) be resent. - */ -p_mesgout_snoop: - test SSTAT1,BUSFREE jnz p_mesgout_done - test SSTAT1,REQINIT jz p_mesgout_snoop - - test SSTAT1,PHASEMIS jnz p_mesgout_done - - or SCSISIGO,ATNO /* turn on ATNO */ - - jmp ITloop - -p_mesgout_phasemis: - mvi CLRSINT1,CLRATNO /* Be sure to turn ATNO off */ -p_mesgout_done: - clr MSG_LEN /* no active msg */ - jmp ITloop - -/* - * Message in phase. Bytes are read using Automatic PIO mode. - */ -p_mesgin: - mvi A call inb_first /* read the 1st message byte */ - mov REJBYTE,A /* save it for the driver */ - - test A,MSG_IDENTIFY jnz mesgin_identify - cmp A,MSG_DISCONNECT je mesgin_disconnect - cmp A,MSG_SDPTRS je mesgin_sdptrs - cmp ALLZEROS,A je mesgin_complete - cmp A,MSG_RDPTRS je mesgin_rdptrs - cmp A,MSG_EXTENDED je mesgin_extended - cmp A,MSG_REJECT je mesgin_reject - -rej_mesgin: -/* - * We have no idea what this message in is, and there's no way - * to pass it up to the kernel, so we issue a message reject and - * hope for the best. Since we're now using manual PIO mode to - * read in the message, there should no longer be a race condition - * present when we assert ATN. In any case, rejection should be a - * rare occurrence - signal the driver when it happens. - */ - or SCSISIGO,ATNO /* turn on ATNO */ - mvi INTSTAT,SEND_REJECT /* let driver know */ - - mvi MSG_REJECT call mk_mesg - -mesgin_done: - call inb_last /*ack & turn auto PIO back on*/ - jmp ITloop - - -mesgin_complete: -/* - * We got a "command complete" message, so put the SCB_TAG into QUEUEOUT, - * and trigger a completion interrupt. Check status for non zero return - * and interrupt driver if needed. This allows the driver to interpret - * errors only when they occur instead of always uploading the scb. If - * the status is SCSI_CHECK, the driver will download a new scb requesting - * sense to replace the old one, modify the "waiting for selection" SCB list - * and set RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE the - * sequencer imediately jumps to main loop where it will run down the waiting - * SCB list and process the sense request. If the kernel driver does not - * wish to request sense, it need only clear RETURN_1, and the command is - * allowed to complete. We don't bother to post to the QOUTFIFO in the - * error case since it would require extra work in the kernel driver to - * ensure that the entry was removed before the command complete code tried - * processing it. - * - * First check for residuals - */ - test SCB_RESID_SGCNT,0xff jz check_status -/* - * If we have a residual count, interrupt and tell the host. Other - * alternatives are to pause the sequencer on all command completes (yuck), - * dma the resid directly to the host (slick, we may have space to do it now) - * or have the sequencer pause itself when it encounters a non-zero resid - * (unnecessary pause just to flag the command -yuck-, but takes one instruction - * and since it shouldn't happen that often is good enough for our purposes). - */ -resid: - mvi INTSTAT,RESIDUAL - -check_status: - test SCB_TARGET_STATUS,0xff jz status_ok /* Good Status? */ - mvi INTSTAT,BAD_STATUS /* let driver know */ - cmp RETURN_1, SEND_SENSE jne status_ok - jmp mesgin_done - -status_ok: -/* First, mark this target as free. */ - test SCB_CONTROL,TAG_ENB jnz test_immediate /* - * Tagged commands - * don't busy the - * target. - */ - mov FUNCTION1,SCB_TCL - mov A,FUNCTION1 - test SCB_TCL,0x88 jz clear_a - xor ACTIVE_B,A - jmp test_immediate - -clear_a: - xor ACTIVE_A,A - -test_immediate: - test SCB_CMDLEN,0xff jnz complete /* Immediate message complete */ -/* - * Pause the sequencer until the driver gets around to handling the command - * complete. This is so that any action that might require careful timing - * with the completion of this command can occur. - */ - mvi INTSTAT,IMMEDDONE - jmp start -complete: - mov QOUTFIFO,SCB_TAG - mvi INTSTAT,CMDCMPLT - jmp mesgin_done - - -/* - * Is it an extended message? We only support the synchronous and wide data - * transfer request messages, which will probably be in response to - * WDTR or SDTR message outs from us. If it's not SDTR or WDTR, reject it - - * apparently this can be done after any message in byte, according - * to the SCSI-2 spec. - */ -mesgin_extended: - mvi ARG_1 call inb_next /* extended message length */ - mvi REJBYTE_EXT call inb_next /* extended message code */ - - cmp REJBYTE_EXT,MSG_SDTR je p_mesginSDTR - cmp REJBYTE_EXT,MSG_WDTR je p_mesginWDTR - jmp rej_mesgin - -p_mesginWDTR: - cmp ARG_1,2 jne rej_mesgin /* extended mesg length=2 */ - mvi ARG_1 call inb_next /* Width of bus */ - mvi INTSTAT,WDTR_MSG /* let driver know */ - test RETURN_1,0xff jz mesgin_done /* Do we need to send WDTR? */ - cmp RETURN_1,SEND_REJ je rej_mesgin /* - * Bus width was too large - * Reject it. - */ - -/* We didn't initiate the wide negotiation, so we must respond to the request */ - and RETURN_1,0x7f /* Clear the SEND_WDTR Flag */ - mvi DINDEX,MSG0 - mvi MSG0 call mk_wdtr /* build WDTR message */ - or SCSISIGO,ATNO /* turn on ATNO */ - jmp mesgin_done - -p_mesginSDTR: - cmp ARG_1,3 jne rej_mesgin /* extended mesg length=3 */ - mvi ARG_1 call inb_next /* xfer period */ - mvi A call inb_next /* REQ/ACK offset */ - mvi INTSTAT,SDTR_MSG /* call driver to convert */ - - test RETURN_1,0xff jz mesgin_done /* Do we need to mk_sdtr/rej */ - cmp RETURN_1,SEND_REJ je rej_mesgin /* - * Requested SDTR too small - * Reject it. - */ - clr ARG_1 /* Use the scratch ram rate */ - mvi DINDEX, MSG0 - mvi MSG0 call mk_sdtr - or SCSISIGO,ATNO /* turn on ATNO */ - jmp mesgin_done - -/* - * Is it a disconnect message? Set a flag in the SCB to remind us - * and await the bus going free. - */ -mesgin_disconnect: - or SCB_CONTROL,DISCONNECTED - test FLAGS, PAGESCBS jz mesgin_done -/* - * Link this SCB into the DISCONNECTED list. This list holds the - * candidates for paging out an SCB if one is needed for a new command. - * Modifying the disconnected list is a critical(pause dissabled) section. - */ - mvi SCB_PREV, SCB_LIST_NULL - mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */ - mov SCB_NEXT, DISCONNECTED_SCBH - mov DISCONNECTED_SCBH, SCBPTR - cmp SCB_NEXT,SCB_LIST_NULL je linkdone - mov SCBPTR,SCB_NEXT - mov SCB_PREV,DISCONNECTED_SCBH - mov SCBPTR,DISCONNECTED_SCBH -linkdone: - mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ - jmp mesgin_done - -/* - * Save data pointers message? Copy working values into the SCB, - * usually in preparation for a disconnect. - */ -mesgin_sdptrs: - call sg_ram2scb - jmp mesgin_done - -/* - * Restore pointers message? Data pointers are recopied from the - * SCB anytime we enter a data phase for the first time, so all - * we need to do is clear the DPHASE flag and let the data phase - * code do the rest. - */ -mesgin_rdptrs: - and FLAGS,0xef /* - * !DPHASE we'll reload them - * the next time through - */ - jmp mesgin_done - -/* - * Identify message? For a reconnecting target, this tells us the lun - * that the reconnection is for - find the correct SCB and switch to it, - * clearing the "disconnected" bit so we don't "find" it by accident later. - */ -mesgin_identify: - test A,0x78 jnz rej_mesgin /*!DiscPriv|!LUNTAR|!Reserved*/ - - and A,0x07 /* lun in lower three bits */ - or SAVED_TCL,A,SELID - and SAVED_TCL,0xf7 - and A,SELBUSB,SBLKCTL /* B Channel?? */ - or SAVED_TCL,A - call inb_last /* ACK */ - -/* - * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message. - * If we get one, we use the tag returned to switch to find the proper - * SCB. With SCB paging, this requires using findSCB for both tagged - * and non-tagged transactions since the SCB may exist in any slot. - * If we're not using SCB paging, we can use the tag as the direct - * index to the SCB. - */ - mvi ARG_1,SCB_LIST_NULL /* Default to no-tag */ -snoop_tag_loop: - test SSTAT1,BUSFREE jnz use_findSCB - test SSTAT1,REQINIT jz snoop_tag_loop - test SSTAT1,PHASEMIS jnz use_findSCB - mvi A call inb_first - cmp A,MSG_SIMPLE_TAG jne use_findSCB -get_tag: - mvi ARG_1 call inb_next /* tag value */ -/* - * See if the tag is in range. The tag is < SCBCOUNT if we add - * the complement of SCBCOUNT to the incoming tag and there is - * no carry. - */ - mov A,COMP_SCBCOUNT - add SINDEX,A,ARG_1 - jc abort_tag - -/* - * Ensure that the SCB the tag points to is for a SCB transaction - * to the reconnecting target. - */ - test FLAGS, PAGESCBS jz index_by_tag - call inb_last /* Ack Tag */ -use_findSCB: - mov ALLZEROS call findSCB /* Have to search */ -setup_SCB: - and SCB_CONTROL,0xfb /* clear disconnect bit in SCB */ - or FLAGS,IDENTIFY_SEEN /* make note of IDENTIFY */ - jmp ITloop -index_by_tag: - mov SCBPTR,ARG_1 - mov A,SAVED_TCL - cmp SCB_TCL,A jne abort_tag - test SCB_CONTROL,TAG_ENB jz abort_tag - call inb_last /* Ack Successful tag */ - jmp setup_SCB - -abort_tag: - or SCSISIGO,ATNO /* turn on ATNO */ - mvi INTSTAT,ABORT_TAG /* let driver know */ - mvi MSG_ABORT_TAG call mk_mesg /* ABORT TAG message */ - jmp mesgin_done - -/* - * Message reject? Let the kernel driver handle this. If we have an - * outstanding WDTR or SDTR negotiation, assume that it's a response from - * the target selecting 8bit or asynchronous transfer, otherwise just ignore - * it since we have no clue what it pertains to. - */ -mesgin_reject: - mvi INTSTAT, REJECT_MSG - jmp mesgin_done - -/* - * [ ADD MORE MESSAGE HANDLING HERE ] - */ - -/* - * Bus free phase. It might be useful to interrupt the device - * driver if we aren't expecting this. For now, make sure that - * ATN isn't being asserted and look for a new command. - */ -p_busfree: - mvi CLRSINT1,CLRATNO - clr LASTPHASE - -/* - * if this is an immediate command, perform a pseudo command complete to - * notify the driver. - */ - test SCB_CMDLEN,0xff jz status_ok - jmp start - -/* - * Locking the driver out, build a one-byte message passed in SINDEX - * if there is no active message already. SINDEX is returned intact. - */ -mk_mesg: - mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */ - test MSG_LEN,0xff jz mk_mesg1 /* Should always succeed */ - - /* - * Hmmm. For some reason the mesg buffer is in use. - * Tell the driver. It should look at SINDEX to find - * out what we wanted to use the buffer for and resolve - * the conflict. - */ - mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ - mvi INTSTAT,MSG_BUFFER_BUSY - -mk_mesg1: - mvi MSG_LEN,1 /* length = 1 */ - mov MSG0,SINDEX /* 1-byte message */ - mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ - -/* - * Functions to read data in Automatic PIO mode. - * - * According to Adaptec's documentation, an ACK is not sent on input from - * the target until SCSIDATL is read from. So we wait until SCSIDATL is - * latched (the usual way), then read the data byte directly off the bus - * using SCSIBUSL. When we have pulled the ATN line, or we just want to - * acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI - * spec guarantees that the target will hold the data byte on the bus until - * we send our ACK. - * - * The assumption here is that these are called in a particular sequence, - * and that REQ is already set when inb_first is called. inb_{first,next} - * use the same calling convention as inb. - */ - -inb_next: - or CLRSINT0, CLRSPIORDY - mov NONE,SCSIDATL /*dummy read from latch to ACK*/ -inb_next_wait: - test SSTAT1,PHASEMIS jnz mesgin_phasemis - test SSTAT0,SPIORDY jz inb_next_wait /* wait for next byte */ -inb_first: - mov DINDEX,SINDEX - test SSTAT1,PHASEMIS jnz mesgin_phasemis - mov DINDIR,SCSIBUSL ret /*read byte directly from bus*/ -inb_last: - mov NONE,SCSIDATL ret /*dummy read from latch to ACK*/ - -mesgin_phasemis: -/* - * We expected to receive another byte, but the target changed phase - */ - mvi INTSTAT, MSGIN_PHASEMIS - jmp ITloop - -/* - * DMA data transfer. HADDR and HCNT must be loaded first, and - * SINDEX should contain the value to load DFCNTRL with - 0x3d for - * host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared - * during initialization. - */ -dma: - mov DFCNTRL,SINDEX -dma1: - test SSTAT0,DMADONE jnz dma3 - test SSTAT1,PHASEMIS jz dma1 /* ie. underrun */ - -/* - * We will be "done" DMAing when the transfer count goes to zero, or - * the target changes the phase (in light of this, it makes sense that - * the DMA circuitry doesn't ACK when PHASEMIS is active). If we are - * doing a SCSI->Host transfer, the data FIFO should be flushed auto- - * magically on STCNT=0 or a phase change, so just wait for FIFO empty - * status. - */ -dma3: - test SINDEX,DIRECTION jnz dma5 -dma4: - test DFSTATUS,FIFOEMP jz dma4 - -/* - * Now shut the DMA enables off and make sure that the DMA enables are - * actually off first lest we get an ILLSADDR. - */ -dma5: - /* disable DMA, but maintain WIDEODD */ - and DFCNTRL,WIDEODD -dma6: - test DFCNTRL,0x38 jnz dma6 /* SCSIENACK|SDMAENACK|HDMAENACK */ - - ret - -/* - * Common SCSI initialization for selection and reselection. Expects - * the target SCSI ID to be in the upper four bits of SINDEX, and A's - * contents are stomped on return. - */ -initialize_scsiid: - and SINDEX,0xf0 /* Get target ID */ - and A,0x0f,SCSIID - or SINDEX,A - mov SCSIID,SINDEX ret - -/* - * Assert that if we've been reselected, then we've seen an IDENTIFY - * message. - */ -assert: - test FLAGS,RESELECTED jz return /* reselected? */ - test FLAGS,IDENTIFY_SEEN jnz return /* seen IDENTIFY? */ - - mvi INTSTAT,NO_IDENT ret /* no - cause a kernel panic */ - -/* - * Locate the SCB matching the target ID/channel/lun in SAVED_TCL, and the tag - * value in ARG_1. If ARG_1 == SCB_LIST_NULL, we're looking for a non-tagged - * SCB. Have the kernel print a warning message if it can't be found, and - * generate an ABORT/ABORT_TAG message to the target. SINDEX should be - * cleared on call. - */ -findSCB: - mov A,SAVED_TCL - mov SCBPTR,SINDEX /* switch to next SCB */ - mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */ - cmp SCB_TCL,A jne findSCB1 /* target ID/channel/lun match? */ - test SCB_CONTROL,DISCONNECTED jz findSCB1 /*should be disconnected*/ - test SCB_CONTROL,TAG_ENB jnz findTaggedSCB - cmp ARG_1,SCB_LIST_NULL je foundSCB - jmp findSCB1 -findTaggedSCB: - mov A, ARG_1 /* Tag passed in ARG_1 */ - cmp SCB_TAG,A jne findSCB1 /* Found it? */ -foundSCB: - test FLAGS,PAGESCBS jz foundSCB_ret -/* Remove this SCB from the disconnection list */ - cmp SCB_NEXT,SCB_LIST_NULL je unlink_prev - mov SAVED_LINKPTR, SCB_PREV - mov SCBPTR, SCB_NEXT - mov SCB_PREV, SAVED_LINKPTR - mov SCBPTR, SINDEX -unlink_prev: - cmp SCB_PREV,SCB_LIST_NULL je rHead/* At the head of the list */ - mov SAVED_LINKPTR, SCB_NEXT - mov SCBPTR, SCB_PREV - mov SCB_NEXT, SAVED_LINKPTR - mov SCBPTR, SINDEX - mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ -rHead: - mov DISCONNECTED_SCBH,SCB_NEXT -foundSCB_ret: - mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ - -findSCB1: - mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ - inc SINDEX - mov A,SCBCOUNT - cmp SINDEX,A jne findSCB - - mvi INTSTAT,NO_MATCH /* not found - signal kernel */ - cmp RETURN_1,SCB_PAGEDIN je return - or SCSISIGO,ATNO /* assert ATNO */ - cmp ARG_1,SCB_LIST_NULL jne find_abort_tag - mvi MSG_ABORT call mk_mesg - jmp ITloop -find_abort_tag: - mvi MSG_ABORT_TAG call mk_mesg - jmp ITloop - -/* - * Make a working copy of the scatter-gather parameters from the SCB. - */ -sg_scb2ram: - mov HADDR0, SCB_DATAPTR0 - mov HADDR1, SCB_DATAPTR1 - mov HADDR2, SCB_DATAPTR2 - mov HADDR3, SCB_DATAPTR3 - mov HCNT0, SCB_DATACNT0 - mov HCNT1, SCB_DATACNT1 - mov HCNT2, SCB_DATACNT2 - - mov STCNT0, HCNT0 - mov STCNT1, HCNT1 - mov STCNT2, HCNT2 - - mov SG_COUNT,SCB_SGCOUNT - - mov SG_NEXT0, SCB_SGPTR0 - mov SG_NEXT1, SCB_SGPTR1 - mov SG_NEXT2, SCB_SGPTR2 - mov SG_NEXT3, SCB_SGPTR3 ret - -/* - * Copying RAM values back to SCB, for Save Data Pointers message, but - * only if we've actually been into a data phase to change them. This - * protects against bogus data in scratch ram and the residual counts - * since they are only initialized when we go into data_in or data_out. - */ -sg_ram2scb: - test FLAGS, DPHASE jz return - mov SCB_SGCOUNT,SG_COUNT - - mov SCB_SGPTR0,SG_NEXT0 - mov SCB_SGPTR1,SG_NEXT1 - mov SCB_SGPTR2,SG_NEXT2 - mov SCB_SGPTR3,SG_NEXT3 - - mov SCB_DATAPTR0,SHADDR0 - mov SCB_DATAPTR1,SHADDR1 - mov SCB_DATAPTR2,SHADDR2 - mov SCB_DATAPTR3,SHADDR3 - -/* - * Use the residual number since STCNT is corrupted by any message transfer - */ - mov SCB_DATACNT0,SCB_RESID_DCNT0 - mov SCB_DATACNT1,SCB_RESID_DCNT1 - mov SCB_DATACNT2,SCB_RESID_DCNT2 ret - -/* - * Add the array base TARG_SCRATCH to the target offset (the target address - * is in SCSIID), and return the result in SINDEX. The accumulator - * contains the 3->8 decoding of the target ID on return. - */ -ndx_dtr: - shr A,SCSIID,4 - test SBLKCTL,SELBUSB jz ndx_dtr_2 - or A,0x08 /* Channel B entries add 8 */ -ndx_dtr_2: - add SINDEX,TARG_SCRATCH,A ret - -/* - * If we need to negotiate transfer parameters, build the WDTR or SDTR message - * starting at the address passed in SINDEX. DINDEX is modified on return. - * The SCSI-II spec requires that Wide negotiation occur first and you can - * only negotiate one or the other at a time otherwise in the event of a message - * reject, you wouldn't be able to tell which message was the culprit. - */ -mk_dtr: - test SCB_CONTROL,NEEDWDTR jnz mk_wdtr_16bit - mvi ARG_1, MAXOFFSET /* Force an offset of 15 or 8 if WIDE */ - -mk_sdtr: - mvi DINDIR,1 /* extended message */ - mvi DINDIR,3 /* extended message length = 3 */ - mvi DINDIR,1 /* SDTR code */ - call sdtr_to_rate - mov DINDIR,RETURN_1 /* REQ/ACK transfer period */ - cmp ARG_1, MAXOFFSET je mk_sdtr_max_offset - and DINDIR,0x0f,SINDIR /* Sync Offset */ - -mk_sdtr_done: - add MSG_LEN,COMP_MSG0,DINDEX ret /* update message length */ - -mk_sdtr_max_offset: -/* - * We're initiating sync negotiation, so request the max offset we can (15 or 8) - */ - /* Talking to a WIDE device? */ - test SCSIRATE, WIDEXFER jnz wmax_offset - mvi DINDIR, MAX_OFFSET_8BIT - jmp mk_sdtr_done - -wmax_offset: - mvi DINDIR, MAX_OFFSET_16BIT - jmp mk_sdtr_done - -mk_wdtr_16bit: - mvi ARG_1,BUS_16_BIT -mk_wdtr: - mvi DINDIR,1 /* extended message */ - mvi DINDIR,2 /* extended message length = 2 */ - mvi DINDIR,3 /* WDTR code */ - mov DINDIR,ARG_1 /* bus width */ - - add MSG_LEN,COMP_MSG0,DINDEX ret /* update message length */ - -sdtr_to_rate: - call ndx_dtr /* index scratch space for target */ - shr A,SINDIR,0x4 - dec SINDEX /* Preserve SINDEX */ - and A,0x7 - clr RETURN_1 -sdtr_to_rate_loop: - test A,0x0f jz sdtr_to_rate_done - add RETURN_1,0x19 - dec A - jmp sdtr_to_rate_loop -sdtr_to_rate_done: - shr RETURN_1,0x2 - add RETURN_1,0x19 - test SXFRCTL0,ULTRAEN jz return - shr RETURN_1,0x1 -return: - ret diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx_asm.c linux/drivers/scsi/aic7xxx_asm.c --- v2.0.30/linux/drivers/scsi/aic7xxx_asm.c Sat Apr 20 10:59:10 1996 +++ linux/drivers/scsi/aic7xxx_asm.c Wed Dec 31 16:00:00 1969 @@ -1,734 +0,0 @@ -/*+M************************************************************************* - * Adaptec AIC7xxx sequencer code assembler. - * - * Copyright (c) 1994 John Aycock - * The University of Calgary Department of Computer Science. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Comments are started by `#' and continue to the end of the line; lines - * may be of the form: - *