From f54277de5a6f4deb2f06230285ac29ae6a1da137 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 23 Nov 2007 15:11:28 -0500 Subject: [PATCH] Import 2.0.31pre3 --- Documentation/Configure.help | 119 +- Documentation/isdn/00-INDEX | 7 +- Documentation/isdn/CREDITS | 11 +- Documentation/isdn/INTERFACE | 101 +- Documentation/isdn/README | 177 +- Documentation/isdn/README.HiSax | 323 ++ Documentation/isdn/README.audio | 18 +- Documentation/isdn/README.avmb1 | 62 + Documentation/isdn/README.icn | 88 +- Documentation/isdn/README.sc | 274 ++ Documentation/isdn/README.teles | 73 - arch/alpha/defconfig | 1 + arch/i386/defconfig | 1 + drivers/isdn/Config.in | 19 +- drivers/isdn/Makefile | 52 +- drivers/isdn/avmb1/Makefile | 77 + drivers/isdn/avmb1/b1capi.c | 946 +++++ drivers/isdn/avmb1/b1lli.c | 594 +++ drivers/isdn/avmb1/b1pci.c | 121 + drivers/isdn/avmb1/capi.c | 540 +++ drivers/isdn/avmb1/capicmd.h | 119 + drivers/isdn/avmb1/capidev.h | 29 + drivers/isdn/avmb1/capidrv.c | 1764 +++++++++ drivers/isdn/avmb1/capidrv.h | 111 + drivers/isdn/avmb1/capiutil.c | 974 +++++ drivers/isdn/avmb1/capiutil.h | 501 +++ drivers/isdn/avmb1/compat.h | 30 + drivers/isdn/hisax/Makefile | 54 + drivers/isdn/hisax/avm_a1.c | 960 +++++ drivers/isdn/hisax/avm_a1.h | 25 + drivers/isdn/hisax/callc.c | 1975 ++++++++++ drivers/isdn/hisax/config.c | 425 +++ drivers/isdn/hisax/elsa.c | 1487 ++++++++ drivers/isdn/hisax/elsa.h | 90 + drivers/isdn/{teles => hisax}/fsm.c | 74 +- drivers/isdn/hisax/hisax.h | 540 +++ drivers/isdn/hisax/isdnl1.c | 1378 +++++++ drivers/isdn/hisax/isdnl1.h | 51 + drivers/isdn/hisax/isdnl2.c | 1325 +++++++ drivers/isdn/hisax/isdnl2.h | 18 + drivers/isdn/hisax/isdnl3.c | 201 + drivers/isdn/hisax/isdnl3.h | 38 + drivers/isdn/hisax/ix1_micro.c | 937 +++++ drivers/isdn/hisax/ix1_micro.h | 50 + drivers/isdn/hisax/l3_1tr6.c | 796 ++++ .../isdn/{teles/l3_1TR6.h => hisax/l3_1tr6.h} | 20 +- drivers/isdn/hisax/l3dss1.c | 809 ++++ drivers/isdn/{teles => hisax}/q931.c | 365 +- drivers/isdn/hisax/siemens.h | 71 + drivers/isdn/hisax/tei.c | 317 ++ drivers/isdn/hisax/teles0.c | 968 +++++ drivers/isdn/hisax/teles0.h | 21 + drivers/isdn/hisax/teles3.c | 1034 +++++ drivers/isdn/hisax/teles3.h | 21 + drivers/isdn/icn/icn.c | 2627 +++++++------ drivers/isdn/icn/icn.h | 242 +- drivers/isdn/isdn_audio.c | 907 ++--- drivers/isdn/isdn_audio.h | 35 +- drivers/isdn/isdn_cards.c | 52 +- drivers/isdn/isdn_cards.h | 12 +- drivers/isdn/isdn_common.c | 2134 ++++++----- drivers/isdn/isdn_common.h | 56 +- drivers/isdn/isdn_net.c | 1789 +++++---- drivers/isdn/isdn_net.h | 65 +- drivers/isdn/isdn_ppp.c | 1588 +++++--- drivers/isdn/isdn_ppp.h | 60 +- drivers/isdn/isdn_syms.c | 55 + drivers/isdn/isdn_tty.c | 3332 ++++++++++------- drivers/isdn/isdn_tty.h | 46 +- drivers/isdn/pcbit/callbacks.c | 18 +- drivers/isdn/pcbit/capi.c | 16 +- drivers/isdn/pcbit/drv.c | 24 +- drivers/isdn/pcbit/layer2.c | 829 ++-- drivers/isdn/pcbit/module.c | 12 +- drivers/isdn/sc/Makefile | 44 + drivers/isdn/sc/card.h | 110 + drivers/isdn/sc/command.c | 563 +++ drivers/isdn/sc/debug.c | 80 + drivers/isdn/sc/debug.h | 35 + drivers/isdn/sc/event.c | 75 + drivers/isdn/sc/hardware.h | 117 + drivers/isdn/sc/includes.h | 15 + drivers/isdn/sc/init.c | 599 +++ drivers/isdn/sc/interrupt.c | 258 ++ drivers/isdn/sc/ioctl.c | 513 +++ drivers/isdn/sc/message.c | 300 ++ drivers/isdn/sc/message.h | 256 ++ drivers/isdn/sc/packet.c | 228 ++ drivers/isdn/sc/scioc.h | 101 + drivers/isdn/sc/shmem.c | 165 + drivers/isdn/sc/timer.c | 172 + drivers/isdn/teles/Makefile | 17 - drivers/isdn/teles/buffers.c | 326 -- drivers/isdn/teles/callc.c | 1482 -------- drivers/isdn/teles/card.c | 1900 ---------- drivers/isdn/teles/config.c | 48 - drivers/isdn/teles/isdnl2.c | 1317 ------- drivers/isdn/teles/isdnl3.c | 663 ---- drivers/isdn/teles/l3_1TR6.c | 535 --- drivers/isdn/teles/llglue.c | 151 - drivers/isdn/teles/mod.c | 143 - drivers/isdn/teles/proto.h | 18 - drivers/isdn/teles/tei.c | 248 -- drivers/isdn/teles/teles.h | 487 --- drivers/net/ne.c | 127 +- drivers/pci/pci.c | 4 + drivers/scsi/Config.in | 1 + drivers/scsi/Makefile | 12 + drivers/scsi/gdth.c | 3275 ++++++++++++++++ drivers/scsi/gdth.h | 719 ++++ drivers/scsi/gdth_ioctl.h | 86 + drivers/scsi/gdth_proc.c | 635 ++++ drivers/scsi/gdth_proc.h | 24 + drivers/scsi/hosts.c | 7 + include/linux/b1lli.h | 141 + include/linux/capi.h | 127 + include/linux/isdn.h | 137 +- include/linux/isdn_ppp.h | 58 +- include/linux/isdnif.h | 86 +- include/linux/kernelcapi.h | 96 + include/linux/pci.h | 7 + include/linux/proc_fs.h | 1 + init/main.c | 19 +- kernel/panic.c | 4 + kernel/sys.c | 15 +- 125 files changed, 37470 insertions(+), 14158 deletions(-) create mode 100644 Documentation/isdn/README.HiSax create mode 100644 Documentation/isdn/README.avmb1 create mode 100644 Documentation/isdn/README.sc delete mode 100644 Documentation/isdn/README.teles create mode 100644 drivers/isdn/avmb1/Makefile create mode 100644 drivers/isdn/avmb1/b1capi.c create mode 100644 drivers/isdn/avmb1/b1lli.c create mode 100644 drivers/isdn/avmb1/b1pci.c create mode 100644 drivers/isdn/avmb1/capi.c create mode 100644 drivers/isdn/avmb1/capicmd.h create mode 100644 drivers/isdn/avmb1/capidev.h create mode 100644 drivers/isdn/avmb1/capidrv.c create mode 100644 drivers/isdn/avmb1/capidrv.h create mode 100644 drivers/isdn/avmb1/capiutil.c create mode 100644 drivers/isdn/avmb1/capiutil.h create mode 100644 drivers/isdn/avmb1/compat.h create mode 100644 drivers/isdn/hisax/Makefile create mode 100644 drivers/isdn/hisax/avm_a1.c create mode 100644 drivers/isdn/hisax/avm_a1.h create mode 100644 drivers/isdn/hisax/callc.c create mode 100644 drivers/isdn/hisax/config.c create mode 100644 drivers/isdn/hisax/elsa.c create mode 100644 drivers/isdn/hisax/elsa.h rename drivers/isdn/{teles => hisax}/fsm.c (62%) create mode 100644 drivers/isdn/hisax/hisax.h create mode 100644 drivers/isdn/hisax/isdnl1.c create mode 100644 drivers/isdn/hisax/isdnl1.h create mode 100644 drivers/isdn/hisax/isdnl2.c create mode 100644 drivers/isdn/hisax/isdnl2.h create mode 100644 drivers/isdn/hisax/isdnl3.c create mode 100644 drivers/isdn/hisax/isdnl3.h create mode 100644 drivers/isdn/hisax/ix1_micro.c create mode 100644 drivers/isdn/hisax/ix1_micro.h create mode 100644 drivers/isdn/hisax/l3_1tr6.c rename drivers/isdn/{teles/l3_1TR6.h => hisax/l3_1tr6.h} (90%) create mode 100644 drivers/isdn/hisax/l3dss1.c rename drivers/isdn/{teles => hisax}/q931.c (79%) create mode 100644 drivers/isdn/hisax/siemens.h create mode 100644 drivers/isdn/hisax/tei.c create mode 100644 drivers/isdn/hisax/teles0.c create mode 100644 drivers/isdn/hisax/teles0.h create mode 100644 drivers/isdn/hisax/teles3.c create mode 100644 drivers/isdn/hisax/teles3.h create mode 100644 drivers/isdn/isdn_syms.c create mode 100644 drivers/isdn/sc/Makefile create mode 100644 drivers/isdn/sc/card.h create mode 100644 drivers/isdn/sc/command.c create mode 100644 drivers/isdn/sc/debug.c create mode 100644 drivers/isdn/sc/debug.h create mode 100644 drivers/isdn/sc/event.c create mode 100644 drivers/isdn/sc/hardware.h create mode 100644 drivers/isdn/sc/includes.h create mode 100644 drivers/isdn/sc/init.c create mode 100644 drivers/isdn/sc/interrupt.c create mode 100644 drivers/isdn/sc/ioctl.c create mode 100644 drivers/isdn/sc/message.c create mode 100644 drivers/isdn/sc/message.h create mode 100644 drivers/isdn/sc/packet.c create mode 100644 drivers/isdn/sc/scioc.h create mode 100644 drivers/isdn/sc/shmem.c create mode 100644 drivers/isdn/sc/timer.c delete mode 100644 drivers/isdn/teles/Makefile delete mode 100644 drivers/isdn/teles/buffers.c delete mode 100644 drivers/isdn/teles/callc.c delete mode 100644 drivers/isdn/teles/card.c delete mode 100644 drivers/isdn/teles/config.c delete mode 100644 drivers/isdn/teles/isdnl2.c delete mode 100644 drivers/isdn/teles/isdnl3.c delete mode 100644 drivers/isdn/teles/l3_1TR6.c delete mode 100644 drivers/isdn/teles/llglue.c delete mode 100644 drivers/isdn/teles/mod.c delete mode 100644 drivers/isdn/teles/proto.h delete mode 100644 drivers/isdn/teles/tei.c delete mode 100644 drivers/isdn/teles/teles.h create mode 100644 drivers/scsi/gdth.c create mode 100644 drivers/scsi/gdth.h create mode 100644 drivers/scsi/gdth_ioctl.h create mode 100644 drivers/scsi/gdth_proc.c create mode 100644 drivers/scsi/gdth_proc.h create mode 100644 include/linux/b1lli.h create mode 100644 include/linux/capi.h create mode 100644 include/linux/kernelcapi.h diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 17db40aa897d..89e81e97a7de 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -1747,6 +1747,15 @@ CONFIG_SCSI_AM53C974 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 @@ -3828,8 +3837,8 @@ CONFIG_ISDN_AUDIO (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 @@ -3842,14 +3851,89 @@ CONFIG_ISDN_DRV_ICN 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 @@ -3860,6 +3944,21 @@ CONFIG_ISDN_DRV_PCBIT 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 This enables support for a sparc based parallel multi-computer diff --git a/Documentation/isdn/00-INDEX b/Documentation/isdn/00-INDEX index b707c346d48b..401697921b9d 100644 --- a/Documentation/isdn/00-INDEX +++ b/Documentation/isdn/00-INDEX @@ -10,12 +10,13 @@ README.audio - 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 --git a/Documentation/isdn/CREDITS b/Documentation/isdn/CREDITS index 6c546c4e1e6d..44e6554a7647 100644 --- a/Documentation/isdn/CREDITS +++ b/Documentation/isdn/CREDITS @@ -12,11 +12,18 @@ Volker G 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 @@ Max Riegel (riegel@max.franken.de) 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 --git a/Documentation/isdn/INTERFACE b/Documentation/isdn/INTERFACE index fe2a4440cd80..81dddb08177b 100644 --- a/Documentation/isdn/INTERFACE +++ b/Documentation/isdn/INTERFACE @@ -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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id. command = ISDN_CMD_LOCK arg = unused. - num = unused. + para = unused. ISDN_CMD_UNLOCK: @@ -462,7 +467,7 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_RUN arg = unused. - num = unused. + para = unused. ISDN_STAT_STOP: @@ -524,7 +533,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_STOP arg = unused. - num = unused. + para = unused. ISDN_STAT_DCONN: @@ -535,7 +544,7 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_UNLOAD arg = unused. - num = unused. + para = unused. ISDN_STAT_BSENT: @@ -608,7 +617,7 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 @@ Description of the Interface between Linklevel and Hardwarelevel 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 --git a/Documentation/isdn/README b/Documentation/isdn/README index 36cc1336f033..770694f2b819 100644 --- a/Documentation/isdn/README +++ b/Documentation/isdn/README @@ -27,7 +27,14 @@ README for the ISDN-subsystem 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 @@ README for the ISDN-subsystem 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 @@ README for the ISDN-subsystem 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 @@ README for the ISDN-subsystem 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 @@ README for the ISDN-subsystem 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 @@ README for the ISDN-subsystem 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 @@ README for the ISDN-subsystem 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 @@ README for the ISDN-subsystem 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 @@ README for the ISDN-subsystem 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 @@ README for the ISDN-subsystem 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 @@ README for the ISDN-subsystem 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 @@ README for the ISDN-subsystem "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 --git a/Documentation/isdn/README.HiSax b/Documentation/isdn/README.HiSax new file mode 100644 index 000000000000..20b578d83221 --- /dev/null +++ b/Documentation/isdn/README.HiSax @@ -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 --git a/Documentation/isdn/README.audio b/Documentation/isdn/README.audio index c2c2d272d188..c01a116bcf2d 100644 --- a/Documentation/isdn/README.audio +++ b/Documentation/isdn/README.audio @@ -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 @@ Commands for enabling/disabling audio mode: 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 @@ General behavior and description of data formats/protocol. 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 @@ General behavior and description of data formats/protocol. 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 --git a/Documentation/isdn/README.avmb1 b/Documentation/isdn/README.avmb1 new file mode 100644 index 000000000000..68bdf0985e47 --- /dev/null +++ b/Documentation/isdn/README.avmb1 @@ -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 --git a/Documentation/isdn/README.icn b/Documentation/isdn/README.icn index f2dd3ba03ed1..cb8908d58767 100644 --- a/Documentation/isdn/README.icn +++ b/Documentation/isdn/README.icn @@ -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 @@ -62,3 +62,87 @@ Setting up the IO-address dipswitches for the ICN-ISDN-card: 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 --git a/Documentation/isdn/README.sc b/Documentation/isdn/README.sc new file mode 100644 index 000000000000..0ea8ca165ebc --- /dev/null +++ b/Documentation/isdn/README.sc @@ -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 --git a/Documentation/isdn/README.teles b/Documentation/isdn/README.teles deleted file mode 100644 index 904a55925fd0..000000000000 --- a/Documentation/isdn/README.teles +++ /dev/null @@ -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 --git a/arch/alpha/defconfig b/arch/alpha/defconfig index d10428b032d3..c6e8bd8dc77b 100644 --- a/arch/alpha/defconfig +++ b/arch/alpha/defconfig @@ -169,6 +169,7 @@ CONFIG_DE4X5=y # ISDN subsystem # # CONFIG_ISDN is not set +CONFIG_HISAX_EURO=y # # CD-ROM drivers (not for SCSI or IDE/ATAPI drives) diff --git a/arch/i386/defconfig b/arch/i386/defconfig index 07227fdb3fae..74dbc3f86961 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -124,6 +124,7 @@ CONFIG_EL3=y # ISDN subsystem # # CONFIG_ISDN is not set +CONFIG_HISAX_EURO=y # # CD-ROM drivers (not for SCSI or IDE/ATAPI drives) diff --git a/drivers/isdn/Config.in b/drivers/isdn/Config.in index 96f7e5a43b8e..2213ac6dc7d5 100644 --- a/drivers/isdn/Config.in +++ b/drivers/isdn/Config.in @@ -11,4 +11,21 @@ fi 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 --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile index 3b441835e722..6de004157943 100644 --- a/drivers/isdn/Makefile +++ b/drivers/isdn/Makefile @@ -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 @@ O_TARGET := 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 @@ else 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 @@ else 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 @@ -66,5 +66,35 @@ else 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 + include $(TOPDIR)/Rules.make diff --git a/drivers/isdn/avmb1/Makefile b/drivers/isdn/avmb1/Makefile new file mode 100644 index 000000000000..cce4af1310a7 --- /dev/null +++ b/drivers/isdn/avmb1/Makefile @@ -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 --git a/drivers/isdn/avmb1/b1capi.c b/drivers/isdn/avmb1/b1capi.c new file mode 100644 index 000000000000..b379d8f7dae8 --- /dev/null +++ b/drivers/isdn/avmb1/b1capi.c @@ -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 --git a/drivers/isdn/avmb1/b1lli.c b/drivers/isdn/avmb1/b1lli.c new file mode 100644 index 000000000000..bc1cb1cd34f9 --- /dev/null +++ b/drivers/isdn/avmb1/b1lli.c @@ -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 --git a/drivers/isdn/avmb1/b1pci.c b/drivers/isdn/avmb1/b1pci.c new file mode 100644 index 000000000000..af7b58407795 --- /dev/null +++ b/drivers/isdn/avmb1/b1pci.c @@ -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 --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c new file mode 100644 index 000000000000..34fde7bd983e --- /dev/null +++ b/drivers/isdn/avmb1/capi.c @@ -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 --git a/drivers/isdn/avmb1/capicmd.h b/drivers/isdn/avmb1/capicmd.h new file mode 100644 index 000000000000..104ef824f66b --- /dev/null +++ b/drivers/isdn/avmb1/capicmd.h @@ -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 --git a/drivers/isdn/avmb1/capidev.h b/drivers/isdn/avmb1/capidev.h new file mode 100644 index 000000000000..f2e0d6d2d579 --- /dev/null +++ b/drivers/isdn/avmb1/capidev.h @@ -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 --git a/drivers/isdn/avmb1/capidrv.c b/drivers/isdn/avmb1/capidrv.c new file mode 100644 index 000000000000..f6bb51f13635 --- /dev/null +++ b/drivers/isdn/avmb1/capidrv.c @@ -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 --git a/drivers/isdn/avmb1/capidrv.h b/drivers/isdn/avmb1/capidrv.h new file mode 100644 index 000000000000..f30c3f4dd1de --- /dev/null +++ b/drivers/isdn/avmb1/capidrv.h @@ -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 --git a/drivers/isdn/avmb1/capiutil.c b/drivers/isdn/avmb1/capiutil.c new file mode 100644 index 000000000000..b3c25cd2af9f --- /dev/null +++ b/drivers/isdn/avmb1/capiutil.c @@ -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 --git a/drivers/isdn/avmb1/capiutil.h b/drivers/isdn/avmb1/capiutil.h new file mode 100644 index 000000000000..38209212dbd9 --- /dev/null +++ b/drivers/isdn/avmb1/capiutil.h @@ -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 --git a/drivers/isdn/avmb1/compat.h b/drivers/isdn/avmb1/compat.h new file mode 100644 index 000000000000..551b20d604e1 --- /dev/null +++ b/drivers/isdn/avmb1/compat.h @@ -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 --git a/drivers/isdn/hisax/Makefile b/drivers/isdn/hisax/Makefile new file mode 100644 index 000000000000..2cc325b1f246 --- /dev/null +++ b/drivers/isdn/hisax/Makefile @@ -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 --git a/drivers/isdn/hisax/avm_a1.c b/drivers/isdn/hisax/avm_a1.c new file mode 100644 index 000000000000..43b14709f5f5 --- /dev/null +++ b/drivers/isdn/hisax/avm_a1.c @@ -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 --git a/drivers/isdn/hisax/avm_a1.h b/drivers/isdn/hisax/avm_a1.h new file mode 100644 index 000000000000..85f44675e0a8 --- /dev/null +++ b/drivers/isdn/hisax/avm_a1.h @@ -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 --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c new file mode 100644 index 000000000000..91c868942082 --- /dev/null +++ b/drivers/isdn/hisax/callc.c @@ -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 --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c new file mode 100644 index 000000000000..595fb713e56a --- /dev/null +++ b/drivers/isdn/hisax/config.c @@ -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 --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c new file mode 100644 index 000000000000..0eb025361832 --- /dev/null +++ b/drivers/isdn/hisax/elsa.c @@ -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 --git a/drivers/isdn/hisax/elsa.h b/drivers/isdn/hisax/elsa.h new file mode 100644 index 000000000000..5187e6abbf81 --- /dev/null +++ b/drivers/isdn/hisax/elsa.h @@ -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 --git a/drivers/isdn/teles/fsm.c b/drivers/isdn/hisax/fsm.c similarity index 62% rename from drivers/isdn/teles/fsm.c rename to drivers/isdn/hisax/fsm.c index c0b2f494bf94..d0bb2f14f0ea 100644 --- a/drivers/isdn/teles/fsm.c +++ b/drivers/isdn/hisax/fsm.c @@ -1,25 +1,39 @@ -/* $Id: fsm.c,v 1.2 1996/04/29 22:49:57 fritz Exp $ +/* $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.2 1996/04/29 22:49:57 fritz - * Removed compatibility-macros. + * 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/04/13 10:23:41 fritz + * Revision 1.1 1996/10/13 20:04:52 keil * Initial revision * * */ #define __NO_VERSION__ -#include "teles.h" +#include "hisax.h" + +#define FSM_TIMER_DEBUG 0 void FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount) { - int i; + int i; - fsm->jumpmatrix = (int *) Smalloc(4L * fsm->state_count * fsm->event_count, - GFP_KERNEL, "Fsm jumpmatrix"); + 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++) @@ -30,14 +44,14 @@ FsmNew(struct Fsm *fsm, void FsmFree(struct Fsm *fsm) { - Sfree((void *) fsm->jumpmatrix); + kfree((void *) fsm->jumpmatrix); } int FsmEvent(struct FsmInst *fi, int event, void *arg) { - void (*r) (struct FsmInst *, int, void *); - char str[80]; + void (*r) (struct FsmInst *, int, void *); + char str[80]; r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; if (r) { @@ -63,7 +77,7 @@ FsmEvent(struct FsmInst *fi, int event, void *arg) void FsmChangeState(struct FsmInst *fi, int newstate) { - char str[80]; + char str[80]; fi->state = newstate; if (fi->debug) { @@ -76,6 +90,13 @@ FsmChangeState(struct FsmInst *fi, int newstate) 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); } @@ -85,26 +106,27 @@ 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) { - long flags; - -#if 0 +#if FSM_TIMER_DEBUG if (ft->fi->debug) { - sprintf(str, "FsmDelTimer %lx %d", ft, where); + char str[40]; + sprintf(str, "FsmDelTimer %lx %d", (long) ft, where); ft->fi->printdebug(ft->fi, str); } #endif - - save_flags(flags); - cli(); - if (ft->tl.next) - del_timer(&ft->tl); - restore_flags(flags); + del_timer(&ft->tl); } int @@ -112,15 +134,17 @@ FsmAddTimer(struct FsmTimer *ft, int millisec, int event, void *arg, int where) { -#if 0 +#if FSM_TIMER_DEBUG if (ft->fi->debug) { - sprintf(str, "FsmAddTimer %lx %d %d", ft, millisec, where); + char str[40]; + sprintf(str, "FsmAddTimer %lx %d %d", (long) ft, millisec, where); ft->fi->printdebug(ft->fi, str); } #endif - if (ft->tl.next) { + 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); diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h new file mode 100644 index 000000000000..efe7d53290df --- /dev/null +++ b/drivers/isdn/hisax/hisax.h @@ -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 --git a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c new file mode 100644 index 000000000000..f0aaba0c9035 --- /dev/null +++ b/drivers/isdn/hisax/isdnl1.c @@ -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 --git a/drivers/isdn/hisax/isdnl1.h b/drivers/isdn/hisax/isdnl1.h new file mode 100644 index 000000000000..98418c821841 --- /dev/null +++ b/drivers/isdn/hisax/isdnl1.h @@ -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 --git a/drivers/isdn/hisax/isdnl2.c b/drivers/isdn/hisax/isdnl2.c new file mode 100644 index 000000000000..87f4814a5a1d --- /dev/null +++ b/drivers/isdn/hisax/isdnl2.c @@ -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 --git a/drivers/isdn/hisax/isdnl2.h b/drivers/isdn/hisax/isdnl2.h new file mode 100644 index 000000000000..ac21d1c4ddb7 --- /dev/null +++ b/drivers/isdn/hisax/isdnl2.h @@ -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 --git a/drivers/isdn/hisax/isdnl3.c b/drivers/isdn/hisax/isdnl3.c new file mode 100644 index 000000000000..e945bd4bdf0c --- /dev/null +++ b/drivers/isdn/hisax/isdnl3.c @@ -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 --git a/drivers/isdn/hisax/isdnl3.h b/drivers/isdn/hisax/isdnl3.h new file mode 100644 index 000000000000..bed989a18613 --- /dev/null +++ b/drivers/isdn/hisax/isdnl3.h @@ -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 --git a/drivers/isdn/hisax/ix1_micro.h b/drivers/isdn/hisax/ix1_micro.h new file mode 100644 index 000000000000..b1800974bb96 --- /dev/null +++ b/drivers/isdn/hisax/ix1_micro.h @@ -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 --git a/drivers/isdn/hisax/l3_1tr6.c b/drivers/isdn/hisax/l3_1tr6.c new file mode 100644 index 000000000000..99e318ac1660 --- /dev/null +++ b/drivers/isdn/hisax/l3_1tr6.c @@ -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 --git a/drivers/isdn/teles/l3_1TR6.h b/drivers/isdn/hisax/l3_1tr6.h similarity index 90% rename from drivers/isdn/teles/l3_1TR6.h rename to drivers/isdn/hisax/l3_1tr6.h index e3b538e260e9..6e2fee72f4b5 100644 --- a/drivers/isdn/teles/l3_1TR6.h +++ b/drivers/isdn/hisax/l3_1tr6.h @@ -1,19 +1,19 @@ -/* $Id: l3_1TR6.h,v 1.4 1996/09/23 01:53:52 fritz Exp $ +/* $Id: l3_1tr6.h,v 1.1 1996/10/13 20:03:48 keil 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). + * German 1TR6 D-channel protocol defines * - * 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 + * $Log: l3_1tr6.h,v $ + * Revision 1.1 1996/10/13 20:03:48 keil * Initial revision * * + * */ -#ifndef l3_1TR6 -#define l3_1TR6 +#ifndef l3_1tr6 +#define l3_1tr6 + +#define PROTO_DIS_N0 0x40 +#define PROTO_DIS_N1 0x41 /* * MsgType N0 diff --git a/drivers/isdn/hisax/l3dss1.c b/drivers/isdn/hisax/l3dss1.c new file mode 100644 index 000000000000..73c49d9d2809 --- /dev/null +++ b/drivers/isdn/hisax/l3dss1.c @@ -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 --git a/drivers/isdn/teles/q931.c b/drivers/isdn/hisax/q931.c similarity index 79% rename from drivers/isdn/teles/q931.c rename to drivers/isdn/hisax/q931.c index c9f5fa6922e5..1e1ee47723c0 100644 --- a/drivers/isdn/teles/q931.c +++ b/drivers/isdn/hisax/q931.c @@ -1,35 +1,32 @@ -/* $Id: q931.c,v 1.6 1996/09/23 01:53:53 fritz Exp $ +/* $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 * - * 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). + * Karsten Keil cause texts, display information element for 1TR6 + * * - * Revision 1.5 1996/06/03 20:03:40 fritz - * Fixed typos. + * $Log: q931.c,v $ + * Revision 1.5 1997/04/06 22:56:43 keil + * Some cosmetic changes * - * Revision 1.4 1996/05/17 03:46:17 fritz - * General cleanup. + * Revision 1.4 1997/02/09 00:29:11 keil + * new interface handling, one interface per card * - * Revision 1.3 1996/04/30 22:06:50 isdn4dev - * logging 1TR6 messages correctly Karsten Keil + * Revision 1.3 1997/01/21 22:24:59 keil + * cleanups * - * Revision 1.2 1996/04/20 16:48:19 fritz - * Misc. typos + * Revision 1.2 1996/10/27 22:12:45 keil + * reporting unknown level 3 protocol ids * - * Revision 1.1 1996/04/13 10:27:49 fritz + * Revision 1.1 1996/10/13 20:04:56 keil * Initial revision * * @@ -37,15 +34,14 @@ #define __NO_VERSION__ -#include "teles.h" -#include "proto.h" -#include "l3_1TR6.h" +#include "hisax.h" +#include "l3_1tr6.h" -byte * -findie(byte * p, int size, byte ie, int wanted_set) +u_char * +findie(u_char * p, int size, u_char ie, int wanted_set) { - int l, codeset, maincodeset; - byte *pend = p + size; + int l, codeset, maincodeset; + u_char *pend = p + size; /* skip protocol discriminator, callref and message type */ p++; @@ -80,10 +76,10 @@ findie(byte * p, int size, byte ie, int wanted_set) } void -iecpy(byte * dest, byte * iestart, int ieoffset) +iecpy(u_char * dest, u_char * iestart, int ieoffset) { - byte *p; - int l; + u_char *p; + int l; p = iestart + ieoffset + 2; l = iestart[1] - ieoffset; @@ -93,7 +89,7 @@ iecpy(byte * dest, byte * iestart, int ieoffset) } int -getcallref(byte * p) +getcallref(u_char * p) { p++; /* prot discr */ p++; /* callref length */ @@ -105,8 +101,8 @@ getcallref(byte * p) */ static struct MessageType { - byte nr; - char *descr; + u_char nr; + char *descr; } mtlist[] = { { @@ -271,10 +267,10 @@ int fac_1tr6_len = (sizeof(fac_1tr6) / sizeof(struct MessageType)); -static int -prbits(char *dest, byte b, int start, int len) +static int +prbits(char *dest, u_char b, int start, int len) { - char *dp = dest; + char *dp = dest; b = b << (8 - start); while (len--) { @@ -288,8 +284,8 @@ prbits(char *dest, byte b, int start, int len) } static -byte * -skipext(byte * p) +u_char * +skipext(u_char * p) { while (!(*p++ & 0x80)); return (p); @@ -304,9 +300,9 @@ skipext(byte * p) static struct CauseValue { - byte nr; - char *edescr; - char *ddescr; + u_char nr; + char *edescr; + char *ddescr; } cvlist[] = { { @@ -516,11 +512,11 @@ struct CauseValue { static int -prcause(char *dest, byte * p) +prcause(char *dest, u_char * p) { - byte *end; - char *dp = dest; - int i, cause; + u_char *end; + char *dp = dest; + int i, cause; end = p + p[1] + 1; p += 2; @@ -593,9 +589,9 @@ struct MessageType cause_1tr6[] = int cause_1tr6_len = (sizeof(cause_1tr6) / sizeof(struct MessageType)); static int -prcause_1tr6(char *dest, byte * p) +prcause_1tr6(char *dest, u_char * p) { - char *dp = dest; + char *dp = dest; int i, cause; p++; @@ -628,7 +624,8 @@ prcause_1tr6(char *dest, byte * p) } static int -prchident(char *dest, byte * p) { +prchident(char *dest, u_char * p) +{ char *dp = dest; p += 2; @@ -639,9 +636,10 @@ prchident(char *dest, byte * p) { } static int -prcalled(char *dest, byte * p) { - int l; - char *dp = dest; +prcalled(char *dest, u_char * p) +{ + int l; + char *dp = dest; p++; l = *p++ - 1; @@ -655,9 +653,10 @@ prcalled(char *dest, byte * p) { return (dp - dest); } static int -prcalling(char *dest, byte * p) { - int l; - char *dp = dest; +prcalling(char *dest, u_char * p) +{ + int l; + char *dp = dest; p++; l = *p++ - 1; @@ -681,9 +680,9 @@ prcalling(char *dest, byte * p) { static int -prbearer(char *dest, byte * p) +prbearer(char *dest, u_char * p) { - char *dp = dest, ch; + char *dp = dest, ch; p += 2; dp += sprintf(dp, " octet 3 "); @@ -727,10 +726,11 @@ prbearer(char *dest, byte * p) } static int -general(char *dest, byte * p) { - char *dp = dest; - char ch = ' '; - int l, octet = 3; +general(char *dest, u_char * p) +{ + char *dp = dest; + char ch = ' '; + int l, octet = 3; p++; l = *p++; @@ -753,7 +753,8 @@ general(char *dest, byte * p) { } static int -prcharge(char *dest, byte * p) { +prcharge(char *dest, u_char * p) +{ char *dp = dest; int l; @@ -767,9 +768,10 @@ prcharge(char *dest, byte * p) { *dp++ = *p++; *dp++ = '\n'; return (dp - dest); -} +} static int -prtext(char *dest, byte * p) { +prtext(char *dest, u_char * p) +{ char *dp = dest; int l; @@ -783,10 +785,11 @@ prtext(char *dest, byte * p) { return (dp - dest); } static int -display(char *dest, byte * p) { - char *dp = dest; - char ch = ' '; - int l, octet = 3; +display(char *dest, u_char * p) +{ + char *dp = dest; + char ch = ' '; + int l, octet = 3; p++; l = *p++; @@ -811,10 +814,10 @@ display(char *dest, byte * p) { } int -prfacility(char *dest, byte * p) +prfacility(char *dest, u_char * p) { - char *dp = dest; - int l, l2; + char *dp = dest; + int l, l2; p++; l = *p++; @@ -842,9 +845,9 @@ prfacility(char *dest, byte * p) static struct InformationElement { - byte nr; - char *descr; - int (*f) (char *, byte *); + u_char nr; + char *descr; + int (*f) (char *, u_char *); } ielist[] = { { @@ -979,77 +982,128 @@ static struct InformationElement we_6[] = }; 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, byte * buf, int size, char *comment) { - byte *bend = buf + size; - char *dp; +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); - { - 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 */ + 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 == buf[3]) + if (mt_n0[i].nr == mt) break; - /* display message type iff it exists */ + /* display message type if it exists */ if (i == mt_n0_len) - dp += sprintf(dp, "Unknown message type N0 %x!\n", buf[3]); + 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, "call reference %d size %d message type %s\n", - buf[2], size, mt_n0[i].descr); + 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 == buf[3]) + if (mt_n1[i].nr == mt) break; - /* display message type iff it exists */ + /* display message type if it exists */ if (i == mt_n1_len) - dp += sprintf(dp, "Unknown message type N1 %x!\n", buf[3]); + 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, "call reference %d size %d message type %s\n", - buf[2], size, mt_n1[i].descr); + 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 */ - 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"); + 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; - } - if (*buf == 0xa1) { - dp += sprintf(dp, " Sending complete\n"); - } - break; - /* fall through */ - default: - dp += sprintf(dp, " Reserved %x\n", *buf); - break; } buf++; continue; @@ -1087,47 +1141,56 @@ dlogframe(struct IsdnCardState *sp, byte * buf, int size, char *comment) { } buf += buf[1] + 2; } - } else if (buf[0]==PROTO_EURO) { /* EURO */ + } 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 == buf[3]) + if (mtlist[i].nr == mt) break; - /* display message type iff it exists */ + /* display message type if it exists */ if (i == MTSIZE) - dp += sprintf(dp, "Unknown message type %x!\n", buf[3]); + 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, "call reference %d size %d message type %s\n", - buf[2], size, mtlist[i].descr); + 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 */ - 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"); + 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; - } - if (*buf == 0xa1) { - dp += sprintf(dp, " Sending complete\n"); - } - break; - /* fall through */ - default: - dp += sprintf(dp, " Reserved %x\n", *buf); - break; } buf++; continue; @@ -1147,9 +1210,9 @@ dlogframe(struct IsdnCardState *sp, byte * buf, int size, char *comment) { /* Skip to next element */ buf += buf[1] + 2; } + } else { + dp += sprintf(dp, "Unknown protocol %x!", buf[0]); } - else dp += sprintf(dp,"Unnown frame type %.2x, ignored\n",buf[0]); - dp += sprintf(dp, "\n"); - teles_putstatus(sp->dlogspace); + HiSax_putstatus(sp, sp->dlogspace); } diff --git a/drivers/isdn/hisax/siemens.h b/drivers/isdn/hisax/siemens.h new file mode 100644 index 000000000000..f356a30a9474 --- /dev/null +++ b/drivers/isdn/hisax/siemens.h @@ -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 --git a/drivers/isdn/hisax/tei.c b/drivers/isdn/hisax/tei.c new file mode 100644 index 000000000000..f13f9cd1fa47 --- /dev/null +++ b/drivers/isdn/hisax/tei.c @@ -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 --git a/drivers/isdn/hisax/teles0.c b/drivers/isdn/hisax/teles0.c new file mode 100644 index 000000000000..2e063fdd9904 --- /dev/null +++ b/drivers/isdn/hisax/teles0.c @@ -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 --git a/drivers/isdn/hisax/teles0.h b/drivers/isdn/hisax/teles0.h new file mode 100644 index 000000000000..3baaa5cbd0fd --- /dev/null +++ b/drivers/isdn/hisax/teles0.h @@ -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 --git a/drivers/isdn/hisax/teles3.c b/drivers/isdn/hisax/teles3.c new file mode 100644 index 000000000000..d93ca76d1ece --- /dev/null +++ b/drivers/isdn/hisax/teles3.c @@ -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 --git a/drivers/isdn/hisax/teles3.h b/drivers/isdn/hisax/teles3.h new file mode 100644 index 000000000000..8f8dc808126c --- /dev/null +++ b/drivers/isdn/hisax/teles3.h @@ -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 --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c index f65becc50d5e..1d61ac8d11fb 100644 --- a/drivers/isdn/icn/icn.c +++ b/drivers/isdn/icn/icn.c @@ -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 @@ static void icn_free_queue(struct sk_buff_head *queue) * 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 @@ static inline void icn_map_channel(icn_card *card, int channel) * 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 @@ static inline void icn_maprelease_channel(icn_card *card, int channel) * 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 @@ static void icn_pollbchan_receive(int channel, icn_card *card) * 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; - - 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; + 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; } 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 @@ static void icn_pollbchan_send(int channel, icn_card *card) * 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; - - 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; + 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; } 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 @@ static icn_stat icn_stat_table[] = { * 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->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; + 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) +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); + 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) +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 @@ static void icn_polldchan(unsigned long data) * 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 @@ static int icn_sendbuf(int channel, struct sk_buff *skb, icn_card * card) * 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 @@ int slsec = sec; \ #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)); + 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) +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; + 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; - - 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; + 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; } /* 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; - - 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); + 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) +static void +icn_stopallcards(void) { - icn_card *p = cards; + icn_card *p = cards; - while (p) { - icn_stopcard(p); - p = p->next; - } + while (p) { + icn_stopcard(p); + p = p->next; + } } -static int icn_command(isdn_ctrl * c, icn_card * card) +/* + * Unmap all cards, because some of them may be mapped accidetly during + * autoprobing of some network drivers (SMC-driver?) + */ +static void +icn_disable_cards(void) { - 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; + icn_card *card = cards; + unsigned long flags; + + 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\n)02;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; } /* * Find card with given driverId */ static inline icn_card * - icn_findcard(int driverid) +icn_findcard(int driverid) { - icn_card *p = cards; - - while (p) { - if (p->myid == driverid) - return p; - p = p->next; - } - return (icn_card *)0; + icn_card *p = cards; + + while (p) { + if (p->myid == driverid) + return p; + p = p->next; + } + return (icn_card *) 0; } /* * Wrapper functions for interface to linklevel */ -static int if_command(isdn_ctrl * c) +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; + 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) +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; + 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 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; + 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) +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; + 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; + 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) +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; + 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) +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)); + 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 --git a/drivers/isdn/icn/icn.h b/drivers/isdn/icn/icn.h index 7ce78b434b61..991d57e4ddee 100644 --- a/drivers/isdn/icn/icn.h +++ b/drivers/isdn/icn/icn.h @@ -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 @@ typedef struct icn_cdef { #include #include -#endif /* __KERNEL__ */ +#endif /* __KERNEL__ */ /* some useful macros for debugging */ #ifdef ICN_DEBUG_PORT @@ -155,138 +169,154 @@ typedef struct icn_cdef { #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_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_BOOT_TIMEOUT1 (HZ) /* Delay for Boot-download (jiffies) */ +#define ICN_CHANLOCK_DELAY (HZ/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_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_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_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 @@ char *icn_id2 = "\0"; /* 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 @@ char *icn_id2 = "\0"; /* 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 @@ char *icn_id2 = "\0"; /* 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 @@ char *icn_id2 = "\0"; /* 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 @@ char *icn_id2 = "\0"; #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 --git a/drivers/isdn/isdn_audio.c b/drivers/isdn/isdn_audio.c index a356b512e7c5..89446a4fa216 100644 --- a/drivers/isdn/isdn_audio.c +++ b/drivers/isdn/isdn_audio.c @@ -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 @@ static char isdn_audio_ulaw_to_alaw[] = { #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 @@ isdn_audio_alaw2ulaw(unsigned char *buff, unsigned long len) */ -#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 @@ isdn_audio_xlaw2adpcm (adpcm_state *s, int fmt, unsigned char *in, * 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 @@ isdn_audio_eval_dtmf(modem_info *info) * 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 --git a/drivers/isdn/isdn_audio.h b/drivers/isdn/isdn_audio.h index a4a5c9a13b22..0cf8267cfa59 100644 --- a/drivers/isdn/isdn_audio.h +++ b/drivers/isdn/isdn_audio.h @@ -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 --git a/drivers/isdn/isdn_cards.c b/drivers/isdn/isdn_cards.c index a70d6992ec33..c7f4e8b08c89 100644 --- a/drivers/isdn/isdn_cards.c +++ b/drivers/isdn/isdn_cards.c @@ -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 HiSax_init(void); 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 --git a/drivers/isdn/isdn_cards.h b/drivers/isdn/isdn_cards.h index 5bece603b93b..e6e2aa12763f 100644 --- a/drivers/isdn/isdn_cards.h +++ b/drivers/isdn/isdn_cards.h @@ -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 --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c index 61212368e101..86ef2fbe0a3a 100644 --- a/drivers/isdn/isdn_common.c +++ b/drivers/isdn/isdn_common.c @@ -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 @@ extern char *isdn_audio_revision; 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 @@ void isdn_dumppkt(char *s, u_char * p, int len, int dumplen) } #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; - - save_flags(flags); - cli(); - if (skb_queue_len(queue)) - while ((skb = skb_dequeue(queue))) - isdn_trash_skb(skb, FREE_READ); - restore_flags(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); } -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 @@ int isdn_dc2minor(int di, int ch) 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 @@ static void isdn_timer_funct(ulong dummy) 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 @@ void isdn_timer_ctrl(int tf, int onoff) 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; + } + if (retval != 1) { + cmd.driver = di; + cmd.arg = c->arg; + cmd.command = ISDN_CMD_UNLOCK; + dev->drv[di]->interface->command(&cmd); } - 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 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); + 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"); -#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 @@ static int isdn_status_callback(isdn_ctrl * c) /* * 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 @@ int isdn_getnum(char **p) * 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 @@ int isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, int 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 @@ static char * /* Module interface-code */ -void isdn_info_update(void) +void +isdn_info_update(void) { infostruct *p = dev->infochain; @@ -924,7 +848,8 @@ void isdn_info_update(void) 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 @@ static RWTYPE isdn_read(struct inode *inode, struct file *file, char *buf, RWARG 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 @@ static RWTYPE isdn_read(struct inode *inode, struct file *file, char *buf, RWARG 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 @@ static RWTYPE isdn_read(struct inode *inode, struct file *file, char *buf, RWARG 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 @@ static RWTYPE isdn_read(struct inode *inode, struct file *file, char *buf, RWARG 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 @@ static RWTYPE isdn_read(struct inode *inode, struct file *file, char *buf, RWARG 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 @@ static RWTYPE isdn_write(struct inode *inode, struct file *file, const char *buf 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 @@ static int isdn_select(struct inode *inode, struct file *file, int type, select_ 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 @@ static int isdn_set_allcfg(char *src) 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 @@ static int isdn_set_allcfg(char *src) 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 @@ static int isdn_get_allcfg(char *dest) 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 @@ static int isdn_get_allcfg(char *dest) 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 @@ static int isdn_get_allcfg(char *dest) 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 @@ static int isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong ar 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); + 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) { - 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 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 @@ static int isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong ar 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 @@ static int isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong ar * 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 @@ static int isdn_open(struct inode *ino, struct file *filep) 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; @@ -1695,37 +1660,39 @@ static void isdn_close(struct inode *ino, struct file *filep) else dev->infochain = (infostruct *) (p->next); kfree(p); - return; + return CLOSEVAL; + kfree(p); } q = p; p = (infostruct *) (p->next); } + return CLOSEVAL; printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n"); - return; } if (minor < ISDN_MINOR_CTRL) { drvidx = isdn_minor2drv(minor); + return CLOSEVAL; if (drvidx < 0) - return; c.command = ISDN_CMD_UNLOCK; c.driver = drvidx; + return CLOSEVAL; (void) dev->drv[drvidx]->interface->command(&c); - return; } if (minor <= ISDN_MINOR_CTRLMAX) { drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); + return CLOSEVAL; if (drvidx < 0) - return; if (dev->profd == current) dev->profd = NULL; c.command = ISDN_CMD_UNLOCK; c.driver = drvidx; + return CLOSEVAL; (void) dev->drv[drvidx]->interface->command(&c); - return; } #ifdef CONFIG_ISDN_PPP if (minor <= ISDN_MINOR_PPPMAX) isdn_ppp_release(minor - ISDN_MINOR_PPP, filep); + return CLOSEVAL; #endif } @@ -1734,17 +1701,21 @@ static struct file_operations isdn_fops = 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; @@ -1762,13 +1733,14 @@ char * * 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(); @@ -1786,10 +1758,10 @@ int isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev 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 { @@ -1797,10 +1769,10 @@ int isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev 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; } @@ -1815,7 +1787,8 @@ int isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev /* * 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; @@ -1829,15 +1802,15 @@ void isdn_free_channel(int di, int ch, int usage) (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); @@ -1846,7 +1819,8 @@ void isdn_free_channel(int di, int ch, int usage) /* * 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; @@ -1874,55 +1848,57 @@ void isdn_unexclusive_channel(int di, int ch) * 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; } /* @@ -1933,32 +1909,36 @@ int isdn_writebuf_stub(int drvidx, int chan, const u_char *buf, int len, * 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; @@ -1974,9 +1954,9 @@ int register_isdn(isdn_if * i) 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; @@ -1995,17 +1975,17 @@ int register_isdn(isdn_if * i) 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"); @@ -2039,17 +2019,17 @@ int register_isdn(isdn_if * i) 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) { @@ -2078,7 +2058,8 @@ extern int printk(const char *fmt,...); #define isdn_init init_module #endif -static char *isdn_getrev(const char *revision) +static char * +isdn_getrev(const char *revision) { char *rev; char *p; @@ -2092,29 +2073,18 @@ static char *isdn_getrev(const char *revision) 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))) { @@ -2122,10 +2092,8 @@ int isdn_init(void) 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; @@ -2158,21 +2126,20 @@ int isdn_init(void) 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"); @@ -2188,7 +2155,8 @@ int isdn_init(void) /* * Unload module */ -void cleanup_module(void) +void +cleanup_module(void) { int flags; int i; @@ -2214,9 +2182,9 @@ void cleanup_module(void) 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 --git a/drivers/isdn/isdn_common.h b/drivers/isdn/isdn_common.h index 81d7905a2d71..d0df7fe7965a 100644 --- a/drivers/isdn/isdn_common.h +++ b/drivers/isdn/isdn_common.h @@ -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 --git a/drivers/isdn/isdn_net.c b/drivers/isdn/isdn_net.c index e1da45bf34d4..65a58c1ae926 100644 --- a/drivers/isdn/isdn_net.c +++ b/drivers/isdn/isdn_net.c @@ -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 @@ isdn_net_open(struct device *dev) 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 @@ isdn_net_open(struct device *dev) p = (((isdn_net_local *) p->priv)->slave); } } - isdn_MOD_INC_USE_COUNT(); return 0; } @@ -211,15 +283,15 @@ isdn_net_open(struct device *dev) 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 @@ isdn_net_unbind_channel(isdn_net_local * lp) 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 @@ void 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 @@ isdn_net_stat_callback(int idx, int cmd) 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 @@ isdn_net_stat_callback(int idx, int cmd) 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 @@ isdn_net_stat_callback(int idx, int cmd) * 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_dial(void) 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_net_hangup(struct device *d) 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 @@ isdn_net_hangup(struct device *d) 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 @@ typedef struct { 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 @@ isdn_net_log_packet(u_char * buf, isdn_net_local * lp) p[14], p[15], p[16], p[17], p[24], p[25], p[26], p[27]); break; - } + } } /* @@ -772,28 +890,27 @@ isdn_net_log_packet(u_char * buf, isdn_net_local * lp) * 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 @@ isdn_net_send_skb(struct device *ndev, isdn_net_local *lp, */ 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 @@ isdn_net_xmit(struct device *ndev, isdn_net_local *lp, struct sk_buff *skb) /* 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 @@ isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev) 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 @@ isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev) 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 @@ isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev) 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 @@ isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev) 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 @@ isdn_net_close(struct device *dev) 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 @@ isdn_net_close(struct device *dev) p = (((isdn_net_local *) p->priv)->slave); } } + isdn_net_hangup(dev); isdn_MOD_DEC_USE_COUNT(); return 0; } @@ -1016,7 +1127,7 @@ isdn_net_close(struct device *dev) * 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 @@ static struct enet_statistics * * 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_receive(struct device *ndev, struct sk_buff *skb) { 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 @@ isdn_net_receive(struct device *ndev, struct sk_buff *skb) 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; + 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; + 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 @@ isdn_net_receive(struct device *ndev, struct sk_buff *skb) * 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 @@ isdn_net_rcv_skb(int idx, struct sk_buff *skb) 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 @@ my_eth_header(struct sk_buff *skb, struct device *dev, unsigned short type, * 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; - - 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 - } + int ret = 0; + + 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 + } 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 @@ static int 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 @@ isdn_net_init(struct device *ndev) 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; - - for (i = 0; i < ETH_ALEN; i++) - ndev->broadcast[i]=0xff; + 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 < 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 @@ isdn_net_wildmat(char *s, char *p) 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 @@ isdn_net_swapbind(int drvidx) 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 @@ isdn_net_swap_usage(int i1, int i2) * 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_find_icall(int di, int ch, int idx, char *num) 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 @@ isdn_net_find_icall(int di, int ch, int idx, char *num) 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 @@ isdn_net_find_icall(int di, int ch, int idx, char *num) 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 @@ isdn_net_find_icall(int di, int ch, int idx, char *num) 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 @@ isdn_net_find_icall(int di, int ch, int idx, char *num) #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 @@ isdn_net_find_icall(int di, int ch, int idx, char *num) 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 @@ isdn_net_find_icall(int di, int ch, int idx, char *num) 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 @@ isdn_net_find_icall(int di, int ch, int idx, char *num) #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 @@ isdn_net_find_icall(int di, int ch, int idx, char *num) 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 @@ isdn_net_find_icall(int di, int ch, int idx, char *num) * 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 @@ isdn_net_dev * * 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 @@ int isdn_net_force_dial_lp(isdn_net_local * lp) 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 @@ int isdn_net_force_dial_lp(isdn_net_local * lp) * 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 @@ isdn_net_new(char *name, struct device *master) 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 @@ isdn_net_new(char *name, struct device *master) 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 @@ isdn_net_new(char *name, struct device *master) 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 @@ isdn_net_newslave(char *parm) /* 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 @@ isdn_net_newslave(char *parm) * 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 @@ int isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) 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 @@ int isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) /* 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 @@ int isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) 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 @@ int isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) 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 @@ int isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) /* * 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 @@ int isdn_net_getcfg(isdn_net_ioctl_cfg * cfg) 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 @@ int isdn_net_getcfg(isdn_net_ioctl_cfg * cfg) /* * 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 @@ int isdn_net_addphone(isdn_net_ioctl_phone * phone) /* * 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 @@ int isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones) 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 @@ int isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones) * 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 @@ int isdn_net_delphone(isdn_net_ioctl_phone * phone) m = n; n = (isdn_net_phone *) n->next; } + restore_flags(flags); return -EINVAL; } return -ENODEV; @@ -2298,7 +2443,8 @@ int isdn_net_delphone(isdn_net_ioctl_phone * phone) /* * 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 @@ static int isdn_net_rmallphone(isdn_net_dev * p) } p->local.phone[i] = NULL; } + p->local.dial = NULL; restore_flags(flags); return 0; } @@ -2323,7 +2470,8 @@ static int isdn_net_rmallphone(isdn_net_dev * p) /* * 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 @@ int isdn_net_force_hangup(char *name) 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 @@ int isdn_net_force_hangup(char *name) /* * 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 @@ static int isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q) 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 @@ static int isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q) /* * 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 @@ int isdn_net_rm(char *name) /* * Remove all network-interfaces */ -int isdn_net_rmall(void) +int +isdn_net_rmall(void) { int flags; int ret; @@ -2446,7 +2594,7 @@ int isdn_net_rmall(void) /* 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 @@ int isdn_net_rmall(void) 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 --git a/drivers/isdn/isdn_net.h b/drivers/isdn/isdn_net.h index 5b6754e66271..56df21081e9c 100644 --- a/drivers/isdn/isdn_net.h +++ b/drivers/isdn/isdn_net.h @@ -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 --git a/drivers/isdn/isdn_ppp.c b/drivers/isdn/isdn_ppp.c index f7ec2e7d4fc3..c13dadd485f7 100644 --- a/drivers/isdn/isdn_ppp.c +++ b/drivers/isdn/isdn_ppp.c @@ -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 @@ int isdn_ppp_free(isdn_net_local *lp) 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 @@ int isdn_ppp_free(isdn_net_local *lp) 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 @@ int isdn_ppp_free(isdn_net_local *lp) /* * 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 @@ int isdn_ppp_bind(isdn_net_local * lp) 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 @@ int isdn_ppp_bind(isdn_net_local * lp) * 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 @@ int isdn_ppp_bind(isdn_net_local * lp) 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 @@ int isdn_ppp_bind(isdn_net_local * lp) * (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 @@ void isdn_ppp_wakeup_daemon(isdn_net_local *lp) /* * 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 @@ static int isdn_ppp_closewait(int slot) * 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); + + /* 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 = 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 */ + 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 @@ int isdn_ppp_open(int min, struct file *file) /* * 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 @@ void isdn_ppp_release(int min, struct file *file) 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 @@ void isdn_ppp_release(int min, struct file *file) 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 @@ void isdn_ppp_release(int min, struct file *file) /* * 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 @@ static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot) 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 @@ static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot) 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 @@ static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot) 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 @@ static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot) * 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 @@ int isdn_ppp_read(int min, struct file *file, char *buf, int count) 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 @@ int isdn_ppp_write(int min, struct file *file, const char *buf, int count) 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 isdn_ppp_write(int min, struct file *file, const char *buf, int count) 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); + 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); + 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 @@ int isdn_ppp_write(int min, struct file *file, const char *buf, int count) } /* - * 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 @@ int isdn_ppp_init(void) 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 @@ void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buf 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 @@ void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buf } } 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 @@ void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buf * 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 @@ void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buf 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 @@ void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buf } } } 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 @@ static void isdn_ppp_push_higher(isdn_net_dev *net_dev, isdn_net_local *lp, stru } /* - * 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 @@ printk(KERN_DEBUG "readdressing %lx to %lx\n",ipfr->saddr,mdev->pa_addr); } 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 @@ printk(KERN_DEBUG "readdressing %lx to %lx\n",ipfr->saddr,mdev->pa_addr); } #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 @@ printk(KERN_DEBUG "readdressing %lx to %lx\n",ipfr->saddr,mdev->pa_addr); 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 */ + /* + * 'link' compression + */ + skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1); - 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( (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 */ } - 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 + /* 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 @@ printk(KERN_DEBUG "readdressing %lx to %lx\n",ipfr->saddr,mdev->pa_addr); #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_sqqueue(isdn_net_dev * p) } -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 int isdn_ppp_bundle(struct ippp_struct *is, int unit) } -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 void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask) } } -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 @@ static int isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff ** skb, int 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 @@ static int isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff ** skb, int 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 @@ static int isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff ** skb, int * 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 @@ static void isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min_sqno) /* 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 @@ static void isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min_sqno) #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 @@ void isdn_ppp_timer_timeout(void) 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 @@ void isdn_ppp_timer_timeout(void) * 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 @@ int isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd) 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 @@ int isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd) 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 @@ int isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd) 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 @@ int isdn_ppp_dial_slave(char *name) #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 @@ int isdn_ppp_hangup_slave(char *name) #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 -static struct symbol_table isdn_ppp_syms = { + 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 = +{ #include - X(isdn_ppp_register_compressor), - X(isdn_ppp_unregister_compressor), + X(isdn_ppp_register_compressor), + X(isdn_ppp_unregister_compressor), #include }; #endif + + diff --git a/drivers/isdn/isdn_ppp.h b/drivers/isdn/isdn_ppp.h index 4568f8d2b1fe..d19f1e09e351 100644 --- a/drivers/isdn/isdn_ppp.h +++ b/drivers/isdn/isdn_ppp.h @@ -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 --git a/drivers/isdn/isdn_syms.c b/drivers/isdn/isdn_syms.c new file mode 100644 index 000000000000..567f8dda95bf --- /dev/null +++ b/drivers/isdn/isdn_syms.c @@ -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 --git a/drivers/isdn/isdn_tty.c b/drivers/isdn/isdn_tty.c index 3a0b7ab0e557..a5f31eaf3e29 100644 --- a/drivers/isdn/isdn_tty.c +++ b/drivers/isdn/isdn_tty.c @@ -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 @@ char *isdn_tty_revision = "$Revision: 1.23 $"; * 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 @@ int isdn_tty_try_read(modem_info *info, struct sk_buff *skb) * 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 @@ void isdn_tty_readmodem(void) 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 @@ void isdn_tty_readmodem(void) 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 @@ void isdn_tty_readmodem(void) } 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) +{ + ulong flags; + int midx; +#ifdef CONFIG_ISDN_AUDIO + int ifmt; +#endif + modem_info *info; + + 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))) { - 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); + 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 + 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) +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); + 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 -int isdn_tty_countDLE(unsigned char *buf, int len) +static int +isdn_tty_countDLE(unsigned char *buf, int len) { - int count = 0; + int count = 0; - while (len--) - if (*buf++ == DLE) - count++; - return count; + 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; - - 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; + 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; +#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; + 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 & 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); + 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 - } -#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; + 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 @@ static void isdn_tty_senddown(modem_info * info) * 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; - } + 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 @@ static void isdn_tty_dial(char *n, modem_info * info, atemu * m) 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 @@ static void isdn_tty_dial(char *n, modem_info * info, atemu * m) * 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); -#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; + printk(KERN_DEBUG "Mhup ttyI%d\n", info->line); +#endif + 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 - 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->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; 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 @@ static inline int isdn_tty_paranoia_check(modem_info * info, kdev_t device, cons * 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 @@ static void isdn_tty_change_speed(modem_info * info) } 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 void isdn_tty_change_speed(modem_info * info) } } -static int isdn_tty_startup(modem_info * info) +static int +isdn_tty_startup(modem_info * info) { ulong flags; @@ -750,12 +978,12 @@ static int isdn_tty_startup(modem_info * info) 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 @@ static int isdn_tty_startup(modem_info * info) * 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 @@ static void isdn_tty_shutdown(modem_info * info) 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 @@ static void isdn_tty_shutdown(modem_info * info) * - 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 @@ static int isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * 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); + if ((info->online > 1) +#ifdef CONFIG_ISDN_AUDIO + || (info->vonline & 3) +#endif + ) { + atemu *m = &info->emu; + +#ifdef CONFIG_ISDN_AUDIO + 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 & 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 -#endif - info->xmit_count += c; + 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 + info->xmit_count += c; if (m->mdmreg[13] & 1) { - isdn_tty_senddown(info); - isdn_tty_tint(info); - } + isdn_tty_senddown(info); + isdn_tty_tint(info); + } } else { - info->msr |= UART_MSR_CTS; - info->lsr |= UART_LSR_TEMT; -#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 -#endif - if (info->dialing) { - info->dialing = 0; + 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 @@ static int isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * } 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 @@ static int isdn_tty_write_room(struct tty_struct *tty) 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 @@ static int isdn_tty_chars_in_buffer(struct tty_struct *tty) 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; - - save_flags(flags); - cli(); - if (!tty) { - restore_flags(flags); - return; - } - info = (modem_info *) tty->driver_data; + unsigned long flags; + + 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 @@ static void isdn_tty_flush_chars(struct tty_struct *tty) /* * ------------------------------------------------------------ * 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 @@ static void isdn_tty_throttle(struct tty_struct *tty) 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 @@ static void isdn_tty_unthrottle(struct tty_struct *tty) * 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_lsr_info(modem_info * info, uint * value) } -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 @@ static int isdn_tty_get_modem_info(modem_info * info, uint * value) 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); -#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: + 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: #ifdef ISDN_DEBUG_MODEM_IOCTL - 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); + 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); #ifdef ISDN_DEBUG_MODEM_HUP - printk(KERN_DEBUG "Mhup in TIOCMBIC\n"); -#endif - if (info->online) - info->ncarrier = 1; - isdn_tty_modem_hup(info); - } - } - break; - case TIOCMSET: + printk(KERN_DEBUG "Mhup in TIOCMBIC\n"); +#endif + 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); -#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); + 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); #ifdef ISDN_DEBUG_MODEM_HUP - 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; + printk(KERN_DEBUG "Mhup in TIOCMSET\n"); +#endif + 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 @@ static int isdn_tty_ioctl(struct tty_struct *tty, struct file *file, 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); -#endif - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ + 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() */ #ifdef ISDN_DEBUG_MODEM_IOCTL - 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: + 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: #ifdef ISDN_DEBUG_MODEM_IOCTL - 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: + 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: #ifdef ISDN_DEBUG_MODEM_IOCTL - 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: + 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: #ifdef ISDN_DEBUG_MODEM_IOCTL - 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 */ + 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 */ #ifdef ISDN_DEBUG_MODEM_IOCTL - 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: + 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: #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 @@ static void isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_ter * 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 @@ static int isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, m */ 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 @@ static int isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, m * 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 @@ static int isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, m 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 @@ static int isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, m * 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 @@ static int isdn_tty_open(struct tty_struct *tty, struct file *filp) 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 @@ static void isdn_tty_close(struct tty_struct *tty, struct file *filp) 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 @@ static void isdn_tty_close(struct tty_struct *tty, struct file *filp) * 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 @@ static void isdn_tty_close(struct tty_struct *tty, struct file *filp) 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 @@ static void isdn_tty_close(struct tty_struct *tty, struct file *filp) /* * 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 @@ static void isdn_tty_hangup(struct tty_struct *tty) /* 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 @@ static void isdn_tty_reset_profile(atemu * m) 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 @@ static void modem_write_profile(atemu * m) 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 @@ int isdn_tty_modem_init(void) } 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 @@ int isdn_tty_modem_init(void) 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 @@ int isdn_tty_modem_init(void) * 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 @@ int isdn_tty_find_icall(int di, int ch, char *num) 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 @@ int isdn_tty_find_icall(int di, int ch, char *num) 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 @@ int isdn_tty_find_icall(int di, int ch, char *num) * 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 @@ static void isdn_tty_at_cout(char *msg, modem_info * info) 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 @@ static void isdn_tty_at_cout(char *msg, modem_info * info) /* * 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 @@ static void isdn_tty_off_hook(void) * 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 @@ static void isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pl * 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 @@ void isdn_tty_modem_result(int code, modem_info * info) "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 @@ void isdn_tty_modem_result(int code, modem_info * info) 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 @@ void isdn_tty_modem_result(int code, modem_info * info) 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 @@ void isdn_tty_modem_result(int code, modem_info * info) /* * 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 @@ static void isdn_tty_show_profile(int ridx, modem_info * info) /* * 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 @@ static void isdn_tty_get_msnstr(char *n, char **p) /* * 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 @@ static void isdn_tty_getdial(char *p, char *q) #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; + 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; + 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]; + 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; -#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); + /* 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); -#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; + 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 - 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); + if (!info->vonline) +#endif + isdn_tty_modem_result(0, info); } /* Need own toupper() because standard-toupper is not available @@ -2669,7 +3246,8 @@ static void isdn_tty_parse_at(modem_info * info) * 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 @@ static int isdn_tty_edit_at(const char *p, int count, modem_info * info, int use 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 @@ static int isdn_tty_edit_at(const char *p, int count, modem_info * info, int use /* * 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 @@ void isdn_tty_modem_escape(void) 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 @@ void isdn_tty_modem_escape(void) 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 @@ void isdn_tty_modem_ring(void) * 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 --git a/drivers/isdn/isdn_tty.h b/drivers/isdn/isdn_tty.h index f317d23ae3d2..986cb54f7640 100644 --- a/drivers/isdn/isdn_tty.h +++ b/drivers/isdn/isdn_tty.h @@ -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 --git a/drivers/isdn/pcbit/callbacks.c b/drivers/isdn/pcbit/callbacks.c index f3051e2e5102..14c0bdcb89cd 100644 --- a/drivers/isdn/pcbit/callbacks.c +++ b/drivers/isdn/pcbit/callbacks.c @@ -164,18 +164,12 @@ void cb_in_1(struct pcbit_dev * dev, struct pcbit_chan* chan, * 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 --git a/drivers/isdn/pcbit/capi.c b/drivers/isdn/pcbit/capi.c index 1c752835da30..790da879248d 100644 --- a/drivers/isdn/pcbit/capi.c +++ b/drivers/isdn/pcbit/capi.c @@ -147,7 +147,7 @@ int capi_conn_resp(struct pcbit_chan* chan, struct sk_buff **skb) return -1; } - (*skb)->free = 1; + SET_SKB_FREE((*skb)); *((ushort*) skb_put(*skb, 2) ) = chan->callref; @@ -170,7 +170,7 @@ int capi_conn_active_req(struct pcbit_chan* chan, struct sk_buff **skb) return -1; } - (*skb)->free = 1; + SET_SKB_FREE((*skb)); *((ushort*) skb_put(*skb, 2) ) = chan->callref; @@ -200,7 +200,7 @@ int capi_conn_active_resp(struct pcbit_chan* chan, struct sk_buff **skb) return -1; } - (*skb)->free = 1; + SET_SKB_FREE((*skb)); *((ushort*) skb_put(*skb, 2) ) = chan->callref; @@ -222,7 +222,7 @@ int capi_select_proto_req(struct pcbit_chan *chan, struct sk_buff **skb, return -1; } - (*skb)->free = 1; + SET_SKB_FREE((*skb)); *((ushort*) skb_put(*skb, 2) ) = chan->callref; @@ -285,7 +285,7 @@ int capi_activate_transp_req(struct pcbit_chan *chan, struct sk_buff **skb) return -1; } - (*skb)->free = 1; + SET_SKB_FREE((*skb)); *((ushort*) skb_put(*skb, 2) ) = chan->callref; @@ -338,7 +338,7 @@ int capi_tdata_resp(struct pcbit_chan *chan, struct sk_buff ** skb) return -1; } - (*skb)->free = 1; + SET_SKB_FREE((*skb)); *((ushort*) skb_put(*skb, 2) ) = chan->callref; @@ -357,7 +357,7 @@ int capi_disc_req(ushort callref, struct sk_buff **skb, u_char cause) return -1; } - (*skb)->free = 1; + SET_SKB_FREE((*skb)); *((ushort*) skb_put(*skb, 2) ) = callref; @@ -382,7 +382,7 @@ int capi_disc_resp(struct pcbit_chan *chan, struct sk_buff **skb) return -1; } - (*skb)->free = 1; + SET_SKB_FREE((*skb)); *((ushort*) skb_put(*skb, 2)) = chan->callref; diff --git a/drivers/isdn/pcbit/drv.c b/drivers/isdn/pcbit/drv.c index dc386a133872..e7d765a95c87 100644 --- a/drivers/isdn/pcbit/drv.c +++ b/drivers/isdn/pcbit/drv.c @@ -226,7 +226,6 @@ int pcbit_command(isdn_ctrl* ctl) struct pcbit_dev *dev; struct pcbit_chan *chan; struct callb_data info; - char *cp; dev = finddev(ctl->driver); @@ -245,14 +244,7 @@ int pcbit_command(isdn_ctrl* ctl) 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 @@ int pcbit_command(isdn_ctrl* ctl) 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 @@ int pcbit_writecmd(const u_char* buf, int len, int user, int driver, int channel 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 @@ void pcbit_l3_receive(struct pcbit_dev * dev, ulong msg, #endif } - skb->free = 1; + SET_SKB_FREE(skb); kfree_skb(skb, FREE_READ); @@ -944,7 +930,7 @@ static int pcbit_ioctl(isdn_ctrl* ctl) return -ENODEV; } - cmd = (struct pcbit_ioctl *) ctl->num; + cmd = (struct pcbit_ioctl *) ctl->parm.num; switch(ctl->arg) { case PCBIT_IOCTL_GETSTAT: diff --git a/drivers/isdn/pcbit/layer2.c b/drivers/isdn/pcbit/layer2.c index 4410d1e0c2d1..96e479761499 100644 --- a/drivers/isdn/pcbit/layer2.c +++ b/drivers/isdn/pcbit/layer2.c @@ -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_error(struct pcbit_dev *dev); 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 @@ static __inline__ void pcbit_sched_delivery(struct pcbit_dev *dev) * Called from layer3 */ -int pcbit_l2_write(struct pcbit_dev * dev, ulong msg, ushort refnum, - struct sk_buff *skb, unsigned short hdr_len) - +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; + 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"); + 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; - 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; - 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 @@ static __inline__ void pcbit_tx_update(struct pcbit_dev *dev, ushort len) * 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 @@ static void pcbit_transmit(struct pcbit_dev * dev) flen = MIN(totlen, free); - msg = (struct msg_fmt *) &(frame->msg); + msg = (struct msg_fmt *) &(frame->msg); + + /* + * Board level 2 header + */ + + pcbit_writew(dev, flen - FRAME_HDR_LEN); - /* - * Board level 2 header - */ + pcbit_writeb(dev, msg->cpu); - pcbit_writew(dev, flen - FRAME_HDR_LEN); + pcbit_writeb(dev, msg->proc); - pcbit_writeb(dev, msg->cpu); + /* TH */ + pcbit_writew(dev, frame->hdr_len + PREHDR_LEN); - pcbit_writeb(dev, msg->proc); + /* TD */ + pcbit_writew(dev, frame->dt_len); - /* TH */ - pcbit_writew(dev, frame->hdr_len + PREHDR_LEN); - /* TD */ - pcbit_writew(dev, frame->dt_len); + /* + * Board level 3 fixed-header + */ + /* LEN = TH */ + pcbit_writew(dev, frame->hdr_len + PREHDR_LEN); - /* - * Board level 3 fixed-header - */ - - /* LEN = TH */ - pcbit_writew(dev, frame->hdr_len + PREHDR_LEN); - - /* XX */ - pcbit_writew(dev, 0); + /* XX */ + pcbit_writew(dev, 0); - /* C + S */ - pcbit_writeb(dev, msg->cmd); - pcbit_writeb(dev, msg->scmd); + /* 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 @@ static void pcbit_transmit(struct pcbit_dev * dev) cli(); - if (frame->skb == NULL || frame->copied == frame->skb->len) { - - dev->write_queue = frame->next; - - if (frame->skb != NULL) { - /* free frame */ - dev_kfree_skb(frame->skb, FREE_WRITE); - } + if (frame->skb == NULL || frame->copied == frame->skb->len) { - 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 @@ static void pcbit_transmit(struct pcbit_dev * dev) * 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 @@ void pcbit_deliver(void * data) 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 @@ void pcbit_deliver(void * data) } /* - * 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); - - - if (cpu != 0x06 && cpu != 0x02) - { - printk (KERN_DEBUG "pcbit: invalid cpu value\n"); + } + 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"); 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 @@ static void pcbit_receive(struct pcbit_dev * dev) 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 @@ static void pcbit_receive(struct pcbit_dev * dev) * 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; - - if (chan->queued) { - chan->queued--; - - ictl.driver = dev->id; - ictl.command = ISDN_STAT_BSENT; - ictl.arg = chan->id; - dev->dev_if->statcallb(&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); + } } -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); + 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; + 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_err_recover(unsigned long data) } -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 @@ static void pcbit_l2_error(struct pcbit_dev *dev) * 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 */ + 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; + i = dev->unack_seq; - do { - dev->unack_seq = i = (i + 1) % 8; - dev->free += dev->fsize[i]; - } while (i != ack); + do { + dev->unack_seq = i = (i + 1) % 8; + dev->free += dev->fsize[i]; + } while (i != ack); - count = 0; - while (count < 7 && dev->write_queue) - { + 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 --git a/drivers/isdn/pcbit/module.c b/drivers/isdn/pcbit/module.c index 2f2aa374f1ce..29e11b4866b2 100644 --- a/drivers/isdn/pcbit/module.c +++ b/drivers/isdn/pcbit/module.c @@ -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 void pcbit_terminate(int board); 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 @@ int pcbit_init(void) } /* No symbols to export, hide all symbols */ +#if (LINUX_VERSION_CODE < 0x020111) register_symtab(NULL); +#else + EXPORT_NO_SYMBOLS; +#endif return 0; } diff --git a/drivers/isdn/sc/Makefile b/drivers/isdn/sc/Makefile new file mode 100644 index 000000000000..ff87210b87d1 --- /dev/null +++ b/drivers/isdn/sc/Makefile @@ -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 --git a/drivers/isdn/sc/card.h b/drivers/isdn/sc/card.h new file mode 100644 index 000000000000..3e5370f347d1 --- /dev/null +++ b/drivers/isdn/sc/card.h @@ -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 --git a/drivers/isdn/sc/command.c b/drivers/isdn/sc/command.c new file mode 100644 index 000000000000..28accdc0fbf7 --- /dev/null +++ b/drivers/isdn/sc/command.c @@ -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 --git a/drivers/isdn/sc/debug.c b/drivers/isdn/sc/debug.c new file mode 100644 index 000000000000..3a814de931bb --- /dev/null +++ b/drivers/isdn/sc/debug.c @@ -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 --git a/drivers/isdn/sc/debug.h b/drivers/isdn/sc/debug.h new file mode 100644 index 000000000000..bb5553514eae --- /dev/null +++ b/drivers/isdn/sc/debug.h @@ -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 --git a/drivers/isdn/sc/event.c b/drivers/isdn/sc/event.c new file mode 100644 index 000000000000..3452cbf36361 --- /dev/null +++ b/drivers/isdn/sc/event.c @@ -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 --git a/drivers/isdn/sc/hardware.h b/drivers/isdn/sc/hardware.h new file mode 100644 index 000000000000..b0f07ac3c9cd --- /dev/null +++ b/drivers/isdn/sc/hardware.h @@ -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 --git a/drivers/isdn/sc/includes.h b/drivers/isdn/sc/includes.h new file mode 100644 index 000000000000..6ce4fb06e5a8 --- /dev/null +++ b/drivers/isdn/sc/includes.h @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "debug.h" diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c new file mode 100644 index 000000000000..eeaecdd8ffdf --- /dev/null +++ b/drivers/isdn/sc/init.c @@ -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 --git a/drivers/isdn/sc/interrupt.c b/drivers/isdn/sc/interrupt.c new file mode 100644 index 000000000000..18f6029da7b4 --- /dev/null +++ b/drivers/isdn/sc/interrupt.c @@ -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 --git a/drivers/isdn/sc/ioctl.c b/drivers/isdn/sc/ioctl.c new file mode 100644 index 000000000000..9915cb0d8cac --- /dev/null +++ b/drivers/isdn/sc/ioctl.c @@ -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 --git a/drivers/isdn/sc/message.c b/drivers/isdn/sc/message.c new file mode 100644 index 000000000000..26f0cfeaf410 --- /dev/null +++ b/drivers/isdn/sc/message.c @@ -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 --git a/drivers/isdn/sc/message.h b/drivers/isdn/sc/message.h new file mode 100644 index 000000000000..9b8bcb025a13 --- /dev/null +++ b/drivers/isdn/sc/message.h @@ -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 --git a/drivers/isdn/sc/packet.c b/drivers/isdn/sc/packet.c new file mode 100644 index 000000000000..ccd3b20fc798 --- /dev/null +++ b/drivers/isdn/sc/packet.c @@ -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 --git a/drivers/isdn/sc/scioc.h b/drivers/isdn/sc/scioc.h new file mode 100644 index 000000000000..19db0b7c1175 --- /dev/null +++ b/drivers/isdn/sc/scioc.h @@ -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 --git a/drivers/isdn/sc/shmem.c b/drivers/isdn/sc/shmem.c new file mode 100644 index 000000000000..6ff0812746b2 --- /dev/null +++ b/drivers/isdn/sc/shmem.c @@ -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 --git a/drivers/isdn/sc/timer.c b/drivers/isdn/sc/timer.c new file mode 100644 index 000000000000..42abb3a42208 --- /dev/null +++ b/drivers/isdn/sc/timer.c @@ -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 --git a/drivers/isdn/teles/Makefile b/drivers/isdn/teles/Makefile deleted file mode 100644 index a252f46ba09f..000000000000 --- a/drivers/isdn/teles/Makefile +++ /dev/null @@ -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 --git a/drivers/isdn/teles/buffers.c b/drivers/isdn/teles/buffers.c deleted file mode 100644 index 2b76d95624d8..000000000000 --- a/drivers/isdn/teles/buffers.c +++ /dev/null @@ -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 --git a/drivers/isdn/teles/callc.c b/drivers/isdn/teles/callc.c deleted file mode 100644 index 7333fdcc3e32..000000000000 --- a/drivers/isdn/teles/callc.c +++ /dev/null @@ -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 --git a/drivers/isdn/teles/card.c b/drivers/isdn/teles/card.c deleted file mode 100644 index 2ada8a29d399..000000000000 --- a/drivers/isdn/teles/card.c +++ /dev/null @@ -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 --git a/drivers/isdn/teles/config.c b/drivers/isdn/teles/config.c deleted file mode 100644 index 1a9d2d87322f..000000000000 --- a/drivers/isdn/teles/config.c +++ /dev/null @@ -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 --git a/drivers/isdn/teles/isdnl2.c b/drivers/isdn/teles/isdnl2.c deleted file mode 100644 index 779b7b70ac7a..000000000000 --- a/drivers/isdn/teles/isdnl2.c +++ /dev/null @@ -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 --git a/drivers/isdn/teles/isdnl3.c b/drivers/isdn/teles/isdnl3.c deleted file mode 100644 index 31f616fa016f..000000000000 --- a/drivers/isdn/teles/isdnl3.c +++ /dev/null @@ -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 --git a/drivers/isdn/teles/l3_1TR6.c b/drivers/isdn/teles/l3_1TR6.c deleted file mode 100644 index 1e237e152752..000000000000 --- a/drivers/isdn/teles/l3_1TR6.c +++ /dev/null @@ -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 --git a/drivers/isdn/teles/llglue.c b/drivers/isdn/teles/llglue.c deleted file mode 100644 index 7e32c2f4f43b..000000000000 --- a/drivers/isdn/teles/llglue.c +++ /dev/null @@ -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 --git a/drivers/isdn/teles/mod.c b/drivers/isdn/teles/mod.c deleted file mode 100644 index 821ac8ee3495..000000000000 --- a/drivers/isdn/teles/mod.c +++ /dev/null @@ -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 --git a/drivers/isdn/teles/proto.h b/drivers/isdn/teles/proto.h deleted file mode 100644 index 0d7ae8ef4a36..000000000000 --- a/drivers/isdn/teles/proto.h +++ /dev/null @@ -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 --git a/drivers/isdn/teles/tei.c b/drivers/isdn/teles/tei.c deleted file mode 100644 index 3ab9f3646000..000000000000 --- a/drivers/isdn/teles/tei.c +++ /dev/null @@ -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 --git a/drivers/isdn/teles/teles.h b/drivers/isdn/teles/teles.h deleted file mode 100644 index 15ce659761d7..000000000000 --- a/drivers/isdn/teles/teles.h +++ /dev/null @@ -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<base_addr) #define NE_CMD 0x00 #define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */ @@ -99,6 +113,7 @@ bad_clone_list[] = { 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 @@ int ne_probe(struct device *dev) 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 @@ int ne_probe(struct device *dev) 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 @@ static int ne_probe1(struct device *dev, int ioaddr) 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 @@ static int ne_probe1(struct device *dev, int ioaddr) } - if (pci_irq_line) { + if (pci_irq_line) dev->irq = pci_irq_line; - } if (dev->irq < 2) { autoirq_setup(0); @@ -713,17 +740,17 @@ init_module(void) 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 --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 74dca22f53af..eddd11b0dc99 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -119,6 +119,7 @@ struct pci_dev_info dev_info[] = { 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 +200,7 @@ struct pci_dev_info dev_info[] = { DEVICE( ZEITNET, ZEITNET_1225, "1225"), DEVICE( SPECIALIX, SPECIALIX_XIO, "XIO/SIO host"), DEVICE( SPECIALIX, SPECIALIX_RIO, "RIO host"), + DEVICE( COMPEX, COMPEX_RL2000, "ReadyLink 2000"), DEVICE( RP, RP8OCTA, "RocketPort 8 Oct"), DEVICE( RP, RP8INTF, "RocketPort 8 Intf"), DEVICE( RP, RP16INTF, "RocketPort 16 Intf"), @@ -482,6 +484,7 @@ const char *pci_strvendor(unsigned int vendor) 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"; @@ -520,6 +523,7 @@ const char *pci_strvendor(unsigned int vendor) 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 --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in index 816cfe532c4a..1c6328fc99d3 100644 --- a/drivers/scsi/Config.in +++ b/drivers/scsi/Config.in @@ -87,5 +87,6 @@ dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F $CONFIG_SCSI 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 --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index caeb342eaae5..f1b16eaa3a71 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -19,6 +19,7 @@ MOD_LIST_NAME := SCSI_MODULES SCSI_SRCS = $(wildcard $(L_OBJS:%.o=%.c)) AHA152X = -DDEBUG_AHA152X -DAUTOCONF +GDTH = #-DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS .SUFFIXES: .SUFFIXES: .c .o .h .a @@ -225,6 +226,14 @@ else 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 @@ -364,6 +373,9 @@ BusLogic.o: BusLogic.c FlashPoint.c aha152x.o: aha152x.c $(CC) $(CFLAGS) $(AHA152X) -c aha152x.c +gdth.o: gdth.c gdth.h gdth_proc.c gdth_proc.h + $(CC) $(CFLAGS) $(GDTH) -c gdth.c + aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h $(CC) $(CFLAGS) -c -o $@ aic7xxx.c diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c new file mode 100644 index 000000000000..e909bf0c6a2c --- /dev/null +++ b/drivers/scsi/gdth.c @@ -0,0 +1,3275 @@ +/************************************************************************ + * GDT ISA/EISA/PCI Disk Array Controller driver for Linux * + * * + * gdth.c * + * Copyright (C) 1995-97 ICP vortex Computersysteme GmbH, Achim Leubner * + * * + * * + * * + * 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 kernel; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + * * + * Tested with Linux 1.2.13, ..., 2.0.29 * + * * + * $Log: gdth.c,v $ + * Revision 1.8 1997/04/02 12:14:30 achim + * Version 1.00 (see gdth.h), tested with kernel 2.0.29 + * + * Revision 1.7 1997/03/12 13:33:37 achim + * gdth_reset() changed, new async. events + * + * Revision 1.6 1997/03/04 14:01:11 achim + * Shutdown routine gdth_halt() implemented + * + * Revision 1.5 1997/02/21 09:08:36 achim + * New controller included (RP, RP1, RP2 series) + * IOCTL interface implemented + * + * Revision 1.4 1996/07/05 12:48:55 achim + * Function gdth_bios_param() implemented + * New constant GDTH_MAXC_P_L inserted + * GDT_WRITE_THR, GDT_EXT_INFO implemented + * Function gdth_reset() changed + * + * Revision 1.3 1996/05/10 09:04:41 achim + * Small changes for Linux 1.2.13 + * + * Revision 1.2 1996/05/09 12:45:27 achim + * Loadable module support implemented + * /proc support corrections made + * + * Revision 1.1 1996/04/11 07:35:57 achim + * Initial revision + * + * + * $Id: gdth.c,v 1.8 1997/04/02 12:14:30 achim Exp $ + ************************************************************************/ + +#ifdef MODULE +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if LINUX_VERSION_CODE >= 0x010300 +#include +#else +#include "../block/blk.h" +#endif +#include "scsi.h" +#include "hosts.h" +#include "sd.h" + +#include "gdth.h" + +#if LINUX_VERSION_CODE >= 0x010346 +static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs); +#else +static void gdth_interrupt(int irq,struct pt_regs *regs); +#endif +static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp); +static int gdth_async_event(int hanum,int service); + +static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority); +static void gdth_next(int hanum); +static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b); +static int gdth_special_cmd(int hanum,Scsi_Cmnd *scp,unchar b); +static gdth_evt_str *gdth_store_event(ushort source, ushort idx, + gdth_evt_data *evt); +static int gdth_read_event(int handle, gdth_evt_str *estr); +static void gdth_readapp_event(unchar application, gdth_evt_str *estr); +static void gdth_clear_events(void); + +static void gdth_copy_internal_data(Scsi_Cmnd *scp,char *buffer,ushort count); +static int gdth_internal_cache_cmd(int hanum,Scsi_Cmnd *scp, + unchar b,ulong *flags); +static int gdth_fill_cache_cmd(int hanum,Scsi_Cmnd *scp,ushort hdrive); + +static int gdth_search_eisa(ushort eisa_adr); +static int gdth_search_isa(ulong bios_adr); +static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr); +static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha,int firsttime); +static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha,int firsttime); +static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha,int firsttime); + +static void gdth_enable_int(int hanum); +static int gdth_get_status(unchar *pIStatus,int irq); +static int gdth_test_busy(int hanum); +static int gdth_get_cmd_index(int hanum); +static void gdth_release_event(int hanum); +static int gdth_wait(int hanum,int index,ulong time); +static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong p1, + ulong p2,ulong p3); +static int gdth_search_drives(int hanum,int firsttime); + +static const char *gdth_ctr_name(int hanum); +void gdth_halt(void); + +#ifdef DEBUG_GDTH +static unchar DebugState = DEBUG_GDTH; +extern int sys_syslog(int,char*,int); +#define LOGEN sys_syslog(7,NULL,0); +#define WAITSEC(a) {ulong idx; for(idx=0;idxjiffies);} +#define GDTOFFSOF(a,b) (size_t)&(((a*)0)->b) +#define INDEX_OK(i,t) ((i)hostdata)) +#define HADATA(a) (&((gdth_ext_str *)((a)->hostdata))->haext) +#define CMDDATA(a) (&((gdth_ext_str *)((a)->hostdata))->cmdext) +#define DMADATA(a) (&((gdth_ext_str *)((a)->hostdata))->dmaext) + +static unchar gdth_drq_tab[4] = {5,6,7,7}; /* DRQ table */ +static unchar gdth_irq_tab[6] = {0,10,11,12,14,0}; /* IRQ table */ +static unchar gdth_polling; /* polling if TRUE */ +static unchar gdth_from_wait = FALSE; /* gdth_wait() */ +static int wait_index,wait_hanum; /* gdth_wait() */ +static int gdth_ctr_count = 0; /* controller count */ +static int gdth_ctr_vcount = 0; /* virt. ctr. count */ +static struct Scsi_Host *gdth_ctr_tab[MAXHA]; /* controller table */ +static struct Scsi_Host *gdth_ctr_vtab[MAXHA*MAXBUS]; /* virt. ctr. table */ +static unchar gdth_write_through = FALSE; /* write through */ +static char *gdth_ioctl_tab[4][MAXHA]; /* ioctl buffer */ +static gdth_evt_str ebuffer[MAX_EVENTS]; /* event buffer */ +static int elastidx; +static int eoldidx; + +static struct { + Scsi_Cmnd *cmnd; /* pending request */ + ushort service; /* service */ +} gdth_cmd_tab[GDTH_MAXCMDS][MAXHA]; /* table of pend. requests */ + +#define DIN 1 /* IN data direction */ +#define DOU 2 /* OUT data direction */ +#define DNO DIN /* no data transfer */ +#define DUN DIN /* unknown data direction */ +static unchar gdth_direction_tab[0x100] = { + DNO,DNO,DIN,DIN,DOU,DIN,DIN,DOU,DIN,DUN,DOU,DOU,DUN,DUN,DUN,DIN, + DNO,DIN,DIN,DOU,DIN,DOU,DNO,DNO,DOU,DNO,DIN,DNO,DIN,DOU,DNO,DUN, + DIN,DUN,DIN,DUN,DOU,DIN,DUN,DUN,DIN,DIN,DIN,DUN,DUN,DIN,DIN,DIN, + DIN,DIN,DIN,DNO,DIN,DNO,DNO,DIN,DIN,DIN,DIN,DIN,DIN,DIN,DIN,DIN, + DIN,DIN,DIN,DIN,DIN,DNO,DUN,DNO,DNO,DNO,DUN,DNO,DIN,DIN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DIN,DUN,DUN,DUN,DUN,DIN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DNO,DNO,DUN,DIN,DNO,DIN,DUN,DNO,DUN,DIN,DIN, + DIN,DIN,DIN,DNO,DUN,DIN,DIN,DIN,DIN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DOU,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN +}; + +/* LILO params: gdth= + * + * Where: is any of the valid IRQs for EISA controllers (10,11,12,14) + * Sets the IRQ of the GDT3000/3020 EISA controller to this value, + * if the IRQ can not automat. detect (controller BIOS disabled) + * See gdth_init_eisa() + * + * You can use the command line gdth=0 to disable the driver + */ +static unchar irqs[MAXHA] = {0xff}; +static unchar disable_gdth_scan = FALSE; + +/* /proc support */ +#if LINUX_VERSION_CODE >= 0x010300 +#include +struct proc_dir_entry proc_scsi_gdth = { + PROC_SCSI_GDTH, 4, "gdth", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; +#include "gdth_proc.h" +#include "gdth_proc.c" +#endif + + +/* controller search and initialization functions */ + +static int gdth_search_eisa(ushort eisa_adr) +{ + ulong id; + + TRACE(("gdth_search_eisa() adr. %x\n",eisa_adr)); + id = inl(eisa_adr+ID0REG); + if (id == GDT3A_ID || id == GDT3B_ID) { /* GDT3000A or GDT3000B */ + if ((inb(eisa_adr+EISAREG) & 8) == 0) + return 0; /* not EISA configured */ + return 1; + } + if (id == GDT3_ID) /* GDT3000 */ + return 1; + + return 0; +} + + +static int gdth_search_isa(ulong bios_adr) +{ + ulong id; + + TRACE(("gdth_search_isa() bios adr. %lx\n",bios_adr)); + id = *(ulong *)(bios_adr+BIOS_ID_OFFS); + if (id == GDT2_ID) /* GDT2000 */ + return 1; + return 0; +} + + +static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr) +{ + int error; + ulong base0,base1,base2; + + TRACE(("gdth_search_pci() device_id %d, index %d\n", + device_id,index)); + + if (!pcibios_present()) + return 0; + + if (pcibios_find_device(PCI_VENDOR_ID_VORTEX,device_id,index, + &pcistr->bus,&pcistr->device_fn)) + return 0; + + /* GDT PCI controller found, now read resources from config space */ +#if LINUX_VERSION_CODE >= 0x010300 +#define GDTH_BASEP (int *) +#else +#define GDTH_BASEP +#endif + if ((error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn, + PCI_BASE_ADDRESS_0, + GDTH_BASEP&base0)) || + (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn, + PCI_BASE_ADDRESS_1, + GDTH_BASEP&base1)) || + (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn, + PCI_BASE_ADDRESS_2, + GDTH_BASEP&base2)) || + (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn, + PCI_ROM_ADDRESS, + GDTH_BASEP&pcistr->bios)) || + (error = pcibios_read_config_byte(pcistr->bus,pcistr->device_fn, + PCI_INTERRUPT_LINE,&pcistr->irq))) { + printk("GDT-PCI: error %s reading configuration space", + pcibios_strerror(error)); + return -1; + } + + pcistr->device_id = device_id; + if (device_id <= PCI_DEVICE_ID_VORTEX_GDT6000B || /* GDT6000 or GDT6000B */ + device_id >= PCI_DEVICE_ID_VORTEX_GDT6x17RP) { /* MPR */ + if ((base0 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY) + return -1; + pcistr->dpmem = base0 & PCI_BASE_ADDRESS_MEM_MASK; + } else { /* GDT6110, GDT6120, .. */ + if ((base0 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY || + (base2 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY || + (base1 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_IO) + return -1; + pcistr->dpmem = base2 & PCI_BASE_ADDRESS_MEM_MASK; + pcistr->io_mm = base0 & PCI_BASE_ADDRESS_MEM_MASK; + pcistr->io = base1 & PCI_BASE_ADDRESS_IO_MASK; + } + return 1; +} + + +static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha,int firsttime) +{ + ulong retries,id; + unchar prot_ver,eisacf,i,irq_found; + + TRACE(("gdth_init_eisa() adr. %x\n",eisa_adr)); + + /* disable board interrupts, deinitialize services */ + outb(0xff,eisa_adr+EDOORREG); + outb(0x00,eisa_adr+EDENABREG); + outb(0x00,eisa_adr+EINTENABREG); + + outb(0xff,eisa_adr+LDOORREG); + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (inb(eisa_adr+EDOORREG) != 0xff) { + if (--retries == 0) { + printk("GDT-EISA: Initialization error (DEINIT failed)\n"); + return 0; + } + udelay(1000); + TRACE2(("wait for DEINIT: retries=%ld\n",retries)); + } + prot_ver = inb(eisa_adr+MAILBOXREG); + outb(0xff,eisa_adr+EDOORREG); + if (prot_ver != PROTOCOL_VERSION) { + printk("GDT-EISA: Illegal protocol version\n"); + return 0; + } + ha->brd = (ulong)eisa_adr; + ha->brd_phys = (ulong)eisa_adr >> 12; + + outl(0,eisa_adr+MAILBOXREG); + outl(0,eisa_adr+MAILBOXREG+4); + outl(0,eisa_adr+MAILBOXREG+8); + outl(0,eisa_adr+MAILBOXREG+12); + + /* detect IRQ */ + if ((id = inl(eisa_adr+ID0REG)) == GDT3_ID) { + ha->type = GDT_EISA; + ha->stype = id; + outl(1,eisa_adr+MAILBOXREG+8); + outb(0xfe,eisa_adr+LDOORREG); + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (inb(eisa_adr+EDOORREG) != 0xfe) { + if (--retries == 0) { + printk("GDT-EISA: Initialization error (get IRQ failed)\n"); + return 0; + } + udelay(1000); + } + if (firsttime) + ha->irq = inb(eisa_adr+MAILBOXREG); + outb(0xff,eisa_adr+EDOORREG); + TRACE2(("GDT3000/3020: IRQ=%d\n",ha->irq)); + if (firsttime) { + /* check the result */ + if (ha->irq == 0) { + TRACE2(("Unknown IRQ, check IRQ table from cmd line !\n")); + for (i=0,irq_found=FALSE; iirq = irqs[i]; + irqs[i] = 0; + printk("GDT-EISA: Can not detect controller IRQ,\n"); + printk("Use IRQ setting from command line (IRQ = %d)\n", + ha->irq); + } else { + printk("GDT-EISA: Initialization error (unknown IRQ), Enable\n"); + printk("the controller BIOS or use command line parameters\n"); + return 0; + } + } + } + } else { + eisacf = inb(eisa_adr+EISAREG) & 7; + if (eisacf > 4) /* level triggered */ + eisacf -= 4; + ha->irq = gdth_irq_tab[eisacf]; + ha->type = GDT_EISA; + ha->stype= id; + } + return 1; +} + + +static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha,int firsttime) +{ + register gdt2_dpram_str *dp2_ptr; + int i; + unchar irq_drq,prot_ver; + ulong retries; + + TRACE(("gdth_init_isa() bios adr. %lx\n",bios_adr)); + + ha->brd = bios_adr; + dp2_ptr = (gdt2_dpram_str *)ha->brd; + dp2_ptr->io.memlock = 1; /* switch off write protection */ + /* reset interface area */ + memset((char *)&dp2_ptr->u,0,sizeof(dp2_ptr->u)); + + /* disable board interrupts, read DRQ and IRQ */ + dp2_ptr->io.irqdel = 0xff; + dp2_ptr->io.irqen = 0x00; + dp2_ptr->u.ic.S_Status = 0x00; + dp2_ptr->u.ic.Cmd_Index= 0x00; + + irq_drq = dp2_ptr->io.rq; + for (i=0; i<3; ++i) { + if ((irq_drq & 1)==0) + break; + irq_drq >>= 1; + } + ha->drq = gdth_drq_tab[i]; + + irq_drq = dp2_ptr->io.rq >> 3; + for (i=1; i<5; ++i) { + if ((irq_drq & 1)==0) + break; + irq_drq >>= 1; + } + ha->irq = gdth_irq_tab[i]; + + /* deinitialize services */ + dp2_ptr->u.ic.S_Info[0] = bios_adr; + dp2_ptr->u.ic.S_Cmd_Indx= 0xff; + dp2_ptr->io.event = 0; + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (dp2_ptr->u.ic.S_Status != 0xff) { + if (--retries == 0) { + printk("GDT-ISA: Initialization error (DEINIT failed)\n"); + return 0; + } + udelay(1000); + } + prot_ver = (unchar)dp2_ptr->u.ic.S_Info[0]; + dp2_ptr->u.ic.Status = 0; + dp2_ptr->io.irqdel = 0xff; + if (prot_ver != PROTOCOL_VERSION) { + printk("GDT-ISA: Illegal protocol version\n"); + return 0; + } + + ha->type = GDT_ISA; + ha->ic_all_size = sizeof(dp2_ptr->u); + ha->stype= GDT2_ID; + ha->brd_phys = bios_adr >> 4; + + /* special request to controller BIOS */ + dp2_ptr->u.ic.S_Info[0] = 0x00; + dp2_ptr->u.ic.S_Info[1] = 0x00; + dp2_ptr->u.ic.S_Info[2] = 0x01; + dp2_ptr->u.ic.S_Info[3] = 0x00; + dp2_ptr->u.ic.S_Cmd_Indx= 0xfe; + dp2_ptr->io.event = 0; + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (dp2_ptr->u.ic.S_Status != 0xfe) { + if (--retries == 0) { + printk("GDT-ISA: Initialization error\n"); + return 0; + } + udelay(1000); + } + dp2_ptr->u.ic.Status = 0; + dp2_ptr->io.irqdel = 0xff; + return 1; +} + + +static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha,int firsttime) +{ + register gdt6_dpram_str *dp6_ptr; + register gdt6c_dpram_str *dp6c_ptr; + register gdt6m_dpram_str *dp6m_ptr; + ulong retries; + unchar prot_ver; + unchar remapped = FALSE; + + TRACE(("gdth_init_pci()\n")); + + if (firsttime) { + ha->brd = pcistr->dpmem; + ha->brd_phys = (pcistr->bus << 8) | (pcistr->device_fn & 0xf8); + ha->stype = (ulong)pcistr->device_id; + ha->irq = pcistr->irq; + } + + if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6000B) { /* GDT6000 or GDT6000B */ + TRACE2(("init_pci() dpmem %lx irq %d\n",ha->brd,ha->irq)); + dp6_ptr = (gdt6_dpram_str *)ha->brd; + /* reset interface area */ + memset((char *)&dp6_ptr->u,0,sizeof(dp6_ptr->u)); + if (*(ulong *)&dp6_ptr->u != 0) { + printk("GDT-PCI: Initialization error (DPMEM write error)\n"); + return 0; + } + + /* disable board interrupts, deinit services */ + dp6_ptr->io.irqdel = 0xff; + dp6_ptr->io.irqen = 0x00; + dp6_ptr->u.ic.S_Status = 0x00; + dp6_ptr->u.ic.Cmd_Index= 0x00; + + dp6_ptr->u.ic.S_Info[0] = ha->brd; + dp6_ptr->u.ic.S_Cmd_Indx= 0xff; + dp6_ptr->io.event = 0; + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (dp6_ptr->u.ic.S_Status != 0xff) { + if (--retries == 0) { + printk("GDT-PCI: Initialization error (DEINIT failed)\n"); + return 0; + } + udelay(1000); + } + prot_ver = (unchar)dp6_ptr->u.ic.S_Info[0]; + dp6_ptr->u.ic.S_Status = 0; + dp6_ptr->io.irqdel = 0xff; + if (prot_ver != PROTOCOL_VERSION) { + printk("GDT-PCI: Illegal protocol version\n"); + return 0; + } + + ha->type = GDT_PCI; + ha->ic_all_size = sizeof(dp6_ptr->u); + + /* special command to controller BIOS */ + dp6_ptr->u.ic.S_Info[0] = 0x00; + dp6_ptr->u.ic.S_Info[1] = 0x00; + dp6_ptr->u.ic.S_Info[2] = 0x01; + dp6_ptr->u.ic.S_Info[3] = 0x00; + dp6_ptr->u.ic.S_Cmd_Indx= 0xfe; + dp6_ptr->io.event = 0; + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (dp6_ptr->u.ic.S_Status != 0xfe) { + if (--retries == 0) { + printk("GDT-PCI: Initialization error\n"); + return 0; + } + udelay(1000); + } + dp6_ptr->u.ic.S_Status = 0; + dp6_ptr->io.irqdel = 0xff; + + } else if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6555) { /* GDT6110, GDT6120, .. */ + if (firsttime) { + ha->plx = (gdt6c_plx_regs *)pcistr->io; + } + TRACE2(("init_pci_new() dpmem %lx io %lx irq %d\n", + ha->brd,(ulong)ha->plx,ha->irq)); + dp6c_ptr = (gdt6c_dpram_str *)ha->brd; + /* reset interface area */ + memset((char *)&dp6c_ptr->u,0,sizeof(dp6c_ptr->u)); + if (*(ulong *)&dp6c_ptr->u != 0) { + printk("GDT-PCI: Initialization error (DPMEM write error)\n"); + return 0; + } + + /* disable board interrupts, deinit services */ + outb(0x00,PTR2USHORT(&ha->plx->control1)); + outb(0xff,PTR2USHORT(&ha->plx->edoor_reg)); + + dp6c_ptr->u.ic.S_Status = 0x00; + dp6c_ptr->u.ic.Cmd_Index= 0x00; + + dp6c_ptr->u.ic.S_Info[0] = ha->brd; + dp6c_ptr->u.ic.S_Cmd_Indx= 0xff; + + outb(1,PTR2USHORT(&ha->plx->ldoor_reg)); + + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (dp6c_ptr->u.ic.S_Status != 0xff) { + if (--retries == 0) { + printk("GDT-PCI: Initialization error (DEINIT failed)\n"); + return 0; + } + udelay(1000); + } + prot_ver = (unchar)dp6c_ptr->u.ic.S_Info[0]; + dp6c_ptr->u.ic.Status = 0; + if (prot_ver != PROTOCOL_VERSION) { + printk("GDT-PCI: Illegal protocol version\n"); + return 0; + } + + ha->type = GDT_PCINEW; + ha->ic_all_size = sizeof(dp6c_ptr->u); + + /* special command to controller BIOS */ + dp6c_ptr->u.ic.S_Info[0] = 0x00; + dp6c_ptr->u.ic.S_Info[1] = 0x00; + dp6c_ptr->u.ic.S_Info[2] = 0x01; + dp6c_ptr->u.ic.S_Info[3] = 0x00; + dp6c_ptr->u.ic.S_Cmd_Indx= 0xfe; + + outb(1,PTR2USHORT(&ha->plx->ldoor_reg)); + + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (dp6c_ptr->u.ic.S_Status != 0xfe) { + if (--retries == 0) { + printk("GDT-PCI: Initialization error\n"); + return 0; + } + udelay(1000); + } + dp6c_ptr->u.ic.S_Status = 0; + + } else { /* MPR */ + if (ha->brd > 0xfffff) { /* NOT below 1MB */ +#if LINUX_VERSION_CODE >= 0x010300 + /* Linux 1.3.X allow to remap physical pages adresses greater + than the highest physical memory address to kernel virtual + pages using vremap()/vfree(), Linux 1.2.X doesn't */ + TRACE2(("init_pci_mpr() dpmem %lx irq %d\n",ha->brd,ha->irq)); + ha->brd = (ulong)vremap(ha->brd, sizeof(gdt6m_dpram_str)); + if (ha->brd == 0L) { + printk("GDT-PCI: Initialization error (DPMEM remap error)\n"); + return 0; + } + TRACE2(("init_pci_mpr() remapped dpmem %lx\n",ha->brd)); + remapped = TRUE; +#else + printk("GDT-PCI: Initialization error (DPMEM not below 1MB)\n"); + return 0; +#endif + } + + dp6m_ptr = (gdt6m_dpram_str *)ha->brd; + /* reset interface area */ + memset((char *)&dp6m_ptr->u,0,sizeof(dp6m_ptr->u)); + if (*(ulong *)&dp6m_ptr->u != 0) { + printk("GDT-PCI: Initialization error (DPMEM write error)\n"); +#if LINUX_VERSION_CODE >= 0x010300 + if (remapped) + vfree((void *)ha->brd); +#endif + return 0; + } + + /* disable board interrupts, deinit services */ + dp6m_ptr->i960r.edoor_en_reg |= 4; + dp6m_ptr->i960r.edoor_reg = 0xff; + dp6m_ptr->u.ic.S_Status = 0x00; + dp6m_ptr->u.ic.Cmd_Index = 0x00; + + dp6m_ptr->u.ic.S_Info[0] = ha->brd; + dp6m_ptr->u.ic.S_Cmd_Indx = 0xff; + dp6m_ptr->i960r.ldoor_reg = 1; + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (dp6m_ptr->u.ic.S_Status != 0xff) { + if (--retries == 0) { + printk("GDT-PCI: Initialization error (DEINIT failed)\n"); +#if LINUX_VERSION_CODE >= 0x010300 + if (remapped) + vfree((void *)ha->brd); +#endif + return 0; + } + udelay(1000); + } + prot_ver = (unchar)dp6m_ptr->u.ic.S_Info[0]; + dp6m_ptr->u.ic.S_Status = 0; + if (prot_ver != PROTOCOL_VERSION) { + printk("GDT-PCI: Illegal protocol version\n"); +#if LINUX_VERSION_CODE >= 0x010300 + if (remapped) + vfree((void *)ha->brd); +#endif + return 0; + } + + ha->type = GDT_PCIMPR; + ha->ic_all_size = sizeof(dp6m_ptr->u); + + /* special command to controller BIOS */ + dp6m_ptr->u.ic.S_Info[0] = 0x00; + dp6m_ptr->u.ic.S_Info[1] = 0x00; + dp6m_ptr->u.ic.S_Info[2] = 0x01; + dp6m_ptr->u.ic.S_Info[3] = 0x00; + dp6m_ptr->u.ic.S_Cmd_Indx = 0xfe; + dp6m_ptr->i960r.ldoor_reg = 1; + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (dp6m_ptr->u.ic.S_Status != 0xfe) { + if (--retries == 0) { + printk("GDT-PCI: Initialization error\n"); +#if LINUX_VERSION_CODE >= 0x010300 + if (remapped) + vfree((void *)ha->brd); +#endif + return 0; + } + udelay(1000); + } + dp6m_ptr->u.ic.S_Status = 0; + } + + return 1; +} + + +/* controller protocol functions */ + +static void gdth_enable_int(int hanum) +{ + gdth_ha_str *ha; + ulong flags; + ushort addr; + gdt2_dpram_str *dp2_ptr; + gdt6_dpram_str *dp6_ptr; + gdt6m_dpram_str *dp6m_ptr; + + TRACE(("gdth_enable_int() hanum %d\n",hanum)); + ha = HADATA(gdth_ctr_tab[hanum]); + + save_flags(flags); + cli(); + + if (ha->type == GDT_EISA) { + addr = (ushort)ha->brd; + outb(0xff,addr+EDOORREG); + outb(0xff,addr+EDENABREG); + outb(0x01,addr+EINTENABREG); + } else if (ha->type == GDT_ISA) { + dp2_ptr = (gdt2_dpram_str *)ha->brd; + dp2_ptr->io.irqdel = 1; + dp2_ptr->u.ic.Cmd_Index = 0; + dp2_ptr->io.irqen = 1; + } else if (ha->type == GDT_PCI) { + dp6_ptr = (gdt6_dpram_str *)ha->brd; + dp6_ptr->io.irqdel = 1; + dp6_ptr->u.ic.Cmd_Index = 0; + dp6_ptr->io.irqen = 1; + } else if (ha->type == GDT_PCINEW) { + outb(0xff,PTR2USHORT(&ha->plx->edoor_reg)); + outb(0x03,PTR2USHORT(&ha->plx->control1)); + } else if (ha->type == GDT_PCIMPR) { + dp6m_ptr = (gdt6m_dpram_str *)ha->brd; + dp6m_ptr->i960r.edoor_reg = 0xff; + dp6m_ptr->i960r.edoor_en_reg &= ~4; + } + restore_flags(flags); +} + + +static int gdth_get_status(unchar *pIStatus,int irq) +{ + register gdth_ha_str *ha; + int i; + + TRACE(("gdth_get_status() irq %d ctr_count %d\n", + irq,gdth_ctr_count)); + + *pIStatus = 0; + for (i=0; iirq != (unchar)irq) /* check IRQ */ + continue; + if (ha->type == GDT_EISA) + *pIStatus = inb((ushort)ha->brd+EDOORREG); + else if (ha->type == GDT_ISA) + *pIStatus = ((gdt2_dpram_str *)ha->brd)->u.ic.Cmd_Index; + else if (ha->type == GDT_PCI) + *pIStatus = ((gdt6_dpram_str *)ha->brd)->u.ic.Cmd_Index; + else if (ha->type == GDT_PCINEW) + *pIStatus = inb(PTR2USHORT(&ha->plx->edoor_reg)); + else if (ha->type == GDT_PCIMPR) + *pIStatus = ((gdt6m_dpram_str *)ha->brd)->i960r.edoor_reg; + + if (*pIStatus) + return i; /* board found */ + } + return -1; +} + + +static int gdth_test_busy(int hanum) +{ + register gdth_ha_str *ha; + register int gdtsema0 = 0; + + TRACE(("gdth_test_busy() hanum %d\n",hanum)); + + ha = HADATA(gdth_ctr_tab[hanum]); + if (ha->type == GDT_EISA) + gdtsema0 = (int)inb((ushort)ha->brd+SEMA0REG); + else if (ha->type == GDT_ISA) + gdtsema0 = (int)((gdt2_dpram_str *)ha->brd)->u.ic.Sema0; + else if (ha->type == GDT_PCI) + gdtsema0 = (int)((gdt6_dpram_str *)ha->brd)->u.ic.Sema0; + else if (ha->type == GDT_PCINEW) + gdtsema0 = (int)inb(PTR2USHORT(&ha->plx->sema0_reg)); + else if (ha->type == GDT_PCIMPR) + gdtsema0 = (int)((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg; + + return (gdtsema0 & 1); +} + + +static int gdth_get_cmd_index(int hanum) +{ + register gdth_ha_str *ha; + int i; + + TRACE(("gdth_get_cmd_index() hanum %d\n",hanum)); + + ha = HADATA(gdth_ctr_tab[hanum]); + for (i=0; ipccb->RequestBuffer; + gdth_cmd_tab[i][hanum].service = ha->pccb->Service; + ha->pccb->CommandIndex = (ulong)i+2; + return (i+2); + } + } + return 0; +} + + +static void gdth_set_sema0(int hanum) +{ + register gdth_ha_str *ha; + + TRACE(("gdth_set_sema0() hanum %d\n",hanum)); + + ha = HADATA(gdth_ctr_tab[hanum]); + if (ha->type == GDT_EISA) + outb(1,(ushort)ha->brd+SEMA0REG); + else if (ha->type == GDT_ISA) + ((gdt2_dpram_str *)ha->brd)->u.ic.Sema0 = 1; + else if (ha->type == GDT_PCI) + ((gdt6_dpram_str *)ha->brd)->u.ic.Sema0 = 1; + else if (ha->type == GDT_PCINEW) + outb(1,PTR2USHORT(&ha->plx->sema0_reg)); + else if (ha->type == GDT_PCIMPR) + ((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg = 1; + +} + + +static void gdth_copy_command(int hanum) +{ + register gdth_ha_str *ha; + register gdth_cmd_str *cmd_ptr; + register gdt6m_dpram_str *dp6m_ptr; + register gdt6c_dpram_str *dp6c_ptr; + gdt6_dpram_str *dp6_ptr; + gdt2_dpram_str *dp2_ptr; + ushort cp_count,dp_offset,cmd_no; + + TRACE(("gdth_copy_command() hanum %d\n",hanum)); + + ha = HADATA(gdth_ctr_tab[hanum]); + cp_count = ha->cmd_len; + dp_offset= ha->cmd_offs_dpmem; + cmd_no = ha->cmd_cnt; + cmd_ptr = ha->pccb; + + ++ha->cmd_cnt; + if (ha->type == GDT_EISA) + return; /* no DPMEM, no copy */ + + /* set cpcount dword aligned */ + if (cp_count & 3) + cp_count += (4 - (cp_count & 3)); + + ha->cmd_offs_dpmem += cp_count; + + /* set offset and service, copy command to DPMEM */ + if (ha->type == GDT_ISA) { + dp2_ptr = (gdt2_dpram_str *)ha->brd; + dp2_ptr->u.ic.comm_queue[cmd_no].offset = + dp_offset + DPMEM_COMMAND_OFFSET; + dp2_ptr->u.ic.comm_queue[cmd_no].serv_id = + (ushort)cmd_ptr->Service; + memcpy(&dp2_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); + } else if (ha->type == GDT_PCI) { + dp6_ptr = (gdt6_dpram_str *)ha->brd; + dp6_ptr->u.ic.comm_queue[cmd_no].offset = + dp_offset + DPMEM_COMMAND_OFFSET; + dp6_ptr->u.ic.comm_queue[cmd_no].serv_id = + (ushort)cmd_ptr->Service; + memcpy(&dp6_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); + } else if (ha->type == GDT_PCINEW) { + dp6c_ptr = (gdt6c_dpram_str *)ha->brd; + dp6c_ptr->u.ic.comm_queue[cmd_no].offset = + dp_offset + DPMEM_COMMAND_OFFSET; + dp6c_ptr->u.ic.comm_queue[cmd_no].serv_id = + (ushort)cmd_ptr->Service; + memcpy(&dp6c_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); + } else if (ha->type == GDT_PCIMPR) { + dp6m_ptr = (gdt6m_dpram_str *)ha->brd; + dp6m_ptr->u.ic.comm_queue[cmd_no].offset = + dp_offset + DPMEM_COMMAND_OFFSET; + dp6m_ptr->u.ic.comm_queue[cmd_no].serv_id = + (ushort)cmd_ptr->Service; + memcpy(&dp6m_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); + } +} + + +static void gdth_release_event(int hanum) +{ + register gdth_ha_str *ha; + +#ifdef GDTH_STATISTICS + ulong i,j; + for (i=0,j=0; jpccb->OpCode == GDT_INIT) + ha->pccb->Service |= 0x80; + + if (ha->type == GDT_EISA) { + outb(ha->pccb->Service,(ushort)ha->brd+LDOORREG); + if (ha->pccb->OpCode == GDT_INIT) /* store DMA buffer */ + outl((ulong)ha->pccb,(ushort)ha->brd+MAILBOXREG); + } else if (ha->type == GDT_ISA) + ((gdt2_dpram_str *)ha->brd)->io.event = 0; + else if (ha->type == GDT_PCI) + ((gdt6_dpram_str *)ha->brd)->io.event = 0; + else if (ha->type == GDT_PCINEW) + outb(1,PTR2USHORT(&ha->plx->ldoor_reg)); + else if (ha->type == GDT_PCIMPR) + ((gdt6m_dpram_str *)ha->brd)->i960r.ldoor_reg = 1; +} + + +static int gdth_wait(int hanum,int index,ulong time) +{ + gdth_ha_str *ha; + int answer_found = FALSE; + + TRACE(("gdth_wait() hanum %d index %d time %ld\n",hanum,index,time)); + + ha = HADATA(gdth_ctr_tab[hanum]); + if (index == 0) + return 1; /* no wait required */ + + gdth_from_wait = TRUE; + do { +#if LINUX_VERSION_CODE >= 0x010346 + gdth_interrupt((int)ha->irq,NULL,NULL); +#else + gdth_interrupt((int)ha->irq,NULL); +#endif + if (wait_hanum==hanum && wait_index==index) { + answer_found = TRUE; + break; + } + udelay(1000); + } while (--time); + gdth_from_wait = FALSE; + + while (gdth_test_busy(hanum)) + udelay(1); + + return (answer_found); +} + + +static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong p1, + ulong p2,ulong p3) +{ + register gdth_ha_str *ha; + register gdth_cmd_str *cmd_ptr; + int retries,index; + + TRACE2(("gdth_internal_cmd() service %d opcode %d\n",service,opcode)); + + ha = HADATA(gdth_ctr_tab[hanum]); + cmd_ptr = ha->pccb; + memset((char*)cmd_ptr,0,sizeof(gdth_cmd_str)); + + /* make command */ + for (retries = INIT_RETRIES;;) { + cmd_ptr->Service = service; + cmd_ptr->RequestBuffer = INTERNAL_CMND; + if (!(index=gdth_get_cmd_index(hanum))) { + TRACE(("GDT: No free command index found\n")); + return 0; + } + gdth_set_sema0(hanum); + cmd_ptr->OpCode = opcode; + cmd_ptr->BoardNode = LOCALBOARD; + if (service == CACHESERVICE) { + if (opcode == GDT_IOCTL) { + cmd_ptr->u.ioctl.subfunc = p1; + cmd_ptr->u.ioctl.channel = p2; + cmd_ptr->u.ioctl.param_size = (ushort)p3; + cmd_ptr->u.ioctl.p_param = (ulong)ha->pscratch; + } else { + cmd_ptr->u.cache.DeviceNo = (ushort)p1; + cmd_ptr->u.cache.BlockNo = p2; + } + } else if (service == SCSIRAWSERVICE) { + cmd_ptr->u.raw.direction = p1; + cmd_ptr->u.raw.bus = (unchar)p2; + cmd_ptr->u.raw.target = (unchar)p3; + cmd_ptr->u.raw.lun = 0; + } + ha->cmd_len = sizeof(gdth_cmd_str); + ha->cmd_offs_dpmem = 0; + ha->cmd_cnt = 0; + gdth_copy_command(hanum); + gdth_release_event(hanum); + JIFFYWAIT(2); + if (!gdth_wait(hanum,index,INIT_TIMEOUT)) { + printk("GDT: Initialization error (timeout service %d)\n",service); + return 0; + } + if (ha->status != S_BSY || --retries == 0) + break; + udelay(1000); + } + + return (ha->status != S_OK ? 0:1); +} + + +/* search for devices */ + +static int gdth_search_drives(int hanum,int firsttime) +{ + register gdth_ha_str *ha; + ushort cdev_cnt,i; + unchar b,t,pos_found; + ulong drv_cyls, drv_hds, drv_secs; + ulong bus_no; + gdth_getch_str *chn; + + TRACE(("gdth_search_drives() hanum %d flag %d\n",hanum,firsttime)); + ha = HADATA(gdth_ctr_tab[hanum]); + + /* initialize controller services, at first: screen service */ + if (!gdth_internal_cmd(hanum,SCREENSERVICE,GDT_INIT,0,0,0)) { + printk("GDT: Initialization error screen service (code %d)\n", + ha->status); + return 0; + } + TRACE2(("gdth_search_drives(): SCREENSERVICE initialized\n")); + + /* initialize cache service */ + if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) { + printk("GDT: Initialization error cache service (code %d)\n", + ha->status); + return 0; + } + TRACE2(("gdth_search_drives(): CACHESERVICE initialized\n")); + cdev_cnt = (ushort)ha->info; + + if (firsttime) { + /* mount all cache devices */ + if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_MOUNT,0xffff,1,0)) { + printk("GDT: Initialization error cache service (code %d)\n", + ha->status); + return 0; + } + TRACE2(("gdth_search_drives(): mountall CACHESERVICE OK\n")); + + /* initialize cache service after mountall */ + if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) { + printk("GDT: Initialization error cache service (code %d)\n", + ha->status); + return 0; + } + TRACE2(("gdth_search_drives() CACHES. init. after mountall\n")); + cdev_cnt = (ushort)ha->info; + + /* detect number of SCSI buses */ + chn = (gdth_getch_str *)DMADATA(gdth_ctr_tab[hanum]); + for (bus_no=0; bus_nochannel_no = bus_no; + if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL, + SCSI_CHAN_CNT | L_CTRL_PATTERN, + IO_CHANNEL | INVALID_CHANNEL, + sizeof(gdth_getch_str))) { + if (bus_no == 0) { + printk("GDT: Error detecting SCSI channel count (0x%x)\n", + ha->status); + return 0; + } + break; + } + if (chn->siop_id < MAXID) + ha->id[bus_no][chn->siop_id].type = SIOP_DTYP; + } + ha->bus_cnt = (unchar)bus_no; + TRACE2(("gdth_search_drives() %d SCSI channels\n",ha->bus_cnt)); + + /* read cache configuration */ + if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,CACHE_INFO, + INVALID_CHANNEL,sizeof(gdth_cinfo_str))) { + printk("GDT: Initialization error cache service (code %d)\n", + ha->status); + return 0; + } + ha->cpar = ((gdth_cinfo_str *)DMADATA(gdth_ctr_tab[hanum]))->cpar; + TRACE2(("gdth_search_drives() cinfo: vs %lx sta %d str %d dw %d b %d\n", + ha->cpar.version,ha->cpar.state,ha->cpar.strategy, + ha->cpar.write_back,ha->cpar.block_size)); + } + + /* initialize raw service */ + if (!gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INIT,0,0,0)) { + printk("GDT: Initialization error raw service (code %d)\n", + ha->status); + return 0; + } + TRACE2(("gdth_search_drives(): RAWSERVICE initialized\n")); + + /* set/get features raw service (scatter/gather) */ + ha->raw_feat = 0; + if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_SET_FEAT,SCATTER_GATHER, + 0,0)) { + TRACE2(("gdth_search_drives(): set features RAWSERVICE OK\n")); + if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_GET_FEAT,0,0,0)) + { + TRACE2(("gdth_search_dr(): get feat RAWSERVICE %ld\n", + ha->info)); + ha->raw_feat = (ushort)ha->info; + } + } + + /* set/get features cache service (equal to raw service) */ + if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_SET_FEAT,0, + SCATTER_GATHER,0)) { + TRACE2(("gdth_search_drives(): set features CACHESERVICE OK\n")); + if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_GET_FEAT,0,0,0)) { + TRACE2(("gdth_search_dr(): get feat CACHESERV. %ld\n", + ha->info)); + ha->cache_feat = (ushort)ha->info; + } + } + + /* if it is not the first scan, we are ready */ + if (!firsttime) + return 1; + + /* scanning for raw devices */ + for (b=0; bbus_cnt; ++b) { + for (t=0; tid[b][t].type != SIOP_DTYP && + gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INFO,0,b,t)) { + ha->id[b][t].type = RAW_DTYP; + } + } + } + + /* scanning for cache devices */ + for (i=0; ibus_cnt; ++b) { + for (t=0; tid[b][t].type == EMPTY_DTYP) { + pos_found = TRUE; + break; + } + } + if (pos_found) + break; + } + TRACE(("gdth_search_dr() drive %d free pos at bus/id %d/%d\n", + i,b,t)); + + ha->id[b][t].type = CACHE_DTYP; + ha->id[b][t].devtype = 0; + ha->id[b][t].size = ha->info; + ha->id[b][t].hostdrive = i; + + /* evaluate mapping (sectors per head, heads per cylinder) */ + ha->id[b][t].size &= ~SECS32; + drv_cyls = ha->id[b][t].size /HEADS/SECS; + if (drv_cyls <= MAXCYLS) { + drv_hds = HEADS; + drv_secs= SECS; + } else { /* too high for 64*32 */ + drv_cyls = ha->id[b][t].size /MEDHEADS/MEDSECS; + if (drv_cyls <= MAXCYLS) { + drv_hds = MEDHEADS; + drv_secs= MEDSECS; + } else { /* too high for 127*63 */ + drv_cyls = ha->id[b][t].size /BIGHEADS/BIGSECS; + drv_hds = BIGHEADS; + drv_secs= BIGSECS; + } + } + ha->id[b][t].heads = (unchar)drv_hds; + ha->id[b][t].secs = (unchar)drv_secs; + /* round size */ + ha->id[b][t].size = drv_cyls * drv_hds * drv_secs; + TRACE(("gdth_search_dr() cdr. %d size %ld hds %ld scs %ld\n", + i,ha->id[b][t].size,drv_hds,drv_secs)); + + /* get informations about device */ + if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_DEVTYPE,i, + 0,0)) { + TRACE(("gdth_search_dr() cache drive %d devtype %ld\n", + i,ha->info)); + ha->id[b][t].devtype = (ushort)ha->info; + } + } + } + + TRACE(("gdth_search_drives() OK\n")); + return 1; +} + + +/* command queueing/sending functions */ + +static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority) +{ + register gdth_ha_str *ha; + register Scsi_Cmnd *pscp; + register Scsi_Cmnd *nscp; + ulong flags; + unchar b, t; + + TRACE(("gdth_putq() priority %d\n",priority)); + save_flags(flags); + cli(); + + ha = HADATA(gdth_ctr_tab[hanum]); + scp->SCp.this_residual = (int)priority; + gdth_update_timeout(scp, scp->timeout * 6); +#if LINUX_VERSION_CODE >= 0x010400 + b = scp->channel; +#else + b = NUMDATA(nscp->host)->busnum; +#endif + t = scp->target; +#if LINUX_VERSION_CODE >= 0x010300 + if (priority >= DEFAULT_PRI && ha->id[b][t].lock) { + TRACE2(("gdth_putq(): locked IO -> update_timeout()\n")); + scp->SCp.buffers_residual = gdth_update_timeout(scp, 0); + } +#endif + + if (ha->req_first==NULL) { + ha->req_first = scp; /* queue was empty */ + scp->SCp.ptr = NULL; + } else { /* queue not empty */ + pscp = ha->req_first; + nscp = (Scsi_Cmnd *)pscp->SCp.ptr; + /* priority: 0-highest,..,0xff-lowest */ + while (nscp && (unchar)nscp->SCp.this_residual <= priority) { + pscp = nscp; + nscp = (Scsi_Cmnd *)pscp->SCp.ptr; + } + pscp->SCp.ptr = (char *)scp; + scp->SCp.ptr = (char *)nscp; + } + restore_flags(flags); + +#ifdef GDTH_STATISTICS + flags = 0; + for (nscp=ha->req_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr) + ++flags; + if (max_rq < flags) { + max_rq = flags; + TRACE3(("GDT: max_rq = %d\n",(ushort)max_rq)); + } +#endif +} + +static void gdth_next(int hanum) +{ + register gdth_ha_str *ha; + register Scsi_Cmnd *pscp; + register Scsi_Cmnd *nscp; + unchar b, t, next_cmd, firsttime; + ushort hdrive; + ulong flags; + int cmd_index; + + TRACE(("gdth_next() hanum %d\n",hanum)); + save_flags(flags); + cli(); + + ha = HADATA(gdth_ctr_tab[hanum]); + ha->cmd_cnt = ha->cmd_offs_dpmem = 0; + next_cmd = firsttime = TRUE; + cmd_index = 0; + + for (nscp = pscp = ha->req_first; nscp; nscp = (Scsi_Cmnd *)nscp->SCp.ptr) { + if (nscp != pscp && nscp != (Scsi_Cmnd *)pscp->SCp.ptr) + pscp = (Scsi_Cmnd *)pscp->SCp.ptr; +#if LINUX_VERSION_CODE >= 0x010400 + b = nscp->channel; +#else + b = NUMDATA(nscp->host)->busnum; +#endif + t = nscp->target; + if (nscp->SCp.this_residual < DEFAULT_PRI || !ha->id[b][t].lock) { + + if (firsttime) { + if (gdth_test_busy(hanum)) { /* controller busy ? */ + TRACE(("gdth_next() controller %d busy !\n",hanum)); + if (!gdth_polling) { + restore_flags(flags); + return; + } + while (gdth_test_busy(hanum)) + udelay(1000); + } + firsttime = FALSE; + } + +#if LINUX_VERSION_CODE >= 0x010300 + if (nscp->done == gdth_scsi_done) { + if (!(cmd_index=gdth_special_cmd(hanum,nscp,b))) + next_cmd = FALSE; + } else +#endif + if (ha->id[b][t].type != CACHE_DTYP) { + if (!(cmd_index=gdth_fill_raw_cmd(hanum,nscp,b))) + next_cmd = FALSE; + } else { + hdrive = ha->id[b][t].hostdrive; + switch (nscp->cmnd[0]) { + case TEST_UNIT_READY: + case INQUIRY: + case REQUEST_SENSE: + case READ_CAPACITY: + case VERIFY: + case START_STOP: + case MODE_SENSE: + TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0], + nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3], + nscp->cmnd[4],nscp->cmnd[5])); + gdth_internal_cache_cmd(hanum,nscp,b,&flags); + break; + + case ALLOW_MEDIUM_REMOVAL: + TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0], + nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3], + nscp->cmnd[4],nscp->cmnd[5])); + if ( (nscp->cmnd[4]&1) && !(ha->id[b][t].devtype&1) ) { + TRACE2(("Prevent r. nonremov. drive->do nothing\n")); + nscp->result = DID_OK << 16; + restore_flags( flags ); + nscp->scsi_done(nscp); + save_flags( flags ); + cli(); + } else { + nscp->cmnd[3] = (ha->id[b][t].devtype&1) ? 1:0; + TRACE2(("Prevent/allow r. %d rem. drive %d\n", + nscp->cmnd[4],nscp->cmnd[3])); + if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,hdrive))) + next_cmd = FALSE; + } + break; + + case READ_6: + case WRITE_6: + case READ_10: + case WRITE_10: + if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,hdrive))) + next_cmd = FALSE; + break; + + default: + TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0], + nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3], + nscp->cmnd[4],nscp->cmnd[5])); + printk("GDT: Unknown SCSI command 0x%x to cache service !\n", + nscp->cmnd[0]); + nscp->result = DID_ABORT << 16; + restore_flags( flags ); + nscp->scsi_done( nscp ); + save_flags( flags ); + cli(); + break; + } + } + + if (!next_cmd) + break; + if (nscp == ha->req_first) + ha->req_first = pscp = (Scsi_Cmnd *)nscp->SCp.ptr; + else + pscp->SCp.ptr = nscp->SCp.ptr; + if (gdth_polling) + break; + } + } + + if (ha->cmd_cnt > 0) { + gdth_release_event(hanum); + } + + restore_flags(flags); + + if (gdth_polling && ha->cmd_cnt > 0) { + if (!gdth_wait(hanum,cmd_index,POLL_TIMEOUT)) + printk("GDT: Controller %d: Command %d timed out !\n", + hanum,cmd_index); + } +} + +static void gdth_copy_internal_data(Scsi_Cmnd *scp,char *buffer,ushort count) +{ + ushort cpcount,i; + ushort cpsum,cpnow; + struct scatterlist *sl; + + cpcount = count<=(ushort)scp->bufflen ? count:(ushort)scp->bufflen; + if (scp->use_sg) { + sl = (struct scatterlist *)scp->request_buffer; + for (i=0,cpsum=0; iuse_sg; ++i,++sl) { + cpnow = (ushort)sl->length; + TRACE(("copy_internal() now %d sum %d count %d %d\n", + cpnow,cpsum,cpcount,(ushort)scp->bufflen)); + if (cpsum+cpnow > cpcount) + cpnow = cpcount - cpsum; + cpsum += cpnow; + memcpy((char*)sl->address,buffer,cpnow); + if (cpsum == cpcount) + break; + buffer += cpnow; + } + } else { + TRACE(("copy_internal() count %d\n",cpcount)); + memcpy((char*)scp->request_buffer,buffer,cpcount); + } +} + +static int gdth_internal_cache_cmd(int hanum,Scsi_Cmnd *scp, + unchar b,ulong *flags) +{ + register gdth_ha_str *ha; + ushort hdrive; + unchar t; + gdth_inq_data inq; + gdth_rdcap_data rdc; + gdth_sense_data sd; + gdth_modep_data mpd; + + ha = HADATA(gdth_ctr_tab[hanum]); + t = scp->target; + hdrive = ha->id[b][t].hostdrive; + TRACE(("gdth_internal_cache_cmd() cmd 0x%x hdrive %d\n", + scp->cmnd[0],hdrive)); + + if (scp->lun !=0) + scp->result = DID_BAD_TARGET << 16; + else { + switch (scp->cmnd[0]) { + case TEST_UNIT_READY: + case VERIFY: + case START_STOP: + TRACE2(("Test/Verify/Start hdrive %d\n",hdrive)); + break; + + case INQUIRY: + TRACE2(("Inquiry hdrive %d devtype %d\n", + hdrive,ha->id[b][t].devtype)); + inq.type_qual = (ha->id[b][t].devtype&4) ? TYPE_ROM:TYPE_DISK; + /* you can here set all disks to removable, if you want to do + a flush using the ALLOW_MEDIUM_REMOVAL command */ + inq.modif_rmb = ha->id[b][t].devtype&1 ? 0x80:0x00; + inq.version = 2; + inq.resp_aenc = 2; + inq.add_length= 32; + strcpy(inq.vendor,"ICP "); + sprintf(inq.product,"Host Drive #%02d",hdrive); + strcpy(inq.revision," "); + gdth_copy_internal_data(scp,(char*)&inq,sizeof(gdth_inq_data)); + break; + + case REQUEST_SENSE: + TRACE2(("Request sense hdrive %d\n",hdrive)); + sd.errorcode = 0x70; + sd.segno = 0x00; + sd.key = NO_SENSE; + sd.info = 0; + sd.add_length= 0; + gdth_copy_internal_data(scp,(char*)&sd,sizeof(gdth_sense_data)); + break; + + case MODE_SENSE: + TRACE2(("Mode sense hdrive %d\n",hdrive)); + memset((char*)&mpd,0,sizeof(gdth_modep_data)); + mpd.hd.data_length = sizeof(gdth_modep_data); + mpd.hd.dev_par = (ha->id[b][t].devtype&2) ? 0x80:0; + mpd.hd.bd_length = sizeof(mpd.bd); + mpd.bd.block_length[0] = (SECTOR_SIZE & 0x00ff0000) >> 16; + mpd.bd.block_length[1] = (SECTOR_SIZE & 0x0000ff00) >> 8; + mpd.bd.block_length[2] = (SECTOR_SIZE & 0x000000ff); + gdth_copy_internal_data(scp,(char*)&mpd,sizeof(gdth_modep_data)); + break; + + case READ_CAPACITY: + TRACE2(("Read capacity hdrive %d\n",hdrive)); + rdc.last_block_no = ntohl(ha->id[b][t].size-1); + rdc.block_length = ntohl(SECTOR_SIZE); + gdth_copy_internal_data(scp,(char*)&rdc,sizeof(gdth_rdcap_data)); + break; + + default: + TRACE2(("Internal cache cmd 0x%x unknown\n",scp->cmnd[0])); + break; + } + scp->result = DID_OK << 16; + } + + restore_flags(*flags); + scp->scsi_done(scp); + save_flags(*flags); + cli(); + return 1; +} + +static int gdth_fill_cache_cmd(int hanum,Scsi_Cmnd *scp,ushort hdrive) +{ + register gdth_ha_str *ha; + register gdth_cmd_str *cmdp; + struct scatterlist *sl; + ushort i; + int cmd_index; + + ha = HADATA(gdth_ctr_tab[hanum]); + cmdp = ha->pccb; + TRACE(("gdth_fill_cache_cmd() cmd 0x%x cmdsize %d hdrive %d\n", + scp->cmnd[0],scp->cmd_len,hdrive)); + + if (ha->type==GDT_EISA && ha->cmd_cnt>0) + return 0; + + cmdp->Service = CACHESERVICE; + cmdp->RequestBuffer = scp; + /* search free command index */ + if (!(cmd_index=gdth_get_cmd_index(hanum))) { + TRACE(("GDT: No free command index found\n")); + return 0; + } + /* if it's the first command, set command semaphore */ + if (ha->cmd_cnt == 0) + gdth_set_sema0(hanum); + + /* fill command */ + if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) { + if (scp->cmnd[4] & 1) /* prevent ? */ + cmdp->OpCode = GDT_MOUNT; + else if (scp->cmnd[3] & 1) /* removable drive ? */ + cmdp->OpCode = GDT_UNMOUNT; + else + cmdp->OpCode = GDT_FLUSH; + } else { + if (scp->cmnd[0]==WRITE_6 || scp->cmnd[0]==WRITE_10) { + if (gdth_write_through) + cmdp->OpCode = GDT_WRITE_THR; + else + cmdp->OpCode = GDT_WRITE; + } else { + cmdp->OpCode = GDT_READ; + } + } + + cmdp->BoardNode = LOCALBOARD; + cmdp->u.cache.DeviceNo = hdrive; + + if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) { + cmdp->u.cache.BlockNo = 1; + cmdp->u.cache.sg_canz = 0; + } else { + if (scp->cmd_len != 6) { + cmdp->u.cache.BlockNo = ntohl(*(ulong*)&scp->cmnd[2]); + cmdp->u.cache.BlockCnt= (ulong)ntohs(*(ushort*)&scp->cmnd[7]); + } else { + cmdp->u.cache.BlockNo = ntohl(*(ulong*)&scp->cmnd[0]) & 0x001fffffUL; + cmdp->u.cache.BlockCnt= scp->cmnd[4]==0 ? 0x100 : scp->cmnd[4]; + } + + if (scp->use_sg) { + cmdp->u.cache.DestAddr= -1UL; + sl = (struct scatterlist *)scp->request_buffer; + for (i=0; iuse_sg; ++i,++sl) { + cmdp->u.cache.sg_lst[i].sg_ptr = (ulong)sl->address; + cmdp->u.cache.sg_lst[i].sg_len = (ulong)sl->length; + } + cmdp->u.cache.sg_canz = (ulong)i; + +#ifdef GDTH_STATISTICS + if (max_sg < (ulong)i) { + max_sg = (ulong)i; + TRACE3(("GDT: max_sg = %d\n",i)); + } +#endif + if (iu.cache.sg_lst[i].sg_len = 0; + } else { + if (ha->cache_feat & SCATTER_GATHER) { + cmdp->u.cache.DestAddr = -1UL; + cmdp->u.cache.sg_canz = 1; + cmdp->u.cache.sg_lst[0].sg_ptr = (ulong)scp->request_buffer; + cmdp->u.cache.sg_lst[0].sg_len = scp->request_bufflen; + cmdp->u.cache.sg_lst[1].sg_len = 0; + } else { + cmdp->u.cache.DestAddr = (ulong)scp->request_buffer; + cmdp->u.cache.sg_canz= 0; + } + } + } + TRACE(("cache cmd: addr. %lx sganz %lx sgptr0 %lx sglen0 %lx\n", + cmdp->u.cache.DestAddr,cmdp->u.cache.sg_canz, + cmdp->u.cache.sg_lst[0].sg_ptr, + cmdp->u.cache.sg_lst[0].sg_len)); + TRACE(("cache cmd: cmd %d blockno. %ld, blockcnt %ld\n", + cmdp->OpCode,cmdp->u.cache.BlockNo,cmdp->u.cache.BlockCnt)); + + /* evaluate command size, check space */ + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) + + (ushort)cmdp->u.cache.sg_canz * sizeof(gdth_sg_str); + if (ha->cmd_len & 3) + ha->cmd_len += (4 - (ha->cmd_len & 3)); + + if (ha->cmd_cnt > 0) { + if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) > + ha->ic_all_size) { + TRACE2(("gdth_fill_cache() DPMEM overflow\n")); + gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND; + return 0; + } + } + + /* copy command */ + gdth_copy_command(hanum); + return cmd_index; +} + +static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b) +{ + register gdth_ha_str *ha; + register gdth_cmd_str *cmdp; + struct scatterlist *sl; + ushort i; + int cmd_index; + unchar t,l; + + ha = HADATA(gdth_ctr_tab[hanum]); + t = scp->target; + l = scp->lun; + cmdp = ha->pccb; + TRACE(("gdth_fill_raw_cmd() cmd 0x%x bus %d ID %d LUN %d\n", + scp->cmnd[0],b,t,l)); + + if (ha->type==GDT_EISA && ha->cmd_cnt>0) + return 0; + + cmdp->Service = SCSIRAWSERVICE; + cmdp->RequestBuffer = scp; + /* search free command index */ + if (!(cmd_index=gdth_get_cmd_index(hanum))) { + TRACE(("GDT: No free command index found\n")); + return 0; + } + /* if it's the first command, set command semaphore */ + if (ha->cmd_cnt == 0) + gdth_set_sema0(hanum); + + /* fill command */ + cmdp->OpCode = GDT_WRITE; /* always */ + cmdp->BoardNode = LOCALBOARD; + cmdp->u.raw.reserved = 0; + cmdp->u.raw.mdisc_time = 0; + cmdp->u.raw.mcon_time = 0; + cmdp->u.raw.clen = scp->cmd_len; + cmdp->u.raw.target = t; + cmdp->u.raw.lun = l; + cmdp->u.raw.bus = b; + cmdp->u.raw.priority = 0; + cmdp->u.raw.link_p = NULL; + cmdp->u.raw.sdlen = scp->request_bufflen; + cmdp->u.raw.sense_len = 16; + cmdp->u.raw.sense_data = (ulong)scp->sense_buffer; + cmdp->u.raw.direction = + gdth_direction_tab[scp->cmnd[0]]==DOU ? DATA_OUT : DATA_IN; + memcpy(cmdp->u.raw.cmd,scp->cmnd,12); + + if (scp->use_sg) { + cmdp->u.raw.sdata = -1UL; + sl = (struct scatterlist *)scp->request_buffer; + for (i=0; iuse_sg; ++i,++sl) { + cmdp->u.raw.sg_lst[i].sg_ptr = (ulong)sl->address; + cmdp->u.raw.sg_lst[i].sg_len = (ulong)sl->length; + } + cmdp->u.raw.sg_ranz = (ulong)i; + +#ifdef GDTH_STATISTICS + if (max_sg < (ulong)i) { + max_sg = (ulong)i; + TRACE3(("GDT: max_sg = %d\n",i)); + } +#endif + if (iu.raw.sg_lst[i].sg_len = 0; + } else { + if (ha->raw_feat & SCATTER_GATHER) { + cmdp->u.raw.sdata = -1UL; + cmdp->u.raw.sg_ranz= 1; + cmdp->u.raw.sg_lst[0].sg_ptr = (ulong)scp->request_buffer; + cmdp->u.raw.sg_lst[0].sg_len = scp->request_bufflen; + cmdp->u.raw.sg_lst[1].sg_len = 0; + } else { + cmdp->u.raw.sdata = (ulong)scp->request_buffer; + cmdp->u.raw.sg_ranz= 0; + } + } + TRACE(("raw cmd: addr. %lx sganz %lx sgptr0 %lx sglen0 %lx\n", + cmdp->u.raw.sdata,cmdp->u.raw.sg_ranz, + cmdp->u.raw.sg_lst[0].sg_ptr, + cmdp->u.raw.sg_lst[0].sg_len)); + + /* evaluate command size, check space */ + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) + + (ushort)cmdp->u.raw.sg_ranz * sizeof(gdth_sg_str); + if (ha->cmd_len & 3) + ha->cmd_len += (4 - (ha->cmd_len & 3)); + + if (ha->cmd_cnt > 0) { + if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) > + ha->ic_all_size) { + TRACE2(("gdth_fill_raw() DPMEM overflow\n")); + gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND; + return 0; + } + } + + /* copy command */ + gdth_copy_command(hanum); + return cmd_index; +} + +static int gdth_special_cmd(int hanum,Scsi_Cmnd *scp,unchar b) +{ + register gdth_ha_str *ha; + register gdth_cmd_str *cmdp; + int cmd_index; + + ha = HADATA(gdth_ctr_tab[hanum]); + cmdp= ha->pccb; + TRACE2(("gdth_special_cmd(): ")); + + if (ha->type==GDT_EISA && ha->cmd_cnt>0) + return 0; + + memcpy( cmdp, scp->request_buffer, sizeof(gdth_cmd_str)); + cmdp->RequestBuffer = scp; + + /* search free command index */ + if (!(cmd_index=gdth_get_cmd_index(hanum))) { + TRACE(("GDT: No free command index found\n")); + return 0; + } + + /* if it's the first command, set command semaphore */ + if (ha->cmd_cnt == 0) + gdth_set_sema0(hanum); + + /* evaluate command size, check space */ + if (cmdp->OpCode == GDT_IOCTL) { + TRACE2(("IOCTL\n")); + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.ioctl.p_param) + sizeof(ulong); + } else if (cmdp->Service == CACHESERVICE) { + TRACE2(("cache command %d\n",cmdp->OpCode)); + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) + sizeof(gdth_sg_str); + } else if (cmdp->Service == SCSIRAWSERVICE) { + TRACE2(("raw command %d/%d\n",cmdp->OpCode,cmdp->u.raw.cmd[0])); + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) + sizeof(gdth_sg_str); + } + + if (ha->cmd_len & 3) + ha->cmd_len += (4 - (ha->cmd_len & 3)); + + if (ha->cmd_cnt > 0) { + if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) > + ha->ic_all_size) { + TRACE2(("gdth_special_cmd() DPMEM overflow\n")); + gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND; + return 0; + } + } + + /* copy command */ + gdth_copy_command(hanum); + return cmd_index; +} + + +/* Controller event handling functions */ +static gdth_evt_str *gdth_store_event(ushort source, ushort idx, + gdth_evt_data *evt) +{ + gdth_evt_str *e; + ulong flags; + struct timeval tv; + + TRACE2(("gdth_store_event() source %d idx %d\n", source, idx)); + if (source == 0) /* no source -> no event */ + return 0; + + save_flags(flags); + cli(); + if (ebuffer[elastidx].event_source == source && + ebuffer[elastidx].event_idx == idx && + !memcmp((char *)&ebuffer[elastidx].event_data.eu, + (char *)&evt->eu, evt->size)) { + e = &ebuffer[elastidx]; + do_gettimeofday(&tv); + e->last_stamp = tv.tv_sec; + ++e->same_count; + } else { + if (ebuffer[elastidx].event_source != 0) { /* entry not free ? */ + ++elastidx; + if (elastidx == MAX_EVENTS) + elastidx = 0; + if (elastidx == eoldidx) { /* reached mark ? */ + ++eoldidx; + if (eoldidx == MAX_EVENTS) + eoldidx = 0; + } + } + e = &ebuffer[elastidx]; + e->event_source = source; + e->event_idx = idx; + do_gettimeofday(&tv); + e->first_stamp = e->last_stamp = tv.tv_sec; + e->same_count = 1; + e->event_data = *evt; + } + restore_flags(flags); + return e; +} + +static int gdth_read_event(int handle, gdth_evt_str *estr) +{ + gdth_evt_str *e; + int eindex; + ulong flags; + + TRACE2(("gdth_read_event() handle %d\n", handle)); + save_flags(flags); + cli(); + if (handle == -1) + eindex = eoldidx; + else + eindex = handle; + estr->event_source = 0; + + if (eindex >= MAX_EVENTS) { + restore_flags(flags); + return eindex; + } + e = &ebuffer[eindex]; + if (e->event_source != 0) { + if (eindex != elastidx) { + if (++eindex == MAX_EVENTS) + eindex = 0; + } else { + eindex = -1; + } + memcpy(estr, e, sizeof(gdth_evt_str)); + } + restore_flags(flags); + return eindex; +} + +static void gdth_readapp_event(unchar application, gdth_evt_str *estr) +{ + gdth_evt_str *e; + int eindex; + ulong flags; + unchar found = FALSE; + + TRACE2(("gdth_readapp_event() app. %d\n", application)); + save_flags(flags); + cli(); + eindex = eoldidx; + for (;;) { + e = &ebuffer[eindex]; + if (e->event_source == 0) + break; + if ((e->application & application) == 0) { + e->application |= application; + found = TRUE; + break; + } + if (eindex == elastidx) + break; + if (++eindex == MAX_EVENTS) + eindex = 0; + } + if (found) + memcpy(estr, e, sizeof(gdth_evt_str)); + else + estr->event_source = 0; + restore_flags(flags); +} + +static void gdth_clear_events() +{ + ulong flags; + + TRACE(("gdth_clear_events()")); + save_flags(flags); + cli(); + + eoldidx = elastidx = 0; + ebuffer[0].event_source = 0; + restore_flags(flags); +} + + +/* SCSI interface functions */ + +#if LINUX_VERSION_CODE >= 0x010346 +static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs) +#else +static void gdth_interrupt(int irq,struct pt_regs *regs) +#endif +{ + register gdth_ha_str *ha; + gdt6m_dpram_str *dp6m_ptr; + gdt6_dpram_str *dp6_ptr; + gdt2_dpram_str *dp2_ptr; + Scsi_Cmnd *scp; + int hanum; + unchar IStatus; + ushort CmdStatus, Service = 0; + ulong InfoBytes, InfoBytes2 = 0; + gdth_evt_data dvr; + + TRACE(("gdth_interrupt() IRQ %d\n",irq)); + + /* if polling and not from gdth_wait() -> return */ + if (gdth_polling) { + if (!gdth_from_wait) { + return; + } + } + + wait_index = 0; + + /* search controller */ + if ((hanum = gdth_get_status(&IStatus,irq)) == -1) { + /* + TRACE2(("gdth_interrupt(): Spurious interrupt received\n")); + */ + return; + } + +#ifdef GDTH_STATISTICS + ++act_ints; +#endif + + ha = HADATA(gdth_ctr_tab[hanum]); + if (ha->type == GDT_EISA) { + if (IStatus & 0x80) { /* error flag */ + IStatus &= ~0x80; + CmdStatus = inw((ushort)ha->brd+MAILBOXREG+8); + TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); + if (IStatus == ASYNCINDEX) { /* async. event ? */ + Service = inw((ushort)ha->brd+MAILBOXREG+10); + InfoBytes2 = inl((ushort)ha->brd+MAILBOXREG+4); + } + } else /* no error */ + CmdStatus = S_OK; + InfoBytes = inl((ushort)ha->brd+MAILBOXREG+12); + outb(0xff,(ushort)ha->brd+EDOORREG); /* acknowledge interrupt */ + outb(0x00,(ushort)ha->brd+SEMA1REG); /* reset status semaphore */ + } else if (ha->type == GDT_ISA) { + dp2_ptr = (gdt2_dpram_str *)ha->brd; + if (IStatus & 0x80) { /* error flag */ + IStatus &= ~0x80; + CmdStatus = dp2_ptr->u.ic.Status; + TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); + if (IStatus == ASYNCINDEX) { /* async. event ? */ + Service = dp2_ptr->u.ic.Service; + InfoBytes2 = dp2_ptr->u.ic.Info[1]; + } + } else /* no error */ + CmdStatus = S_OK; + InfoBytes = dp2_ptr->u.ic.Info[0]; + dp2_ptr->io.irqdel = 0xff; /* acknowledge interrupt */ + dp2_ptr->u.ic.Cmd_Index = 0; /* reset command index */ + dp2_ptr->io.Sema1 = 0; /* reset status semaphore */ + } else if (ha->type == GDT_PCI) { + dp6_ptr = (gdt6_dpram_str *)ha->brd; + if (IStatus & 0x80) { /* error flag */ + IStatus &= ~0x80; + CmdStatus = dp6_ptr->u.ic.Status; + TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); + if (IStatus == ASYNCINDEX) { /* async. event ? */ + Service = dp6_ptr->u.ic.Service; + InfoBytes2 = dp6_ptr->u.ic.Info[1]; + } + } else /* no error */ + CmdStatus = S_OK; + InfoBytes = dp6_ptr->u.ic.Info[0]; + dp6_ptr->io.irqdel = 0xff; /* acknowledge interrupt */ + dp6_ptr->u.ic.Cmd_Index = 0; /* reset command index */ + dp6_ptr->io.Sema1 = 0; /* reset status semaphore */ + } else if (ha->type == GDT_PCINEW) { + if (IStatus & 0x80) { /* error flag */ + IStatus &= ~0x80; + CmdStatus = inw(PTR2USHORT(&ha->plx->status)); + TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); + if (IStatus == ASYNCINDEX) { /* async. event ? */ + Service = inw(PTR2USHORT(&ha->plx->service)); + InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1])); + } + } else + CmdStatus = S_OK; + + InfoBytes = inl(PTR2USHORT(&ha->plx->info[0])); + outb(0xff,PTR2USHORT(&ha->plx->edoor_reg)); + outb(0x00,PTR2USHORT(&ha->plx->sema1_reg)); + } else if (ha->type == GDT_PCIMPR) { + dp6m_ptr = (gdt6m_dpram_str *)ha->brd; + if (IStatus & 0x80) { /* error flag */ + IStatus &= ~0x80; + CmdStatus = dp6m_ptr->i960r.status; + TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); + if (IStatus == ASYNCINDEX) { /* async. event ? */ + Service = dp6m_ptr->i960r.service; + InfoBytes2 = dp6m_ptr->i960r.info[1]; + } + } else /* no error */ + CmdStatus = S_OK; + InfoBytes = dp6m_ptr->i960r.info[0]; + dp6m_ptr->i960r.edoor_reg = 0xff; + dp6m_ptr->i960r.sema1_reg = 0; + } else { + TRACE2(("gdth_interrupt() unknown controller type\n")); + return; + } + + TRACE(("gdth_interrupt() index %d stat %d info %ld\n", + IStatus,CmdStatus,InfoBytes)); + ha->status = CmdStatus; + ha->info = InfoBytes; + ha->info2 = InfoBytes2; + + if (gdth_from_wait) { + wait_hanum = hanum; + wait_index = (int)IStatus; + } + + if (IStatus == ASYNCINDEX) { + TRACE2(("gdth_interrupt() async. event\n")); + gdth_async_event(hanum,Service); + } else { + if (IStatus == SPEZINDEX) { + TRACE2(("Service unknown or not initialized !\n")); + dvr.size = sizeof(dvr.eu.driver); + dvr.eu.driver.ionode = hanum; + gdth_store_event(ES_DRIVER, 4, &dvr); + return; + } + scp = gdth_cmd_tab[IStatus-2][hanum].cmnd; + Service = gdth_cmd_tab[IStatus-2][hanum].service; + gdth_cmd_tab[IStatus-2][hanum].cmnd = UNUSED_CMND; + if (scp == UNUSED_CMND) { + TRACE2(("gdth_interrupt() index to unused command (%d)\n",IStatus)); + dvr.size = sizeof(dvr.eu.driver); + dvr.eu.driver.ionode = hanum; + dvr.eu.driver.index = IStatus; + gdth_store_event(ES_DRIVER, 1, &dvr); + return; + } + if (scp == INTERNAL_CMND) { + TRACE(("gdth_interrupt() answer to internal command\n")); + return; + } + TRACE(("gdth_interrupt() sync. status\n")); + gdth_sync_event(hanum,Service,IStatus,scp); + } + gdth_next(hanum); +} + +static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp) +{ + register gdth_ha_str *ha; + gdth_msg_str *msg; + gdth_cmd_str *cmdp; + char c='\r'; + ushort i; + gdth_evt_data dvr; + + ha = HADATA(gdth_ctr_tab[hanum]); + cmdp = ha->pccb; + TRACE(("gdth_sync_event() scp %lx serv %d status %d\n", + (ulong)scp,service,ha->status)); + + if (service == SCREENSERVICE) { + msg = (gdth_msg_str *)ha->pscratch; + TRACE(("len: %ld, answer: %d, ext: %d, alen: %ld\n", + msg->msg_len,msg->msg_answer,msg->msg_ext,msg->msg_alen)); + if (msg->msg_len) + if (!(msg->msg_answer && msg->msg_ext)) { + msg->msg_text[msg->msg_len] = '\0'; + printk("%s",msg->msg_text); + } + + if (msg->msg_ext && !msg->msg_answer) { + while (gdth_test_busy(hanum)) + udelay(1); + cmdp->Service = SCREENSERVICE; + cmdp->RequestBuffer = SCREEN_CMND; + gdth_get_cmd_index(hanum); + gdth_set_sema0(hanum); + cmdp->OpCode = GDT_READ; + cmdp->BoardNode = LOCALBOARD; + cmdp->u.screen.reserved = 0; + cmdp->u.screen.msg_handle= msg->msg_handle; + cmdp->u.screen.msg_addr = (ulong)msg; + ha->cmd_offs_dpmem = 0; + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) + + sizeof(ulong); + ha->cmd_cnt = 0; + gdth_copy_command(hanum); + gdth_release_event(hanum); + return 1; + } + + if (msg->msg_answer && msg->msg_alen) { + for (i=0; imsg_alen && imsg_text[i] = c; + } + msg->msg_alen -= i; + if (c!='\r' && msg->msg_alen!=0) { + msg->msg_answer = 1; + msg->msg_ext = 1; + } else { + msg->msg_ext = 0; + msg->msg_answer = 0; + } + msg->msg_len = i; + while (gdth_test_busy(hanum)) + udelay(1); + cmdp->Service = SCREENSERVICE; + cmdp->RequestBuffer = SCREEN_CMND; + gdth_get_cmd_index(hanum); + gdth_set_sema0(hanum); + cmdp->OpCode = GDT_WRITE; + cmdp->BoardNode = LOCALBOARD; + cmdp->u.screen.reserved = 0; + cmdp->u.screen.msg_handle= msg->msg_handle; + cmdp->u.screen.msg_addr = (ulong)msg; + ha->cmd_offs_dpmem = 0; + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) + + sizeof(ulong); + ha->cmd_cnt = 0; + gdth_copy_command(hanum); + gdth_release_event(hanum); + return 1; + } + printk("\n"); + + } else { + scp->SCp.Message = (int)ha->status; + /* cache or raw service */ + if (ha->status == S_OK) { + scp->result = DID_OK << 16; + } else if (ha->status == S_BSY) { + TRACE2(("Controller busy -> retry !\n")); + gdth_putq(hanum,scp,DEFAULT_PRI); + return 1; + } else { + if (service == CACHESERVICE) { + memset((char*)scp->sense_buffer,0,16); + scp->sense_buffer[0] = 0x70; + scp->sense_buffer[2] = NOT_READY; + scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1); + + if (scp->done != gdth_scsi_done) { + dvr.size = sizeof(dvr.eu.sync); + dvr.eu.sync.ionode = hanum; + dvr.eu.sync.service = service; + dvr.eu.sync.status = ha->status; + dvr.eu.sync.info = ha->info; + dvr.eu.sync.hostdrive = +#if LINUX_VERSION_CODE >= 0x010400 + ha->id[scp->channel][scp->target].hostdrive; +#else + ha->id[NUMDATA(scp->host)->busnum][scp->target].hostdrive; +#endif + if (ha->status >= 0x8000) + gdth_store_event(ES_SYNC, 0, &dvr); + else + gdth_store_event(ES_SYNC, service, &dvr); + } + } else { + if (ha->status!=S_RAW_SCSI || ha->status==S_RAW_ILL) { + scp->result = DID_BAD_TARGET << 16; + } else { + scp->result = (DID_OK << 16) | ha->info; + } + } + } + scp->SCp.have_data_in++; + scp->scsi_done(scp); + } + + return 1; +} + +static char *async_cache_tab[] = { +/* 0*/ "\011\000\002\002\002\004\002\006\004" + "GDT HA %u, service %u, async. status %u/%lu unknown", +/* 1*/ "\011\000\002\002\002\004\002\006\004" + "GDT HA %u, service %u, async. status %u/%lu unknown", +/* 2*/ "\005\000\002\006\004" + "GDT HA %u, Host Drive %lu not ready", +/* 3*/ "\005\000\002\006\004" + "GDT HA %u, Host Drive %lu: REASSIGN not successful and/or data error on reassigned blocks. Drive may crash in the future and should be replaced", +/* 4*/ "\005\000\002\006\004" + "GDT HA %u, mirror update on Host Drive %lu failed", +/* 5*/ "\005\000\002\006\004" + "GDT HA %u, Mirror Drive %lu failed", +/* 6*/ "\005\000\002\006\004" + "GDT HA %u, Mirror Drive %lu: REASSIGN not successful and/or data error on reassigned blocks. Drive may crash in the future and should be replaced", +/* 7*/ "\005\000\002\006\004" + "GDT HA %u, Host Drive %lu write protected", +/* 8*/ "\005\000\002\006\004" + "GDT HA %u, media changed in Host Drive %lu", +/* 9*/ "\005\000\002\006\004" + "GDT HA %u, Host Drive %lu is offline", +/*10*/ "\005\000\002\006\004" + "GDT HA %u, media change of Mirror Drive %lu", +/*11*/ "\005\000\002\006\004" + "GDT HA %u, Mirror Drive %lu is write protected", +/*12*/ "\005\000\002\006\004" + "GDT HA %u, general error on Host Drive %lu. Please check the devices of this drive!", +/*13*/ "\007\000\002\006\002\010\002" + "GDT HA %u, Array Drive %u: Cache Drive %u failed", +/*14*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: FAIL state entered", +/*15*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: error", +/*16*/ "\007\000\002\006\002\010\002" + "GDT HA %u, Array Drive %u: failed drive replaced by Cache Drive %u", +/*17*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: parity build failed", +/*18*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: drive rebuild failed", +/*19*/ "\007\000\002\010\002" + "GDT HA %u, Test of Hot Fix %u failed", +/*20*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: drive build finished successfully", +/*21*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: drive rebuild finished successfully", +/*22*/ "\007\000\002\006\002\010\002" + "GDT HA %u, Array Drive %u: Hot Fix %u activated", +/*23*/ "\005\000\002\006\002" + "GDT HA %u, Host Drive %u: processing of i/o aborted due to serious drive error", +/*24*/ "\005\000\002\010\002" + "GDT HA %u, mirror update on Cache Drive %u completed", +/*25*/ "\005\000\002\010\002" + "GDT HA %u, mirror update on Cache Drive %lu failed", +/*26*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: drive rebuild started", +/*27*/ "\005\000\002\012\001" + "GDT HA %u, Fault bus %u: SHELF OK detected", +/*28*/ "\005\000\002\012\001" + "GDT HA %u, Fault bus %u: SHELF not OK detected", +/*29*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug started", +/*30*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: new disk detected", +/*31*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: old disk detected", +/*32*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: plugging an active disk is illegal", +/*33*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: illegal device detected", +/*34*/ "\011\000\002\012\001\013\001\006\004" + "GDT HA %u, Fault bus %u, ID %u: insufficient disk capacity (%lu MB required)", +/*35*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: disk write protected", +/*36*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: disk not available", +/*37*/ "\007\000\002\012\001\006\004" + "GDT HA %u, Fault bus %u: swap detected (%lu)", +/*38*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug finished successfully", +/*39*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug aborted due to user Hot Plug", +/*40*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug aborted", +/*41*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug for Hot Fix started", +/*42*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: drive build started", +/*43*/ "\003\000\002" + "GDT HA %u, DRAM parity error detected", +/*44*/ "\005\000\002\006\002" + "GDT HA %u, Mirror Drive %u: update started", +/*45*/ "\007\000\002\006\002\010\002" + "GDT HA %u, Mirror Drive %u: Hot Fix %u activated", +/*46*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: no matching Pool Hot Fix Drive available", +/*47*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: Pool Hot Fix Drive available", +/*48*/ "\005\000\002\006\002" + "GDT HA %u, Mirror Drive %u: no matching Pool Hot Fix Drive available", +/*49*/ "\005\000\002\006\002" + "GDT HA %u, Mirror Drive %u: Pool Hot Fix Drive available", +/*50*/ "\007\000\002\012\001\013\001" + "GDT HA %u, SCSI bus %u, ID %u: IGNORE_WIDE_RESIDUE message received", +/*51*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: expand started", +/*52*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: expand finished successfully", +/*53*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: expand failed", +/*54*/ "\003\000\002" + "GDT HA %u, CPU temperature critical", +/*55*/ "\003\000\002" + "GDT HA %u, CPU temperature OK", +}; + + +static int gdth_async_event(int hanum,int service) +{ + gdth_stackframe stack; + gdth_evt_data dvr; + char *f = NULL; + int i,j; + gdth_ha_str *ha; + gdth_msg_str *msg; + gdth_cmd_str *cmdp; + int cmd_index; + + ha = HADATA(gdth_ctr_tab[hanum]); + cmdp= ha->pccb; + msg = (gdth_msg_str *)ha->pscratch; + TRACE2(("gdth_async_event() ha %d serv %d\n", + hanum,service)); + + if (service == SCREENSERVICE) { + if (ha->status == MSG_REQUEST) { + while (gdth_test_busy(hanum)) + udelay(1); + cmdp->Service = SCREENSERVICE; + cmdp->RequestBuffer = SCREEN_CMND; + cmd_index = gdth_get_cmd_index(hanum); + gdth_set_sema0(hanum); + cmdp->OpCode = GDT_READ; + cmdp->BoardNode = LOCALBOARD; + cmdp->u.screen.reserved = 0; + cmdp->u.screen.msg_handle= MSG_INV_HANDLE; + cmdp->u.screen.msg_addr = (ulong)msg; + ha->cmd_offs_dpmem = 0; + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) + + sizeof(ulong); + ha->cmd_cnt = 0; + gdth_copy_command(hanum); + if (ha->type == GDT_EISA) + printk("[EISA slot %d] ",(ushort)ha->brd_phys); + else if (ha->type == GDT_ISA) + printk("[DPMEM 0x%4X] ",(ushort)ha->brd_phys); + else + printk("[PCI %d/%d] ",(ushort)(ha->brd_phys>>8), + (ushort)((ha->brd_phys>>3)&0x1f)); + gdth_release_event(hanum); + } + + } else { + dvr.size = sizeof(dvr.eu.async); + dvr.eu.async.ionode = hanum; + dvr.eu.async.service = service; + dvr.eu.async.status = ha->status; + dvr.eu.async.info = ha->info; + *(ulong *)dvr.eu.async.scsi_coord = ha->info2; + gdth_store_event(ES_ASYNC, service, &dvr); + + if (service==CACHESERVICE && INDEX_OK(ha->status,async_cache_tab)) { + TRACE2(("GDT: Async. event cache service, event no.: %d\n", + ha->status)); + + f = async_cache_tab[ha->status]; + + /* i: parameter to push, j: stack element to fill */ + for (j=0,i=1; i < f[0]; i+=2) { + switch (f[i+1]) { + case 4: + stack.b[j++] = *(ulong*)&dvr.eu.stream[(int)f[i]]; + break; + case 2: + stack.b[j++] = *(ushort*)&dvr.eu.stream[(int)f[i]]; + break; + case 1: + stack.b[j++] = *(unchar*)&dvr.eu.stream[(int)f[i]]; + break; + default: + break; + } + } + + printk(&f[f[0]],stack); printk("\n"); + + } else { + printk("GDT: Unknown async. event service %d event no. %d\n", + service,ha->status); + } + } + return 1; +} + +#ifdef GDTH_STATISTICS +void gdth_timeout(void) +{ + ulong flags,i; + Scsi_Cmnd *nscp; + gdth_ha_str *ha; + int hanum = 0; + + save_flags(flags); + cli(); + + for (act_stats=0,i=0; ireq_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr) + ++act_rq; + + TRACE2(("gdth_to(): ints %ld, ios %ld, act_stats %ld, act_rq %ld\n", + act_ints, act_ios, act_stats, act_rq)); + act_ints = act_ios = 0; + + timer_table[GDTH_TIMER].expires = jiffies + 30*HZ; + timer_active |= 1<irq,ha->drq); + + save_flags(flags); + cli(); +#if LINUX_VERSION_CODE >= 0x010346 + if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL)) +#else + if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth")) +#endif + { + printk("GDT-ISA: Unable to allocate IRQ\n"); + restore_flags(flags); + scsi_unregister(shp); + continue; + } + if (request_dma(ha->drq,"gdth")) { + printk("GDT-ISA: Unable to allocate DMA channel\n"); +#if LINUX_VERSION_CODE >= 0x010346 + free_irq(ha->irq,NULL); +#else + free_irq(ha->irq); +#endif + restore_flags(flags); + scsi_unregister(shp); + continue; + } + set_dma_mode(ha->drq,DMA_MODE_CASCADE); + enable_dma(ha->drq); + shp->unchecked_isa_dma = 1; + shp->irq = ha->irq; + shp->dma_channel = ha->drq; + for (i=0; iid[0][i].type==SIOP_DTYP) { + shp->this_id = i; + break; + } + } + hanum = gdth_ctr_count; + gdth_ctr_tab[gdth_ctr_count++] = shp; + gdth_ctr_vtab[gdth_ctr_vcount++] = shp; + + NUMDATA(shp)->hanum = (ushort)hanum; + NUMDATA(shp)->busnum= 0; + + ha->pccb = CMDDATA(shp); + ha->pscratch = DMADATA(shp); + ha->req_first = NULL; + for (i=0; iid[i][j].type = EMPTY_DTYP; + ha->id[i][j].lock = 0; + } + } + restore_flags(flags); + + if (!gdth_search_drives(hanum,TRUE)) { + printk("GDT-ISA: Error during device scan\n"); + --gdth_ctr_count; + save_flags(flags); + cli(); +#if LINUX_VERSION_CODE >= 0x010346 + free_irq(ha->irq,NULL); +#else + free_irq(ha->irq); +#endif + restore_flags(flags); + scsi_unregister(shp); + continue; + } + +#if LINUX_VERSION_CODE >= 0x010400 + shp->max_id = 8; + shp->max_lun = 8; + shp->max_channel = ha->bus_cnt - 1; +#else + /* register addit. SCSI channels as virtual controllers */ + for (b=1; bbus_cnt; ++b) { + shp = scsi_register(shtp,sizeof(gdth_num_str)); + shp->unchecked_isa_dma = 1; + shp->irq = ha->irq; + shp->dma_channel = ha->drq; + for (i=0; iid[b][i].type==SIOP_DTYP) { + shp->this_id = i; + break; + } + } + gdth_ctr_vtab[gdth_ctr_vcount++] = shp; + NUMDATA(shp)->hanum = (ushort)hanum; + NUMDATA(shp)->busnum = b; + } +#endif + + gdth_enable_int(hanum); + } + } + + /* scanning for EISA controllers */ + for (eisa_slot=0x1000; eisa_slot<=0x8000; eisa_slot+=0x1000) { + if (gdth_search_eisa(eisa_slot)) { /* controller found */ + shp = scsi_register(shtp,sizeof(gdth_ext_str)); + ha = HADATA(shp); + if (!gdth_init_eisa(eisa_slot,ha,TRUE)) { + scsi_unregister(shp); + continue; + } + /* controller found and initialized */ + printk("Configuring GDT-EISA HA at Slot %d IRQ %u\n", + eisa_slot>>12,ha->irq); + + save_flags(flags); + cli(); +#if LINUX_VERSION_CODE >= 0x010346 + if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL)) +#else + if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth")) +#endif + { + printk("GDT-EISA: Unable to allocate IRQ\n"); + restore_flags(flags); + scsi_unregister(shp); + continue; + } + shp->unchecked_isa_dma = 0; + shp->irq = ha->irq; + shp->dma_channel = 0xff; + for (i=0; iid[0][i].type==SIOP_DTYP) { + shp->this_id = i; + break; + } + } + hanum = gdth_ctr_count; + gdth_ctr_tab[gdth_ctr_count++] = shp; + gdth_ctr_vtab[gdth_ctr_vcount++] = shp; + + NUMDATA(shp)->hanum = (ushort)hanum; + NUMDATA(shp)->busnum= 0; + TRACE2(("EISA detect Bus 0: shp %lx hanum %d\n", + (ulong)shp,NUMDATA(shp)->hanum)); + + ha->pccb = CMDDATA(shp); + ha->pscratch = DMADATA(shp); + ha->req_first = NULL; + for (i=0; iid[i][j].type = EMPTY_DTYP; + ha->id[i][j].lock = 0; + } + } + restore_flags(flags); + + if (!gdth_search_drives(hanum,TRUE)) { + printk("GDT-EISA: Error during device scan\n"); + --gdth_ctr_count; + save_flags(flags); + cli(); +#if LINUX_VERSION_CODE >= 0x010346 + free_irq(ha->irq,NULL); +#else + free_irq(ha->irq); +#endif + restore_flags(flags); + scsi_unregister(shp); + continue; + } + +#if LINUX_VERSION_CODE >= 0x010400 + shp->max_id = 8; + shp->max_lun = 8; + shp->max_channel = ha->bus_cnt - 1; +#else + /* register addit. SCSI channels as virtual controllers */ + for (b=1; bbus_cnt; ++b) { + shp = scsi_register(shtp,sizeof(gdth_num_str)); + shp->unchecked_isa_dma = 0; + shp->irq = ha->irq; + shp->dma_channel = 0xff; + for (i=0; iid[b][i].type==SIOP_DTYP) { + shp->this_id = i; + break; + } + } + gdth_ctr_vtab[gdth_ctr_vcount++] = shp; + NUMDATA(shp)->hanum = (ushort)hanum; + NUMDATA(shp)->busnum = b; + TRACE2(("EISA detect Bus %d: shp %lx hanum %d\n", + NUMDATA(shp)->busnum,(ulong)shp, + NUMDATA(shp)->hanum)); + } +#endif + + gdth_enable_int(hanum); + } + } + + /* scanning for PCI controllers */ + for (device_id = 0; device_id <= PCI_DEVICE_ID_VORTEX_GDT6x21RP2; ++device_id) { + if (device_id > PCI_DEVICE_ID_VORTEX_GDT6555 && + device_id < PCI_DEVICE_ID_VORTEX_GDT6x17RP) + continue; + for (index = 0; ; ++index) { + if (!gdth_search_pci(device_id,index,&pcistr)) + break; /* next device_id */ + shp = scsi_register(shtp,sizeof(gdth_ext_str)); + ha = HADATA(shp); + if (!gdth_init_pci(&pcistr,ha,TRUE)) { + scsi_unregister(shp); + continue; + } + /* controller found and initialized */ + printk("Configuring GDT-PCI HA at %d/%d IRQ %u\n", + pcistr.bus,pcistr.device_fn>>3,ha->irq); + + save_flags(flags); + cli(); +#if LINUX_VERSION_CODE >= 0x010346 + if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL)) +#else + if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth")) +#endif + { + printk("GDT-PCI: Unable to allocate IRQ\n"); + restore_flags(flags); + scsi_unregister(shp); + continue; + } + shp->unchecked_isa_dma = 0; + shp->irq = ha->irq; + shp->dma_channel = 0xff; + for (i=0; iid[0][i].type==SIOP_DTYP) { + shp->this_id = i; + break; + } + } + hanum = gdth_ctr_count; + gdth_ctr_tab[gdth_ctr_count++] = shp; + gdth_ctr_vtab[gdth_ctr_vcount++] = shp; + + NUMDATA(shp)->hanum = (ushort)hanum; + NUMDATA(shp)->busnum= 0; + + ha->pccb = CMDDATA(shp); + ha->pscratch = DMADATA(shp); + ha->req_first = NULL; + for (i=0; iid[i][j].type = EMPTY_DTYP; + ha->id[i][j].lock = 0; + } + } + restore_flags(flags); + + if (!gdth_search_drives(hanum,TRUE)) { + printk("GDT-PCI: Error during device scan\n"); + --gdth_ctr_count; + save_flags(flags); + cli(); +#if LINUX_VERSION_CODE >= 0x010346 + free_irq(ha->irq,NULL); +#else + free_irq(ha->irq); +#endif + restore_flags(flags); + scsi_unregister(shp); + continue; + } + +#if LINUX_VERSION_CODE >= 0x010400 + shp->max_id = 8; + shp->max_lun = 8; + shp->max_channel = ha->bus_cnt - 1; +#else + /* register addit. SCSI channels as virtual controllers */ + for (b=1; bbus_cnt; ++b) { + shp = scsi_register(shtp,sizeof(gdth_num_str)); + shp->unchecked_isa_dma = 0; + shp->irq = ha->irq; + shp->dma_channel = 0xff; + for (i=0; iid[b][i].type==SIOP_DTYP) { + shp->this_id = i; + break; + } + } + gdth_ctr_vtab[gdth_ctr_vcount++] = shp; + NUMDATA(shp)->hanum = (ushort)hanum; + NUMDATA(shp)->busnum = b; + } +#endif + + gdth_enable_int(hanum); + } + } + + TRACE2(("gdth_detect() %d controller detected\n",gdth_ctr_count)); + +#ifdef GDTH_STATISTICS + TRACE2(("gdth_detect(): Initializing timer !\n")); + timer_table[GDTH_TIMER].fn = gdth_timeout; + timer_table[GDTH_TIMER].expires = jiffies + HZ; + timer_active |= 1<busnum == 0) { + if (shp->irq) { +#if LINUX_VERSION_CODE >= 0x010346 + free_irq(shp->irq,NULL); +#else + free_irq(shp->irq); +#endif + } + if (shp->dma_channel != 0xff) { + free_dma(shp->dma_channel); + } + } + + restore_flags(flags); + scsi_unregister(shp); + return 0; +} + + +static const char *gdth_ctr_name(int hanum) +{ + gdth_ha_str *ha; + + TRACE2(("gdth_ctr_name()\n")); + + ha = HADATA(gdth_ctr_tab[hanum]); + + if (ha->type == GDT_EISA) { + switch (ha->stype) { + case GDT3_ID: + return("GDT3000/3020 (EISA)"); + case GDT3A_ID: + return("GDT3000A/3020A/3050A (EISA)"); + case GDT3B_ID: + return("GDT3000B/3010A (EISA)"); + } + } else if (ha->type == GDT_ISA) { + return("GDT2000/2020 (ISA)"); + } else if (ha->type == GDT_PCI) { + switch (ha->stype) { + case PCI_DEVICE_ID_VORTEX_GDT60x0: + return("GDT6000/6020/6050 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6000B: + return("GDT6000B/6010 (PCI)"); + } + } else if (ha->type == GDT_PCINEW) { + switch (ha->stype) { + case PCI_DEVICE_ID_VORTEX_GDT6x10: + return("GDT6110/6510 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x20: + return("GDT6120/6520 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6530: + return("GDT6530 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6550: + return("GDT6550 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x17: + return("GDT6117/6517 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x27: + return("GDT6127/6527 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6537: + return("GDT6537 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6557: + return("GDT6557/6557-ECC (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x15: + return("GDT6115/6515 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x25: + return("GDT6125/6525 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6535: + return("GDT6535 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6555: + return("GDT6555/6555-ECC (PCI)"); + } + } else if (ha->type == GDT_PCIMPR) { + switch (ha->stype) { + case PCI_DEVICE_ID_VORTEX_GDT6x17RP: + return("GDT6117RP/GDT6517RP (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x27RP: + return("GDT6127RP/GDT6527RP (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6537RP: + return("GDT6537RP (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6557RP: + return("GDT6557RP (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x11RP: + return("GDT6111RP/GDT6511RP (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x21RP: + return("GDT6121RP/GDT6521RP (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x17RP1: + return("GDT6117RP1/GDT6517RP1 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x27RP1: + return("GDT6127RP1/GDT6527RP1 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6537RP1: + return("GDT6537RP1 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6557RP1: + return("GDT6557RP1 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x11RP1: + return("GDT6111RP1/GDT6511RP1 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x21RP1: + return("GDT6121RP1/GDT6521RP1 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x17RP2: + return("GDT6117RP2/GDT6517RP2 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x27RP2: + return("GDT6127RP2/GDT6527RP2 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6537RP2: + return("GDT6537RP2 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6557RP2: + return("GDT6557RP2 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x11RP2: + return("GDT6111RP2/GDT6511RP2 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x21RP2: + return("GDT6121RP2/GDT6521RP2 (PCI)"); + } + } + return(""); +} + +const char *gdth_info(struct Scsi_Host *shp) +{ + int hanum; + + TRACE2(("gdth_info()\n")); + hanum = NUMDATA(shp)->hanum; + + return (gdth_ctr_name(hanum)); +} + + +int gdth_abort(Scsi_Cmnd *scp) +{ + TRACE2(("gdth_abort() reason %d\n",scp->abort_reason)); + return SCSI_ABORT_SNOOZE; +} + +#if LINUX_VERSION_CODE >= 0x010346 +int gdth_reset(Scsi_Cmnd *scp, unsigned int reset_flags) +#else +int gdth_reset(Scsi_Cmnd *scp) +#endif +{ + TRACE2(("gdth_reset()\n")); + return SCSI_RESET_PUNT; +} + + +#if LINUX_VERSION_CODE >= 0x010300 +int gdth_bios_param(Disk *disk,kdev_t dev,int *ip) +#else +int gdth_bios_param(Disk *disk,int dev,int *ip) +#endif +{ + TRACE2(("gdth_bios_param()\n")); + + ip[2] = disk->capacity / HEADS / SECS; + if (ip[2] <= MAXCYLS) { + ip[0] = HEADS; + ip[1] = SECS; + } else { + ip[2] = disk->capacity / MEDHEADS / MEDSECS; + if (ip[2] <= MAXCYLS) { + ip[0] = MEDHEADS; + ip[1] = MEDSECS; + } else { + ip[2] = disk->capacity / BIGHEADS / BIGSECS; + ip[0] = BIGHEADS; + ip[1] = BIGSECS; + } + } + TRACE2(("gdth_bios_param(): %d heads, %d secs, %d cyls\n", + ip[0],ip[1],ip[2])); + return 0; +} + + +static void internal_done(Scsi_Cmnd *scp) +{ + scp->SCp.sent_command++; +} + +int gdth_command(Scsi_Cmnd *scp) +{ + TRACE2(("gdth_command()\n")); + + scp->SCp.sent_command = 0; + gdth_queuecommand(scp,internal_done); + + while (!scp->SCp.sent_command) + barrier(); + return scp->result; +} + + +int gdth_queuecommand(Scsi_Cmnd *scp,void (*done)(Scsi_Cmnd *)) +{ + int hanum; + int priority; + + TRACE(("gdth_queuecommand() cmd 0x%x id %d lun %d\n", + scp->cmnd[0],scp->target,scp->lun)); + + scp->scsi_done = (void *)done; + scp->SCp.have_data_in = 0; + hanum = NUMDATA(scp->host)->hanum; +#ifdef GDTH_STATISTICS + ++act_ios; +#endif + + priority = DEFAULT_PRI; +#if LINUX_VERSION_CODE >= 0x010300 + if (scp->done == gdth_scsi_done) + priority = scp->SCp.this_residual; +#endif + gdth_putq( hanum, scp, priority ); + gdth_next( hanum ); + return 0; +} + + +/* shutdown routine */ +void gdth_halt() +{ + int hanum, i, j; + gdth_ha_str *ha; + Scsi_Cmnd scp; + Scsi_Device sdev; + gdth_cmd_str gdtcmd; + char cmnd[12]; + + TRACE2(("gdth_halt()\n")); + printk("GDT: Flushing all host drives .. "); + + for (hanum = 0; hanum < gdth_ctr_count; ++hanum) { + ha = HADATA(gdth_ctr_tab[hanum]); + memset(&sdev,0,sizeof(Scsi_Device)); + memset(&scp, 0,sizeof(Scsi_Cmnd)); + sdev.host = gdth_ctr_tab[hanum]; + sdev.id = sdev.host->this_id; + scp.cmd_len = 12; + scp.host = gdth_ctr_tab[hanum]; + scp.target = sdev.host->this_id; + scp.device = &sdev; + scp.use_sg = 0; + + /* flush */ + for (i = 0; i < MAXBUS; ++i) { + for (j = 0; j < MAXID; ++j) { + if (ha->id[i][j].type == CACHE_DTYP) { + gdtcmd.BoardNode = LOCALBOARD; + gdtcmd.Service = CACHESERVICE; + gdtcmd.OpCode = GDT_FLUSH; + gdtcmd.u.cache.DeviceNo = ha->id[i][j].hostdrive; + gdtcmd.u.cache.BlockNo = 1; + gdtcmd.u.cache.sg_canz = 0; + TRACE2(("gdth_halt(): flush ha %d drive %d\n", + hanum, ha->id[i][j].hostdrive)); + { + struct semaphore sem = MUTEX_LOCKED; + scp.request.rq_status = RQ_SCSI_BUSY; + scp.request.sem = &sem; + scsi_do_cmd(&scp, cmnd, &gdtcmd, + sizeof(gdth_cmd_str), gdth_scsi_done, + 30*HZ, 1); + down(&sem); + } + } + } + } + + /* controller reset */ + gdtcmd.BoardNode = LOCALBOARD; + gdtcmd.Service = CACHESERVICE; + gdtcmd.OpCode = GDT_RESET; + TRACE2(("gdth_halt(): reset controller %d\n", hanum)); + { + struct semaphore sem = MUTEX_LOCKED; + scp.request.rq_status = RQ_SCSI_BUSY; + scp.request.sem = &sem; + scsi_do_cmd(&scp, cmnd, &gdtcmd, + sizeof(gdth_cmd_str), gdth_scsi_done, + 10*HZ, 1); + down(&sem); + } + } + printk("Done.\n"); +} + + +/* called from init/main.c */ +void gdth_setup(char *str,int *ints) +{ + static size_t setup_idx = 0; + + TRACE2(("gdth_setup() str %s ints[0] %d ints[1] %d\n", + str ? str:"NULL", ints[0], + ints[0] ? ints[1]:0)); + + if (setup_idx >= MAXHA) { + printk("GDT: gdth_setup() called too many times. Bad LILO params ?\n"); + return; + } + if (ints[0] != 1) { + printk("GDT: Illegal command line !\n"); + printk("Usage: gdth=\n"); + printk("Where: : valid EISA controller IRQ (10,11,12,14)\n"); + printk(" or 0 to disable controller driver\n"); + return; + } + if (ints[1] == 10 || ints[1] == 11 || ints[1] == 12 || ints[1] == 14) { + irqs[setup_idx++] = ints[1]; + irqs[setup_idx] = 0xff; + return; + } + if (ints[1] == 0) { + disable_gdth_scan = TRUE; + return; + } + printk("GDT: Invalid IRQ (%d) specified\n",ints[1]); +} + + +#ifdef MODULE +Scsi_Host_Template driver_template = GDTH; +#include "scsi_module.c" +#endif + diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h new file mode 100644 index 000000000000..3c92be263a47 --- /dev/null +++ b/drivers/scsi/gdth.h @@ -0,0 +1,719 @@ +#ifndef _GDTH_H +#define _GDTH_H + +/* + * Header file for the GDT ISA/EISA/PCI Disk Array Controller driver for Linux + * + * gdth.h Copyright (C) 1995-97 ICP vortex Computersysteme GmbH, Achim Leubner + * See gdth.c for further informations and + * below for supported controller types + * + * + * + * $Id: gdth.h,v 1.7 1997/03/20 16:01:59 achim Exp $ + */ + +#include +#include + +#ifndef NULL +#define NULL 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* defines, macros */ + +/* driver version */ +#define GDTH_VERSION_STR "1.00" +#define GDTH_VERSION 1 +#define GDTH_SUBVERSION 0 + +/* protocol version */ +#define PROTOCOL_VERSION 1 + +/* controller classes */ +#define GDT_ISA 0x01 /* ISA controller */ +#define GDT_EISA 0x02 /* EISA controller */ +#define GDT_PCI 0x03 /* PCI controller */ +#define GDT_PCINEW 0x04 /* new PCI controller */ +#define GDT_PCIMPR 0x05 /* PCI MPR controller */ +/* GDT_EISA, controller subtypes EISA */ +#define GDT3_ID 0x0130941c /* GDT3000/3020 */ +#define GDT3A_ID 0x0230941c /* GDT3000A/3020A/3050A */ +#define GDT3B_ID 0x0330941c /* GDT3000B/3010A */ +/* GDT_ISA */ +#define GDT2_ID 0x0120941c /* GDT2000/2020 */ +/* vendor ID, device IDs (PCI) */ +/* these defines should already exist in */ +#ifndef PCI_VENDOR_ID_VORTEX +#define PCI_VENDOR_ID_VORTEX 0x1119 /* PCI controller vendor ID */ +#endif +#ifndef PCI_DEVICE_ID_VORTEX_GDT60x0 +/* GDT_PCI */ +#define PCI_DEVICE_ID_VORTEX_GDT60x0 0 /* GDT6000/6020/6050 */ +#define PCI_DEVICE_ID_VORTEX_GDT6000B 1 /* GDT6000B/6010 */ +/* GDT_PCINEW */ +#define PCI_DEVICE_ID_VORTEX_GDT6x10 2 /* GDT6110/6510 */ +#define PCI_DEVICE_ID_VORTEX_GDT6x20 3 /* GDT6120/6520 */ +#define PCI_DEVICE_ID_VORTEX_GDT6530 4 /* GDT6530 */ +#define PCI_DEVICE_ID_VORTEX_GDT6550 5 /* GDT6550 */ +/* GDT_PCINEW, wide/ultra SCSI controllers */ +#define PCI_DEVICE_ID_VORTEX_GDT6x17 6 /* GDT6117/6517 */ +#define PCI_DEVICE_ID_VORTEX_GDT6x27 7 /* GDT6127/6527 */ +#define PCI_DEVICE_ID_VORTEX_GDT6537 8 /* GDT6537 */ +#define PCI_DEVICE_ID_VORTEX_GDT6557 9 /* GDT6557/6557-ECC */ +/* GDT_PCINEW, wide SCSI controllers */ +#define PCI_DEVICE_ID_VORTEX_GDT6x15 10 /* GDT6115/6515 */ +#define PCI_DEVICE_ID_VORTEX_GDT6x25 11 /* GDT6125/6525 */ +#define PCI_DEVICE_ID_VORTEX_GDT6535 12 /* GDT6535 */ +#define PCI_DEVICE_ID_VORTEX_GDT6555 13 /* GDT6555/6555-ECC */ +#endif + +#ifndef PCI_DEVICE_ID_VORTEX_GDT6x17RP +/* GDT_MPR, RP series, wide/ultra SCSI */ +#define PCI_DEVICE_ID_VORTEX_GDT6x17RP 0x100 /* GDT6117RP/GDT6517RP */ +#define PCI_DEVICE_ID_VORTEX_GDT6x27RP 0x101 /* GDT6127RP/GDT6527RP */ +#define PCI_DEVICE_ID_VORTEX_GDT6537RP 0x102 /* GDT6537RP */ +#define PCI_DEVICE_ID_VORTEX_GDT6557RP 0x103 /* GDT6557RP */ +/* GDT_MPR, RP series, narrow/ultra SCSI */ +#define PCI_DEVICE_ID_VORTEX_GDT6x11RP 0x104 /* GDT6111RP/GDT6511RP */ +#define PCI_DEVICE_ID_VORTEX_GDT6x21RP 0x105 /* GDT6121RP/GDT6521RP */ +/* GDT_MPR, RP1 series, wide/ultra SCSI */ +#define PCI_DEVICE_ID_VORTEX_GDT6x17RP1 0x110 /* GDT6117RP1/GDT6517RP1 */ +#define PCI_DEVICE_ID_VORTEX_GDT6x27RP1 0x111 /* GDT6127RP1/GDT6527RP1 */ +#define PCI_DEVICE_ID_VORTEX_GDT6537RP1 0x112 /* GDT6537RP1 */ +#define PCI_DEVICE_ID_VORTEX_GDT6557RP1 0x113 /* GDT6557RP1 */ +/* GDT_MPR, RP1 series, narrow/ultra SCSI */ +#define PCI_DEVICE_ID_VORTEX_GDT6x11RP1 0x114 /* GDT6111RP1/GDT6511RP1 */ +#define PCI_DEVICE_ID_VORTEX_GDT6x21RP1 0x115 /* GDT6121RP1/GDT6521RP1 */ +/* GDT_MPR, RP2 series, wide/ultra SCSI */ +#define PCI_DEVICE_ID_VORTEX_GDT6x17RP2 0x120 /* GDT6117RP2/GDT6517RP2 */ +#define PCI_DEVICE_ID_VORTEX_GDT6x27RP2 0x121 /* GDT6127RP2/GDT6527RP2 */ +#define PCI_DEVICE_ID_VORTEX_GDT6537RP2 0x122 /* GDT6537RP2 */ +#define PCI_DEVICE_ID_VORTEX_GDT6557RP2 0x123 /* GDT6557RP2 */ +/* GDT_MPR, RP2 series, narrow/ultra SCSI */ +#define PCI_DEVICE_ID_VORTEX_GDT6x11RP2 0x124 /* GDT6111RP2/GDT6511RP2 */ +#define PCI_DEVICE_ID_VORTEX_GDT6x21RP2 0x125 /* GDT6121RP2/GDT6521RP2 */ +#endif + +/* limits */ +#define GDTH_SCRATCH 4096 /* 4KB scratch buffer */ +#define GDTH_MAXCMDS 124 +#define GDTH_MAXC_P_L 16 /* max. cmds per lun */ +#define MAXOFFSETS 128 +#define MAXHA 8 +#define MAXID 8 +#define MAXLUN 8 +#define MAXBUS 5 +#define MAX_HDRIVES 35 /* max. host drive count */ +#define MAX_EVENTS 100 /* event buffer count */ +#define MAXCYLS 1024 +#define HEADS 64 +#define SECS 32 /* mapping 64*32 */ +#define MEDHEADS 127 +#define MEDSECS 63 /* mapping 127*63 */ +#define BIGHEADS 255 +#define BIGSECS 63 /* mapping 255*63 */ + +/* special command ptr. */ +#define UNUSED_CMND ((Scsi_Cmnd *)-1) +#define INTERNAL_CMND ((Scsi_Cmnd *)-2) +#define SCREEN_CMND ((Scsi_Cmnd *)-3) +#define SPECIAL_SCP(p) (p==UNUSED_CMND || p==INTERNAL_CMND || p==SCREEN_CMND) + +/* device types */ +#define EMPTY_DTYP 0 +#define CACHE_DTYP 1 +#define RAW_DTYP 2 +#define SIOP_DTYP 3 /* the SCSI processor */ + +/* controller services */ +#define SCSIRAWSERVICE 3 +#define CACHESERVICE 9 +#define SCREENSERVICE 11 + +/* screenservice defines */ +#define MSG_INV_HANDLE -1 /* special message handle */ +#define MSGLEN 16 /* size of message text */ +#define MSG_SIZE 34 /* size of message structure */ +#define MSG_REQUEST 0 /* async. event: message */ + +/* cacheservice defines */ +#define SECTOR_SIZE 0x200 /* always 512 bytes per sector */ + +/* DPMEM constants */ +#define IC_HEADER_BYTES 48 +#define IC_QUEUE_BYTES 4 +#define DPMEM_COMMAND_OFFSET IC_HEADER_BYTES+IC_QUEUE_BYTES*MAXOFFSETS + +/* service commands */ +#define GDT_INIT 0 /* service initialization */ +#define GDT_READ 1 /* read command */ +#define GDT_WRITE 2 /* write command */ +#define GDT_INFO 3 /* information about devices */ +#define GDT_FLUSH 4 /* flush dirty cache buffers */ +#define GDT_IOCTL 5 /* ioctl command */ +#define GDT_DEVTYPE 9 /* additional information */ +#define GDT_MOUNT 10 /* mount cache device */ +#define GDT_UNMOUNT 11 /* unmount cache device */ +#define GDT_SET_FEAT 12 /* set feat. (scatter/gather) */ +#define GDT_GET_FEAT 13 /* get features */ +#define GDT_RESERVE 14 /* reserve dev. to raw service */ +#define GDT_WRITE_THR 16 /* write through */ +#define GDT_EXT_INFO 18 /* extended info */ +#define GDT_RESET 19 /* controller reset */ + +/* IOCTL command defines */ +#define SCSI_CHAN_CNT 5 /* subfunctions */ +#define L_CTRL_PATTERN 0x20000000L +#define CACHE_INFO 4 +#define CACHE_CONFIG 5 +#define IO_CHANNEL 0x00020000L /* channels */ +#define INVALID_CHANNEL 0x0000ffffL + +/* IOCTLs */ +#define GDTIOCTL_MASK ('J'<<8) +#define GDTIOCTL_GENERAL (GDTIOCTL_MASK | 0) /* general IOCTL */ +#define GDTIOCTL_DRVERS (GDTIOCTL_MASK | 1) /* get driver version */ +#define GDTIOCTL_CTRTYPE (GDTIOCTL_MASK | 2) /* get controller type */ +#define GDTIOCTL_CTRCNT (GDTIOCTL_MASK | 5) /* get controller count */ +#define GDTIOCTL_LOCKDRV (GDTIOCTL_MASK | 6) /* lock host drive */ +#define GDTIOCTL_LOCKCHN (GDTIOCTL_MASK | 7) /* lock channel */ +#define GDTIOCTL_EVENT (GDTIOCTL_MASK | 8) /* read controller events */ + +/* service errors */ +#define S_OK 1 /* no error */ +#define S_BSY 7 /* controller busy */ +#define S_RAW_SCSI 12 /* raw serv.: target error */ +#define S_RAW_ILL 0xff /* raw serv.: illegal */ + +/* timeout values */ +#define INIT_RETRIES 10000 /* 10000 * 1ms = 10s */ +#define INIT_TIMEOUT 100000 /* 1000 * 1ms = 1s */ +#define POLL_TIMEOUT 10000 /* 10000 * 1ms = 10s */ + +/* priorities */ +#define DEFAULT_PRI 0x20 +#define IOCTL_PRI 0x10 + +/* data directions */ +#define DATA_IN 0x01000000L /* data from target */ +#define DATA_OUT 0x00000000L /* data to target */ + +/* BMIC registers (EISA controllers) */ +#define ID0REG 0x0c80 /* board ID */ +#define EINTENABREG 0x0c89 /* interrupt enable */ +#define SEMA0REG 0x0c8a /* command semaphore */ +#define SEMA1REG 0x0c8b /* status semaphore */ +#define LDOORREG 0x0c8d /* local doorbell */ +#define EDENABREG 0x0c8e /* EISA system doorbell enable */ +#define EDOORREG 0x0c8f /* EISA system doorbell */ +#define MAILBOXREG 0x0c90 /* mailbox reg. (16 bytes) */ +#define EISAREG 0x0cc0 /* EISA configuration */ + +/* other defines */ +#define LINUX_OS 8 /* used for cache optim. */ +#define SCATTER_GATHER 1 /* s/g feature */ +#define GDTH_MAXSG 32 /* max. s/g elements */ +#define SECS32 0x1f /* round capacity */ +#define BIOS_ID_OFFS 0x10 /* offset contr. ID in ISABIOS */ +#define LOCALBOARD 0 /* board node always 0 */ +#define ASYNCINDEX 0 /* cmd index async. event */ +#define SPEZINDEX 1 /* cmd index unknown service */ +#define GDT_WR_THROUGH 0x100 /* WRITE_THROUGH supported */ + +/* typedefs */ + +#pragma pack(1) + +typedef struct { + char buffer[GDTH_SCRATCH]; /* scratch buffer */ +} gdth_scratch_str; + +/* screenservice message */ +typedef struct { + ulong msg_handle; /* message handle */ + ulong msg_len; /* size of message */ + ulong msg_alen; /* answer length */ + unchar msg_answer; /* answer flag */ + unchar msg_ext; /* more messages */ + unchar msg_reserved[2]; + char msg_text[MSGLEN+2]; /* the message text */ +} gdth_msg_str; + +/* get channel count IOCTL */ +typedef struct { + ulong channel_no; /* number of channel */ + ulong drive_cnt; /* number of drives */ + unchar siop_id; /* SCSI processor ID */ + unchar siop_state; /* SCSI processor state */ +} gdth_getch_str; + +/* cache info/config IOCTL */ +typedef struct { + ulong version; /* firmware version */ + ushort state; /* cache state (on/off) */ + ushort strategy; /* cache strategy */ + ushort write_back; /* write back state (on/off) */ + ushort block_size; /* cache block size */ +} gdth_cpar_str; + +typedef struct { + ulong csize; /* cache size */ + ulong read_cnt; /* read/write counter */ + ulong write_cnt; + ulong tr_hits; /* hits */ + ulong sec_hits; + ulong sec_miss; /* misses */ +} gdth_cstat_str; + +typedef struct { + gdth_cpar_str cpar; + gdth_cstat_str cstat; +} gdth_cinfo_str; + +/* scatter/gather element */ +typedef struct { + ulong sg_ptr; /* address */ + ulong sg_len; /* length */ +} gdth_sg_str; + +/* command structure */ +typedef struct { + ulong BoardNode; /* board node (always 0) */ + ulong CommandIndex; /* command number */ + ushort OpCode; /* the command (READ,..) */ + union { + struct { + ushort DeviceNo; /* number of cache drive */ + ulong BlockNo; /* block number */ + ulong BlockCnt; /* block count */ + ulong DestAddr; /* dest. addr. (if s/g: -1) */ + ulong sg_canz; /* s/g element count */ + gdth_sg_str sg_lst[GDTH_MAXSG]; /* s/g list */ + } cache; /* cache service cmd. str. */ + struct { + ushort param_size; /* size of p_param buffer */ + ulong subfunc; /* IOCTL function */ + ulong channel; /* device */ + ulong p_param; /* buffer */ + } ioctl; /* IOCTL command structure */ + struct { + ushort reserved; + ulong msg_handle; /* message handle */ + ulong msg_addr; /* message buffer address */ + } screen; /* screen service cmd. str. */ + struct { + ushort reserved; + ulong direction; /* data direction */ + ulong mdisc_time; /* disc. time (0: no timeout)*/ + ulong mcon_time; /* connect time(0: no to.) */ + ulong sdata; /* dest. addr. (if s/g: -1) */ + ulong sdlen; /* data length (bytes) */ + ulong clen; /* SCSI cmd. length(6,10,12) */ + unchar cmd[12]; /* SCSI command */ + unchar target; /* target ID */ + unchar lun; /* LUN */ + unchar bus; /* SCSI bus number */ + unchar priority; /* only 0 used */ + ulong sense_len; /* sense data length */ + ulong sense_data; /* sense data addr. */ + struct raw *link_p; /* linked cmds (not supp.) */ + ulong sg_ranz; /* s/g element count */ + gdth_sg_str sg_lst[GDTH_MAXSG]; /* s/g list */ + } raw; /* raw service cmd. struct. */ + } u; + /* additional variables */ + unchar Service; /* controller service */ + ushort Status; /* command result */ + ulong Info; /* additional information */ + Scsi_Cmnd *RequestBuffer; /* request buffer */ +} gdth_cmd_str; + +/* controller event structure */ +#define ES_ASYNC 1 +#define ES_DRIVER 2 +#define ES_TEST 3 +#define ES_SYNC 4 +typedef struct { + ushort size; /* size of structure */ + union { + char stream[16]; + struct { + ushort ionode; + ushort service; + ulong index; + } driver; + struct { + ushort ionode; + ushort service; + ushort status; + ulong info; + unchar scsi_coord[3]; + } async; + struct { + ushort ionode; + ushort service; + ushort status; + ulong info; + ushort hostdrive; + unchar scsi_coord[3]; + unchar sense_key; + } sync; + struct { + ulong l1, l2, l3, l4; + } test; + } eu; +} gdth_evt_data; + +typedef struct { + ulong first_stamp; + ulong last_stamp; + ushort same_count; + ushort event_source; + ushort event_idx; + unchar application; + unchar reserved; + gdth_evt_data event_data; +} gdth_evt_str; + + +/* DPRAM structures */ + +/* interface area ISA/PCI */ +typedef struct { + unchar S_Cmd_Indx; /* special command */ + unchar volatile S_Status; /* status special command */ + ushort reserved1; + ulong S_Info[4]; /* add. info special command */ + unchar volatile Sema0; /* command semaphore */ + unchar reserved2[3]; + unchar Cmd_Index; /* command number */ + unchar reserved3[3]; + ushort volatile Status; /* command status */ + ushort Service; /* service(for async.events) */ + ulong Info[2]; /* additional info */ + struct { + ushort offset; /* command offs. in the DPRAM*/ + ushort serv_id; /* service */ + } comm_queue[MAXOFFSETS]; /* command queue */ + ulong bios_reserved[2]; + unchar gdt_dpr_cmd[1]; /* commands */ +} gdt_dpr_if; + +/* SRAM structure PCI controllers */ +typedef struct { + ulong magic; /* controller ID from BIOS */ + ushort need_deinit; /* switch betw. BIOS/driver */ + unchar switch_support; /* see need_deinit */ + unchar padding[9]; + unchar os_used[16]; /* OS code per service */ + unchar unused[28]; + unchar fw_magic; /* contr. ID from firmware */ +} gdt_pci_sram; + +/* SRAM structure EISA controllers (but NOT GDT3000/3020) */ +typedef struct { + unchar os_used[16]; /* OS code per service */ + ushort need_deinit; /* switch betw. BIOS/driver */ + unchar switch_support; /* see need_deinit */ + unchar padding; +} gdt_eisa_sram; + + +/* DPRAM ISA controllers */ +typedef struct { + union { + struct { + unchar bios_used[0x3c00-32]; /* 15KB - 32Bytes BIOS */ + ulong magic; /* controller (EISA) ID */ + ushort need_deinit; /* switch betw. BIOS/driver */ + unchar switch_support; /* see need_deinit */ + unchar padding[9]; + unchar os_used[16]; /* OS code per service */ + } dp_sram; + unchar bios_area[0x4000]; /* 16KB reserved for BIOS */ + } bu; + union { + gdt_dpr_if ic; /* interface area */ + unchar if_area[0x3000]; /* 12KB for interface */ + } u; + struct { + unchar memlock; /* write protection DPRAM */ + unchar event; /* release event */ + unchar irqen; /* board interrupts enable */ + unchar irqdel; /* acknowledge board int. */ + unchar volatile Sema1; /* status semaphore */ + unchar rq; /* IRQ/DRQ configuration */ + } io; +} gdt2_dpram_str; + +/* DPRAM PCI controllers */ +typedef struct { + union { + gdt_dpr_if ic; /* interface area */ + unchar if_area[0xff0-sizeof(gdt_pci_sram)]; + } u; + gdt_pci_sram gdt6sr; /* SRAM structure */ + struct { + unchar unused0[1]; + unchar volatile Sema1; /* command semaphore */ + unchar unused1[3]; + unchar irqen; /* board interrupts enable */ + unchar unused2[2]; + unchar event; /* release event */ + unchar unused3[3]; + unchar irqdel; /* acknowledge board int. */ + unchar unused4[3]; + } io; +} gdt6_dpram_str; + +/* PLX register structure (new PCI controllers) */ +typedef struct { + unchar cfg_reg; /* DPRAM cfg.(2:below 1MB,0:anywhere)*/ + unchar unused1[0x3f]; + unchar volatile sema0_reg; /* command semaphore */ + unchar volatile sema1_reg; /* status semaphore */ + unchar unused2[2]; + ushort volatile status; /* command status */ + ushort service; /* service */ + ulong info[2]; /* additional info */ + unchar unused3[0x10]; + unchar ldoor_reg; /* PCI to local doorbell */ + unchar unused4[3]; + unchar volatile edoor_reg; /* local to PCI doorbell */ + unchar unused5[3]; + unchar control0; /* control0 register(unused) */ + unchar control1; /* board interrupts enable */ + unchar unused6[0x16]; +} gdt6c_plx_regs; + +/* DPRAM new PCI controllers */ +typedef struct { + union { + gdt_dpr_if ic; /* interface area */ + unchar if_area[0x4000-sizeof(gdt_pci_sram)]; + } u; + gdt_pci_sram gdt6sr; /* SRAM structure */ +} gdt6c_dpram_str; + +/* i960 register structure (PCI MPR controllers) */ +typedef struct { + unchar unused1[16]; + unchar volatile sema0_reg; /* command semaphore */ + unchar unused2; + unchar volatile sema1_reg; /* status semaphore */ + unchar unused3; + ushort volatile status; /* command status */ + ushort service; /* service */ + ulong info[2]; /* additional info */ + unchar ldoor_reg; /* PCI to local doorbell */ + unchar unused4[11]; + unchar volatile edoor_reg; /* local to PCI doorbell */ + unchar unused5[7]; + unchar edoor_en_reg; /* board interrupts enable */ + unchar unused6[27]; + ulong unused7[1004]; /* size: 4 KB */ +} gdt6m_i960_regs; + +/* DPRAM PCI MPR controllers */ +typedef struct { + gdt6m_i960_regs i960r; /* 4KB i960 registers */ + union { + gdt_dpr_if ic; /* interface area */ + unchar if_area[0x3000-sizeof(gdt_pci_sram)]; + } u; + gdt_pci_sram gdt6sr; /* SRAM structure */ +} gdt6m_dpram_str; + + +/* PCI resources */ +typedef struct { + ushort device_id; /* device ID (0,..,9) */ + unchar bus; /* PCI bus */ + unchar device_fn; /* PCI device/function no. */ + ulong dpmem; /* DPRAM address */ + ulong io; /* IO address */ + ulong io_mm; /* IO address mem. mapped */ + ulong bios; /* BIOS address */ + unchar irq; /* IRQ */ +} gdth_pci_str; + + +/* controller information structure */ +typedef struct { + unchar bus_cnt; /* SCSI bus count */ + unchar type; /* controller class */ + ushort raw_feat; /* feat. raw service (s/g,..) */ + ushort cache_feat; /* feat. cache serv. (s/g,..) */ + ulong stype; /* controller subtype */ + ulong brd; /* BMIC/DPRAM address */ + ulong brd_phys; /* slot number/BIOS address */ + gdt6c_plx_regs *plx; /* PLX regs (new PCI contr.) */ + gdth_cmd_str *pccb; /* address command structure */ + gdth_scratch_str *pscratch; + unchar irq; /* IRQ */ + unchar drq; /* DRQ (ISA controllers) */ + ushort status; /* command status */ + ulong info; + ulong info2; /* additional info */ + Scsi_Cmnd *req_first; /* top of request queue */ + struct { + unchar type; /* device type */ + unchar heads; /* mapping */ + unchar secs; + unchar lock; /* drive locked ? (hot plug) */ + ushort hostdrive; /* host drive number */ + ushort devtype; /* further information */ + ulong size; /* capacity */ + } id[MAXBUS][MAXID]; + ushort cmd_cnt; /* command count in DPRAM */ + ushort cmd_len; /* length of actual command */ + ushort cmd_offs_dpmem; /* actual offset in DPRAM */ + ushort ic_all_size; /* sizeof DPRAM interf. area */ + unchar reserved; + unchar mode; /* information from /proc */ + ushort param_size; + gdth_cpar_str cpar; /* controller cache par. */ +} gdth_ha_str; + +/* structure for scsi_register(), SCSI bus != 0 */ +typedef struct { + ushort hanum; + ushort busnum; +} gdth_num_str; + +/* structure for scsi_register() */ +typedef struct { + gdth_num_str numext; /* must be the first element */ + gdth_ha_str haext; + gdth_cmd_str cmdext; + gdth_scratch_str dmaext; +} gdth_ext_str; + + +/* INQUIRY data format */ +typedef struct { + unchar type_qual; + unchar modif_rmb; + unchar version; + unchar resp_aenc; + unchar add_length; + unchar reserved1; + unchar reserved2; + unchar misc; + unchar vendor[8]; + unchar product[16]; + unchar revision[4]; +} gdth_inq_data; + +/* READ_CAPACITY data format */ +typedef struct { + ulong last_block_no; + ulong block_length; +} gdth_rdcap_data; + +/* REQUEST_SENSE data format */ +typedef struct { + unchar errorcode; + unchar segno; + unchar key; + ulong info; + unchar add_length; + ulong cmd_info; + unchar adsc; + unchar adsq; + unchar fruc; + unchar key_spec[3]; +} gdth_sense_data; + +/* MODE_SENSE data format */ +typedef struct { + struct { + unchar data_length; + unchar med_type; + unchar dev_par; + unchar bd_length; + } hd; + struct { + unchar dens_code; + unchar block_count[3]; + unchar reserved; + unchar block_length[3]; + } bd; +} gdth_modep_data; + +typedef struct { + ulong b[10]; /* 32 bit compiler ! */ +} gdth_stackframe; + +#pragma pack() + +/* function prototyping */ + +int gdth_detect(Scsi_Host_Template *); +int gdth_release(struct Scsi_Host *); +int gdth_command(Scsi_Cmnd *); +int gdth_queuecommand(Scsi_Cmnd *,void (*done)(Scsi_Cmnd *)); +int gdth_abort(Scsi_Cmnd *); +#if LINUX_VERSION_CODE >= 0x010346 +int gdth_reset(Scsi_Cmnd *, unsigned int reset_flags); +#else +int gdth_reset(Scsi_Cmnd *); +#endif +const char *gdth_info(struct Scsi_Host *); + + +#if LINUX_VERSION_CODE >= 0x010300 +int gdth_bios_param(Disk *,kdev_t,int *); +extern struct proc_dir_entry proc_scsi_gdth; +int gdth_proc_info(char *,char **,off_t,int,int,int); +#define GDTH { NULL, NULL, \ + &proc_scsi_gdth, \ + gdth_proc_info, \ + "GDT SCSI Disk Array Controller", \ + gdth_detect, \ + gdth_release, \ + gdth_info, \ + gdth_command, \ + gdth_queuecommand, \ + gdth_abort, \ + gdth_reset, \ + NULL, \ + gdth_bios_param, \ + GDTH_MAXCMDS, \ + -1, \ + GDTH_MAXSG, \ + GDTH_MAXC_P_L, \ + 0, \ + 1, \ + ENABLE_CLUSTERING} +#else +int gdth_bios_param(Disk *,int,int *); +#define GDTH { NULL, NULL, \ + "GDT SCSI Disk Array Controller", \ + gdth_detect, \ + gdth_release, \ + gdth_info, \ + gdth_command, \ + gdth_queuecommand, \ + gdth_abort, \ + gdth_reset, \ + NULL, \ + gdth_bios_param, \ + GDTH_MAXCMDS, \ + -1, \ + GDTH_MAXSG, \ + GDTH_MAXC_P_L, \ + 0, \ + 1, \ + ENABLE_CLUSTERING} +#endif + +#endif + diff --git a/drivers/scsi/gdth_ioctl.h b/drivers/scsi/gdth_ioctl.h new file mode 100644 index 000000000000..01f5db4c545a --- /dev/null +++ b/drivers/scsi/gdth_ioctl.h @@ -0,0 +1,86 @@ +#ifndef _GDTH_IOCTL_H +#define _GDTH_IOCTL_H + +/* gdth_ioctl.h + * $Id: gdth_ioctl.h,v 1.1 1997/02/21 08:07:27 achim Exp $ + */ + +/* IOCTLs */ +#define GDTIOCTL_MASK ('J'<<8) +#define GDTIOCTL_GENERAL (GDTIOCTL_MASK | 0) /* general IOCTL */ +#define GDTIOCTL_DRVERS (GDTIOCTL_MASK | 1) /* get driver version */ +#define GDTIOCTL_CTRTYPE (GDTIOCTL_MASK | 2) /* get controller type */ +#define GDTIOCTL_OSVERS (GDTIOCTL_MASK | 3) /* get OS version */ +#define GDTIOCTL_CTRCNT (GDTIOCTL_MASK | 5) /* get controller count */ +#define GDTIOCTL_LOCKDRV (GDTIOCTL_MASK | 6) /* lock host drive */ +#define GDTIOCTL_LOCKCHN (GDTIOCTL_MASK | 7) /* lock channel */ +#define GDTIOCTL_EVENT (GDTIOCTL_MASK | 8) /* read controller events */ + +#define GDTIOCTL_MAGIC 0x06030f07UL + + +/* IOCTL structure (write) */ +typedef struct { + ulong magic; /* IOCTL magic */ + ushort ioctl; /* IOCTL */ + ushort ionode; /* controller number */ + ushort service; /* controller service */ + ushort timeout; /* timeout */ + union { + struct { + unchar command[512]; /* controller command */ + unchar data[1]; /* add. data */ + } general; + struct { + unchar lock; /* lock/unlock */ + unchar drive_cnt; /* drive count */ + ushort drives[35]; /* drives */ + } lockdrv; + struct { + unchar lock; /* lock/unlock */ + unchar channel; /* channel */ + } lockchn; + struct { + int erase; /* erase event ? */ + int handle; + } event; + } iu; +} gdth_iowr_str; + +/* IOCTL structure (read) */ +typedef struct { + ulong size; /* buffer size */ + ulong status; /* IOCTL error code */ + union { + struct { + unchar data[1]; /* data */ + } general; + struct { + ushort version; /* driver version */ + } drvers; + struct { + unchar type; /* controller type */ + ushort info; /* slot etc. */ + ushort oem_id; /* OEM ID */ + ushort bios_ver; /* not used */ + ushort access; /* not used */ + ushort ext_type; /* extended type */ + } ctrtype; + struct { + unchar version; /* OS version */ + unchar subversion; /* OS subversion */ + ushort revision; /* revision */ + } osvers; + struct { + ushort count; /* controller count */ + } ctrcnt; + struct { + int handle; + unchar evt[32]; /* event structure */ + } event; + } iu; +} gdth_iord_str; + + +#endif + diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c new file mode 100644 index 000000000000..269a3c87648d --- /dev/null +++ b/drivers/scsi/gdth_proc.c @@ -0,0 +1,635 @@ +/* gdth_proc.c + * $Id: gdth_proc.c,v 1.4 1997/02/25 13:33:47 achim Exp $ + */ + +#include "gdth_ioctl.h" + +int gdth_proc_info(char *buffer,char **start,off_t offset,int length, + int hostno,int inout) +{ + int hanum,busnum,i; + + TRACE2(("gdth_proc_info() length %d ha %d offs %d inout %d\n", + length,hostno,offset,inout)); + + for (i=0; ihost_no == hostno) + break; + } + if (i==gdth_ctr_vcount) + return(-EINVAL); + + hanum = NUMDATA(gdth_ctr_vtab[i])->hanum; + busnum= NUMDATA(gdth_ctr_vtab[i])->busnum; + + if (inout) + return(gdth_set_info(buffer,length,i,hanum,busnum)); + else + return(gdth_get_info(buffer,start,offset,length,i,hanum,busnum)); +} + +static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum) +{ + int ret_val; + Scsi_Cmnd scp; + Scsi_Device sdev; + gdth_iowr_str *piowr; + + TRACE2(("gdth_set_info() ha %d bus %d\n",hanum,busnum)); + piowr = (gdth_iowr_str *)buffer; + + memset(&sdev,0,sizeof(Scsi_Device)); + memset(&scp, 0,sizeof(Scsi_Cmnd)); + sdev.host = gdth_ctr_vtab[vh]; + sdev.id = sdev.host->this_id; + scp.cmd_len = 12; + scp.host = gdth_ctr_vtab[vh]; + scp.target = sdev.host->this_id; + scp.device = &sdev; + scp.use_sg = 0; + + if (length >= 4) { + if (strncmp(buffer,"gdth",4) == 0) { + buffer += 5; + length -= 5; + ret_val = gdth_set_asc_info( buffer, length, hanum, scp ); + } else if (piowr->magic == GDTIOCTL_MAGIC) { + ret_val = gdth_set_bin_info( buffer, length, hanum, scp ); + } else { + printk("GDT: Wrong signature: %6s\n",buffer); + ret_val = -EINVAL; + } + } else { + ret_val = -EINVAL; + } + return ret_val; +} + +static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp) +{ + int orig_length, drive, wb_mode; + char cmnd[12]; + int i, j, found; + gdth_ha_str *ha; + gdth_cmd_str gdtcmd; + gdth_cpar_str *pcpar; + + TRACE2(("gdth_set_asc_info() ha %d\n",hanum)); + ha = HADATA(gdth_ctr_tab[hanum]); + memset(cmnd, 0,10); + orig_length = length + 5; + drive = -1; + wb_mode = 0; + found = FALSE; + + if (length >= 5 && strncmp(buffer,"flush",5)==0) { + buffer += 6; + length -= 6; + if (length && *buffer>='0' && *buffer<='9') { + drive = (int)(*buffer-'0'); + ++buffer; --length; + if (length && *buffer>='0' && *buffer<='9') { + drive = drive*10 + (int)(*buffer-'0'); + ++buffer; --length; + } + printk("GDT: Flushing host drive %d .. ",drive); + } else { + printk("GDT: Flushing all host drives .. "); + } + for (i = 0; i < MAXBUS; ++i) { + for (j = 0; j < MAXID; ++j) { + if (ha->id[i][j].type == CACHE_DTYP) { + if (drive != -1 && + ha->id[i][j].hostdrive != (ushort)drive) + continue; + found = TRUE; + gdtcmd.BoardNode = LOCALBOARD; + gdtcmd.Service = CACHESERVICE; + gdtcmd.OpCode = GDT_FLUSH; + gdtcmd.u.cache.DeviceNo = ha->id[i][j].hostdrive; + gdtcmd.u.cache.BlockNo = 1; + gdtcmd.u.cache.sg_canz = 0; + { + struct semaphore sem = MUTEX_LOCKED; + scp.request.rq_status = RQ_SCSI_BUSY; + scp.request.sem = &sem; + scsi_do_cmd(&scp, cmnd, &gdtcmd, + sizeof(gdth_cmd_str), gdth_scsi_done, + 30*HZ, 1); + down(&sem); + } + } + } + } + if (!found) + printk("\nNo host drive found !\n"); + else + printk("Done.\n"); + return(orig_length); + } + + if (length >= 7 && strncmp(buffer,"wbp_off",7)==0) { + buffer += 8; + length -= 8; + printk("GDT: Disabling write back permanently .. "); + wb_mode = 1; + } else if (length >= 6 && strncmp(buffer,"wbp_on",6)==0) { + buffer += 7; + length -= 7; + printk("GDT: Enabling write back permanently .. "); + wb_mode = 2; + } else if (length >= 6 && strncmp(buffer,"wb_off",6)==0) { + buffer += 7; + length -= 7; + printk("GDT: Disabling write back commands .. "); + if (ha->cache_feat & GDT_WR_THROUGH) { + gdth_write_through = TRUE; + printk("Done.\n"); + } else { + printk("Not supported !\n"); + } + return(orig_length); + } else if (length >= 5 && strncmp(buffer,"wb_on",5)==0) { + buffer += 6; + length -= 6; + printk("GDT: Enabling write back commands .. "); + gdth_write_through = FALSE; + printk("Done.\n"); + return(orig_length); + } + + if (wb_mode) { + pcpar = (gdth_cpar_str *)kmalloc( sizeof(gdth_cpar_str), + GFP_ATOMIC | GFP_DMA ); + if (pcpar == NULL) { + TRACE2(("gdth_set_info(): Unable to allocate memory.\n")); + printk("Unable to allocate memory.\n"); + return(-EINVAL); + } + memcpy( pcpar, &ha->cpar, sizeof(gdth_cpar_str) ); + gdtcmd.BoardNode = LOCALBOARD; + gdtcmd.Service = CACHESERVICE; + gdtcmd.OpCode = GDT_IOCTL; + gdtcmd.u.ioctl.p_param = (ulong)pcpar; + gdtcmd.u.ioctl.param_size = sizeof(gdth_cpar_str); + gdtcmd.u.ioctl.subfunc = CACHE_CONFIG; + gdtcmd.u.ioctl.channel = INVALID_CHANNEL; + pcpar->write_back = wb_mode==1 ? 0:1; + { + struct semaphore sem = MUTEX_LOCKED; + scp.request.rq_status = RQ_SCSI_BUSY; + scp.request.sem = &sem; + scsi_do_cmd(&scp, cmnd, &gdtcmd, sizeof(gdth_cmd_str), + gdth_scsi_done, 30*HZ, 1); + down(&sem); + } + kfree( pcpar ); + printk("Done.\n"); + return(orig_length); + } + + printk("GDT: Unknown command: %s Length: %d\n",buffer,length); + return(-EINVAL); +} + +static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp) +{ + char cmnd[12]; + int id; + unchar i, j, k, found; + gdth_ha_str *ha; + gdth_iowr_str *piowr; + gdth_iord_str *piord; + gdth_cmd_str *pcmd; + ulong *ppadd; + ulong add_size, flags; + + + TRACE2(("gdth_set_bin_info() ha %d\n",hanum)); + ha = HADATA(gdth_ctr_tab[hanum]); + memset(cmnd, 0,10); + piowr = (gdth_iowr_str *)buffer; + piord = NULL; + pcmd = NULL; + + if (length < GDTOFFSOF(gdth_iowr_str,iu)) + return(-EINVAL); + + switch (piowr->ioctl) { + case GDTIOCTL_GENERAL: + if (length < GDTOFFSOF(gdth_iowr_str,iu.general.data[0])) + return(-EINVAL); + pcmd = (gdth_cmd_str *)piowr->iu.general.command; + pcmd->Service = piowr->service; + if (pcmd->OpCode == GDT_IOCTL) { + ppadd = &pcmd->u.ioctl.p_param; + add_size = pcmd->u.ioctl.param_size; + } else if (piowr->service == CACHESERVICE) { + add_size = pcmd->u.cache.BlockCnt * SECTOR_SIZE; + if (ha->cache_feat & SCATTER_GATHER) { + ppadd = &pcmd->u.cache.sg_lst[0].sg_ptr; + pcmd->u.cache.DestAddr = -1UL; + pcmd->u.cache.sg_lst[0].sg_len = add_size; + pcmd->u.cache.sg_canz = 1; + } else { + ppadd = &pcmd->u.cache.DestAddr; + pcmd->u.cache.sg_canz = 0; + } + } else if (piowr->service == SCSIRAWSERVICE) { + add_size = pcmd->u.raw.sdlen; + if (ha->raw_feat & SCATTER_GATHER) { + ppadd = &pcmd->u.raw.sg_lst[0].sg_ptr; + pcmd->u.raw.sdata = -1UL; + pcmd->u.raw.sg_lst[0].sg_len = add_size; + pcmd->u.raw.sg_ranz = 1; + } else { + ppadd = &pcmd->u.raw.sdata; + pcmd->u.raw.sg_ranz = 0; + } + } else { + return(-EINVAL); + } + id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) + add_size ); + if (id == -1) + return(-EBUSY); + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + + piord->size = sizeof(gdth_iord_str) + add_size; + if (add_size > 0) { + memcpy(piord->iu.general.data, piowr->iu.general.data, add_size); + *ppadd = (ulong)piord->iu.general.data; + } + /* do IOCTL */ + { + struct semaphore sem = MUTEX_LOCKED; + scp.request.rq_status = RQ_SCSI_BUSY; + scp.request.sem = &sem; + scp.SCp.this_residual = IOCTL_PRI; + scsi_do_cmd(&scp, cmnd, pcmd, + sizeof(gdth_cmd_str), gdth_scsi_done, + piowr->timeout*HZ, 1); + down(&sem); + piord->status = (ulong)scp.SCp.Message; + } + break; + + case GDTIOCTL_DRVERS: + id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ); + if (id == -1) + return(-EBUSY); + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + piord->size = sizeof(gdth_iord_str); + piord->status = S_OK; + piord->iu.drvers.version = (GDTH_VERSION<<8) | GDTH_SUBVERSION; + break; + + case GDTIOCTL_CTRTYPE: + id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ); + if (id == -1) + return(-EBUSY); + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + piord->size = sizeof(gdth_iord_str); + piord->status = S_OK; + if (ha->type == GDT_ISA || ha->type == GDT_EISA) { + piord->iu.ctrtype.type = (unchar)((ha->stype>>20) - 10); + } else if (ha->type != GDT_PCIMPR) { + piord->iu.ctrtype.type = (unchar)((ha->stype<<8) + 6); + } else { + piord->iu.ctrtype.type = 0xfe; + piord->iu.ctrtype.ext_type = 0x6000 | ha->stype; + } + piord->iu.ctrtype.info = ha->brd_phys; + piord->iu.ctrtype.oem_id = (ushort)GDT3_ID; + break; + + case GDTIOCTL_CTRCNT: + id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ); + if (id == -1) + return(-EBUSY); + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + piord->size = sizeof(gdth_iord_str); + piord->status = S_OK; + piord->iu.ctrcnt.count = (ushort)gdth_ctr_count; + break; + + case GDTIOCTL_OSVERS: + id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ); + if (id == -1) + return(-EBUSY); + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + piord->size = sizeof(gdth_iord_str); + piord->status = S_OK; + piord->iu.osvers.version = (unchar)(LINUX_VERSION_CODE >> 16); + piord->iu.osvers.subversion = (unchar)(LINUX_VERSION_CODE >> 8); + piord->iu.osvers.revision = (ushort)(LINUX_VERSION_CODE & 0xff); + break; + + case GDTIOCTL_LOCKDRV: + id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ); + if (id == -1) + return(-EBUSY); + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + for (i = k = 0; i < piowr->iu.lockdrv.drive_cnt; ++i) { + found = FALSE; + for (j = 0; j < ha->bus_cnt; ++j) { + for (k = 0; k < MAXID; ++k) { + if (ha->id[j][k].type == CACHE_DTYP && + ha->id[j][k].hostdrive == piowr->iu.lockdrv.drives[i]) { + found = TRUE; + break; + } + } + if (found) + break; + } + if (!found) + continue; + + if (piowr->iu.lockdrv.lock) { + save_flags( flags ); + cli(); + ha->id[j][k].lock = 1; + restore_flags( flags ); + gdth_wait_completion( hanum, j, k ); + gdth_stop_timeout( hanum, j, k ); + } else { + save_flags( flags ); + cli(); + ha->id[j][k].lock = 0; + restore_flags( flags ); + gdth_start_timeout( hanum, j, k ); + gdth_next( hanum ); + } + } + piord->size = sizeof(gdth_iord_str); + piord->status = S_OK; + break; + + case GDTIOCTL_LOCKCHN: + id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ); + if (id == -1) + return(-EBUSY); + for (k = 0, j = piowr->iu.lockchn.channel; k < MAXID; ++k) { + if (ha->id[j][k].type != RAW_DTYP) + continue; + + if (piowr->iu.lockchn.lock) { + save_flags( flags ); + cli(); + ha->id[j][k].lock = 1; + restore_flags( flags ); + gdth_wait_completion( hanum, j, k ); + gdth_stop_timeout( hanum, j, k ); + } else { + save_flags( flags ); + cli(); + ha->id[j][k].lock = 0; + restore_flags( flags ); + gdth_start_timeout( hanum, j, k ); + gdth_next( hanum ); + } + } + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + piord->size = sizeof(gdth_iord_str); + piord->status = S_OK; + break; + + case GDTIOCTL_EVENT: + id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ); + if (id == -1) + return(-EBUSY); + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + if (piowr->iu.event.erase == 0) { + piord->iu.event.handle = gdth_read_event( piowr->iu.event.handle, + (gdth_evt_str *)piord->iu.event.evt ); + } else { + piord->iu.event.handle = piowr->iu.event.handle; + gdth_readapp_event( (unchar)piowr->iu.event.erase, + (gdth_evt_str *)piord->iu.event.evt ); + } + piord->size = sizeof(gdth_iord_str); + piord->status = S_OK; + break; + + default: + return(-EINVAL); + } + /* we return a buffer ID to detect the right buffer during READ-IOCTL */ + return id; +} + +static int gdth_get_info(char *buffer,char **start,off_t offset, + int length,int vh,int hanum,int busnum) +{ + int size = 0,len = 0; + off_t begin = 0,pos = 0; + gdth_ha_str *ha; + gdth_iord_str *piord; + int id; + + TRACE2(("gdth_get_info() ha %d bus %d\n",hanum,busnum)); + ha = HADATA(gdth_ctr_tab[hanum]); + id = length; + + /* look for buffer ID in length */ + if (id > 4) { +#if LINUX_VERSION_CODE >= 0x010400 + size = sprintf(buffer+len, + "%s SCSI Disk Array Controller\n", + gdth_ctr_name(hanum)); +#else + size = sprintf(buffer+len, + "%s SCSI Disk Array Controller (SCSI Bus %d)\n", + gdth_ctr_name(hanum),busnum); +#endif + len += size; pos = begin + len; + size = sprintf(buffer+len, + "Firmware Version: %d.%2d\tDriver Version: %s\n", + (unchar)(ha->cpar.version>>8), + (unchar)(ha->cpar.version),GDTH_VERSION_STR); + len += size; pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + if (pos > offset + length) + goto stop_output; + + } else { + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + if (piord == NULL) + goto stop_output; + length = piord->size; + memcpy(buffer+len, (char *)piord, length); + gdth_ioctl_free(hanum, id); + len += length; pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + if (pos > offset + length) + goto stop_output; + } + +stop_output: + *start = buffer +(offset-begin); + len -= (offset-begin); + if (len > length) + len = length; + TRACE2(("get_info() len %d pos %d begin %d offset %d length %d size %d\n", + len,pos,begin,offset,length,size)); + return(len); +} + + +void gdth_scsi_done(Scsi_Cmnd *scp) +{ + TRACE2(("gdth_scsi_done()\n")); + + scp->request.rq_status = RQ_SCSI_DONE; + + if (scp->request.sem != NULL) + up(scp->request.sem); +} + +static int gdth_ioctl_alloc(int hanum, ushort size) +{ + ulong flags; + int i; + + if (size == 0) + return -1; + + save_flags(flags); + cli(); + + for (i = 0; i < 4; ++i) { + if (gdth_ioctl_tab[i][hanum] == NULL) { + gdth_ioctl_tab[i][hanum] = kmalloc( size, GFP_ATOMIC | GFP_DMA ); + break; + } + } + + restore_flags(flags); + if (i == 4 || gdth_ioctl_tab[i][hanum] == NULL) + return -1; + return (i+1); +} + +static void gdth_ioctl_free(int hanum, int idx) +{ + ulong flags; + + save_flags(flags); + cli(); + + kfree( gdth_ioctl_tab[idx-1][hanum] ); + gdth_ioctl_tab[idx-1][hanum] = NULL; + + restore_flags(flags); +} + +static void gdth_wait_completion(int hanum, int busnum, int id) +{ + ulong flags; + int i; + Scsi_Cmnd *scp; + + save_flags(flags); + cli(); + + for (i = 0; i < GDTH_MAXCMDS; ++i) { + scp = gdth_cmd_tab[i][hanum].cmnd; + if (!SPECIAL_SCP(scp) && scp->target == (unchar)id && +#if LINUX_VERSION_CODE >= 0x010400 + scp->channel == (unchar)busnum) +#else + NUMDATA(scp->host)->busnum == (unchar)busnum) +#endif + { + restore_flags(flags); + while (!scp->SCp.have_data_in) + barrier(); + save_flags(flags); + cli(); + } + } + restore_flags(flags); +} + +static void gdth_stop_timeout(int hanum, int busnum, int id) +{ + ulong flags; + Scsi_Cmnd *scp; + gdth_ha_str *ha; + + save_flags(flags); + cli(); + ha = HADATA(gdth_ctr_tab[hanum]); + + for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) { + if (scp->target == (unchar)id && +#if LINUX_VERSION_CODE >= 0x010400 + scp->channel == (unchar)busnum) +#else + NUMDATA(scp->host)->busnum == (unchar)busnum) +#endif + { + TRACE2(("gdth_stop_timeout(): update_timeout()\n")); + scp->SCp.buffers_residual = gdth_update_timeout(scp, 0); + } + } + restore_flags(flags); +} + +static void gdth_start_timeout(int hanum, int busnum, int id) +{ + ulong flags; + Scsi_Cmnd *scp; + gdth_ha_str *ha; + + save_flags(flags); + cli(); + ha = HADATA(gdth_ctr_tab[hanum]); + + for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) { + if (scp->target == (unchar)id && +#if LINUX_VERSION_CODE >= 0x010400 + scp->channel == (unchar)busnum) +#else + NUMDATA(scp->host)->busnum == (unchar)busnum) +#endif + { + TRACE2(("gdth_start_timeout(): update_timeout()\n")); + gdth_update_timeout(scp, scp->SCp.buffers_residual); + } + } + restore_flags(flags); +} + +static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout) +{ + ulong flags; + int oldto; + + save_flags(flags); + cli(); + + oldto = scp->timeout; + scp->timeout = timeout; + if (timeout > 0) { + if (timer_table[SCSI_TIMER].expires == 0) { + timer_table[SCSI_TIMER].expires = jiffies + timeout; + timer_active |= 1 << SCSI_TIMER; + } else { + if (jiffies + timeout < timer_table[SCSI_TIMER].expires) + timer_table[SCSI_TIMER].expires = jiffies + timeout; + } + } + + restore_flags(flags); + return oldto; +} + diff --git a/drivers/scsi/gdth_proc.h b/drivers/scsi/gdth_proc.h new file mode 100644 index 000000000000..a3d5dcd71ca5 --- /dev/null +++ b/drivers/scsi/gdth_proc.h @@ -0,0 +1,24 @@ +#ifndef _GDTH_PROC_H +#define _GDTH_PROC_H + +/* gdth_proc.h + * $Id: gdth_proc.h,v 1.2 1997/02/21 08:08:51 achim Exp $ + */ + +static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum); +static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp); +static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp); +static int gdth_get_info(char *buffer,char **start,off_t offset, + int length,int vh,int hanum,int busnum); + +static int gdth_ioctl_alloc(int hanum, ushort size); +static void gdth_ioctl_free(int hanum, int id); +static void gdth_wait_completion(int hanum, int busnum, int id); +static void gdth_stop_timeout(int hanum, int busnum, int id); +static void gdth_start_timeout(int hanum, int busnum, int id); +static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout); + +void gdth_scsi_done(Scsi_Cmnd *scp); + +#endif + diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index de8305b23457..287344ea9eeb 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -169,6 +169,10 @@ #include "ide-scsi.h" #endif +#ifdef CONFIG_SCSI_GDTH +#include "gdth.h" +#endif + #ifdef CONFIG_SCSI_DEBUG #include "scsi_debug.h" #endif @@ -307,6 +311,9 @@ static Scsi_Host_Template builtin_scsi_hosts[] = #ifdef CONFIG_SCSI_SUNESP SCSI_SPARC_ESP, #endif +#ifdef CONFIG_SCSI_GDTH + GDTH, +#endif #ifdef CONFIG_BLK_DEV_IDESCSI IDESCSI, #endif diff --git a/include/linux/b1lli.h b/include/linux/b1lli.h new file mode 100644 index 000000000000..401933c3f031 --- /dev/null +++ b/include/linux/b1lli.h @@ -0,0 +1,141 @@ +/* + * $Id: b1lli.h,v 1.1 1997/03/04 21:27:32 calle Exp $ + * + * ISDN lowlevel-module for AVM B1-card. + * + * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: b1lli.h,v $ + * Revision 1.1 1997/03/04 21:27:32 calle + * First 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 _B1LLI_H_ +#define _B1LLI_H_ +/* + * struct for loading t4 file + */ +typedef struct avmb1_t4file { + int len; + unsigned char *data; +} avmb1_t4file; + +typedef struct avmb1_loaddef { + int contr; + avmb1_t4file t4file; +} avmb1_loaddef; + +typedef struct avmb1_resetdef { + int contr; +} avmb1_resetdef; + +/* + * struct for adding new cards + */ +typedef struct avmb1_carddef { + int port; + int irq; +} avmb1_carddef; + +#define AVMB1_LOAD 0 /* load image to card */ +#define AVMB1_ADDCARD 1 /* add a new card */ +#define AVMB1_RESETCARD 2 /* reset a card */ + + + +#ifdef __KERNEL__ + +/* + * card states for startup + */ + +#define CARD_NONE 0 +#define CARD_DETECTED 1 +#define CARD_LOADING 2 +#define CARD_INITSTATE 4 +#define CARD_RUNNING 5 +#define CARD_ACTIVE 6 + +#define AVMB1_PORTLEN 0x1f + +#define AVM_MAXVERSION 8 +#define AVM_NBCHAN 2 + +#define AVM_NAPPS 30 +#define AVM_NPLCI 5 +#define AVM_NNCCI 6 + +/* + * Main driver data + */ + +typedef struct avmb1_card { + struct avmb1_card *next; + int cnr; + unsigned short port; + unsigned irq; + volatile unsigned short cardstate; + int interrupt; + int blocked; + int versionlen; + char versionbuf[1024]; + char *version[AVM_MAXVERSION]; + char msgbuf[128]; /* capimsg msg part */ + char databuf[2048]; /* capimsg data part */ + capi_version cversion; + char name[10]; +} avmb1_card; + +/* + * Versions + */ + +#define VER_DRIVER 0 +#define VER_CARDTYPE 1 +#define VER_HWID 2 +#define VER_SERIAL 3 +#define VER_OPTION 4 +#define VER_PROTO 5 +#define VER_PROFILE 6 +#define VER_CAPI 7 + + +/* b1lli.c */ +int B1_detect(unsigned short base); +void B1_reset(unsigned short base); +int B1_load_t4file(unsigned short base, avmb1_t4file * t4file); +int B1_loaded(unsigned short base); +unsigned char B1_assign_irq(unsigned short base, unsigned irq); +unsigned char B1_enable_irq(unsigned short base); +unsigned char B1_disable_irq(unsigned short base); +int B1_valid_irq(unsigned irq); +void B1_handle_interrupt(avmb1_card * card); +void B1_send_init(unsigned short port, + unsigned int napps, unsigned int nncci, unsigned int cardnr); +void B1_send_register(unsigned short port, + __u16 appid, __u32 nmsg, + __u32 nb3conn, __u32 nb3blocks, __u32 b3bsize); +void B1_send_release(unsigned short port, __u16 appid); +void B1_send_message(unsigned short port, struct sk_buff *skb); + +/* b1capi.c */ +void avmb1_handle_new_ncci(avmb1_card * card, + __u16 appl, __u32 ncci, __u32 winsize); +void avmb1_handle_free_ncci(avmb1_card * card, + __u16 appl, __u32 ncci); +void avmb1_handle_capimsg(avmb1_card * card, __u16 appl, struct sk_buff *skb); +void avmb1_card_ready(avmb1_card * card); + +int avmb1_addcard(int port, int irq); +int avmb1_probecard(int port, int irq); + +#endif /* __KERNEL__ */ + +#endif /* _B1LLI_H_ */ diff --git a/include/linux/capi.h b/include/linux/capi.h new file mode 100644 index 000000000000..9876da085561 --- /dev/null +++ b/include/linux/capi.h @@ -0,0 +1,127 @@ +/* + * $Id: capi.h,v 1.1 1997/03/04 21:27:33 calle Exp $ + * + * CAPI 2.0 Interface for Linux + * + * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: capi.h,v $ + * Revision 1.1 1997/03/04 21:27:33 calle + * First 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 __LINUX_CAPI_H__ +#define __LINUX_CAPI_H__ + +#include +#include +#ifndef __KERNEL__ +#include +#endif + +/* + * CAPI_REGISTER + */ + +typedef struct capi_register_params { /* CAPI_REGISTER */ + __u32 level3cnt; /* No. of simulatneous user data connections */ + __u32 datablkcnt; /* No. of buffered data messages */ + __u32 datablklen; /* Size of buffered data messages */ +} capi_register_params; + +#define CAPI_REGISTER _IOW('C',0x01,struct capi_register_params) + +/* + * CAPI_GET_MANUFACTURER + */ + +#define CAPI_MANUFACTURER_LEN 64 + +#define CAPI_GET_MANUFACTURER _IOWR('C',0x06,CAPI_MANUFACTURER_LEN) + +/* + * CAPI_GET_VERSION + */ + +typedef struct capi_version { + __u32 majorversion; + __u32 minorversion; + __u32 majormanuversion; + __u32 minormanuversion; +} capi_version; + +#define CAPI_GET_VERSION _IOWR('C',0x07,struct capi_version) + +/* + * CAPI_GET_SERIAL + */ + +#define CAPI_SERIAL_LEN 8 +#define CAPI_GET_SERIAL _IOWR('C',0x08, CAPI_SERIAL_LEN) + +/* + * CAPI_GET_PROFILE + */ + +typedef struct capi_profile { + __u16 ncontroller; /* number of installed controller */ + __u16 nbchannel; /* number of B-Channels */ + __u32 goptions; /* global options */ + __u32 support1; /* B1 protocols support */ + __u32 support2; /* B2 protocols support */ + __u32 support3; /* B3 protocols support */ + __u32 reserved[6]; /* reserved */ + __u32 manu[5]; /* manufacturer specific information */ +} capi_profile; + +#define CAPI_GET_PROFILE _IOWR('C',0x09,struct capi_profile) + +typedef struct capi_manufacturer_cmd { + unsigned long cmd; + void *data; +} capi_manufacturer_cmd; + +/* + * CAPI_MANUFACTURER_CMD + */ + +#define CAPI_MANUFACTURER_CMD _IOWR('C',0x20, struct capi_manufacturer_cmd) + +/* + * CAPI_GET_ERRCODE + * capi errcode is set, * if read, write, or ioctl returns EIO, + * ioctl returns errcode directly, and in arg, if != 0 + */ + +#define CAPI_GET_ERRCODE _IOR('C',0x21, __u16) + +/* + * CAPI_INSTALLED + */ +#define CAPI_INSTALLED _IOR('C',0x22, __u16) + +/* + * member contr is input for + * CAPI_GET_MANUFACTURER, CAPI_VERSION, CAPI_GET_SERIAL + * and CAPI_GET_PROFILE + */ +typedef union capi_ioctl_struct { + __u32 contr; + capi_register_params rparams; + __u8 manufacturer[CAPI_MANUFACTURER_LEN]; + capi_version version; + __u8 serial[CAPI_SERIAL_LEN]; + capi_profile profile; + capi_manufacturer_cmd cmd; + __u16 errcode; +} capi_ioctl_struct; + +#endif /* __LINUX_CAPI_H__ */ diff --git a/include/linux/isdn.h b/include/linux/isdn.h index cf6a8bf6ae2e..5e8f35b6ada0 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -1,4 +1,4 @@ -/* $Id: isdn.h,v 1.18 1996/11/06 17:37:50 keil Exp $ +/* $Id: isdn.h,v 1.30 1997/06/17 13:07:23 hipp Exp $ * * Main header for the Linux ISDN subsystem (linklevel). * @@ -21,6 +21,49 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn.h,v $ + * Revision 1.30 1997/06/17 13:07:23 hipp + * compression changes , MP changes + * + * Revision 1.29 1997/05/27 15:18:02 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.28 1997/03/07 01:33:01 fritz + * Added proper ifdef's for CONFIG_ISDN_AUDIO + * + * Revision 1.27 1997/03/05 21:11:49 fritz + * Minor fixes. + * + * Revision 1.26 1997/02/28 02:37:53 fritz + * Added some comments. + * + * Revision 1.25 1997/02/23 16:54:23 hipp + * some initial changes for future PPP compresion + * + * Revision 1.24 1997/02/18 09:42:45 fritz + * Bugfix: Increased ISDN_MODEM_ANZREG. + * Increased TTY_DV. + * + * Revision 1.23 1997/02/10 22:07:13 fritz + * Added 2 modem registers for numbering plan and screening info. + * + * Revision 1.22 1997/02/03 23:42:08 fritz + * Added ISDN_TIMER_RINGING + * Misc. changes for Kernel 2.1.X compatibility + * + * Revision 1.21 1997/01/17 01:19:10 fritz + * Applied chargeint patch. + * + * Revision 1.20 1997/01/17 00:41:19 fritz + * Increased TTY_DV. + * + * Revision 1.19 1997/01/14 01:41:07 fritz + * Added ATI2 related variables. + * Added variables for audio support in skbuffs. + * * Revision 1.18 1996/11/06 17:37:50 keil * more changes for 2.1.X * @@ -159,7 +202,7 @@ #define ISDN_USAGE_EXCLUSIVE 64 /* This bit is set, if channel is exclusive */ #define ISDN_USAGE_OUTGOING 128 /* This bit is set, if channel is outgoing */ -#define ISDN_MODEM_ANZREG 21 /* Number of Modem-Registers */ +#define ISDN_MODEM_ANZREG 23 /* Number of Modem-Registers */ #define ISDN_MSNLEN 20 typedef struct { @@ -175,12 +218,12 @@ typedef struct { typedef struct { char name[10]; - char phone[20]; + char phone[ISDN_MSNLEN]; int outgoing; } isdn_net_ioctl_phone; -#define NET_DV 0x01 /* Data version for net_cfg */ -#define TTY_DV 0x01 /* Data version for iprofd etc. */ +#define NET_DV 0x03 /* Data version for net_cfg */ +#define TTY_DV 0x04 /* Data version for iprofd etc. */ typedef struct { char name[10]; /* Name of interface */ @@ -196,6 +239,7 @@ typedef struct { int exclusive; /* Channel, if bound exclusive */ int dialmax; /* Dial Retry-Counter */ int slavedelay; /* Delay until slave starts up */ + int triggercps; /* BogoCPS needed for triggering slave */ int cbdelay; /* Delay before Callback */ int chargehup; /* Flag: Charge-Hangup */ int ihup; /* Flag: Hangup-Timeout on incoming line */ @@ -203,6 +247,7 @@ typedef struct { int callback; /* Flag: Callback */ int cbhup; /* Flag: Reject Call before Callback */ int pppbind; /* ippp device for bindings */ + int chargeint; /* Use fixed charge interval length */ } isdn_net_ioctl_cfg; #ifdef __KERNEL__ @@ -279,6 +324,7 @@ typedef struct { #define ISDN_TIMER_RES 3 /* Main Timer-Resolution */ #define ISDN_TIMER_02SEC (HZ/(ISDN_TIMER_RES+1)/5) /* Slow-Timer1 .2 sec */ #define ISDN_TIMER_1SEC (HZ/(ISDN_TIMER_RES+1)) /* Slow-Timer2 1 sec */ +#define ISDN_TIMER_RINGING 5 /* tty RINGs = ISDN_TIMER_1SEC * this factor */ #define ISDN_TIMER_MODEMREAD 1 #define ISDN_TIMER_MODEMPLUS 2 #define ISDN_TIMER_MODEMRING 4 @@ -317,7 +363,7 @@ typedef struct { /* Phone-list-element */ typedef struct { void *next; - char num[20]; + char num[ISDN_MSNLEN]; } isdn_net_phone; /* Local interface-data */ @@ -361,6 +407,7 @@ typedef struct isdn_net_local_s { /* bit0: chargeint is invalid */ /* bit1: Getting charge-interval */ /* bit2: Do charge-unit-hangup */ + /* bit3: Do hangup even on incoming */ int outgoing; /* Flag: outgoing call */ int onhtime; /* Time to keep link up */ int chargeint; /* Interval between charge-infos */ @@ -370,6 +417,7 @@ typedef struct isdn_net_local_s { int sqfull; /* Flag: netdev-queue overloaded */ ulong sqfull_stamp; /* Start-Time of overload */ ulong slavedelay; /* Dynamic bundling delaytime */ + int triggercps; /* BogoCPS needed for trigger slave */ struct device *srobin; /* Ptr to Master device for slaves */ isdn_net_phone *phone[2]; /* List of remote-phonenumbers */ /* phone[0] = Incoming Numbers */ @@ -382,11 +430,28 @@ typedef struct isdn_net_local_s { struct isdn_net_dev_s *netdev; /* Ptr to netdev */ struct sk_buff *first_skb; /* Ptr to skb that triggers dialing */ struct sk_buff *sav_skb; /* Ptr to skb, rejected by LL-driver*/ +#if (LINUX_VERSION_CODE < 0x02010F) /* Ptr to orig. header_cache_bind */ - void (*org_hcb)(struct hh_cache **, struct device *, - unsigned short, __u32); + void (*org_hcb)(struct hh_cache **, + struct device *, + unsigned short, + __u32); +#else +#if (LINUX_VERSION_CODE < 0x2011E) + /* Ptr to orig. hard_header_cache */ + int (*org_hhc)(struct dst_entry *dst, + struct dst_entry *neigh, + struct hh_cache *hh); +#else + /* Ptr to orig. hard_header_cache */ + int (*org_hhc)(struct dst_entry *dst, + struct neighbour *neigh, + struct hh_cache *hh); +#endif +#endif /* Ptr to orig. header_cache_update */ - void (*org_hcu)(struct hh_cache *, struct device *, + void (*org_hcu)(struct hh_cache *, + struct device *, unsigned char *); int pppbind; /* ippp device for bindings */ } isdn_net_local; @@ -435,17 +500,35 @@ typedef struct isdn_net_dev_s { #define ISDN_SERIAL_TYPE_NORMAL 1 #define ISDN_SERIAL_TYPE_CALLOUT 2 +#ifdef CONFIG_ISDN_AUDIO +/* For using sk_buffs with audio we need some private variables + * within each sk_buff. For this purpose, we declare a struct here, + * and put it always at skb->head. A few macros help accessing the + * variables. Of course, we need to check skb_headroom prior to + * any access. + */ +typedef struct isdn_audio_skb { + unsigned short dle_count; + unsigned char lock; +} isdn_audio_skb; + +#define ISDN_AUDIO_SKB_DLECOUNT(skb) (((isdn_audio_skb*)skb->head)->dle_count) +#define ISDN_AUDIO_SKB_LOCK(skb) (((isdn_audio_skb*)skb->head)->lock) +#endif + /* Private data of AT-command-interpreter */ typedef struct atemu { u_char profile[ISDN_MODEM_ANZREG]; /* Modem-Regs. Profile 0 */ u_char mdmreg[ISDN_MODEM_ANZREG]; /* Modem-Registers */ char pmsn[ISDN_MSNLEN]; /* EAZ/MSNs Profile 0 */ char msn[ISDN_MSNLEN];/* EAZ/MSN */ +#ifdef CONFIG_ISDN_AUDIO u_char vpar[10]; /* Voice-parameters */ + int lastDLE; /* Flag for voice-coding: DLE seen */ +#endif int mdmcmdl; /* Length of Modem-Commandbuffer */ int pluscount; /* Counter for +++ sequence */ int lastplus; /* Timestamp of last + */ - int lastDLE; /* Flag for voice-coding: DLE seen */ char mdmcmd[255]; /* Modem-Commandbuffer */ } atemu; @@ -462,26 +545,39 @@ typedef struct modem_info { int blocked_open; /* # of blocked opens */ long session; /* Session of opening process */ long pgrp; /* pgrp of opening process */ - int online; /* B-Channel is up */ - int vonline; /* Voice-channel status */ + int online; /* 1 = B-Channel is up, drop data */ + /* 2 = B-Channel is up, deliver d.*/ int dialing; /* Dial in progress */ int rcvsched; /* Receive needs schedule */ int isdn_driver; /* Index to isdn-driver */ int isdn_channel; /* Index to isdn-channel */ int drv_index; /* Index to dev->usage */ int ncarrier; /* Flag: schedule NO CARRIER */ + unsigned char last_cause[8]; /* Last cause message */ + unsigned char last_num[ISDN_MSNLEN]; + /* Last phone-number */ + unsigned char last_l2; /* Last layer-2 protocol */ + unsigned char last_si; /* Last service */ + unsigned char last_lhup; /* Last hangup local? */ + unsigned char last_dir; /* Last direction (in or out) */ struct timer_list nc_timer; /* Timer for delayed NO CARRIER */ int send_outstanding;/* # of outstanding send-requests */ int xmit_size; /* max. # of chars in xmit_buf */ int xmit_count; /* # of chars in xmit_buf */ unsigned char *xmit_buf; /* transmit buffer */ struct sk_buff_head xmit_queue; /* transmit queue */ +#ifdef CONFIG_ISDN_AUDIO + int vonline; /* Voice-channel status */ + /* Bit 0 = recording */ + /* Bit 1 = playback */ + /* Bit 2 = playback, DLE-ETX seen */ struct sk_buff_head dtmf_queue; /* queue for dtmf results */ - struct tty_struct *tty; /* Pointer to corresponding tty */ - atemu emu; /* AT-emulator data */ void *adpcms; /* state for adpcm decompression */ void *adpcmr; /* state for adpcm compression */ void *dtmf_state; /* state for dtmf decoder */ +#endif + struct tty_struct *tty; /* Pointer to corresponding tty */ + atemu emu; /* AT-emulator data */ struct termios normal_termios; /* For saving termios structs */ struct termios callout_termios; struct wait_queue *open_wait; @@ -513,8 +609,8 @@ typedef struct { struct sqqueue { struct sqqueue *next; - int sqno_start; - int sqno_end; + long sqno_start; + long sqno_end; struct sk_buff *skb; long timer; }; @@ -522,7 +618,7 @@ struct sqqueue { struct mpqueue { struct mpqueue *next; struct mpqueue *last; - int sqno; + long sqno; struct sk_buff *skb; int BEbyte; unsigned long time; @@ -561,6 +657,8 @@ struct ippp_struct { struct slcompress *slcomp; #endif unsigned long debug; + struct isdn_ppp_compressor *compressor,*link_compressor; + void *decomp_stat,*comp_stat,*link_decomp_stat,*link_comp_stat; }; #endif @@ -588,7 +686,9 @@ typedef struct { isdn_if *interface; /* Interface to driver */ int *rcverr; /* Error-counters for B-Ch.-receive */ int *rcvcount; /* Byte-counters for B-Ch.-receive */ +#ifdef CONFIG_ISDN_AUDIO unsigned long DLEflag; /* Flags: Insert DLE at next read */ +#endif struct sk_buff_head *rpqueue; /* Pointers to start of Rcv-Queue */ struct wait_queue **rcv_waitq; /* Wait-Queues for B-Channel-Reads */ struct wait_queue **snd_waitq; /* Wait-Queue for B-Channel-Send's */ @@ -612,7 +712,8 @@ typedef struct isdn_devt { int chanmap[ISDN_MAX_CHANNELS];/* Map minor->device-channel */ int drvmap[ISDN_MAX_CHANNELS]; /* Map minor->driver-index */ int usage[ISDN_MAX_CHANNELS]; /* Used by tty/ip/voice */ - char num[ISDN_MAX_CHANNELS][20];/* Remote number of active ch.*/ + char num[ISDN_MAX_CHANNELS][ISDN_MSNLEN]; + /* Remote number of active ch.*/ int m_idx[ISDN_MAX_CHANNELS]; /* Index for mdm.... */ driver *drv[ISDN_MAX_DRIVERS]; /* Array of drivers */ isdn_net_dev *netdev; /* Linked list of net-if's */ diff --git a/include/linux/isdn_ppp.h b/include/linux/isdn_ppp.h index 5ce86f868e27..177646520470 100644 --- a/include/linux/isdn_ppp.h +++ b/include/linux/isdn_ppp.h @@ -4,25 +4,29 @@ extern int isdn_ppp_dial_slave(char *); extern int isdn_ppp_hangup_slave(char *); -struct pppinfo +#define CALLTYPE_INCOMING 0x1 +#define CALLTYPE_OUTGOING 0x2 +#define CALLTYPE_CALLBACK 0x4 + +struct pppcallinfo { - int type; /* set by user */ - union { - char clid[32]; /* calling ID */ - int bundles; - int linknumber; - } info; + int calltype; + unsigned char local_num[64]; + unsigned char remote_num[64]; + int charge_units; }; - -#define PPPIOCLINKINFO _IOWR('t',128,struct pppinfo) +#define PPPIOCGCALLINFO _IOWR('t',128,struct pppcallinfo) #define PPPIOCBUNDLE _IOW('t',129,int) #define PPPIOCGMPFLAGS _IOR('t',130,int) #define PPPIOCSMPFLAGS _IOW('t',131,int) #define PPPIOCSMPMTU _IOW('t',132,int) #define PPPIOCSMPMRU _IOW('t',133,int) +#define PPPIOCGCOMPRESSORS _IOR('t',134,unsigned long) +#define PPPIOCSCOMPRESSOR _IOW('t',135,int) -#define PPP_MP 0x003d +#define PPP_MP 0x003d +#define PPP_LINK_COMP 0x00fb #define SC_MP_PROT 0x00000200 #define SC_REJ_MP_PROT 0x00000400 @@ -32,4 +36,36 @@ struct pppinfo #define MP_END_FRAG 0x40 #define MP_BEGIN_FRAG 0x80 -#endif +#ifdef __KERNEL__ +/* + * this is an 'old friend' from ppp-comp.h under a new name + * check the original include for more information + */ +struct isdn_ppp_compressor { + struct isdn_ppp_compressor *next,*prev; + int num; /* CCP compression protocol number */ + void *(*comp_alloc) (unsigned char *options, int opt_len); + void (*comp_free) (void *state); + int (*comp_init) (void *state, unsigned char *options, int opt_len, + int unit, int opthdr, int debug); + void (*comp_reset) (void *state); + int (*compress) (void *state,struct sk_buff *in, struct sk_buff *skb_out, + int proto); + void (*comp_stat) (void *state, struct compstat *stats); + void *(*decomp_alloc) (unsigned char *options, int opt_len); + void (*decomp_free) (void *state); + int (*decomp_init) (void *state, unsigned char *options, + int opt_len, int unit, int opthdr, int mru, int debug); + void (*decomp_reset) (void *state); + int (*decompress) (void *state, unsigned char *ibuf, int isize, unsigned char *obuf, int osize); + void (*incomp) (void *state, unsigned char *ibuf, int icnt); + void (*decomp_stat) (void *state, struct compstat *stats); +}; + +extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *); +extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_ISDN_PPP_H */ + diff --git a/include/linux/isdnif.h b/include/linux/isdnif.h index 76837784710e..5b1f18a88213 100644 --- a/include/linux/isdnif.h +++ b/include/linux/isdnif.h @@ -1,4 +1,4 @@ -/* $Id: isdnif.h,v 1.13 1996/11/13 02:39:59 fritz Exp $ +/* $Id: isdnif.h,v 1.20 1997/05/27 15:18:06 fritz Exp $ * * Linux ISDN subsystem * @@ -22,6 +22,32 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdnif.h,v $ + * Revision 1.20 1997/05/27 15:18:06 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.19 1997/03/25 23:13:56 keil + * NI-1 US protocol + * + * Revision 1.18 1997/03/04 22:09:18 calle + * Change macros copy_from_user and copy_to_user in inline function. + * These are now correct replacements of the functions for 2.1.xx + * + * Revision 1.17 1997/02/10 21:12:53 fritz + * More setup-interface changes. + * + * Revision 1.16 1997/02/10 19:42:57 fritz + * New interface for reporting incoming calls. + * + * Revision 1.15 1997/02/09 00:18:42 keil + * leased line support + * + * Revision 1.14 1997/02/03 23:43:00 fritz + * Misc changes for Kernel 2.1.X compatibility. + * * Revision 1.13 1996/11/13 02:39:59 fritz * More compatibility changes. * @@ -76,6 +102,8 @@ #define ISDN_PTYPE_UNKNOWN 0 /* Protocol undefined */ #define ISDN_PTYPE_1TR6 1 /* german 1TR6-protocol */ #define ISDN_PTYPE_EURO 2 /* EDSS1-protocol */ +#define ISDN_PTYPE_LEASED 3 /* for leased lines */ +#define ISDN_PTYPE_NI1 4 /* US NI-1 protocol */ /* * Values for Layer-2-protocol-selection @@ -156,6 +184,16 @@ #define ISDN_FEATURE_P_UNKNOWN (0x1000 << ISDN_PTYPE_UNKNOWN) #define ISDN_FEATURE_P_1TR6 (0x1000 << ISDN_PTYPE_1TR6) #define ISDN_FEATURE_P_EURO (0x1000 << ISDN_PTYPE_EURO) +#define ISDN_FEATURE_P_NI1 (0x1000 << ISDN_PTYPE_NI1) + +typedef struct setup_parm { + char phone[32]; /* Remote Phone-Number */ + char eazmsn[32]; /* Local EAZ or MSN */ + unsigned char si1; /* Service Indicator 1 */ + unsigned char si2; /* Service Indicator 2 */ + unsigned char plan; /* Numbering plan */ + unsigned char screen; /* Screening info */ +} setup_parm; /* * Structure for exchanging above infos @@ -165,7 +203,10 @@ typedef struct { int driver; /* Lowlevel-Driver-ID */ int command; /* Command or Status (see above) */ ulong arg; /* Additional Data */ - char num[50]; /* Additional Data */ + union { + char num[50]; /* Additional Data */ + setup_parm setup; + } parm; } isdn_ctrl; /* @@ -321,8 +362,25 @@ extern int register_isdn(isdn_if*); #endif #if (LINUX_VERSION_CODE < 0x020100) #include -#define copy_from_user memcpy_fromfs -#define copy_to_user memcpy_tofs + +static inline unsigned long copy_from_user(void *to, const void *from, unsigned long n) +{ + int i; + if ((i = verify_area(VERIFY_READ, from, n)) != 0) + return i; + memcpy_fromfs(to, from, n); + return 0; +} + +static inline unsigned long copy_to_user(void *to, const void *from, unsigned long n) +{ + int i; + if ((i = verify_area(VERIFY_WRITE, to, n)) != 0) + return i; + memcpy_tofs(to, from, n); + return 0; +} + #define GET_USER(x, addr) ( x = get_user(addr) ) #define RWTYPE int #define LSTYPE int @@ -338,6 +396,24 @@ extern int register_isdn(isdn_if*); #define LSARG long long #endif +#if (LINUX_VERSION_CODE < 0x02010F) +#define SET_SKB_FREE(x) ( x->free = 1 ) +#else +#define SET_SKB_FREE(x) +#endif + +#if (LINUX_VERSION_CODE < 0x02011F) +#define CLOSETYPE void +#define CLOSEVAL +#else +#define CLOSETYPE int +#define CLOSEVAL (0) +#endif + +#if (LINUX_VERSION_CODE < 0x020125) +#define test_and_clear_bit clear_bit +#define test_and_set_bit set_bit +#endif + #endif /* __KERNEL__ */ #endif /* isdnif_h */ - diff --git a/include/linux/kernelcapi.h b/include/linux/kernelcapi.h new file mode 100644 index 000000000000..da0dea8c0555 --- /dev/null +++ b/include/linux/kernelcapi.h @@ -0,0 +1,96 @@ +/* + * $Id: kernelcapi.h,v 1.1 1997/03/04 21:27:33 calle Exp $ + * + * Kernel CAPI 2.0 Interface for Linux + * + * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: kernelcapi.h,v $ + * Revision 1.1 1997/03/04 21:27:33 calle + * First 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 __KERNEL_CAPI_H__ +#define __KERNEL_CAPI_H__ + +#define CAPI_MAXAPPL 20 /* + * maximum number of applications + */ +#define CAPI_MAXCONTR 4 /* + * maximum number of controller + */ +#define CAPI_MAXDATAWINDOW 8 + +#ifdef __KERNEL__ + +struct capi_interface { + int (*capi_installed) (void); + + __u16(*capi_register) (capi_register_params * rparam, __u16 * applidp); + __u16(*capi_release) (__u16 applid); + __u16(*capi_put_message) (__u16 applid, struct sk_buff * msg); + __u16(*capi_get_message) (__u16 applid, struct sk_buff ** msgp); + __u16(*capi_set_signal) (__u16 applid, + void (*signal) (__u16 applid, __u32 param), + __u32 param); + __u16(*capi_get_manufacturer) (__u16 contr, __u8 buf[CAPI_MANUFACTURER_LEN]); + __u16(*capi_get_version) (__u16 contr, struct capi_version * verp); + __u16(*capi_get_serial) (__u16 contr, __u8 serial[CAPI_SERIAL_LEN]); + __u16(*capi_get_profile) (__u16 contr, struct capi_profile * profp); + + /* + * to init controllers, data is always in user memory + */ + int (*capi_manufacturer) (unsigned int cmd, void *data); + +}; + +#define KCI_CONTRUP 0 +#define KCI_CONTRDOWN 1 + +struct capi_interface_user { + char name[20]; + void (*callback) (unsigned int cmd, __u16 contr, void *data); + struct capi_interface_user *next; +}; + +struct capi_interface *attach_capi_interface(struct capi_interface_user *); +int detach_capi_interface(struct capi_interface_user *); + + +#define CAPI_NOERROR 0x0000 + +#define CAPI_TOOMANYAPPLS 0x1001 +#define CAPI_LOGBLKSIZETOSMALL 0x1002 +#define CAPI_BUFFEXECEEDS64K 0x1003 +#define CAPI_MSGBUFSIZETOOSMALL 0x1004 +#define CAPI_ANZLOGCONNNOTSUPPORTED 0x1005 +#define CAPI_REGRESERVED 0x1006 +#define CAPI_REGBUSY 0x1007 +#define CAPI_REGOSRESOURCEERR 0x1008 +#define CAPI_REGNOTINSTALLED 0x1009 +#define CAPI_REGCTRLERNOTSUPPORTEXTEQUIP 0x100a +#define CAPI_REGCTRLERONLYSUPPORTEXTEQUIP 0x100b + +#define CAPI_ILLAPPNR 0x1101 +#define CAPI_ILLCMDORSUBCMDORMSGTOSMALL 0x1102 +#define CAPI_SENDQUEUEFULL 0x1103 +#define CAPI_RECEIVEQUEUEEMPTY 0x1104 +#define CAPI_RECEIVEOVERFLOW 0x1105 +#define CAPI_UNKNOWNNOTPAR 0x1106 +#define CAPI_MSGBUSY 0x1107 +#define CAPI_MSGOSRESOURCEERR 0x1108 +#define CAPI_MSGNOTINSTALLED 0x1109 +#define CAPI_MSGCTRLERNOTSUPPORTEXTEQUIP 0x110a +#define CAPI_MSGCTRLERONLYSUPPORTEXTEQUIP 0x110b + +#endif /* __KERNEL__ */ + +#endif /* __KERNEL_CAPI_H__ */ diff --git a/include/linux/pci.h b/include/linux/pci.h index d6cfad56df2a..4cdf7ae0d45d 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -351,6 +351,10 @@ #define PCI_VENDOR_ID_OAK 0x104e #define PCI_DEVICE_ID_OAK_OTI107 0x0107 +/* Winbond have two vendor ID! See 0x10ad as well */ +#define PCI_VENDOR_ID_WINBOND2 0x1050 +#define PCI_DEVICE_ID_WINBOND2_89C940 0x0940 + #define PCI_VENDOR_ID_PROMISE 0x105a #define PCI_DEVICE_ID_PROMISE_5300 0x5300 @@ -507,6 +511,9 @@ #define PCI_DEVICE_ID_SPECIALIX_XIO 0x4000 #define PCI_DEVICE_ID_SPECIALIX_RIO 0x8000 +#define PCI_VENDOR_ID_COMPEX 0x11f6 +#define PCI_DEVICE_ID_COMPEX_RL2000 0x1401 + #define PCI_VENDOR_ID_RP 0x11fe #define PCI_DEVICE_ID_RP8OCTA 0x0001 #define PCI_DEVICE_ID_RP8INTF 0x0002 diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 08cc269e38e4..2498662facdc 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -141,6 +141,7 @@ enum scsi_directory_inos { PROC_SCSI_A2091, PROC_SCSI_GVP11, PROC_SCSI_ATARI, + PROC_SCSI_GDTH, PROC_SCSI_IDESCSI, PROC_SCSI_SCSI_DEBUG, PROC_SCSI_NOT_PRESENT, diff --git a/init/main.c b/init/main.c index 1943074a2f0a..de32b6164bbf 100644 --- a/init/main.c +++ b/init/main.c @@ -89,6 +89,7 @@ extern void generic_NCR5380_setup(char *str, int *intr); extern void generic_NCR53C400_setup(char *str, int *intr); extern void aha152x_setup(char *str, int *ints); extern void aha1542_setup(char *str, int *ints); +extern void gdth_setup(char *str, int *ints); extern void aic7xxx_setup(char *str, int *ints); extern void AM53C974_setup(char *str, int *ints); extern void BusLogic_Setup(char *str, int *ints); @@ -147,8 +148,11 @@ static void no_initrd(char *s,int *ints); #ifdef CONFIG_ISDN_DRV_ICN extern void icn_setup(char *str, int *ints); #endif -#ifdef CONFIG_ISDN_DRV_TELES -extern void teles_setup(char *str, int *ints); +#ifdef CONFIG_ISDN_DRV_HISAX +extern void HiSax_setup(char *str, int *ints); +#endif +#ifdef CONFIG_ISDN_DRV_PCBIT +extern void pcbit_setup(char *str, int *ints); #endif #ifdef CONFIG_ATARIMOUSE @@ -166,9 +170,6 @@ extern void gvp11_setup (char *str, int *ints); #ifdef CONFIG_DIGI extern void pcxx_setup(char *str, int *ints); #endif -#ifdef CONFIG_ISDN_DRV_PCBIT -extern void pcbit_setup(char *str, int *ints); -#endif #ifdef CONFIG_RISCOM8 extern void riscom8_setup(char *str, int *ints); #endif @@ -318,6 +319,9 @@ struct { #ifdef CONFIG_SCSI_AHA1542 { "aha1542=", aha1542_setup}, #endif +#ifdef CONFIG_SCSI_GDTH + { "gdth=", gdth_setup}, +#endif #ifdef CONFIG_SCSI_AIC7XXX { "aic7xxx=", aic7xxx_setup}, #endif @@ -393,8 +397,9 @@ struct { #ifdef CONFIG_ISDN_DRV_ICN { "icn=", icn_setup }, #endif -#ifdef CONFIG_ISDN_DRV_TELES - { "teles=", teles_setup }, +#ifdef CONFIG_ISDN_DRV_HISAX + { "hisax=", HiSax_setup }, + { "HiSax=", HiSax_setup }, #endif #ifdef CONFIG_ISDN_DRV_PCBIT { "pcbit=", pcbit_setup }, diff --git a/kernel/panic.c b/kernel/panic.c index 78459e8383da..9d2693bd7f99 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -17,6 +17,7 @@ asmlinkage void sys_sync(void); /* it's really int */ extern void hard_reset_now(void); extern void do_unblank_screen(void); +extern void gdth_halt(void); extern int C_A_D; int panic_timeout = 0; @@ -53,6 +54,9 @@ NORET_TYPE void panic(const char * fmt, ...) printk(KERN_EMERG "Rebooting in %d seconds..",panic_timeout); for(i = 0; i < (panic_timeout*1000); i++) udelay(1000); +#ifdef CONFIG_SCSI_GDTH + gdth_halt(); +#endif hard_reset_now(); } for(;;); diff --git a/kernel/sys.c b/kernel/sys.c index 377fcc649ddf..d4f3c12aaba5 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -34,6 +34,7 @@ int C_A_D = 1; extern void adjust_clock(void); +extern void gdth_halt(void); asmlinkage int sys_ni_syscall(void) { @@ -186,9 +187,12 @@ asmlinkage int sys_reboot(int magic, int magic_too, int flag) return -EPERM; if (magic != 0xfee1dead || magic_too != 672274793) return -EINVAL; - if (flag == 0x01234567) + if (flag == 0x01234567) { +#ifdef CONFIG_SCSI_GDTH + gdth_halt(); +#endif hard_reset_now(); - else if (flag == 0x89ABCDEF) + } else if (flag == 0x89ABCDEF) C_A_D = 1; else if (!flag) C_A_D = 0; @@ -211,9 +215,12 @@ asmlinkage int sys_reboot(int magic, int magic_too, int flag) */ void ctrl_alt_del(void) { - if (C_A_D) + if (C_A_D) { +#ifdef CONFIG_SCSI_GDTH + gdth_halt(); +#endif hard_reset_now(); - else + } else kill_proc(1, SIGINT, 1); } -- 2.39.5