From: Linus Torvalds Date: Fri, 23 Nov 2007 20:15:05 +0000 (-0500) Subject: Linux 2.1.92 - Feature Freeze X-Git-Tag: 2.1.92 X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=581efda067a91f2b3981fb4f8f519a8dd0d91d1d;p=history.git Linux 2.1.92 - Feature Freeze Ok, there's a fairly large patch out there, but as of 2.1.92 I think we have a real feature-freeze, and we'll try to get a real code-freeze going soon. There are known problems with the sound drivers etc, which is why a code-freeze isn't the best suggestion right now, and there are probably still bugs with some of the new code, but I'll freeze new features for the upcoming 2.2 kernel. Yes, some people will scream bloody murder, but others will be relieved that it finally happened. Thanks especially to David Miller who has been doing a great job of getting the TCP stack from its problems just a few weeks ago to really shining new heights. That was my main worry about 2.2 not all that long ago, and was the main reason for having such a slushy period for a while. 2.1.92 does: - ISDN updates - alpha update (yes, SMP finally works, although not really stable yet) - networking fixes - "getcwd()" system call (not very long, the dcache makes this so trivial it is scary) - the mm responsiveness updates (they were in 2.1.92-pre2, people seemed to have found them very effective) - some other (mainly driver updates) Please do test it all out. Feature-freeze doesn't mean that it is supposed to be bug-free yet, but it does mean that we should be moving into bugfixing mode in quick order. And no, this is not an April 1 thing. But this way I can use April 1 as an excuse if something doesn't actually compile. Linus --- diff --git a/CREDITS b/CREDITS index 5bed63d68fbd..82b2c2092163 100644 --- a/CREDITS +++ b/CREDITS @@ -438,8 +438,8 @@ E: bj0rn@blox.se W: http://www.pi.se/blox/ D: Extended support for loadable modules D: D-Link pocket adapter drivers -S: Myrstuguv. 83 -S: S-143 32 VARBY +S: Grevgatan 11 +S: S-114 53 Stockholm S: Sweden N: Paal-Kristian Engstad @@ -1625,11 +1625,13 @@ S: F-35042 Rennes Cedex S: France N: Jon Tombs -E: jon@gtex02.us.es +E: jon@gte.esi.us.es +W: http://www.esi.us.es/~jon D: NFS mmap() D: XF86_S3 D: Kernel modules -S: C/ Carlos de Cepeda 36 2-5 +D: Parts of varios other programs (xfig, open, ...) +S: C/ Federico Garcia Lorca 1 10-A S: Sevilla 41005 S: Spain diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 71ca8d481ab8..64cb05702728 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -7066,6 +7066,12 @@ CONFIG_ISDN_AUDIO is the only voice-supporting driver. See Documentation/isdn/README.audio for more information. +X.25 PLP on top of ISDN (EXPERIMENTAL) +CONFIG_ISDN_X25 + This experimental feature provides X.25 over ISDN. See + Documentation/isdn/README.x25 for more information about how to + configure and what other options must be enabled for using X.25. + ICN 2B and 4B support CONFIG_ISDN_DRV_ICN This enables support for two kinds of ISDN-cards made by a German @@ -7079,6 +7085,14 @@ CONFIG_ISDN_DRV_ICN want), say M here and read Documentation/modules.txt. The module will be called icn.o. +isdnloop support +CONFIG_ISDN_DRV_LOOP + This driver provides a virtual ISDN card. It's primary purpose is + testing of linklevel features or configuration without getting + charged by your service-provider for lots of phone calls. + You need will need the loopctrl utility from the latest isdn4k-utils + package to set up this driver. + HiSax SiemensChipSet driver support CONFIG_ISDN_DRV_HISAX This is a driver supporting the Siemens chipset on various @@ -7104,36 +7118,95 @@ CONFIG_HISAX_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. + non-standard irq/port settings. + +HiSax Support for Teles 16.3c +CONFIG_HISAX_TELES3C + This enables HiSax support for the Teles ISDN-cards 16.3c. + See Documentation/isdn/README.HiSax on how to configure it + using the different cards, a different D-channel protocol, or + non-standard irq/port settings. HiSax Support for AVM 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. + non-standard irq/port settings. HiSax Support for Elsa ISA cards -CONFIG_HISAX_ELSA_PCC - This enables HiSax support for the Elsa Mircolink ISA cards and - for the Elsa Quickstep series cards. +CONFIG_HISAX_ELSA + This enables HiSax support for the Elsa Mircolink ISA cards, + for the Elsa Quickstep series cards and Elsa 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 Elsa PCMCIA card -CONFIG_HISAX_ELSA_PCMCIA - This enables HiSax support for the Elsa PCMCIA cards. - See Documentation/isdn/README.HiSax on how to configure it - using the different cards, a different D-channel protocol, or - non-standard IRQ/port/shmem settings. + non-standard irq/port settings. HiSax Support for ITK ix1-micro Revision 2 CONFIG_HISAX_IX1MICROR2 This enables HiSax support for the ITK ix1-micro Revision 2 card. See Documentation/isdn/README.HiSax on how to configure it using the different cards, a different D-channel protocol, or - non-standard IRQ/port/shmem settings. + non-standard irq/port settings. + +HiSax Support for Eicon.Diehl Diva cards +CONFIG_HISAX_DIEHLDIVA + This enables HiSax support for the Eicon.Diehl Diva none PRO versions + passive ISDN cards. + See Documentation/isdn/README.HiSax on how to configure it + using the different cards, a different D-channel protocol, or + non-standard irq/port settings. + +HiSax Support for ASUSCOM cards +CONFIG_HISAX_ASUSCOM + This enables HiSax support for the AsusCom and their OEM versions + passive ISDN cards. + See Documentation/isdn/README.HiSax on how to configure it + using the different cards, a different D-channel protocol, or + non-standard irq/port settings. + +HiSax Support for TELEINT cards +CONFIG_HISAX_TELEINT + This enables HiSax support for the TELEINT SA1 semiactiv ISDN 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 settings. + +HiSax Support for Sedlbauer speed card/win-star +CONFIG_HISAX_SEDLBAUER + This enables HiSax support for the Sedlbauer passive ISDN cards. + See Documentation/isdn/README.HiSax on how to configure it + using the different cards, a different D-channel protocol, or + non-standard irq/port settings. + +HiSax Support for USR Sportster internal TA +CONFIG_HISAX_SPORTSTER + This enables HiSax support for the USR Sportster internal TA card. + See Documentation/isdn/README.HiSax on how to configure it + using a different D-channel protocol, or non-standard irq/port settings. + +HiSax Support for MIC card +CONFIG_HISAX_MIC + This enables HiSax support for the ITH MIC card. + See Documentation/isdn/README.HiSax on how to configure it + using a different D-channel protocol, or non-standard irq/port settings. + +HiSax Support for NETjet card +CONFIG_HISAX_NETJET + This enables HiSax support for the NetJet from Traverse Technologies. + See Documentation/isdn/README.HiSax on how to configure it + using a different D-channel protocol, or non-standard irq/port settings. + +HiSax Support for Niccy PnP/PCI card +CONFIG_HISAX_NICCY + This enables HiSax support for the Dr. Neuhaus Niccy PnP or PCI. + See Documentation/isdn/README.HiSax on how to configure it + using a different D-channel protocol, or non-standard irq/port settings. + +HiSax Support for Am7930 (EXPERIMENTAL) +CONFIG_HISAX_AMD7930 + This enables HiSax support for the AMD7930 chips on some sparcs. + This code is not finished yet. HiSax Support for EURO/DSS1 CONFIG_HISAX_EURO @@ -7142,7 +7215,18 @@ CONFIG_HISAX_EURO NOTE: This is mutually exclusive with HiSax Support for German 1TR6 if you have only one ISDN card installed. -HiSax Support for US/NI-1 +Support for german tarifinfo +CONFIG_DE_AOC + If you want, that HiSax send messages to the linklevel on each + AOCD/AOCE, enable this. This works only in Germany. + +Support for australian Microlink service (not for std. EURO) +CONFIG_HISAX_ML + If you are in Australia and connected on the Microlink telephone + network enable this, because here are little differences in protocol. + Please don't enable this in other countries. + +HiSax Support for US/NI-1 (not released yet) CONFIG_HISAX_NI1 You should choose the D-channel protocol your local telephone service provider uses here by saying Y or N. @@ -7199,6 +7283,14 @@ CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON disconnecting. This will increase the size of the kernel by 7K. If unsure, say Y. +IBM Active 2000 support (EXPERIMENTAL) +CONFIG_ISDN_DRV_ACT2000 + This enables support for IBM Active 2000 ISDN card. In order to use + this card, additional firmware is necessary, which has to be loaded + into the card using a utility which is part of the latest isdn4k-utils + package. Please read the file Documentation/isdn/README.act2000 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 401697921b9d..919af9057654 100644 --- a/Documentation/isdn/00-INDEX +++ b/Documentation/isdn/00-INDEX @@ -19,4 +19,12 @@ README.syncppp syncPPP.FAQ - frequently asked questions about running PPP over ISDN. README.avmb1 - - info on driver for AVM-B1 ISDN card + - info on driver for AVM-B1 ISDN card. +README.act2000 + - info on driver for IBM ACT-2000 card. +README.concap + - info on "CONCAP" ecapsulation protocol interface used for X.25. +README.sc + - info on driver for Spellcaster cards. +README.x25 + _ info for running X.25 over ISDN. diff --git a/Documentation/isdn/CREDITS b/Documentation/isdn/CREDITS index 44e6554a7647..6fd4c9ed5beb 100644 --- a/Documentation/isdn/CREDITS +++ b/Documentation/isdn/CREDITS @@ -8,6 +8,9 @@ Thomas Bogend Alan Cox (alan@cymru.net) For help getting into standard-kernel. +Henner Eisen (eis@baty.hanse.de) + For X.25 implementation. + Volker Götz (volker@oops.franken.de) For contribution of man-pages, the imontty-tool and a perfect maintaining of the mailing-list at hub-wue. @@ -37,18 +40,24 @@ Pedro Roque Marques (roque@di.fc.ul.pt) Eberhard Moenkeberg (emoenke@gwdg.de) For testing and help to get into kernel. +Thomas Neumann (tn@ruhr.de) + For help with Cisco-SLARP and keepalive + Jan den Ouden (denouden@groovin.xs4all.nl) - For contribution of the teles-driver + For contribution of the original teles-driver + +Carsten Paeth (calle@calle.in-berlin.de) + For the AVM-B1-CAPI2.0 driver + +Thomas Pfeiffer (pfeiffer@pds.de) + For V.110, extended T.70 and Hylafax extensions in isdn_tty.c Max Riegel (riegel@max.franken.de) For making the ICN hardware-documentation and test-equipment available. -Gerhard 'Fido' Schneider (fido@wuff.franken.de) +Gerhard 'Fido' Schneider (fido@wuff.mayn.de) For heavy-duty-beta-testing with his BBS ;) 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 81dddb08177b..5b25c9597088 100644 --- a/Documentation/isdn/INTERFACE +++ b/Documentation/isdn/INTERFACE @@ -1,4 +1,4 @@ -$Id: INTERFACE,v 1.6 1997/02/10 22:40:57 fritz Exp $ +$Id: INTERFACE,v 1.8 1998/02/20 17:38:20 fritz Exp $ Description of the Interface between Linklevel and Hardwarelevel of isdn4linux: @@ -22,7 +22,7 @@ Description of the Interface between Linklevel and Hardwarelevel got a separate version number. These numbers are shown at initialization, separated by slashes: - c.c/t.t/n.n/p.p/a.a + c.c/t.t/n.n/p.p/a.a/v.v where @@ -31,11 +31,13 @@ Description of the Interface between Linklevel and Hardwarelevel n.n is the revision of the network related code. p.p is the revision of the ppp related code. a.a is the revision of the audio related code. + v.v is the revision of the V.110 related code. Changes in this document are marked with '***CHANGEx' where x representing the version number. If that number starts with 0, it refers to the old, separately distributed package. If it starts with one of the letters above, it refers to the revision of the corresponding module. + ***CHANGEIx refers to the revision number of the isdnif.h 1. Description of the fields of isdn_if: @@ -65,32 +67,16 @@ Description of the Interface between Linklevel and Hardwarelevel unsigned short hl_hdrlen; - ***CHANGED.7.4: New field. + ***CHANGE0.7.4: New field. To be preset by the HL-driver, if it supports sk_buff's. The driver should put here the amount of additional space needed in sk-buff's for its internal purposes. Drivers not supporting sk_buff's should put initialize this field to 0. - void (*rcvcallb)(int, int, u_char*, int); - - ***CHANGEc1.14: Declared obsolete. Do NOT use this field/function - anymore, since it will be removed when all current - LL drivers have been changed accordingly. Use - rcvcallb_skb instead. - - This field will be set by LL. The HL-driver delivers received data- - packets by calling this function. - - Parameter: - int driver-Id - int Channel-number locally to the driver. (starting with 0) - u_char Pointer to received data. (in kernel-space) - int length of data-packet. - void (*rcvcallb_skb)(int, int, struct sk_buff *) - ***CHANGED.7.4: New field. + ***CHANGE0.7.4: New field. This field will be set by LL. The HL-driver delivers received data- packets by calling this function. Upon calling, the HL-driver must @@ -138,41 +124,22 @@ Description of the Interface between Linklevel and Hardwarelevel Returnvalue: >=0 on success, else error-code (-ENODEV etc.) - int (*writebuf)(int, int, u_char*, int, int); + int (*writebuf_skb)(int, int, int, struct sk_buff *) - ***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. - - This field has to be preset by the HL-driver. The given function will - be called by the LL for delivering data to be send via B-Channel. - - Parameter: - int driver-Id ***CHANGE.7.4: New parameter. - int channel-number locally to the HL-driver. (starts with 0) - u_char* pointer to data. - int length of data-packet. - int flag: 0 = call from within kernel-space. (HL-driver must use - memcpy, may NOT use schedule()) - 1 = call from user-space. (HL-driver must use - memcpy_fromfs, use of schedule() allowed) - - Returnvalue: - Length of data accepted on success, else error-code (-EINVAL on - oversized packets etc.) - - int (*writebuf_skb)(int, int, struct sk_buff *) - - ***CHANGED.7.4: New field. + ***CHANGE0.7.4: New field. + ***CHANGEI.1.21: New field. This field has to be preset by the HL-driver. The given function will be called by the LL for delivering data to be send via B-Channel. Parameter: - int driver-Id ***CHANGE.7.4: New parameter. + int driver-Id ***CHANGE0.7.4: New parameter. int channel-number locally to the HL-driver. (starts with 0) + int ack ***ChangeI1.21: New parameter + If this is !0, the driver has to signal the delivery + by sending an ISDN_STAT_BSENT. If this is 0, the driver + MUST NOT send an ISDN_STAT_BSENT. struct sk_buff * Pointer to sk_buff containing data to be send via B-channel. @@ -199,7 +166,7 @@ Description of the Interface between Linklevel and Hardwarelevel int driver-Id. int channel-number locally to the HL-driver. (starts with 0) -***CHANGED1.14: The driver-Id and channel-number are new since this revision. +***CHANGEI1.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 +190,7 @@ Description of the Interface between Linklevel and Hardwarelevel int driver-Id. int channel-number locally to the HL-driver. (starts with 0) -***CHANGED1.14: The driver-Id and channel-number are new since this revision. +***CHANGEI1.14: The driver-Id and channel-number are new since this revision. Returnvalue: Length of data on success, else error-code (-EINVAL etc.) @@ -249,7 +216,7 @@ 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 +***CHANGEI1.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. @@ -552,6 +519,9 @@ Description of the Interface between Linklevel and Hardwarelevel a B-Channel-connection. (Response to ISDN_CMD_ACCEPTB or because the remote-station has initiated establishment) + The HL driver should call this when the logical l2/l3 protocol + connection on top of the physical B-channel is esatblished . + Parameter: driver = driver-Id command = ISDN_STAT_BCONN @@ -577,6 +547,9 @@ Description of the Interface between Linklevel and Hardwarelevel B-Channel-connection. This could be a response to a prior ISDN_CMD_HANGUP, or caused by a remote-hangup. + The HL driver should call this as soon as the logical l2/l3 protocol + connection on top of the physical B-channel is released. + Parameter: driver = driver-Id command = ISDN_STAT_BHUP @@ -617,7 +590,9 @@ 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) - para = unused. + para.length = ***CHANGEI.1.21: New field. + the driver has to set this to the original length + of the skb at the time of receiving it from the linklevel. ISDN_STAT_NODCH: @@ -657,3 +632,16 @@ Description of the Interface between Linklevel and Hardwarelevel arg = channel-number, locally to the driver. (starting with 0) para.num = ASCII string containing CAUSE-message. + ISDN_STAT_L1ERR: + + ***CHANGEI1.21 new status message. + A signal can be sent to the linklevel if an Layer1-error results in + packet-loss on receive or send. The field errcode of the cmd.parm + union describes the error more precisely. + + Parameter: + driver = driver-Id + command = ISDN_STAT_L1ERR + arg = channel-number, locally to the driver. (starting with 0) + para.errcode= ISDN_STAT_L1ERR_SEND: Packet lost while sending. + ISDN_STAT_L1ERR_RECV: Packet lost while receiving. diff --git a/Documentation/isdn/README b/Documentation/isdn/README index 48dec2d836c8..1d94afb68909 100644 --- a/Documentation/isdn/README +++ b/Documentation/isdn/README @@ -120,12 +120,35 @@ 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&Rx Select V.110 bitrate adaption. + This command enables V.110 protocol with 9600 baud + (x=9600), 19200 baud (x=19200) or 38400 baud + (x=38400). A value of x=0 disables V.110 switching + back to default X.75. This command sets the following + Registers: + Reg 14 (Layer-2 protocol): + x = 0: 0 + x = 9600: 7 + x = 19200: 8 + x = 38400: 9 + Reg 18.2 = 1 + Reg 19 (Additional Service Indicator): + x = 0: 0 + x = 9600: 197 + x = 19200: 199 + x = 38400: 198 + Note on value in Reg 19: + There is _NO_ common convention for 38400 baud. + The value 198 is choosen arbitrarily. Users + _MUST_ negotiate this value before establishing + a connection. AT&Sx Set window-size (x = 1..8) (not yet implemented) AT&V Show all settings. AT&W0 Write registers and EAZ/MSN to profile. See also iprofd (5.c in this README). - AT&X0 BTX-mode off (default) - AT&X1 BTX-mode on. (S13.1=1, S14=0, S16=7, S18=7, S19=0) + AT&X0 BTX-mode and T.70-mode off (default) + AT&X1 BTX-mode on. (S13.1=1, S13.5=0 S14=0, S16=7, S18=7, S19=0) + AT&X2 T.70-mode on. (S13.1=1, S13.5=1, S14=0, S16=7, S18=7, S19=0) For voice-mode commands refer to README.audio @@ -184,12 +207,23 @@ README for the ISDN-subsystem 1 = Extended response messages Bit 4: 0 = CALLER NUMBER before every RING. 1 = CALLER NUMBER after first RING. + Bit 5: 0 = T.70 extended protocol off + 1 = T.70 extended protocol on + Bit 6: 0 = Special RUNG Message off + 1 = Special RUNG Message on + "RUNG" is delivered on a ttyI, if + an incoming call happened (RING) and + the remote party hung up before any + local ATA was given. 14 0 Layer-2 protocol: 0 = X75/LAPB with I-frames 1 = X75/LAPB with UI-frames 2 = X75/LAPB with BUI-frames 3 = HDLC 4 = Transparent (audio) + 7 = V.110, 9600 baud + 8 = V.110, 19200 baud + 9 = V.110, 38400 baud 15 0 Layer-3 protocol: (at the moment always 0) 0 = transparent 16 250 Send-Packet-size/16 @@ -406,6 +440,9 @@ README for the ISDN-subsystem are stripped off. ip IP with type-field. Same as IP but the type-field of the MAC-header is preserved. + x25iface x25 interface encapsulation (first byte semantics as defined in + ../networking/x25-iface.txt). Use this for running the linux + x25 network protocol stack (AF_X25 sockets) on top of isdn. cisco-h A special-mode for communicating with a Cisco, which is configured to do "hdlc" ethernet No stripping. Packets are sent with full MAC-header. @@ -415,6 +452,11 @@ README for the ISDN-subsystem uihdlc HDLC with UI-frame-header (for use with DOS ISPA, option -h1) + + NOTE: x25iface encapsulation is currently experimental. Please + read README.x25 for further details + + Watching packets, using standard-tcpdump will fail for all encapsulations except ethernet because tcpdump does not know how to handle packets without MAC-header. A patch for tcpdump is included in the utility-package @@ -423,7 +465,8 @@ README for the ISDN-subsystem "isdnctrl l2_prot " Selects a layer-2-protocol. (With the ICN-driver and the HiSax-driver, "x75i" and "hdlc" is available. - With other drivers, "x75ui", "x75bui" may be possible too.) + With other drivers, "x75ui", "x75bui", "x25dte", "x25dce" may be + possible too. See README.x25 for x25 related l2 protocols.) isdnctrl l3_prot The same for layer-3. (At the moment only "trans" is allowed) diff --git a/Documentation/isdn/README.HiSax b/Documentation/isdn/README.HiSax index 20b578d83221..21f849cbd62b 100644 --- a/Documentation/isdn/README.HiSax +++ b/Documentation/isdn/README.HiSax @@ -23,24 +23,39 @@ Supported cards --------------- Teles 8.0/16.0/16.3 and compatible ones +Teles 16.3c Teles S0/PCMCIA Creatix PnP S0 -AVM A1 (Fritz) +Compaq ISDN S0 ISA card +AVM A1 (Fritz, Teledat 150) ELSA Microlink PCC-16, PCF, PCF-Pro, PCC-8 ELSA Quickstep 1000 +ELSA Quickstep 1000PCI +ELSA Quickstep 3000 (same settings as QS1000) ELSA PCMCIA ITK ix1-micro Rev.2 +Eicon.Diehl Diva 2.0 ISA and PCI (S0 and U interface, no PRO version) +Eicon.Diehl Diva Piccola +ASUSCOM NETWORK INC. ISDNLink 128K PC adapter (order code I-IN100-ST-D) +Dynalink IS64PH (OEM version of ASUSCOM NETWORK INC. ISDNLink 128K adapter) +HFC-2BS0 based cards (TeleInt SA1) +Sedlbauer Speed Card (Speed Win, Teledat 100) +Sedlbauer Speed Star (PCMCIA) +USR Sportster internal TA (compatible Stollmann tina-pp V3) +ith Kommunikationstechnik GmbH MIC 16 ISA card +Traverse Technologie NETjet PCI S0 card +Dr. Neuhaus Niccy PnP/PCI Note: PCF, PCF-Pro: up to now, only the ISDN part is supported PCC-8: not tested yet Teles PCMCIA is EXPERIMENTAL + Teles 16.3c is EXPERIMENTAL + Eicon.Diehl Diva U interface not tested If you know other passive cards with the Siemens chipset, please let me know. To use the PNP cards you need the isapnptools. You can combine any card, if there is no conflict between the ressources -(io, mem, irq), with one exception: The ELSA PCMCIA cannot work with an other -non PCMCIA ELSA card at the same time. You cannot select ELSA ISA and ELSA -PCMCIA support at the same time during kernel config. +(io, mem, irq). Configuring the driver @@ -113,10 +128,26 @@ Card types: 6 ELSA PCC/PCF cards io or nothing for autodetect (the iobase is required only if you have more than one ELSA card in your PC) - 7 ELSA Quickstep 1000 irq, io (from isapnp setup) - 7 ELSA PCMCIA irq, io (set with card manager) + 7 ELSA Quickstep 1000 irq, io (from isapnp setup) 8 Teles 16.3 PCMCIA irq, io 9 ITK ix1-micro Rev.2 irq, io + 10 ELSA PCMCIA irq, io (set with card manager) + 11 Eicon.Diehl Diva ISA PnP irq, io + 11 Eicon.Diehl Diva PCI no parameter + 12 ASUS COM ISDNLink irq, io (from isapnp setup) + 13 HFC-2BS0 based cards irq, io + 14 Teles 16.3c PnP irq, io + 15 Sedlbauer Speed Card irq, io + 16 USR Sportster internal irq, io + 17 MIC card irq, io + 18 ELSA Quickstep 1000PCI no parameter + 19 Compaq ISDN S0 ISA card irq, io0, io1, io (from isapnp setup io=IO2) + 20 NETjet PCI card no parameter + 22 Sedlbauer Speed Star (PCMCIA) irq, io (set with card manager) + 24 Dr. Neuhaus Niccy PnP irq, io0, io1 (from isapnp setup) + 24 Dr. Neuhaus Niccy PCI no parameter + + At the moment IRQ sharing is not possible. Please make sure that your IRQ is free and enabled for ISA use. @@ -181,17 +212,28 @@ where 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 + 1 Teles 16.0 pa=irq pb=membase pc=iobase + 2 Teles 8.0 pa=irq pb=membase + 3 Teles 16.3 pa=irq pb=iobase 4 Creatix/Teles PNP ONLY WORKS AS A MODULE ! - 5 AVM A1 (Fritz) pa=irq pb=iobase + 5 AVM A1 (Fritz) pa=irq pb=iobase 6 ELSA PCC/PCF cards pa=iobase or nothing for autodetect - 7 ELSA Quickstep 1000 ONLY WORKS AS A MODULE ! - 7 ELSA PCMCIA irq, io (set with card manager) - 8 Teles S0 PCMCIA pa=irq pb=iobase + 7 ELSA Quickstep 1000 ONLY WORKS AS A MODULE ! + 8 Teles S0 PCMCIA pa=irq pb=iobase 9 ITK ix1-micro Rev.2 pa=irq pb=iobase - + 10 ELSA PCMCIA pa=irq, pb=io (set with card manager) + 11 Eicon.Diehl Diva ISAPnP ONLY WORKS AS A MODULE ! + 11 Eicon.Diehl Diva PCI no parameter + 12 ASUS COM ISDNLink ONLY WORKS AS A MODULE ! + 13 HFC-2BS0 based cards pa=irq pb=io + 14 Teles 16.3c PnP ONLY WORKS AS A MODULE ! + 15 Sedlbauer Speed Card pa=irq pb=io (Speed Win only as module !) + 16 USR Sportster internal pa=irq pb=io + 17 MIC card pa=irq pb=io + 18 ELSA Quickstep 1000PCI no parameter + 19 Compaq ISDN S0 ISA card ONLY WORKS AS A MODULE ! + 20 NETjet PCI card no parameter + 21 Sedlbauer Speed Star (PCMCIA) pa=irq, pb=io (set with card manager) Running the driver ------------------ @@ -215,8 +257,8 @@ 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. +Cabling problems or line-downs are not detected, and only some ELSA cards can +detect the S0 power. Remember that, according to the new strategy for accessing low-level drivers from within isdn4linux, you should also define a driver ID while doing @@ -226,9 +268,9 @@ 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: +At the moment, debugging messages are enabled with the hisaxctrl tool: - telesctrl DebugCmd + hisaxctrl DebugCmd default is HiSax, if you didn't specified one. @@ -271,7 +313,14 @@ With DebugCmd set to 13: 4 l3 state machine 8 charge info debugging (1TR6) -For example, 'telesctrl HiSax 1 0x3ff' enables full generic debugging. +For example, 'hisaxctrl HiSax 1 0x3ff' enables full generic debugging. + +Because of some obscure problems with some switch equipment, the delay +between CONNECT message and sending the first data on th B-channel is now +configurable with + +hisaxctrl 2 + in ms Value between 50 an 800 ms are recommended. Warning @@ -284,7 +333,7 @@ illegal. Limitations ----------- At this time, HiSax only works on Euro ISDN lines and German 1TR6 lines. - +For leased lines see appendix. Bugs ---- @@ -301,10 +350,20 @@ Special thanks to: Andreas Kool, Pekka Sarnila, Sim Yskes, Johan Myrre'en, Klaus-Peter Nischke (ITK AG), Christof Petig, Werner Fehn (ELSA GmbH), Volker Schmidt + Edgar Toernig and Marcus Niemann for the Sedlbauer driver + Stephan von Krawczynski + Juergen Quade for the Leased Line part + Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE), for ELSA PCMCIA support and more people who are hunting bugs. (If I forgot somebody, please send me a mail). Firma ELSA GmbH + Firma Eicon.Diehl GmbH + Firma Dynalink NL + Firma ASUSCOM NETWORK INC. Taiwan + Firma S.u.S.E + Firma ith Kommunikationstechnik GmbH + Firma Traverse Technologie Australia My girl friend and partner in life Ute for her patience with me. @@ -321,3 +380,171 @@ Appendix: Teles PCMCIA driver See http://www.stud.uni-wuppertal.de/~ea0141/pcmcia.html for instructions. + +Appendix: Linux and ISDN-leased lines +------------------------------------- + +Original from Juergen Quade, new version KKe. + +Attention NEW VERSION, the old leased line syntax won't work !!! + +You can use HiSax to connect your Linux-Box via an ISDN leased line +to i.e. the internet: + +1. Build a kernel which includes the HiSax driver either as a module + or as part of the kernel. + cd /usr/src/linux + make menuconfig + + make clean; make dep; make zImage; make modules; make modules_install +2. Install the new kernel + cp /usr/src/linux/arch/i386/boot/zImage /etc/kernel/linux.isdn + vi /etc/lilo.conf + + lilo +3. in case the hisax driver is a "fixed" part of the kernel, configure + the driver with lilo: + vi /etc/lilo.conf + + lilo + Your lilo.conf _might_ look as the following: + + # LILO configuration-file + # global section + # teles 16.0 on IRQ=5, MEM=0xd8000, PORT=0xd80 + append="hisax=1,3,5,0xd8000,0xd80,HiSax" + # teles 16.3 (non pnp) on IRQ=15, PORT=0xd80 + # append="hisax=3,3,5,0xd8000,0xd80,HiSax" + boot=/dev/sda + compact # faster, but won't work on all systems. + linear + read-only + prompt + timeout=100 + vga = normal # force sane state + # Linux bootable partition config begins + image = /etc/kernel/linux.isdn + root = /dev/sda1 + label = linux.isdn + # + image = /etc/kernel/linux-2.0.30 + root = /dev/sda1 + label = linux.secure + + In the line starting with "append" you have to adapt the parameters + according to your card (see above in this file) + +3. boot the new linux.isdn kernel +4. start the ISDN subsystem: + a) load - if necessary - the modules (depends, whether you compiled + the ISDN driver as module or not) + According to the type of card you have to specify the necessary + driver parameter (irq, io, mem, type, protocol). + For the leased line the protocol is "3". See the table above for + the parameters, which you have to specify depending on your card. + b) configure i4l + /sbin/isdnctrl addif isdn0 + # EAZ 1 -- B1 channel 2 --B2 channel + /sbin/isdnctrl eaz isdn0 1 + /sbin/isdnctrl secure isdn0 on + /sbin/isdnctrl huptimeout isdn0 0 + /sbin/isdnctrl l2_prot isdn0 hdlc + # Attention you must not set a outgoing number !!! This won't work !!! + # The incomming number is LEASED0 for the first card, LEASED1 for the + # second and so on. + /sbin/isdnctrl addphone isdn0 in LEASED0 + # Here is no need to bind the channel. + c) in case the remote partner is a CISCO: + /sbin/isdnctrl encap isdn0 cisco-h + d) configure the interface + /sbin/ifconfig isdn0 ${LOCAL_IP} pointopoint ${REMOTE_IP} + e) set the routes + /sbin/route add -host ${REMOTE_IP} isdn0 + /sbin/route add default gw ${REMOTE_IP} + f) switch the card into leased mode for each used B-channel + /sbin/hisaxctrl HiSax 5 1 + +Remarks: +a) If you have a CISCO don´t forget to switch off the KEEP ALIVE option! + +Here an example script: +#!/bin/sh +# Start/Stop ISDN lesaed line connection + +I4L_AS_MODULE=yes +I4L_REMOTE_IS_CISCO=no +I4L_MODULE_PARAMS="type=16 io=0x268 irq=7 " +I4L_DEBUG=no +I4L_LEASED_128K=yes +LOCAL_IP=192.168.1.1 +REMOTE_IP=192.168.2.1 + +case "$1" in + start) + echo "Starting ISDN ..." + if [ ${I4L_AS_MODULE} = "yes" ]; then + echo "loading modules..." + /sbin/modprobe hisax ${I4L_MODULE_PARAMS} + fi + # configure interface + /sbin/isdnctrl addif isdn0 + /sbin/isdnctrl secure isdn0 on + if [ ${I4L_DEBUG} = "yes" ]; then + /sbin/isdnctrl verbose 7 + /sbin/hisaxctrl HiSax 1 0xffff + /sbin/hisaxctrl HiSax 11 0xff + cat /dev/isdnctrl >/tmp/lea.log & + fi + if [ ${I4L_REMOTE_IS_CISCO} = "yes" ]; then + /sbin/isdnctrl encap isdn0 cisco-h + fi + /sbin/isdnctrl huptimeout isdn0 0 + # B-CHANNEL 1 + /sbin/isdnctrl eaz isdn0 1 + /sbin/isdnctrl l2_prot isdn0 hdlc + # 1. card + /sbin/isdnctrl addphone isdn0 in LEASED0 + if [ ${I4L_LEASED_128K} = "yes" ]; then + /sbin/isdnctrl addslave isdn0 isdn0s + /sbin/isdnctrl secure isdn0s on + /sbin/isdnctrl huptimeout isdn0s 0 + # B-CHANNEL 2 + /sbin/isdnctrl eaz isdn0s 2 + /sbin/isdnctrl l2_prot isdn0s hdlc + # 1. card + /sbin/isdnctrl addphone isdn0s in LEASED0 + if [ ${I4L_REMOTE_IS_CISCO} = "yes" ]; then + /sbin/isdnctrl encap isdn0s cisco-h + fi + fi + # configure tcp/ip + /sbin/ifconfig isdn0 ${LOCAL_IP} pointopoint ${REMOTE_IP} + /sbin/route add -host ${REMOTE_IP} isdn0 + /sbin/route add default gw ${REMOTE_IP} + # switch to leased mode + # B-CHANNEL 1 + /sbin/hisaxctrl HiSax 5 1 + if [ ${I4L_LEASED_128K} = "yes" ]; then + # B-CHANNEL 2 + /sbin/hisaxctrl HiSax 5 2 + fi + ;; + stop) + /sbin/ifconfig isdn0 down + /sbin/isdnctrl delif isdn0 + if [ ${I4L_DEBUG} = "yes" ]; then + killall cat + fi + if [ ${I4L_AS_MODULE} = "yes" ]; then + /sbin/rmmod hisax + /sbin/rmmod isdn + /sbin/rmmod ppp + /sbin/rmmod slhc + fi + ;; + *) + echo "Usage: $0 {start|stop}" + exit 1 +esac +exit 0 + diff --git a/Documentation/isdn/README.act2000 b/Documentation/isdn/README.act2000 new file mode 100644 index 000000000000..953582229e55 --- /dev/null +++ b/Documentation/isdn/README.act2000 @@ -0,0 +1,104 @@ +$Id: README.act2000,v 1.1 1997/09/24 23:50:16 fritz Exp $ + +This document describes the ACT2000 driver for the +IBM Active 2000 ISDN card. + +There are 3 Types of this card available. A ISA-, MCA-, and PCMCIA-Bus +Version. Currently, only the ISA-Bus version of the card is supported. +However MCA and PCMCIA will follow soon. + +The ISA-Bus Version uses 8 IO-ports. The base port adress has to be set +manually using the DIP switches. + +Setting up the DIP switches for the IBM Active 2000 ISDN card: + + Note: S5 and S6 always set off! + + S1 S2 S3 S4 Base-port + on on on on 0x0200 (Factory default) + off on on on 0x0240 + on off on on 0x0280 + off off on on 0x02c0 + on on off on 0x0300 + off on off on 0x0340 + on off off on 0x0380 + on on on off 0xcfe0 + off on on off 0xcfa0 + on off on off 0xcf60 + off off on off 0xcf20 + on on off off 0xcee0 + off on off off 0xcea0 + on off off off 0xce60 + off off off off Card disabled + +IRQ is configured by software. Possible values are: + + 3, 5, 7, 10, 11, 12, 15 and none (polled mode) + + +The ACT2000 driver either may be build into kernel or as a module. +Initialization depends on how the driver is built: + +Driver built into the kernel: + + The ACT2000 driver can be configured using the commandline-feature while + loading the kernel with LILO or LOADLIN. It accepts the following syntax: + + act2000=b,p,i[,idstring] + + where + + b = Bus-Type (1=ISA, 2=MCA, 3=PCMCIA) + p = portbase (-1 means autoprobe) + i = Interrupt (-1 means use next free IRQ, 0 means polled mode) + + The idstring is an arbitrary string used for referencing the card + by the actctrl tool later. + + Defaults used, when no parameters given at all: + + 1,-1,-1,"" + + which means: Autoprobe for an ISA card, use next free IRQ, let the + ISDN linklevel fill the IdString (usually "line0" for the first card). + + If you like to use more than one card, you can use the program + "actctrl" from the utility-package to configure additional cards. + + Using the "actctrl"-utility, portbase and irq can also be changed + during runtime. The D-channel protocol is configured by the "dproto" + option of the "actctrl"-utility after loading the firmware into the + card's memory using the "actctrl"-utility. + +Driver built as module: + + The module act2000.o can be configured during modprobe (insmod) by + appending its parameters to the modprobe resp. insmod commandline. + The following syntax is accepted: + + act_bus=b act_port=p act_irq=i act_id=idstring + + where b, p, i and idstring have the same meanings like parameters + described for the builtin version above. + + Using the "actctrl"-utility, the same features apply to the modularized + version like to the kernel-builtin one. (i.e. loading of firmware and + configuring the D-channel protocol) + +Loading the firmware into the card: + + The firmware is supplied together with the isdn4k-utils package. It + can be found in the subdirectory act2000/firmware/ + + Assumed you have installed the utility-package correctly, the firmware + will be downloaded into the card using the following command: + + actctrl -d idstring load /etc/isdn/bip11.btl + + where idstring is the Name of the card, given during insmod-time or + (for kernel-builtin driver) on the kernel commandline. If only one + ISDN card is used, the -d isdstrin may be omitted. + + For further documentation (adding more IBM Active 2000 cards), refer to + the manpage actctrl.8 which is included in the isdn4k-utils package. + diff --git a/Documentation/isdn/README.concap b/Documentation/isdn/README.concap new file mode 100644 index 000000000000..b6ff4d2d1e97 --- /dev/null +++ b/Documentation/isdn/README.concap @@ -0,0 +1,258 @@ +Description of the "concap" encapsulation protocol interface +============================================================ + +The "concap" interface is intended to be used by network device +drivers that need to process an encapsulation protocol. +It is assumed that the protocol interacts with a linux network device by +- data transmission +- connection control (establish, release) +Thus, the mnemonic: "CONnection CONtrolling eNCAPsulation Protocol". + +This is currently only used inside the isdn subsystem. But it might +also be useful to other kinds of network devices. Thus, if you want +to suggest changes that improve usability or performace of the +interface, please let me know. I'm willing to include them in future +releases (even if I needed to adapt the current isdn code to the +changed interface). + + +Why is this useful? +=================== + +The encapsulation protocol used on top of WAN connections or permanent +point-to-point links are frequently chosen upon bilateral agreement. +Thus, a device driver for a certain type of hardware must support +several different encapsulation protocols at once. + +The isdn device driver did already support several different +encapsulation protocols. The encapsulation protocol is configuered by a +user space utility (isdnctrl). The isdn network interface code then +uses several case statements which select appropriate actions +depending on the currently configuered encapsulation protocol. + +In contrast, LAN network interfaces always used a single encapsulation +protocol which is unique to the hardware type of the interface. The LAN +encapsulation is usually done by just sticking a header at the data. Thus, +traditional linux network device drivers used to process the +encapsulation protocol directly (usually by just providing a hard_header() +method in the device structure) using some hardware type specific support +functions. This is simple, direct and efficient. But it doesn't fit all +the requirements for complex WAN encapsulations. + + + The configurability of the encapsulation protocol to be used + makes isdn network interfaces more flexible, but also much more + complex than traditional lan network interfaces. + + +Many Encapsulation protocols used on top of WAN connections will not just +stick a header at the data. They also might need to set up or release +the WAN connection. They also might want to send other data for their +private purpose over the wire. I.e. ppp does a lot of link level +negotiation before the first peace of user data can be transmitted. +Such encapsulation protocols for WAN devices are typically more complex +than encapsulation protocols for lan devices. Thus, network interfaces +code for typical WAN devices also tends to be more more complex. + + +In order to support Linux' x25 PLP implementation on top of +isdn network interfaces I could have introduced yet another branch to +the various case statements inside drivers/isdn/isdn_net.c. +This eventually made isdn_net.c even more complex. In addition, it made +isdn_net.c harder to maintain. Thus, by identifying an abstract +interface between the network interface code and the encapsulation +protocol, complexity could be reduced and maintainability could be +increased. + + +Likewise, a same encapsulation protocol will frequently be needed by +several different interfaces of even different hardware type. I.e. the +synchronous ppp implementaion used by the isdn driver and the +asyncronous ppp implemntation used by the ppp driver have a lot of +similar code in them. By cleanly separating the encapsulation protocol +from the hardware specific interface stuff such code could be shared +better in future. + + +When operating over dial-up-connections (i.e. telephone lines via modem, +non-permanent virtual circuits of wide area networks, ISDN) many +encapsulation protocols will need to control the connection. Therfore, +some basic connection control primitives are supported. The type and +semantics of the connection (i.e the ISO layer where connection service +is provided) is outside our scope and might be different depending on +the encapsulation protocol used. I.e. for a ppp module using our service +on top of a modem connection a connect_request will result in dialing +a (somewhere else configured) remote phone number. For an X25-interface +module (LAPB semantics, as defined in Documentation/networking/x25-iface.txt) +a connect_request will ask for establishing a reliable lapb +datalink connection. + + +The encapsulation protocol currently provides the follwing +service primitives to the network device. + +- create a new encapsulation protocol instance +- delete encapsulation protocol instance and free all its resources +- initialize (open) the encapsulation protocol instance for use. +- deactivate (close) an encapsulation protocol instance. +- process (xmit) data handed down by upper protocol layer +- receive data from lower (hardware) layer +- process connect indication from lower (hardware) layer +- process disconnect indication from lower (hardware) layer + + +The network interface driver accesses those primitives via callbacks +provided by the encapsulation protocol instance within a +struct concap_proto_ops. + +struct concap_proto_ops{ + + /* create a new encapsulation protocol instance of same type */ + struct concap_proto * (*proto_new) (void); + + /* delete encapsulation protocol instance and free all its resources. + cprot may no loger be referenced after calling this */ + void (*proto_del)(struct concap_proto *cprot); + + /* initialize the protocol's data. To be called at interface startup + or when the device driver resets the interface. All services of the + encapsulation protocol may be used after this*/ + int (*restart)(struct concap_proto *cprot, + struct device *ndev, + struct concap_device_ops *dops); + + /* inactivate an encapsulation protocol instance. The encapsulation + protocol may not call any *dops methods after this. */ + int (*close)(struct concap_proto *cprot); + + /* process a frame handed down to us by upper layer */ + int (*encap_and_xmit)(struct concap_proto *cprot, struct sk_buff *skb); + + /* to be called for each data entity received from lower layer*/ + int (*data_ind)(struct concap_proto *cprot, struct sk_buff *skb); + + /* to be called when a connection was set up/down. + Protocols that don't process these primitives might fill in + dummy methods here */ + int (*connect_ind)(struct concap_proto *cprot); + int (*disconn_ind)(struct concap_proto *cprot); +}; + + +The data structures are defined in the header file include/linux/concap.h. + + +A Network interface using encapsulation protocols must also provide +some service primitives to the encapsulation protocol: + +- request data beeing submitted by lower layer (device hardware) +- request a connection beeing set up by lower layer +- request a connection beeing released by lower layer + +The encapsulations protocol accesses those primitives via callbacks +provided by the network interface within a struct concap_device_ops. + +struct concap_device_ops{ + + /* to request data is submitted by device*/ + int (*data_req)(struct concap_proto *, struct sk_buff *); + + /* Control methods must be set to NULL by devices which do not + support connection control.*/ + /* to request a connection is set up */ + int (*connect_req)(struct concap_proto *); + + /* to request a connection is released */ + int (*disconn_req)(struct concap_proto *); +}; + +The network interface does not explicitly provide a receive service +because the encapsulation protocol directly calls netif_rx(). + + + + +An encapsulation protocol itsself is actually the +struct concap_proto{ + struct device *net_dev; /* net device using our service */ + struct concap_device_ops *dops; /* callbacks provided by device */ + struct concap_proto_ops *pops; /* callbacks provided by us */ + int flags; + void *proto_data; /* protocol specific private data, to + be accessed via *pops methods only*/ + /* + : + whatever + : + */ +}; + +Most of this is filled in when the device requests the protocol to +be reset (opend). The network interface must provide the net_dev and +dops pointers. Other concap_proto members should be considerd private +data that are only accessed by the pops callback functions. Likewise, +a concap proto should access the network device's private data +only by means of the callbacks referred to by the dops pointer. + + +A possible extended device structure which uses the connection controlling +encapsulation services could look like this: + +struct concap_device{ + struct device net_dev; + struct my_priv /* device->local stuff */ + /* the my_priv struct might contain a + struct concap_device_ops *dops; + to provide the device specific callbacks + */ + struct concap_proto *cprot; /* callbacks provided by protocol */ +}; + + + +Misc Thoughts +============= + +The concept of the concap proto might help to reuse protocol code and +reduce the complexity of certain network interface implementations. +The trade off is that it introduces yet another procedure call layer +when processing the protocol. This has of course some impact on +performace. However, typically the concap interface will be used by +devices attached to slow lines (like telephone, isdn, leased synchronous +lines). For such slow lines, the overhead is probably neglectable. +This might no longer hold for certain high speed WAN links (like +ATM). + + +If general linux network interfaces explicitly supported concap +protocols (i.e. by a member struct concap_proto* in struct device) +then the interface of the service function could be changed +by passing a pointer of type (struct device*) instead of +type (struct concap_proto*). Doing so would make many of the service +functions compatible to network device support fuctions. i.e. + +i.e. instead of the concap protocol's service function + + int (*encap_and_xmit)(struct concap_proto *cprot, struct sk_buff *skb); + +we could have + + int (*encap_and_xmit)(struct device *ndev, struct sk_buff *skb); + +As this is compatible to the dev->hard_start_xmit() method, the device +driver could directly register the concap protocol's encap_and_xmit() +fuction as its hard_start_xmit() method. This would eliminate one +procedure call layer. + + +The device's data request function could also be defined as + + int (*data_req)(struct device *ndev, struct sk_buff *skb); + +This might even allow for some protocol stacking. And the network +interface might even register the same data_req() function directly +as its hard_start_xmit() method when a zero layer encapsulation +protocol is configured. Thus, eliminating the performace penalty +of the concap interface when a trivial concap protocol is used. +Nevertheless, the device remains able to support encapsulation +protocol configuration. diff --git a/Documentation/isdn/README.x25 b/Documentation/isdn/README.x25 new file mode 100644 index 000000000000..3737bc97f62e --- /dev/null +++ b/Documentation/isdn/README.x25 @@ -0,0 +1,217 @@ + +X25 support within isdn4linux + + +This is experimental code and should be used with linux version 2.1.72. +or later. Use it completely on your own risk. + + +As new versions appear, the stuff described here might suddenly change +or become invalid without notice. + +Keep in mind: + +You are using an experimental kernel (2.1.x series) with an experimental +x25 protocol implementation and experimental x25-on-top-of-isdn extensions. +Thus, be prepared to face problems related therefrom. + +- If you connect to an x25 neighbour not operated by yourself, ASK the + other side first. Be prepared that bugs in the protocol implementation + might result in problems (even crashing the peer, however such ugly events + should only happen if your peer's protocol implementation has serious bugs). + +- This implementation has never wiped out my whole hard disk yet. But as + this is experimental code, don't blame me if that happened to you. Take + appropriate actions (such as backing up important data) before + trying this code. + +- Monitor your isdn connections while using this software. This should + prevent you from undesired phone bills in case of driver problems. + + + + +How to configure the kernel + + +The ITU-T (former CCITT) X.25 network protocol layer has been implemented +in the Linux source tree since version 2.1.16. The isdn subsystem might be +useful to run X.25 on top of ISDN. If you want to try it, select + + "CCITT X.25 Packet Layer" + +from the networking options as well as + + "ISDN Support" and "X.25 PLP on Top of ISDN" + +from the ISDN subsystem options when you configure your kernel for +compilation. You currently also need to enable +"Prompt for development and/or incomplete code/drivers" from the +"Code maturity level options" menu. For the x25trace utility to work +you also need to enable "Packet socket" (I recommend to choose "y", +not "m" for testing) from the networking options. + + +For testing you should also select the isdnloop driver from the +isdn subsystem's configuration menu. + + + +What's it for? How to use it? + + +X25 on top of isdn might be useful with two different scenarios: + +- You might want to access a public X.25 data network from your Linux box. + You can use i4l if you were physically connected to the X.25 switch + by an ISDN line (leased line as well as dial up connection should work, + but connecting to x.25 network switches is currently untested. Testing + needs to be done by somebody with access to such a switch.) + +- Or you might want to operate certain ISDN teleservices on + your linux box. A lot of those teleservices run on top of the ISO-8208 + network layer protocol. ISO-8208 is essentially the same as ITU-T X.25. + + Popular candidates of such teleservices are EUROFILE transfer or any + teleservice applying ITU-T recommendation T.90 (i.e., AFAIK, G4 Fax). + +To use the X.25 protocol on top of isdn, just create an isdn network +interface as usual, configure your own and/or peer's ISDN numbers, +and choose x25iface encapsulation by + + isdnctrl encap x25iface. + +Once encap is set like this, the device can be used by the x25 packet layer. + +All the stuff needed for x25 is implemented inside the isdn link +level (mainly isdn_net.c and some new source files). Thus, it should +work with every existing HL driver. I was able to successfully open x25 +connections on top of the isdnloop driver and the hisax driver. +"x25iface"-encapsulation bypasses demand dialing. Dialing will be +initiated when the upper (x25 packet) layer requests the lapb datalink to +be established. But hangup timeout is still active. The connection +will not automatically be re-established by the isdn_net module +itself when new data arrives after the hangup timeout. But +the x25 network code will re-establish the datalink connection +(resulting in re-dialing and an x25 protocol reset) when new data is +to be transmitted. (This currently does not work properly with the +isdnloop driver, see "known problems" below) + + +In order to set up a conforming protocol stack you also need to +specify the proper l2_prot parameter: + +To operate in ISO-8208 X.25 DTE-DTE mode, use + + isdnctrl l2_prot x75i + +To access an X.25 network switch via isdn (your linux box is the DTE), use + + isdnctrl l2_prot x25dte + +To mimic an X.25 network switch (DCE side of the connection), use + + isdnctrl l2_prot x25dce + +However, x25dte or x25dce is currently not supported by any real HL +level driver. The main difference between x75 and x25dte/dce is that +x25d[tc]e uses fixed lap_b addresses. With x75i, the side which +initiates the isdn connection uses the DTE's lap_b address while the +called side used the DCE's lap_b address. Thus, l2_prot x75i will +probably work if you access a public x25 network as long as the +corresponding isdn connection is set up by you. However, I've never +tested this. + + + +How to use the test installation? + + +To test x25 on top of isdn, you need to get + +- a patched version of the "isdnctrl" program that supports setting the new + x25 specific parameters. + +- the x25-utils-2.1.x package from ftp.pspt.fi/pub/ham/linux/ax25 + or any mirror site (i.e. ftp://ftp.gwdg.de/pub/linux/misc/ax25/). + +- a kernel patch that enhances isdn4linux to provide x25 network + interface support. (This file is part of that kernel patch). + +- an application that uses linux AF_X25 sockets program. + +Before compiling the user level utilities make sure that the compiler/ +preprocessor will fetch the proper (patched) kernel header files. Either make +/usr/include/linux a symbolic link pointing to your developer kernel's +include/linux directory or set the appropriate compiler flags. + +It is recommended that all isdn drivers and the x25 PLP protocol +are compiled as loadable modules. Like this, you can recover +from certain errors by simply unloading and reloading the modules. + +When all drivers and interfaces are loaded and configured you need to +ifconfig the network interfaces up and add x25-routes to them. Use +the usual ifconfig tool. + +ifconfig up + +But a special x25route tool (distributed with the x25-util package) +is needed to set up x25 routes. I.e. + +x25route add 01 + +will cause all x.25 connections to the destination x.25-address +"01" beeing routed to your created isdn network interface. + + +There are currently no real x25 applications available. However, for +tests, the x25-utils package contains a modified version of telnet +and telnetd that uses x25 sockets instead of tcp/ip sockets. Use +this for your first tests. Furthermore, there is an x25.echod and a client +named "eftp" (which contains some experimental code to download files +from a remote eft server using the EUROfile transfer protocol). +It available at ftp://ftp.hamburg.pop.de/pub/LOCAL/linux/i4l-eft/eftp4linux-* + +The x25-utility package also contains an x25trace tool that can be +used to monitor x25 packets received by the network interfaces. +The /proc/net/x25* files also contain useful information. + +The eftp4linux test release also contains an "ix25test" script that can +be used for testing x25 on top of isdn4linux. Edit +this script according to your local needs and then call it as + +ix25test start + +This will set up a sample configuration using the isdnloop and hisax +driver and create some isdn network interfaces. +It is recommended that all other isdn drivers and the +x25 module is unloaded before calling this script. + + + +Known problems and deficiencies: + +The isdnloop HL driver apparently has problems to re-establish a +connection that has been hang up from the outgoing device. You have to +unload the isdnloop driver after the faked isdn-connection is closed +and insmod it again. With the Hisax driver, this problem is not present. + +Sometimes the x25 module cannot be unloaded (decrementation of its use +count seems to get lost occasionally). + +Using the x25 based telnet and telnetd programm to establish connection +from your own to your own computer repeatedly sometimes totally locked +up my system. However, this kernel patch also modifies +net/x25/af_x25.c to include a workaround. With this workaround +enabled, my system is stable. (If you want to disable the +workaround, just undefine ISDN_X25_FIXES in af_x25.c). + +The latter problem could be reproduced by using hisax as well as the +isdnloop driver. It seems that it is not caused by the isdn code. +Somehow, the inode of a socket is freed while a process still refers +the socket's wait queue. This causes problems when the process tries to +remove itsself from the wait queue (refered by the dangling +sock->sleep pointer) before returning from a select() system call. + +- Henner + diff --git a/Documentation/serial-console.txt b/Documentation/serial-console.txt index 36a3e9c811a5..2894b8008110 100644 --- a/Documentation/serial-console.txt +++ b/Documentation/serial-console.txt @@ -19,10 +19,10 @@ The format of this option is: 9600n8. The maximum baudrate is 115200. You can specify multiple console= options on the kernel command line. -Output will appear on all of them. The first device will be used when +Output will appear on all of them. The last device will be used when you open /dev/console. So, for example: - console=tty0 console=ttyS1,9600 + console=ttyS1,9600 console=tty0 defines that opening /dev/console will get you the current foreground virtual console, and kernel messages will appear on both the VGA @@ -91,4 +91,4 @@ Replace the sample values as needed. for porting the patches from 2.1.4x to 2.1.6x for taking care of the integration of these patches into m68k, ppc and alpha. -Miquel van Smoorenburg , 03-Dec-1997 +Miquel van Smoorenburg , 21-Mar-1998 diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index edec2e2500fe..a4ee0abb2a34 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -543,6 +543,7 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_pread) /* 180 */ .long SYMBOL_NAME(sys_pwrite) .long SYMBOL_NAME(sys_chown) + .long SYMBOL_NAME(sys_getcwd) .rept NR_syscalls-182 .long SYMBOL_NAME(sys_ni_syscall) diff --git a/arch/i386/kernel/ioport.c b/arch/i386/kernel/ioport.c index 44fd26530d67..19587312a0da 100644 --- a/arch/i386/kernel/ioport.c +++ b/arch/i386/kernel/ioport.c @@ -76,8 +76,6 @@ asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) return 0; } -unsigned int *stack; - /* * sys_iopl has to be used when you want to access the IO ports * beyond the 0x3ff range: to get the full 65536 ports bitmapped diff --git a/drivers/char/apm_bios.c b/drivers/char/apm_bios.c index 29aeaa48bc02..f4fcf4f7bb92 100644 --- a/drivers/char/apm_bios.c +++ b/drivers/char/apm_bios.c @@ -1159,8 +1159,10 @@ __initfunc(void apm_bios_init(void)) static struct proc_dir_entry *ent; #ifdef __SMP__ - printk(KERN_NOTICE "APM disabled: APM is not SMP safe.\n"); - return; + if (smp_num_cpus > 1) { + printk(KERN_NOTICE "APM disabled: APM is not SMP safe.\n"); + return; + } #endif if (apm_bios_info.version == 0) { printk(KERN_INFO "APM BIOS not found.\n"); diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 8c024bba58da..2999b70ddfb3 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -163,14 +163,22 @@ static inline int lp_char(char lpchar, int minor) unsigned long count = 0; struct lp_stats *stats; - do { - status = r_str (minor); - count++; + for (;;) { lp_yield(minor); - } while (!LP_READY(minor, status) && count < LP_CHAR(minor)); - - if (count == LP_CHAR(minor)) - return 0; + status = r_str (minor); + if (++count == LP_CHAR(minor)) + return 0; + if (LP_POLLING(minor)) + { + if (LP_READY(minor, status)) + break; + } else { + if (!LP_READY(minor, status)) + return 0; + else + break; + } + } w_dtr(minor, lpchar); stats = &LP_STAT(minor); diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 1ca4412af999..4216d14bd8bd 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -385,9 +385,6 @@ static loff_t memory_lseek(struct file * file, loff_t offset, int orig) default: return -EINVAL; } - if (file->f_pos < 0) - return 0; - return file->f_pos; } #define mmap_kmem mmap_mem diff --git a/drivers/char/serial.c b/drivers/char/serial.c index ce45a97aa561..f5d5895a2002 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -3212,7 +3212,7 @@ __initfunc(int rs_init(void)) * The interrupt of the serial console port * can't be shared. */ - if (sercons.flags & CON_FIRST) { + if (sercons.flags & CON_CONSDEV) { for(i = 0; i < NR_PORTS; i++) if (i != sercons.index && rs_table[i].irq == rs_table[sercons.index].irq) diff --git a/drivers/isdn/Config.in b/drivers/isdn/Config.in index 758e30d2bcae..3787c735fbe1 100644 --- a/drivers/isdn/Config.in +++ b/drivers/isdn/Config.in @@ -9,24 +9,44 @@ if [ "$CONFIG_INET" != "n" ]; then fi fi bool 'Support audio via ISDN' CONFIG_ISDN_AUDIO +if [ "$CONFIG_X25" != "n" ]; then + bool 'X.25 PLP on top of ISDN (EXPERIMENTAL)' CONFIG_ISDN_X25 +fi dep_tristate 'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN +dep_tristate 'isdnloop support' CONFIG_ISDN_DRV_LOOP $CONFIG_ISDN dep_tristate 'PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN dep_tristate 'HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then + bool 'HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO + if [ "$CONFIG_HISAX_EURO" != "n" ]; then + bool 'Support for german tarifinfo' CONFIG_DE_AOC + bool 'Support for australian Microlink service (not for std. EURO)' CONFIG_HISAX_ML + fi + bool 'HiSax Support for german 1TR6' CONFIG_HISAX_1TR6 bool 'HiSax Support for Teles 16.0/8.0' CONFIG_HISAX_16_0 bool 'HiSax Support for Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3 + bool 'HiSax Support for Teles 16.3c' CONFIG_HISAX_TELES3C bool 'HiSax Support for 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 Elsa cards' CONFIG_HISAX_ELSA bool 'HiSax Support for ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2 - bool 'HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO - bool 'HiSax Support for US/NI-1' CONFIG_HISAX_NI1 - bool 'HiSax Support for german 1TR6' CONFIG_HISAX_1TR6 + bool 'HiSax Support for Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA + bool 'HiSax Support for ASUSCOM cards' CONFIG_HISAX_ASUSCOM + bool 'HiSax Support for TELEINT cards' CONFIG_HISAX_TELEINT + bool 'HiSax Support for Sedlbauer speed card/win/star' CONFIG_HISAX_SEDLBAUER + bool 'HiSax Support for USR Sportster internal TA' CONFIG_HISAX_SPORTSTER + bool 'HiSax Support for MIC card' CONFIG_HISAX_MIC + bool 'HiSax Support for NETjet card' CONFIG_HISAX_NETJET + bool 'HiSax Support for Niccy PnP/PCI card' CONFIG_HISAX_NICCY + if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then + bool 'HiSax Support for Am7930' CONFIG_HISAX_AMD7930 + fi fi if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then dep_tristate 'Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN + dep_tristate 'IBM Active 2000 support (EXPERIMENTAL)' CONFIG_ISDN_DRV_ACT2000 $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 c5f508217d88..35d56d14222d 100644 --- a/drivers/isdn/Makefile +++ b/drivers/isdn/Makefile @@ -1,6 +1,6 @@ SUB_DIRS := MOD_SUB_DIRS := -ALL_SUB_DIRS := icn pcbit hisax avmb1 +ALL_SUB_DIRS := icn pcbit hisax avmb1 act2000 L_OBJS := LX_OBJS := @@ -13,11 +13,15 @@ O_TARGET := ifeq ($(CONFIG_ISDN),y) L_TARGET := isdn.a - L_OBJS += isdn_common.o isdn_net.o isdn_tty.o isdn_cards.o - LX_OBJS += isdn_syms.o + L_OBJS += isdn_net.o isdn_tty.o isdn_cards.o isdn_v110.o + LX_OBJS += isdn_common.o ifdef CONFIG_ISDN_PPP L_OBJS += isdn_ppp.o endif + ifdef CONFIG_ISDN_X25 + L_OBJS += isdn_x25iface.o + L_OBJS += isdn_concap.o + endif ifdef CONFIG_ISDN_AUDIO L_OBJS += isdn_audio.o endif @@ -25,11 +29,15 @@ else ifeq ($(CONFIG_ISDN),m) M_OBJS += isdn.o O_TARGET += isdn.o - O_OBJS += isdn_common.o isdn_net.o isdn_tty.o - OX_OBJS += isdn_syms.o + O_OBJS += isdn_net.o isdn_tty.o isdn_v110.o + OX_OBJS += isdn_common.o ifdef CONFIG_ISDN_PPP O_OBJS += isdn_ppp.o endif + ifdef CONFIG_ISDN_X25 + O_OBJS += isdn_x25iface.o + O_OBJS += isdn_concap.o + endif ifdef CONFIG_ISDN_AUDIO O_OBJS += isdn_audio.o endif @@ -96,5 +104,15 @@ else endif endif +ifeq ($(CONFIG_ISDN_DRV_ACT2000),y) + L_OBJS += act2000/act2000.o + SUB_DIRS += act2000 + MOD_SUB_DIRS += act2000 +else + ifeq ($(CONFIG_ISDN_DRV_ACT2000),m) + MOD_SUB_DIRS += act2000 + endif +endif + include $(TOPDIR)/Rules.make diff --git a/drivers/isdn/act2000/Makefile b/drivers/isdn/act2000/Makefile new file mode 100644 index 000000000000..0e3c4a7e18f7 --- /dev/null +++ b/drivers/isdn/act2000/Makefile @@ -0,0 +1,15 @@ +L_OBJS := +M_OBJS := +O_OBJS := module.o capi.o act2000_isa.o + +O_TARGET := +ifeq ($(CONFIG_ISDN_DRV_ACT2000),y) + O_TARGET += act2000.o +else + ifeq ($(CONFIG_ISDN_DRV_ACT2000),m) + O_TARGET += act2000.o + M_OBJS = act2000.o + endif +endif + +include $(TOPDIR)/Rules.make diff --git a/drivers/isdn/act2000/act2000.h b/drivers/isdn/act2000/act2000.h new file mode 100644 index 000000000000..534cc42f96c1 --- /dev/null +++ b/drivers/isdn/act2000/act2000.h @@ -0,0 +1,239 @@ +/* $Id: act2000.h,v 1.5 1997/10/09 22:22:59 fritz Exp $ + * + * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. + * + * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de) + * Thanks to Friedemann Baitinger and IBM Germany + * + * 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: act2000.h,v $ + * Revision 1.5 1997/10/09 22:22:59 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * + * Revision 1.4 1997/09/25 17:25:37 fritz + * Support for adding cards at runtime. + * Support for new Firmware. + * + * Revision 1.3 1997/09/24 23:11:43 fritz + * Optimized IRQ load and polling-mode. + * + * Revision 1.2 1997/09/24 19:44:12 fritz + * Added MSN mapping support, some cleanup. + * + * Revision 1.1 1997/09/23 18:00:05 fritz + * New driver for IBM Active 2000. + * + */ + +#ifndef act2000_h +#define act2000_h + +#ifdef __KERNEL__ +/* Kernel includes */ + +#include +#include +#endif + +#define ACT2000_IOCTL_SETPORT 1 +#define ACT2000_IOCTL_GETPORT 2 +#define ACT2000_IOCTL_SETIRQ 3 +#define ACT2000_IOCTL_GETIRQ 4 +#define ACT2000_IOCTL_SETBUS 5 +#define ACT2000_IOCTL_GETBUS 6 +#define ACT2000_IOCTL_SETPROTO 7 +#define ACT2000_IOCTL_GETPROTO 8 +#define ACT2000_IOCTL_SETMSN 9 +#define ACT2000_IOCTL_GETMSN 10 +#define ACT2000_IOCTL_LOADBOOT 11 +#define ACT2000_IOCTL_ADDCARD 12 + +#define ACT2000_IOCTL_TEST 98 +#define ACT2000_IOCTL_DEBUGVAR 99 + +#define ACT2000_BUS_ISA 1 +#define ACT2000_BUS_MCA 2 +#define ACT2000_BUS_PCMCIA 3 + +/* Struct for adding new cards */ +typedef struct act2000_cdef { + int bus; + int port; + int irq; + char id[10]; +} act2000_cdef; + +/* Struct for downloading firmware */ +typedef struct act2000_ddef { + int length; /* Length of code */ + char *buffer; /* Ptr. to code */ +} act2000_ddef; + +typedef struct act2000_fwid { + char isdn[4]; + char revlen[2]; + char revision[504]; +} act2000_fwid; + +#if defined(__KERNEL__) || defined(__DEBUGVAR__) + +#ifdef __KERNEL__ +/* Kernel includes */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* __KERNEL__ */ + +#define ACT2000_PORTLEN 8 + +#define ACT2000_FLAGS_RUNNING 1 /* Cards driver activated */ +#define ACT2000_FLAGS_PVALID 2 /* Cards port is valid */ +#define ACT2000_FLAGS_IVALID 4 /* Cards irq is valid */ +#define ACT2000_FLAGS_LOADED 8 /* Firmware loaded */ + +#define ACT2000_BCH 2 /* # of channels per card */ + +/* D-Channel states */ +#define ACT2000_STATE_NULL 0 +#define ACT2000_STATE_ICALL 1 +#define ACT2000_STATE_OCALL 2 +#define ACT2000_STATE_IWAIT 3 +#define ACT2000_STATE_OWAIT 4 +#define ACT2000_STATE_IBWAIT 5 +#define ACT2000_STATE_OBWAIT 6 +#define ACT2000_STATE_BWAIT 7 +#define ACT2000_STATE_BHWAIT 8 +#define ACT2000_STATE_BHWAIT2 9 +#define ACT2000_STATE_DHWAIT 10 +#define ACT2000_STATE_DHWAIT2 11 +#define ACT2000_STATE_BSETUP 12 +#define ACT2000_STATE_ACTIVE 13 + +#define ACT2000_MAX_QUEUED 8000 /* 2 * maxbuff */ + +#define ACT2000_LOCK_TX 0 +#define ACT2000_LOCK_RX 1 + +typedef struct act2000_chan { + unsigned short callref; /* Call Reference */ + unsigned short fsm_state; /* Current D-Channel state */ + unsigned short eazmask; /* EAZ-Mask for this Channel */ + short queued; /* User-Data Bytes in TX queue */ + unsigned short plci; + unsigned short ncci; + unsigned char l2prot; /* Layer 2 protocol */ + unsigned char l3prot; /* Layer 3 protocol */ +} act2000_chan; + +typedef struct msn_entry { + char eaz; + char msn[16]; + struct msn_entry * next; +} msn_entry; + +typedef struct irq_data_isa { + __u8 *rcvptr; + __u16 rcvidx; + __u16 rcvlen; + struct sk_buff *rcvskb; + __u8 rcvignore; + __u8 rcvhdr[8]; +} irq_data_isa; + +typedef union irq_data { + irq_data_isa isa; +} irq_data; + +/* + * Per card driver data + */ +typedef struct act2000_card { + unsigned short port; /* Base-port-address */ + unsigned short irq; /* Interrupt */ + u_char ptype; /* Protocol type (1TR6 or Euro) */ + u_char bus; /* Cardtype (ISA, MCA, PCMCIA) */ + struct act2000_card *next; /* Pointer to next device struct */ + int myid; /* Driver-Nr. assigned by linklevel */ + unsigned long flags; /* Statusflags */ + unsigned long ilock; /* Semaphores for IRQ-Routines */ + struct sk_buff_head rcvq; /* Receive-Message queue */ + struct sk_buff_head sndq; /* Send-Message queue */ + struct sk_buff_head ackq; /* Data-Ack-Message queue */ + u_char *ack_msg; /* Ptr to User Data in User skb */ + __u16 need_b3ack; /* Flag: Need ACK for current skb */ + struct sk_buff *sbuf; /* skb which is currently sent */ + struct timer_list ptimer; /* Poll timer */ + struct tq_struct snd_tq; /* Task struct for xmit bh */ + struct tq_struct rcv_tq; /* Task struct for rcv bh */ + struct tq_struct poll_tq; /* Task struct for polled rcv bh */ + msn_entry *msn_list; + unsigned short msgnum; /* Message number fur sending */ + act2000_chan bch[ACT2000_BCH]; /* B-Channel status/control */ + char status_buf[256]; /* Buffer for status messages */ + char *status_buf_read; + char *status_buf_write; + char *status_buf_end; + irq_data idat; /* Data used for IRQ handler */ + isdn_if interface; /* Interface to upper layer */ + char regname[35]; /* Name used for request_region */ +} act2000_card; + +extern act2000_card *cards; + +extern __inline__ void act2000_schedule_tx(act2000_card *card) +{ + queue_task(&card->snd_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +extern __inline__ void act2000_schedule_rx(act2000_card *card) +{ + queue_task(&card->rcv_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +extern __inline__ void act2000_schedule_poll(act2000_card *card) +{ + queue_task(&card->poll_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +extern char *act2000_find_eaz(act2000_card *, char); + +#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */ +#endif /* act2000_h */ diff --git a/drivers/isdn/act2000/act2000_isa.c b/drivers/isdn/act2000/act2000_isa.c new file mode 100644 index 000000000000..078760a0cc5e --- /dev/null +++ b/drivers/isdn/act2000/act2000_isa.c @@ -0,0 +1,525 @@ +/* $Id: act2000_isa.c,v 1.5 1998/02/12 23:06:47 keil Exp $ + * + * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version). + * + * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de) + * Thanks to Friedemann Baitinger and IBM Germany + * + * 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: act2000_isa.c,v $ + * Revision 1.5 1998/02/12 23:06:47 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.4 1997/10/09 22:23:00 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * + * Revision 1.3 1997/09/25 17:25:38 fritz + * Support for adding cards at runtime. + * Support for new Firmware. + * + * Revision 1.2 1997/09/24 23:11:44 fritz + * Optimized IRQ load and polling-mode. + * + * Revision 1.1 1997/09/23 18:00:05 fritz + * New driver for IBM Active 2000. + * + */ + +#define __NO_VERSION__ +#include "act2000.h" +#include "act2000_isa.h" +#include "capi.h" + +static act2000_card *irq2card_map[16] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static int isa_irqs[] = +{ + 3, 5, 7, 10, 11, 12, 15 +}; +#define ISA_NRIRQS (sizeof(isa_irqs)/sizeof(int)) + +static void +isa_delay(long t) +{ + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + t; + schedule(); + sti(); +} + +/* + * Reset Controller, then try to read the Card's signature. + + Return: + * 1 = Signature found. + * 0 = Signature not found. + */ +static int +isa_reset(unsigned short portbase) +{ + unsigned char reg; + int i; + int found; + int serial = 0; + + found = 0; + if ((reg = inb(portbase + ISA_COR)) != 0xff) { + outb(reg | ISA_COR_RESET, portbase + ISA_COR); + udelay(10000); + outb(reg, portbase + ISA_COR); + udelay(10000); + + for (i = 0; i < 16; i++) { + if (inb(portbase + ISA_ISR) & ISA_ISR_SERIAL) + serial |= 0x10000; + serial >>= 1; + } + if (serial == ISA_SER_ID) + found++; + } + return found; +} + +int +isa_detect(unsigned short portbase) +{ + int ret = 0; + unsigned long flags; + + save_flags(flags); + cli(); + if (!check_region(portbase, ISA_REGION)) + ret = isa_reset(portbase); + restore_flags(flags); + return ret; +} + +static void +isa_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + act2000_card *card = irq2card_map[irq]; + u_char istatus; + + if (!card) { + printk(KERN_WARNING + "act2000: Spurious interrupt!\n"); + return; + } + istatus = (inb(ISA_PORT_ISR) & 0x07); + if (istatus & ISA_ISR_OUT) { + /* RX fifo has data */ + istatus &= ISA_ISR_OUT_MASK; + outb(0, ISA_PORT_SIS); + isa_receive(card); + outb(ISA_SIS_INT, ISA_PORT_SIS); + } + if (istatus & ISA_ISR_ERR) { + /* Error Interrupt */ + istatus &= ISA_ISR_ERR_MASK; + printk(KERN_WARNING "act2000: errIRQ\n"); + } + if (istatus) + printk(KERN_DEBUG "act2000: ?IRQ %d %02x\n", irq, istatus); +} + +static void +isa_select_irq(act2000_card * card) +{ + unsigned char reg; + + reg = (inb(ISA_PORT_COR) & ~ISA_COR_IRQOFF) | ISA_COR_PERR; + switch (card->irq) { + case 3: + reg = ISA_COR_IRQ03; + break; + case 5: + reg = ISA_COR_IRQ05; + break; + case 7: + reg = ISA_COR_IRQ07; + break; + case 10: + reg = ISA_COR_IRQ10; + break; + case 11: + reg = ISA_COR_IRQ11; + break; + case 12: + reg = ISA_COR_IRQ12; + break; + case 15: + reg = ISA_COR_IRQ15; + break; + } + outb(reg, ISA_PORT_COR); +} + +static void +isa_enable_irq(act2000_card * card) +{ + isa_select_irq(card); + /* Enable READ irq */ + outb(ISA_SIS_INT, ISA_PORT_SIS); +} + +/* + * Install interrupt handler, enable irq on card. + * If irq is -1, choose next free irq, else irq is given explicitely. + */ +int +isa_config_irq(act2000_card * card, short irq) +{ + int i; + unsigned long flags; + + if (card->flags & ACT2000_FLAGS_IVALID) { + free_irq(card->irq, NULL); + irq2card_map[card->irq] = NULL; + } + card->flags &= ~ACT2000_FLAGS_IVALID; + outb(ISA_COR_IRQOFF, ISA_PORT_COR); + if (!irq) + return 0; + save_flags(flags); + cli(); + if (irq == -1) { + /* Auto select */ + for (i = 0; i < ISA_NRIRQS; i++) { + if (!request_irq(isa_irqs[i], &isa_interrupt, 0, card->regname, NULL)) { + card->irq = isa_irqs[i]; + irq2card_map[card->irq] = card; + card->flags |= ACT2000_FLAGS_IVALID; + break; + } + } + } else { + /* Fixed irq */ + if (!request_irq(irq, &isa_interrupt, 0, card->regname, NULL)) { + card->irq = irq; + irq2card_map[card->irq] = card; + card->flags |= ACT2000_FLAGS_IVALID; + } + } + restore_flags(flags); + if (!card->flags & ACT2000_FLAGS_IVALID) { + printk(KERN_WARNING + "act2000: Could not request irq\n"); + return -EBUSY; + } else { + isa_select_irq(card); + /* Disable READ and WRITE irq */ + outb(0, ISA_PORT_SIS); + outb(0, ISA_PORT_SOS); + } + return 0; +} + +int +isa_config_port(act2000_card * card, unsigned short portbase) +{ + if (card->flags & ACT2000_FLAGS_PVALID) { + release_region(card->port, ISA_REGION); + card->flags &= ~ACT2000_FLAGS_PVALID; + } + if (!check_region(portbase, ISA_REGION)) { + request_region(portbase, ACT2000_PORTLEN, card->regname); + card->port = portbase; + card->flags |= ACT2000_FLAGS_PVALID; + return 0; + } + return -EBUSY; +} + +/* + * Release ressources, used by an adaptor. + */ +void +isa_release(act2000_card * card) +{ + unsigned long flags; + + save_flags(flags); + cli(); + if (card->flags & ACT2000_FLAGS_IVALID) { + free_irq(card->irq, NULL); + irq2card_map[card->irq] = NULL; + } + card->flags &= ~ACT2000_FLAGS_IVALID; + if (card->flags & ACT2000_FLAGS_PVALID) + release_region(card->port, ISA_REGION); + card->flags &= ~ACT2000_FLAGS_PVALID; + restore_flags(flags); +} + +static int +isa_writeb(act2000_card * card, u_char data) +{ + u_char timeout = 40; + + while (timeout) { + if (inb(ISA_PORT_SOS) & ISA_SOS_READY) { + outb(data, ISA_PORT_SDO); + return 0; + } else { + timeout--; + udelay(10); + } + } + return 1; +} + +static int +isa_readb(act2000_card * card, u_char * data) +{ + u_char timeout = 40; + + while (timeout) { + if (inb(ISA_PORT_SIS) & ISA_SIS_READY) { + *data = inb(ISA_PORT_SDI); + return 0; + } else { + timeout--; + udelay(10); + } + } + return 1; +} + +void +isa_receive(act2000_card *card) +{ + u_char c; + + if (test_and_set_bit(ACT2000_LOCK_RX, (void *) &card->ilock) != 0) + return; + while (!isa_readb(card, &c)) { + if (card->idat.isa.rcvidx < 8) { + card->idat.isa.rcvhdr[card->idat.isa.rcvidx++] = c; + if (card->idat.isa.rcvidx == 8) { + int valid = actcapi_chkhdr(card, (actcapi_msghdr *)&card->idat.isa.rcvhdr); + + if (valid) { + card->idat.isa.rcvlen = ((actcapi_msghdr *)&card->idat.isa.rcvhdr)->len; + card->idat.isa.rcvskb = dev_alloc_skb(card->idat.isa.rcvlen); + if (card->idat.isa.rcvskb == NULL) { + card->idat.isa.rcvignore = 1; + printk(KERN_WARNING + "isa_receive: no memory\n"); + test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock); + return; + } + memcpy(skb_put(card->idat.isa.rcvskb, 8), card->idat.isa.rcvhdr, 8); + card->idat.isa.rcvptr = skb_put(card->idat.isa.rcvskb, card->idat.isa.rcvlen - 8); + } else { + card->idat.isa.rcvidx = 0; + printk(KERN_WARNING + "isa_receive: Invalid CAPI msg\n"); + { + int i; __u8 *p; __u8 *c; __u8 tmp[30]; + for (i = 0, p = (__u8 *)&card->idat.isa.rcvhdr, c = tmp; i < 8; i++) + c += sprintf(c, "%02x ", *(p++)); + printk(KERN_WARNING "isa_receive: %s\n", tmp); + } + } + } + } else { + if (!card->idat.isa.rcvignore) + *card->idat.isa.rcvptr++ = c; + if (++card->idat.isa.rcvidx >= card->idat.isa.rcvlen) { + if (!card->idat.isa.rcvignore) { + skb_queue_tail(&card->rcvq, card->idat.isa.rcvskb); + act2000_schedule_rx(card); + } + card->idat.isa.rcvidx = 0; + card->idat.isa.rcvlen = 8; + card->idat.isa.rcvignore = 0; + card->idat.isa.rcvskb = NULL; + card->idat.isa.rcvptr = card->idat.isa.rcvhdr; + } + } + } + if (!(card->flags & ACT2000_FLAGS_IVALID)) { + /* In polling mode, schedule myself */ + if ((card->idat.isa.rcvidx) && + (card->idat.isa.rcvignore || + (card->idat.isa.rcvidx < card->idat.isa.rcvlen))) + act2000_schedule_poll(card); + } + test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock); +} + +void +isa_send(act2000_card * card) +{ + unsigned long flags; + struct sk_buff *skb; + actcapi_msg *msg; + int l; + + if (test_and_set_bit(ACT2000_LOCK_TX, (void *) &card->ilock) != 0) + return; + while (1) { + save_flags(flags); + cli(); + if (!(card->sbuf)) { + if ((card->sbuf = skb_dequeue(&card->sndq))) { + card->ack_msg = card->sbuf->data; + msg = (actcapi_msg *)card->sbuf->data; + if ((msg->hdr.cmd.cmd == 0x86) && + (msg->hdr.cmd.subcmd == 0) ) { + /* Save flags in message */ + card->need_b3ack = msg->msg.data_b3_req.flags; + msg->msg.data_b3_req.flags = 0; + } + } + } + restore_flags(flags); + if (!(card->sbuf)) { + /* No more data to send */ + test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock); + return; + } + skb = card->sbuf; + l = 0; + while (skb->len) { + if (isa_writeb(card, *(skb->data))) { + /* Fifo is full, but more data to send */ +#if 0 + printk(KERN_DEBUG "isa_send: %d bytes\n", l); +#endif + test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock); + /* Schedule myself */ + act2000_schedule_tx(card); + return; + } + skb_pull(skb, 1); + l++; + } + msg = (actcapi_msg *)card->ack_msg; + if ((msg->hdr.cmd.cmd == 0x86) && + (msg->hdr.cmd.subcmd == 0) ) { + /* + * If it's user data, reset data-ptr + * and put skb into ackq. + */ + skb->data = card->ack_msg; + /* Restore flags in message */ + msg->msg.data_b3_req.flags = card->need_b3ack; + skb_queue_tail(&card->ackq, skb); + } else + dev_kfree_skb(skb); + card->sbuf = NULL; +#if 0 + printk(KERN_DEBUG "isa_send: %d bytes\n", l); +#endif + } +} + +/* + * Get firmware ID, check for 'ISDN' signature. + */ +static int +isa_getid(act2000_card * card) +{ + + act2000_fwid fid; + u_char *p = (u_char *) & fid; + int count = 0; + + while (1) { + if (count > 510) + return -EPROTO; + if (isa_readb(card, p++)) + break; + count++; + } + if (count <= 20) { + printk(KERN_WARNING "act2000: No Firmware-ID!\n"); + return -ETIME; + } + *p = '\0'; + fid.revlen[0] = '\0'; + if (strcmp(fid.isdn, "ISDN")) { + printk(KERN_WARNING "act2000: Wrong Firmware-ID!\n"); + return -EPROTO; + } + if ((p = strchr(fid.revision, '\n'))) + *p = '\0'; + printk(KERN_INFO "act2000: Firmware-ID: %s\n", fid.revision); + if (card->flags & ACT2000_FLAGS_IVALID) { + printk(KERN_DEBUG "Enabling Interrupts ...\n"); + isa_enable_irq(card); + } + return 0; +} + +/* + * Download microcode into card, check Firmware signature. + */ +int +isa_download(act2000_card * card, act2000_ddef * cb) +{ + int length; + int ret; + int l; + int c; + long timeout; + u_char *b; + u_char *p; + u_char *buf; + act2000_ddef cblock; + + if (!isa_reset(card->port)) + return -ENXIO; + isa_delay(HZ / 2); + if ((ret = verify_area(VERIFY_READ, (void *) cb, sizeof(cblock)))) + return ret; + copy_from_user(&cblock, (char *) cb, sizeof(cblock)); + length = cblock.length; + p = cblock.buffer; + if ((ret = verify_area(VERIFY_READ, (void *) p, length))) + return ret; + buf = (u_char *) kmalloc(1024, GFP_KERNEL); + if (!buf) + return -ENOMEM; + timeout = 0; + while (length) { + l = (length > 1024) ? 1024 : length; + c = 0; + b = buf; + copy_from_user(buf, p, l); + while (c < l) { + if (isa_writeb(card, *b++)) { + printk(KERN_WARNING + "act2000: loader timed out" + " len=%d c=%d\n", length, c); + kfree(buf); + return -ETIME; + } + c++; + } + length -= l; + p += l; + } + kfree(buf); + isa_delay(HZ / 2); + return (isa_getid(card)); +} diff --git a/drivers/isdn/act2000/act2000_isa.h b/drivers/isdn/act2000/act2000_isa.h new file mode 100644 index 000000000000..b7c01ee2b7b8 --- /dev/null +++ b/drivers/isdn/act2000/act2000_isa.h @@ -0,0 +1,149 @@ +/* $Id: act2000_isa.h,v 1.1 1997/09/23 18:00:07 fritz Exp $ + * + * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version). + * + * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de) + * Thanks to Friedemann Baitinger and IBM Germany + * + * 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: act2000_isa.h,v $ + * Revision 1.1 1997/09/23 18:00:07 fritz + * New driver for IBM Active 2000. + * + */ + +#ifndef act2000_isa_h +#define act2000_isa_h + +#define ISA_POLL_LOOP 40 /* Try to read-write before give up */ + +typedef enum { + INT_NO_CHANGE = 0, /* Do not change the Mask */ + INT_ON = 1, /* Set to Enable */ + INT_OFF = 2, /* Set to Disable */ +} ISA_INT_T; + +/**************************************************************************/ +/* Configuration Register COR (RW) */ +/**************************************************************************/ +/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ +/* Soft Res| IRQM | IRQ Select | N/A | WAIT |Proc err */ +/**************************************************************************/ +#define ISA_COR 0 /* Offset for ISA config register */ +#define ISA_COR_PERR 0x01 /* Processor Error Enabled */ +#define ISA_COR_WS 0x02 /* Insert Wait State if 1 */ +#define ISA_COR_IRQOFF 0x38 /* No Interrupt */ +#define ISA_COR_IRQ07 0x30 /* IRQ 7 Enable */ +#define ISA_COR_IRQ05 0x28 /* IRQ 5 Enable */ +#define ISA_COR_IRQ03 0x20 /* IRQ 3 Enable */ +#define ISA_COR_IRQ10 0x18 /* IRQ 10 Enable */ +#define ISA_COR_IRQ11 0x10 /* IRQ 11 Enable */ +#define ISA_COR_IRQ12 0x08 /* IRQ 12 Enable */ +#define ISA_COR_IRQ15 0x00 /* IRQ 15 Enable */ +#define ISA_COR_IRQPULSE 0x40 /* 0 = Level 1 = Pulse Interrupt */ +#define ISA_COR_RESET 0x80 /* Soft Reset for Transputer */ + +/**************************************************************************/ +/* Interrupt Source Register ISR (RO) */ +/**************************************************************************/ +/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ +/* N/A | N/A | N/A |Err sig |Ser ID |IN Intr |Out Intr| Error */ +/**************************************************************************/ +#define ISA_ISR 1 /* Offset for Interrupt Register */ +#define ISA_ISR_ERR 0x01 /* Error Interrupt */ +#define ISA_ISR_OUT 0x02 /* Output Interrupt */ +#define ISA_ISR_INP 0x04 /* Input Interrupt */ +#define ISA_ISR_SERIAL 0x08 /* Read out Serial ID after Reset */ +#define ISA_ISR_ERRSIG 0x10 /* Error Signal Input */ +#define ISA_ISR_ERR_MASK 0xfe /* Mask Error Interrupt */ +#define ISA_ISR_OUT_MASK 0xfd /* Mask Output Interrupt */ +#define ISA_ISR_INP_MASK 0xfb /* Mask Input Interrupt */ + +/* Signature delivered after Reset at ISA_ISR_SERIAL (LSB first) */ +#define ISA_SER_ID 0x0201 /* ID for ISA Card */ + +/**************************************************************************/ +/* EEPROM Register EPR (RW) */ +/**************************************************************************/ +/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ +/* N/A | N/A | N/A |ROM Hold| ROM CS |ROM CLK | ROM IN |ROM Out */ +/**************************************************************************/ +#define ISA_EPR 2 /* Offset for this Register */ +#define ISA_EPR_OUT 0x01 /* Rome Register Out (RO) */ +#define ISA_EPR_IN 0x02 /* Rom Register In (WR) */ +#define ISA_EPR_CLK 0x04 /* Rom Clock (WR) */ +#define ISA_EPR_CS 0x08 /* Rom Cip Select (WR) */ +#define ISA_EPR_HOLD 0x10 /* Rom Hold Signal (WR) */ + +/**************************************************************************/ +/* EEPROM enable Register EER (unused) */ +/**************************************************************************/ +#define ISA_EER 3 /* Offset for this Register */ + +/**************************************************************************/ +/* SLC Data Input SDI (RO) */ +/**************************************************************************/ +#define ISA_SDI 4 /* Offset for this Register */ + +/**************************************************************************/ +/* SLC Data Output SDO (WO) */ +/**************************************************************************/ +#define ISA_SDO 5 /* Offset for this Register */ + +/**************************************************************************/ +/* IMS C011 Mode 2 Input Status Register for INMOS CPU SIS (RW) */ +/**************************************************************************/ +/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ +/* N/A | N/A | N/A | N/A | N/A | N/A |Int Ena |Data Pre */ +/**************************************************************************/ +#define ISA_SIS 6 /* Offset for this Register */ +#define ISA_SIS_READY 0x01 /* If 1 : data is available */ +#define ISA_SIS_INT 0x02 /* Enable Interrupt for READ */ + +/**************************************************************************/ +/* IMS C011 Mode 2 Output Status Register from INMOS CPU SOS (RW) */ +/**************************************************************************/ +/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ +/* N/A | N/A | N/A | N/A | N/A | N/A |Int Ena |Out Rdy */ +/**************************************************************************/ +#define ISA_SOS 7 /* Offset for this Register */ +#define ISA_SOS_READY 0x01 /* If 1 : we can write Data */ +#define ISA_SOS_INT 0x02 /* Enable Interrupt for WRITE */ + +#define ISA_REGION 8 /* Number of Registers */ + + +/* Macros for accessing ports */ +#define ISA_PORT_COR (card->port+ISA_COR) +#define ISA_PORT_ISR (card->port+ISA_ISR) +#define ISA_PORT_EPR (card->port+ISA_EPR) +#define ISA_PORT_EER (card->port+ISA_EER) +#define ISA_PORT_SDI (card->port+ISA_SDI) +#define ISA_PORT_SDO (card->port+ISA_SDO) +#define ISA_PORT_SIS (card->port+ISA_SIS) +#define ISA_PORT_SOS (card->port+ISA_SOS) + +/* Prototypes */ + +extern int isa_detect(unsigned short portbase); +extern int isa_config_irq(act2000_card * card, short irq); +extern int isa_config_port(act2000_card * card, unsigned short portbase); +extern int isa_download(act2000_card * card, act2000_ddef * cb); +extern void isa_release(act2000_card * card); +extern void isa_receive(act2000_card *card); +extern void isa_send(act2000_card *card); + +#endif /* act2000_isa_h */ diff --git a/drivers/isdn/act2000/capi.c b/drivers/isdn/act2000/capi.c new file mode 100644 index 000000000000..d0310bcc0718 --- /dev/null +++ b/drivers/isdn/act2000/capi.c @@ -0,0 +1,1218 @@ +/* $Id: capi.c,v 1.7 1998/02/23 23:35:41 fritz Exp $ + * + * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. + * CAPI encoder/decoder + * + * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de) + * Thanks to Friedemann Baitinger and IBM Germany + * + * 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: capi.c,v $ + * Revision 1.7 1998/02/23 23:35:41 fritz + * Eliminated some compiler warnings. + * + * Revision 1.6 1998/02/12 23:06:50 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.5 1997/10/09 22:23:02 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * + * Revision 1.4 1997/09/25 17:25:39 fritz + * Support for adding cards at runtime. + * Support for new Firmware. + * + * Revision 1.3 1997/09/24 19:44:14 fritz + * Added MSN mapping support, some cleanup. + * + * Revision 1.2 1997/09/23 19:41:24 fritz + * Disabled Logging of DATA_B3_IND/RESP/REQ/CONF Messages. + * + * Revision 1.1 1997/09/23 18:00:08 fritz + * New driver for IBM Active 2000. + * + */ + +#define __NO_VERSION__ +#include "act2000.h" +#include "capi.h" + +static actcapi_msgdsc valid_msg[] = { + {{ 0x86, 0x02}, "DATA_B3_IND"}, /* DATA_B3_IND/CONF must be first because of speed!!! */ + {{ 0x86, 0x01}, "DATA_B3_CONF"}, + {{ 0x02, 0x01}, "CONNECT_CONF"}, + {{ 0x02, 0x02}, "CONNECT_IND"}, + {{ 0x09, 0x01}, "CONNECT_INFO_CONF"}, + {{ 0x03, 0x02}, "CONNECT_ACTIVE_IND"}, + {{ 0x04, 0x01}, "DISCONNECT_CONF"}, + {{ 0x04, 0x02}, "DISCONNECT_IND"}, + {{ 0x05, 0x01}, "LISTEN_CONF"}, + {{ 0x06, 0x01}, "GET_PARAMS_CONF"}, + {{ 0x07, 0x01}, "INFO_CONF"}, + {{ 0x07, 0x02}, "INFO_IND"}, + {{ 0x08, 0x01}, "DATA_CONF"}, + {{ 0x08, 0x02}, "DATA_IND"}, + {{ 0x40, 0x01}, "SELECT_B2_PROTOCOL_CONF"}, + {{ 0x80, 0x01}, "SELECT_B3_PROTOCOL_CONF"}, + {{ 0x81, 0x01}, "LISTEN_B3_CONF"}, + {{ 0x82, 0x01}, "CONNECT_B3_CONF"}, + {{ 0x82, 0x02}, "CONNECT_B3_IND"}, + {{ 0x83, 0x02}, "CONNECT_B3_ACTIVE_IND"}, + {{ 0x84, 0x01}, "DISCONNECT_B3_CONF"}, + {{ 0x84, 0x02}, "DISCONNECT_B3_IND"}, + {{ 0x85, 0x01}, "GET_B3_PARAMS_CONF"}, + {{ 0x01, 0x01}, "RESET_B3_CONF"}, + {{ 0x01, 0x02}, "RESET_B3_IND"}, + /* {{ 0x87, 0x02, "HANDSET_IND"}, not implemented */ + {{ 0xff, 0x01}, "MANUFACTURER_CONF"}, + {{ 0xff, 0x02}, "MANUFACTURER_IND"}, +#ifdef DEBUG_MSG + /* Requests */ + {{ 0x01, 0x00}, "RESET_B3_REQ"}, + {{ 0x02, 0x00}, "CONNECT_REQ"}, + {{ 0x04, 0x00}, "DISCONNECT_REQ"}, + {{ 0x05, 0x00}, "LISTEN_REQ"}, + {{ 0x06, 0x00}, "GET_PARAMS_REQ"}, + {{ 0x07, 0x00}, "INFO_REQ"}, + {{ 0x08, 0x00}, "DATA_REQ"}, + {{ 0x09, 0x00}, "CONNECT_INFO_REQ"}, + {{ 0x40, 0x00}, "SELECT_B2_PROTOCOL_REQ"}, + {{ 0x80, 0x00}, "SELECT_B3_PROTOCOL_REQ"}, + {{ 0x81, 0x00}, "LISTEN_B3_REQ"}, + {{ 0x82, 0x00}, "CONNECT_B3_REQ"}, + {{ 0x84, 0x00}, "DISCONNECT_B3_REQ"}, + {{ 0x85, 0x00}, "GET_B3_PARAMS_REQ"}, + {{ 0x86, 0x00}, "DATA_B3_REQ"}, + {{ 0xff, 0x00}, "MANUFACTURER_REQ"}, + /* Responses */ + {{ 0x01, 0x03}, "RESET_B3_RESP"}, + {{ 0x02, 0x03}, "CONNECT_RESP"}, + {{ 0x03, 0x03}, "CONNECT_ACTIVE_RESP"}, + {{ 0x04, 0x03}, "DISCONNECT_RESP"}, + {{ 0x07, 0x03}, "INFO_RESP"}, + {{ 0x08, 0x03}, "DATA_RESP"}, + {{ 0x82, 0x03}, "CONNECT_B3_RESP"}, + {{ 0x83, 0x03}, "CONNECT_B3_ACTIVE_RESP"}, + {{ 0x84, 0x03}, "DISCONNECT_B3_RESP"}, + {{ 0x86, 0x03}, "DATA_B3_RESP"}, + {{ 0xff, 0x03}, "MANUFACTURER_RESP"}, +#if 0 +/* CAPI 2.0 */ + {{ 0x05, 0x80}, "LISTEN_REQ (CAPI 2.0)"}, +#endif +#endif + {{ 0x00, 0x00}, NULL}, +}; +#define num_valid_msg (sizeof(valid_msg)/sizeof(actcapi_msgdsc)) +#define num_valid_imsg 27 /* MANUFACTURER_IND */ + +/* + * Check for a valid incoming CAPI message. + * Return: + * 0 = Invalid message + * 1 = Valid message, no B-Channel-data + * 2 = Valid message, B-Channel-data + */ +int +actcapi_chkhdr(act2000_card * card, actcapi_msghdr *hdr) +{ + int i; + + if (hdr->applicationID != 1) + return 0; + if (hdr->len < 9) + return 0; + for (i = 0; i < num_valid_imsg; i++) + if ((hdr->cmd.cmd == valid_msg[i].cmd.cmd) && + (hdr->cmd.subcmd == valid_msg[i].cmd.subcmd)) { + return (i?1:2); + } + return 0; +} + +#define ACTCAPI_MKHDR(l, c, s) { \ + skb = alloc_skb(l + 8, GFP_ATOMIC); \ + if (skb) { \ + m = (actcapi_msg *)skb_put(skb, l + 8); \ + m->hdr.len = l + 8; \ + m->hdr.applicationID = 1; \ + m->hdr.cmd.cmd = c; \ + m->hdr.cmd.subcmd = s; \ + m->hdr.msgnum = actcapi_nextsmsg(card); \ + } \ +} + +#define ACTCAPI_CHKSKB if (!skb) { \ + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); \ + return; \ +} + +#define ACTCAPI_QUEUE_TX { \ + actcapi_debug_msg(skb, 1); \ + skb_queue_tail(&card->sndq, skb); \ + act2000_schedule_tx(card); \ +} + +int +actcapi_listen_req(act2000_card *card) +{ + __u16 eazmask = 0; + int i; + actcapi_msg *m; + struct sk_buff *skb; + + for (i = 0; i < ACT2000_BCH; i++) + eazmask |= card->bch[i].eazmask; + ACTCAPI_MKHDR(9, 0x05, 0x00); + if (!skb) { + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); + return -ENOMEM; + } + m->msg.listen_req.controller = 0; + m->msg.listen_req.infomask = 0x3f; /* All information */ + m->msg.listen_req.eazmask = eazmask; + m->msg.listen_req.simask = (eazmask)?0x86:0; /* All SI's */ + ACTCAPI_QUEUE_TX; + return 0; +} + +int +actcapi_connect_req(act2000_card *card, act2000_chan *chan, char *phone, + char eaz, int si1, int si2) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR((11 + strlen(phone)), 0x02, 0x00); + if (!skb) { + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); + chan->fsm_state = ACT2000_STATE_NULL; + return -ENOMEM; + } + m->msg.connect_req.controller = 0; + m->msg.connect_req.bchan = 0x83; + m->msg.connect_req.infomask = 0x3f; + m->msg.connect_req.si1 = si1; + m->msg.connect_req.si2 = si2; + m->msg.connect_req.eaz = eaz?eaz:'0'; + m->msg.connect_req.addr.len = strlen(phone) + 1; + m->msg.connect_req.addr.tnp = 0x81; + memcpy(m->msg.connect_req.addr.num, phone, strlen(phone)); + chan->callref = m->hdr.msgnum; + ACTCAPI_QUEUE_TX; + return 0; +} + +static void +actcapi_connect_b3_req(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(17, 0x82, 0x00); + ACTCAPI_CHKSKB; + m->msg.connect_b3_req.plci = chan->plci; + memset(&m->msg.connect_b3_req.ncpi, 0, + sizeof(m->msg.connect_b3_req.ncpi)); + m->msg.connect_b3_req.ncpi.len = 13; + m->msg.connect_b3_req.ncpi.modulo = 8; + ACTCAPI_QUEUE_TX; +} + +/* + * Set net type (1TR6) or (EDSS1) + */ +int +actcapi_manufacturer_req_net(act2000_card *card) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(5, 0xff, 0x00); + if (!skb) { + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); + return -ENOMEM; + } + m->msg.manufacturer_req_net.manuf_msg = 0x11; + m->msg.manufacturer_req_net.controller = 1; + m->msg.manufacturer_req_net.nettype = (card->ptype == ISDN_PTYPE_EURO)?1:0; + ACTCAPI_QUEUE_TX; + printk(KERN_INFO "act2000 %s: D-channel protocol now %s\n", + card->interface.id, (card->ptype == ISDN_PTYPE_EURO)?"euro":"1tr6"); + card->interface.features &= + ~(ISDN_FEATURE_P_UNKNOWN | ISDN_FEATURE_P_EURO | ISDN_FEATURE_P_1TR6); + card->interface.features |= + ((card->ptype == ISDN_PTYPE_EURO)?ISDN_FEATURE_P_EURO:ISDN_FEATURE_P_1TR6); + return 0; +} + +/* + * Switch V.42 on or off + */ +int +actcapi_manufacturer_req_v42(act2000_card *card, ulong arg) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(8, 0xff, 0x00); + if (!skb) { + + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); + return -ENOMEM; + } + m->msg.manufacturer_req_v42.manuf_msg = 0x10; + m->msg.manufacturer_req_v42.controller = 0; + m->msg.manufacturer_req_v42.v42control = (arg?1:0); + ACTCAPI_QUEUE_TX; + return 0; +} + +/* + * Set error-handler + */ +int +actcapi_manufacturer_req_errh(act2000_card *card) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(4, 0xff, 0x00); + if (!skb) { + + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); + return -ENOMEM; + } + m->msg.manufacturer_req_err.manuf_msg = 0x03; + m->msg.manufacturer_req_err.controller = 0; + ACTCAPI_QUEUE_TX; + return 0; +} + +/* + * Set MSN-Mapping. + */ +int +actcapi_manufacturer_req_msn(act2000_card *card) +{ + msn_entry *p = card->msn_list; + actcapi_msg *m; + struct sk_buff *skb; + int len; + + while (p) { + int i; + + len = strlen(p->msn); + for (i = 0; i < 2; i++) { + ACTCAPI_MKHDR(6 + len, 0xff, 0x00); + if (!skb) { + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); + return -ENOMEM; + } + m->msg.manufacturer_req_msn.manuf_msg = 0x13 + i; + m->msg.manufacturer_req_msn.controller = 0; + m->msg.manufacturer_req_msn.msnmap.eaz = p->eaz; + m->msg.manufacturer_req_msn.msnmap.len = len; + memcpy(m->msg.manufacturer_req_msn.msnmap.msn, p->msn, len); + ACTCAPI_QUEUE_TX; + } + p = p->next; + } + return 0; +} + +void +actcapi_select_b2_protocol_req(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(10, 0x40, 0x00); + ACTCAPI_CHKSKB; + m->msg.select_b2_protocol_req.plci = chan->plci; + memset(&m->msg.select_b2_protocol_req.dlpd, 0, + sizeof(m->msg.select_b2_protocol_req.dlpd)); + m->msg.select_b2_protocol_req.dlpd.len = 6; + switch (chan->l2prot) { + case ISDN_PROTO_L2_TRANS: + m->msg.select_b2_protocol_req.protocol = 0x03; + m->msg.select_b2_protocol_req.dlpd.dlen = 4000; + break; + case ISDN_PROTO_L2_HDLC: + m->msg.select_b2_protocol_req.protocol = 0x02; + m->msg.select_b2_protocol_req.dlpd.dlen = 4000; + break; + case ISDN_PROTO_L2_X75I: + case ISDN_PROTO_L2_X75UI: + case ISDN_PROTO_L2_X75BUI: + m->msg.select_b2_protocol_req.protocol = 0x01; + m->msg.select_b2_protocol_req.dlpd.dlen = 4000; + m->msg.select_b2_protocol_req.dlpd.laa = 3; + m->msg.select_b2_protocol_req.dlpd.lab = 1; + m->msg.select_b2_protocol_req.dlpd.win = 7; + m->msg.select_b2_protocol_req.dlpd.modulo = 8; + break; + } + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_select_b3_protocol_req(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(17, 0x80, 0x00); + ACTCAPI_CHKSKB; + m->msg.select_b3_protocol_req.plci = chan->plci; + memset(&m->msg.select_b3_protocol_req.ncpd, 0, + sizeof(m->msg.select_b3_protocol_req.ncpd)); + switch (chan->l3prot) { + case ISDN_PROTO_L3_TRANS: + m->msg.select_b3_protocol_req.protocol = 0x04; + m->msg.select_b3_protocol_req.ncpd.len = 13; + m->msg.select_b3_protocol_req.ncpd.modulo = 8; + break; + } + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_listen_b3_req(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(2, 0x81, 0x00); + ACTCAPI_CHKSKB; + m->msg.listen_b3_req.plci = chan->plci; + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_disconnect_req(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(3, 0x04, 0x00); + ACTCAPI_CHKSKB; + m->msg.disconnect_req.plci = chan->plci; + m->msg.disconnect_req.cause = 0; + ACTCAPI_QUEUE_TX; +} + +void +actcapi_disconnect_b3_req(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(17, 0x84, 0x00); + ACTCAPI_CHKSKB; + m->msg.disconnect_b3_req.ncci = chan->ncci; + memset(&m->msg.disconnect_b3_req.ncpi, 0, + sizeof(m->msg.disconnect_b3_req.ncpi)); + m->msg.disconnect_b3_req.ncpi.len = 13; + m->msg.disconnect_b3_req.ncpi.modulo = 8; + chan->fsm_state = ACT2000_STATE_BHWAIT; + ACTCAPI_QUEUE_TX; +} + +void +actcapi_connect_resp(act2000_card *card, act2000_chan *chan, __u8 cause) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(3, 0x02, 0x03); + ACTCAPI_CHKSKB; + m->msg.connect_resp.plci = chan->plci; + m->msg.connect_resp.rejectcause = cause; + if (cause) { + chan->fsm_state = ACT2000_STATE_NULL; + chan->plci = 0x8000; + } else + chan->fsm_state = ACT2000_STATE_IWAIT; + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_connect_active_resp(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(2, 0x03, 0x03); + ACTCAPI_CHKSKB; + m->msg.connect_resp.plci = chan->plci; + if (chan->fsm_state == ACT2000_STATE_IWAIT) + chan->fsm_state = ACT2000_STATE_IBWAIT; + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_connect_b3_resp(act2000_card *card, act2000_chan *chan, __u8 rejectcause) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR((rejectcause?3:17), 0x82, 0x03); + ACTCAPI_CHKSKB; + m->msg.connect_b3_resp.ncci = chan->ncci; + m->msg.connect_b3_resp.rejectcause = rejectcause; + if (!rejectcause) { + memset(&m->msg.connect_b3_resp.ncpi, 0, + sizeof(m->msg.connect_b3_resp.ncpi)); + m->msg.connect_b3_resp.ncpi.len = 13; + m->msg.connect_b3_resp.ncpi.modulo = 8; + chan->fsm_state = ACT2000_STATE_BWAIT; + } + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_connect_b3_active_resp(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(2, 0x83, 0x03); + ACTCAPI_CHKSKB; + m->msg.connect_b3_active_resp.ncci = chan->ncci; + chan->fsm_state = ACT2000_STATE_ACTIVE; + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_info_resp(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(2, 0x07, 0x03); + ACTCAPI_CHKSKB; + m->msg.info_resp.plci = chan->plci; + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_disconnect_b3_resp(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(2, 0x84, 0x03); + ACTCAPI_CHKSKB; + m->msg.disconnect_b3_resp.ncci = chan->ncci; + chan->ncci = 0x8000; + chan->queued = 0; + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_disconnect_resp(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(2, 0x04, 0x03); + ACTCAPI_CHKSKB; + m->msg.disconnect_resp.plci = chan->plci; + chan->plci = 0x8000; + ACTCAPI_QUEUE_TX; +} + +static int +new_plci(act2000_card *card, __u16 plci) +{ + int i; + for (i = 0; i < ACT2000_BCH; i++) + if (card->bch[i].plci == 0x8000) { + card->bch[i].plci = plci; + return i; + } + return -1; +} + +static int +find_plci(act2000_card *card, __u16 plci) +{ + int i; + for (i = 0; i < ACT2000_BCH; i++) + if (card->bch[i].plci == plci) + return i; + return -1; +} + +static int +find_ncci(act2000_card *card, __u16 ncci) +{ + int i; + for (i = 0; i < ACT2000_BCH; i++) + if (card->bch[i].ncci == ncci) + return i; + return -1; +} + +static int +find_dialing(act2000_card *card, __u16 callref) +{ + int i; + for (i = 0; i < ACT2000_BCH; i++) + if ((card->bch[i].callref == callref) && + (card->bch[i].fsm_state == ACT2000_STATE_OCALL)) + return i; + return -1; +} + +static int +actcapi_data_b3_ind(act2000_card *card, struct sk_buff *skb) { + __u16 plci; + __u16 ncci; + __u16 controller; + __u8 blocknr; + int chan; + actcapi_msg *msg = (actcapi_msg *)skb->data; + + EVAL_NCCI(msg->msg.data_b3_ind.fakencci, plci, controller, ncci); + chan = find_ncci(card, ncci); + if (chan < 0) + return 0; + if (card->bch[chan].fsm_state != ACT2000_STATE_ACTIVE) + return 0; + if (card->bch[chan].plci != plci) + return 0; + blocknr = msg->msg.data_b3_ind.blocknr; + skb_pull(skb, 19); + card->interface.rcvcallb_skb(card->myid, chan, skb); + if (!(skb = alloc_skb(11, GFP_ATOMIC))) { + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); + return 1; + } + msg = (actcapi_msg *)skb_put(skb, 11); + msg->hdr.len = 11; + msg->hdr.applicationID = 1; + msg->hdr.cmd.cmd = 0x86; + msg->hdr.cmd.subcmd = 0x03; + msg->hdr.msgnum = actcapi_nextsmsg(card); + msg->msg.data_b3_resp.ncci = ncci; + msg->msg.data_b3_resp.blocknr = blocknr; + ACTCAPI_QUEUE_TX; + return 1; +} + +/* + * Walk over ackq, unlink DATA_B3_REQ from it, if + * ncci and blocknr are matching. + * Decrement queued-bytes counter. + */ +static int +handle_ack(act2000_card *card, act2000_chan *chan, __u8 blocknr) { + unsigned long flags; + struct sk_buff *skb; + struct sk_buff *tmp; + struct actcapi_msg *m; + int ret = 0; + + save_flags(flags); + cli(); + skb = skb_peek(&card->ackq); + restore_flags(flags); + if (!skb) { + printk(KERN_WARNING "act2000: handle_ack nothing found!\n"); + return 0; + } + tmp = skb; + while (1) { + m = (actcapi_msg *)tmp->data; + if ((((m->msg.data_b3_req.fakencci >> 8) & 0xff) == chan->ncci) && + (m->msg.data_b3_req.blocknr == blocknr)) { + /* found corresponding DATA_B3_REQ */ + skb_unlink(tmp); + chan->queued -= m->msg.data_b3_req.datalen; + if (m->msg.data_b3_req.flags) + ret = m->msg.data_b3_req.datalen; + dev_kfree_skb(tmp); + if (chan->queued < 0) + chan->queued = 0; + return ret; + } + save_flags(flags); + cli(); + tmp = skb_peek((struct sk_buff_head *)tmp); + restore_flags(flags); + if ((tmp == skb) || (tmp == NULL)) { + /* reached end of queue */ + printk(KERN_WARNING "act2000: handle_ack nothing found!\n"); + return 0; + } + } +} + +void +actcapi_dispatch(act2000_card *card) +{ + struct sk_buff *skb; + actcapi_msg *msg; + __u16 ccmd; + int chan; + int len; + act2000_chan *ctmp; + isdn_ctrl cmd; + char tmp[170]; + + while ((skb = skb_dequeue(&card->rcvq))) { + actcapi_debug_msg(skb, 0); + msg = (actcapi_msg *)skb->data; + ccmd = ((msg->hdr.cmd.cmd << 8) | msg->hdr.cmd.subcmd); + switch (ccmd) { + case 0x8602: + /* DATA_B3_IND */ + if (actcapi_data_b3_ind(card, skb)) + return; + break; + case 0x8601: + /* DATA_B3_CONF */ + chan = find_ncci(card, msg->msg.data_b3_conf.ncci); + if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_ACTIVE)) { + if (msg->msg.data_b3_conf.info != 0) + printk(KERN_WARNING "act2000: DATA_B3_CONF: %04x\n", + msg->msg.data_b3_conf.info); + len = handle_ack(card, &card->bch[chan], + msg->msg.data_b3_conf.blocknr); + if (len) { + cmd.driver = card->myid; + cmd.command = ISDN_STAT_BSENT; + cmd.arg = chan; + cmd.parm.length = len; + card->interface.statcallb(&cmd); + } + } + break; + case 0x0201: + /* CONNECT_CONF */ + chan = find_dialing(card, msg->hdr.msgnum); + if (chan >= 0) { + if (msg->msg.connect_conf.info) { + card->bch[chan].fsm_state = ACT2000_STATE_NULL; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } else { + card->bch[chan].fsm_state = ACT2000_STATE_OWAIT; + card->bch[chan].plci = msg->msg.connect_conf.plci; + } + } + break; + case 0x0202: + /* CONNECT_IND */ + chan = new_plci(card, msg->msg.connect_ind.plci); + if (chan < 0) { + ctmp = (act2000_chan *)tmp; + ctmp->plci = msg->msg.connect_ind.plci; + actcapi_connect_resp(card, ctmp, 0x11); /* All Card-Cannels busy */ + } else { + card->bch[chan].fsm_state = ACT2000_STATE_ICALL; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_ICALL; + cmd.arg = chan; + cmd.parm.setup.si1 = msg->msg.connect_ind.si1; + cmd.parm.setup.si2 = msg->msg.connect_ind.si2; + if (card->ptype == ISDN_PTYPE_EURO) + strcpy(cmd.parm.setup.eazmsn, + act2000_find_eaz(card, msg->msg.connect_ind.eaz)); + else { + cmd.parm.setup.eazmsn[0] = msg->msg.connect_ind.eaz; + cmd.parm.setup.eazmsn[1] = 0; + } + memset(cmd.parm.setup.phone, 0, sizeof(cmd.parm.setup.phone)); + memcpy(cmd.parm.setup.phone, msg->msg.connect_ind.addr.num, + msg->msg.connect_ind.addr.len - 1); + cmd.parm.setup.plan = msg->msg.connect_ind.addr.tnp; + cmd.parm.setup.screen = 0; + if (card->interface.statcallb(&cmd) == 2) + actcapi_connect_resp(card, &card->bch[chan], 0x15); /* Reject Call */ + } + break; + case 0x0302: + /* CONNECT_ACTIVE_IND */ + chan = find_plci(card, msg->msg.connect_active_ind.plci); + if (chan >= 0) + switch (card->bch[chan].fsm_state) { + case ACT2000_STATE_IWAIT: + actcapi_connect_active_resp(card, &card->bch[chan]); + break; + case ACT2000_STATE_OWAIT: + actcapi_connect_active_resp(card, &card->bch[chan]); + actcapi_select_b2_protocol_req(card, &card->bch[chan]); + break; + } + break; + case 0x8202: + /* CONNECT_B3_IND */ + chan = find_plci(card, msg->msg.connect_b3_ind.plci); + if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_IBWAIT)) { + card->bch[chan].ncci = msg->msg.connect_b3_ind.ncci; + actcapi_connect_b3_resp(card, &card->bch[chan], 0); + } else { + ctmp = (act2000_chan *)tmp; + ctmp->ncci = msg->msg.connect_b3_ind.ncci; + actcapi_connect_b3_resp(card, ctmp, 0x11); /* All Card-Cannels busy */ + } + break; + case 0x8302: + /* CONNECT_B3_ACTIVE_IND */ + chan = find_ncci(card, msg->msg.connect_b3_active_ind.ncci); + if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_BWAIT)) { + actcapi_connect_b3_active_resp(card, &card->bch[chan]); + cmd.driver = card->myid; + cmd.command = ISDN_STAT_BCONN; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } + break; + case 0x8402: + /* DISCONNECT_B3_IND */ + chan = find_ncci(card, msg->msg.disconnect_b3_ind.ncci); + if (chan >= 0) { + ctmp = &card->bch[chan]; + actcapi_disconnect_b3_resp(card, ctmp); + switch (ctmp->fsm_state) { + case ACT2000_STATE_ACTIVE: + ctmp->fsm_state = ACT2000_STATE_DHWAIT2; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_BHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + break; + case ACT2000_STATE_BHWAIT2: + actcapi_disconnect_req(card, ctmp); + ctmp->fsm_state = ACT2000_STATE_DHWAIT; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_BHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + break; + } + } + break; + case 0x0402: + /* DISCONNECT_IND */ + chan = find_plci(card, msg->msg.disconnect_ind.plci); + if (chan >= 0) { + ctmp = &card->bch[chan]; + actcapi_disconnect_resp(card, ctmp); + ctmp->fsm_state = ACT2000_STATE_NULL; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } else { + ctmp = (act2000_chan *)tmp; + ctmp->plci = msg->msg.disconnect_ind.plci; + actcapi_disconnect_resp(card, ctmp); + } + break; + case 0x4001: + /* SELECT_B2_PROTOCOL_CONF */ + chan = find_plci(card, msg->msg.select_b2_protocol_conf.plci); + if (chan >= 0) + switch (card->bch[chan].fsm_state) { + case ACT2000_STATE_ICALL: + case ACT2000_STATE_OWAIT: + ctmp = &card->bch[chan]; + if (msg->msg.select_b2_protocol_conf.info == 0) + actcapi_select_b3_protocol_req(card, ctmp); + else { + ctmp->fsm_state = ACT2000_STATE_NULL; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } + break; + } + break; + case 0x8001: + /* SELECT_B3_PROTOCOL_CONF */ + chan = find_plci(card, msg->msg.select_b3_protocol_conf.plci); + if (chan >= 0) + switch (card->bch[chan].fsm_state) { + case ACT2000_STATE_ICALL: + case ACT2000_STATE_OWAIT: + ctmp = &card->bch[chan]; + if (msg->msg.select_b3_protocol_conf.info == 0) + actcapi_listen_b3_req(card, ctmp); + else { + ctmp->fsm_state = ACT2000_STATE_NULL; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } + } + break; + case 0x8101: + /* LISTEN_B3_CONF */ + chan = find_plci(card, msg->msg.listen_b3_conf.plci); + if (chan >= 0) + switch (card->bch[chan].fsm_state) { + case ACT2000_STATE_ICALL: + ctmp = &card->bch[chan]; + if (msg->msg.listen_b3_conf.info == 0) + actcapi_connect_resp(card, ctmp, 0); + else { + ctmp->fsm_state = ACT2000_STATE_NULL; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } + break; + case ACT2000_STATE_OWAIT: + ctmp = &card->bch[chan]; + if (msg->msg.listen_b3_conf.info == 0) { + actcapi_connect_b3_req(card, ctmp); + ctmp->fsm_state = ACT2000_STATE_OBWAIT; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DCONN; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } else { + ctmp->fsm_state = ACT2000_STATE_NULL; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } + break; + } + break; + case 0x8201: + /* CONNECT_B3_CONF */ + chan = find_plci(card, msg->msg.connect_b3_conf.plci); + if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_OBWAIT)) { + ctmp = &card->bch[chan]; + if (msg->msg.connect_b3_conf.info) { + ctmp->fsm_state = ACT2000_STATE_NULL; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } else { + ctmp->ncci = msg->msg.connect_b3_conf.ncci; + ctmp->fsm_state = ACT2000_STATE_BWAIT; + } + } + break; + case 0x8401: + /* DISCONNECT_B3_CONF */ + chan = find_ncci(card, msg->msg.disconnect_b3_conf.ncci); + if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_BHWAIT)) + card->bch[chan].fsm_state = ACT2000_STATE_BHWAIT2; + break; + case 0x0702: + /* INFO_IND */ + chan = find_plci(card, msg->msg.info_ind.plci); + if (chan >= 0) + /* TODO: Eval Charging info / cause */ + actcapi_info_resp(card, &card->bch[chan]); + break; + case 0x0401: + /* LISTEN_CONF */ + case 0x0501: + /* LISTEN_CONF */ + case 0xff01: + /* MANUFACTURER_CONF */ + break; + case 0xff02: + /* MANUFACTURER_IND */ + if (msg->msg.manuf_msg == 3) { + memset(tmp, 0, sizeof(tmp)); + strncpy(tmp, + &msg->msg.manufacturer_ind_err.errstring, + msg->hdr.len - 16); + if (msg->msg.manufacturer_ind_err.errcode) + printk(KERN_WARNING "act2000: %s\n", tmp); + else { + printk(KERN_DEBUG "act2000: %s\n", tmp); + if ((!strncmp(tmp, "INFO: Trace buffer con", 22)) || + (!strncmp(tmp, "INFO: Compile Date/Tim", 22))) { + card->flags |= ACT2000_FLAGS_RUNNING; + cmd.command = ISDN_STAT_RUN; + cmd.driver = card->myid; + cmd.arg = 0; + actcapi_manufacturer_req_net(card); + actcapi_manufacturer_req_msn(card); + actcapi_listen_req(card); + card->interface.statcallb(&cmd); + } + } + } + break; + default: + printk(KERN_WARNING "act2000: UNHANDLED Message %04x\n", ccmd); + break; + } + dev_kfree_skb(skb); + } +} + +#ifdef DEBUG_MSG +static void +actcapi_debug_caddr(actcapi_addr *addr) +{ + char tmp[30]; + + printk(KERN_DEBUG " Alen = %d\n", addr->len); + if (addr->len > 0) + printk(KERN_DEBUG " Atnp = 0x%02x\n", addr->tnp); + if (addr->len > 1) { + memset(tmp, 0, 30); + memcpy(tmp, addr->num, addr->len - 1); + printk(KERN_DEBUG " Anum = '%s'\n", tmp); + } +} + +static void +actcapi_debug_ncpi(actcapi_ncpi *ncpi) +{ + printk(KERN_DEBUG " ncpi.len = %d\n", ncpi->len); + if (ncpi->len >= 2) + printk(KERN_DEBUG " ncpi.lic = 0x%04x\n", ncpi->lic); + if (ncpi->len >= 4) + printk(KERN_DEBUG " ncpi.hic = 0x%04x\n", ncpi->hic); + if (ncpi->len >= 6) + printk(KERN_DEBUG " ncpi.ltc = 0x%04x\n", ncpi->ltc); + if (ncpi->len >= 8) + printk(KERN_DEBUG " ncpi.htc = 0x%04x\n", ncpi->htc); + if (ncpi->len >= 10) + printk(KERN_DEBUG " ncpi.loc = 0x%04x\n", ncpi->loc); + if (ncpi->len >= 12) + printk(KERN_DEBUG " ncpi.hoc = 0x%04x\n", ncpi->hoc); + if (ncpi->len >= 13) + printk(KERN_DEBUG " ncpi.mod = %d\n", ncpi->modulo); +} + +static void +actcapi_debug_dlpd(actcapi_dlpd *dlpd) +{ + printk(KERN_DEBUG " dlpd.len = %d\n", dlpd->len); + if (dlpd->len >= 2) + printk(KERN_DEBUG " dlpd.dlen = 0x%04x\n", dlpd->dlen); + if (dlpd->len >= 3) + printk(KERN_DEBUG " dlpd.laa = 0x%02x\n", dlpd->laa); + if (dlpd->len >= 4) + printk(KERN_DEBUG " dlpd.lab = 0x%02x\n", dlpd->lab); + if (dlpd->len >= 5) + printk(KERN_DEBUG " dlpd.modulo = %d\n", dlpd->modulo); + if (dlpd->len >= 6) + printk(KERN_DEBUG " dlpd.win = %d\n", dlpd->win); +} + +#ifdef DEBUG_DUMP_SKB +static void dump_skb(struct sk_buff *skb) { + char tmp[80]; + char *p = skb->data; + char *t = tmp; + int i; + + for (i = 0; i < skb->len; i++) { + t += sprintf(t, "%02x ", *p++ & 0xff); + if ((i & 0x0f) == 8) { + printk(KERN_DEBUG "dump: %s\n", tmp); + t = tmp; + } + } + if (i & 0x07) + printk(KERN_DEBUG "dump: %s\n", tmp); +} +#endif + +void +actcapi_debug_msg(struct sk_buff *skb, int direction) +{ + actcapi_msg *msg = (actcapi_msg *)skb->data; + char *descr; + int i; + char tmp[170]; + +#ifndef DEBUG_DATA_MSG + if (msg->hdr.cmd.cmd == 0x86) + return; +#endif + descr = "INVALID"; +#ifdef DEBUG_DUMP_SKB + dump_skb(skb); +#endif + for (i = 0; i < num_valid_msg; i++) + if ((msg->hdr.cmd.cmd == valid_msg[i].cmd.cmd) && + (msg->hdr.cmd.subcmd == valid_msg[i].cmd.subcmd)) { + descr = valid_msg[i].description; + break; + } + printk(KERN_DEBUG "%s %s msg\n", direction?"Outgoing":"Incoming", descr); + printk(KERN_DEBUG " ApplID = %d\n", msg->hdr.applicationID); + printk(KERN_DEBUG " Len = %d\n", msg->hdr.len); + printk(KERN_DEBUG " MsgNum = 0x%04x\n", msg->hdr.msgnum); + printk(KERN_DEBUG " Cmd = 0x%02x\n", msg->hdr.cmd.cmd); + printk(KERN_DEBUG " SubCmd = 0x%02x\n", msg->hdr.cmd.subcmd); + switch (i) { + case 0: + /* DATA B3 IND */ + printk(KERN_DEBUG " BLOCK = 0x%02x\n", + msg->msg.data_b3_ind.blocknr); + break; + case 2: + /* CONNECT CONF */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.connect_conf.plci); + printk(KERN_DEBUG " Info = 0x%04x\n", + msg->msg.connect_conf.info); + break; + case 3: + /* CONNECT IND */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.connect_ind.plci); + printk(KERN_DEBUG " Contr = %d\n", + msg->msg.connect_ind.controller); + printk(KERN_DEBUG " SI1 = %d\n", + msg->msg.connect_ind.si1); + printk(KERN_DEBUG " SI2 = %d\n", + msg->msg.connect_ind.si2); + printk(KERN_DEBUG " EAZ = '%c'\n", + msg->msg.connect_ind.eaz); + actcapi_debug_caddr(&msg->msg.connect_ind.addr); + break; + case 5: + /* CONNECT ACTIVE IND */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.connect_active_ind.plci); + actcapi_debug_caddr(&msg->msg.connect_active_ind.addr); + break; + case 8: + /* LISTEN CONF */ + printk(KERN_DEBUG " Contr = %d\n", + msg->msg.listen_conf.controller); + printk(KERN_DEBUG " Info = 0x%04x\n", + msg->msg.listen_conf.info); + break; + case 11: + /* INFO IND */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.info_ind.plci); + printk(KERN_DEBUG " Imsk = 0x%04x\n", + msg->msg.info_ind.nr.mask); + if (msg->hdr.len > 12) { + int l = msg->hdr.len - 12; + int j; + char *p = tmp; + for (j = 0; j < l ; j++) + p += sprintf(p, "%02x ", msg->msg.info_ind.el.display[j]); + printk(KERN_DEBUG " D = '%s'\n", tmp); + } + break; + case 14: + /* SELECT B2 PROTOCOL CONF */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.select_b2_protocol_conf.plci); + printk(KERN_DEBUG " Info = 0x%04x\n", + msg->msg.select_b2_protocol_conf.info); + break; + case 15: + /* SELECT B3 PROTOCOL CONF */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.select_b3_protocol_conf.plci); + printk(KERN_DEBUG " Info = 0x%04x\n", + msg->msg.select_b3_protocol_conf.info); + break; + case 16: + /* LISTEN B3 CONF */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.listen_b3_conf.plci); + printk(KERN_DEBUG " Info = 0x%04x\n", + msg->msg.listen_b3_conf.info); + break; + case 18: + /* CONNECT B3 IND */ + printk(KERN_DEBUG " NCCI = 0x%04x\n", + msg->msg.connect_b3_ind.ncci); + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.connect_b3_ind.plci); + actcapi_debug_ncpi(&msg->msg.connect_b3_ind.ncpi); + break; + case 19: + /* CONNECT B3 ACTIVE IND */ + printk(KERN_DEBUG " NCCI = 0x%04x\n", + msg->msg.connect_b3_active_ind.ncci); + actcapi_debug_ncpi(&msg->msg.connect_b3_active_ind.ncpi); + break; + case 26: + /* MANUFACTURER IND */ + printk(KERN_DEBUG " Mmsg = 0x%02x\n", + msg->msg.manufacturer_ind_err.manuf_msg); + switch (msg->msg.manufacturer_ind_err.manuf_msg) { + case 3: + printk(KERN_DEBUG " Contr = %d\n", + msg->msg.manufacturer_ind_err.controller); + printk(KERN_DEBUG " Code = 0x%08x\n", + msg->msg.manufacturer_ind_err.errcode); + memset(tmp, 0, sizeof(tmp)); + strncpy(tmp, &msg->msg.manufacturer_ind_err.errstring, + msg->hdr.len - 16); + printk(KERN_DEBUG " Emsg = '%s'\n", tmp); + break; + } + break; + case 30: + /* LISTEN REQ */ + printk(KERN_DEBUG " Imsk = 0x%08x\n", + msg->msg.listen_req.infomask); + printk(KERN_DEBUG " Emsk = 0x%04x\n", + msg->msg.listen_req.eazmask); + printk(KERN_DEBUG " Smsk = 0x%04x\n", + msg->msg.listen_req.simask); + break; + case 35: + /* SELECT_B2_PROTOCOL_REQ */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.select_b2_protocol_req.plci); + printk(KERN_DEBUG " prot = 0x%02x\n", + msg->msg.select_b2_protocol_req.protocol); + if (msg->hdr.len >= 11) + printk(KERN_DEBUG "No dlpd\n"); + else + actcapi_debug_dlpd(&msg->msg.select_b2_protocol_req.dlpd); + break; + case 44: + /* CONNECT RESP */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.connect_resp.plci); + printk(KERN_DEBUG " CAUSE = 0x%02x\n", + msg->msg.connect_resp.rejectcause); + break; + case 45: + /* CONNECT ACTIVE RESP */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.connect_active_resp.plci); + break; + } +} +#endif diff --git a/drivers/isdn/act2000/capi.h b/drivers/isdn/act2000/capi.h new file mode 100644 index 000000000000..901f15ed4fc3 --- /dev/null +++ b/drivers/isdn/act2000/capi.h @@ -0,0 +1,406 @@ +/* $Id: capi.h,v 1.4 1997/10/01 09:21:04 fritz Exp $ + * + * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. + * + * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de) + * Thanks to Friedemann Baitinger and IBM Germany + * + * 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: capi.h,v $ + * Revision 1.4 1997/10/01 09:21:04 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.3 1997/09/25 17:25:41 fritz + * Support for adding cards at runtime. + * Support for new Firmware. + * + * Revision 1.2 1997/09/24 19:44:15 fritz + * Added MSN mapping support, some cleanup. + * + * Revision 1.1 1997/09/23 18:00:10 fritz + * New driver for IBM Active 2000. + * + */ + +#ifndef CAPI_H +#define CAPI_H + +/* Command-part of a CAPI message */ +typedef struct actcapi_msgcmd { + __u8 cmd; + __u8 subcmd; +} actcapi_msgcmd; + +/* CAPI message header */ +typedef struct actcapi_msghdr { + __u16 len; + __u16 applicationID; + actcapi_msgcmd cmd; + __u16 msgnum; +} actcapi_msghdr; + +/* CAPI message description (for debugging) */ +typedef struct actcapi_msgdsc { + actcapi_msgcmd cmd; + char *description; +} actcapi_msgdsc; + +/* CAPI Adress */ +typedef struct actcapi_addr { + __u8 len; /* Length of element */ + __u8 tnp; /* Type/Numbering Plan */ + __u8 num[20]; /* Caller ID */ +} actcapi_addr; + +/* CAPI INFO element mask */ +typedef union actcapi_infonr { /* info number */ + __u16 mask; /* info-mask field */ + struct bmask { /* bit definitions */ + unsigned codes : 3; /* code set */ + unsigned rsvd : 5; /* reserved */ + unsigned svind : 1; /* single, variable length ind. */ + unsigned wtype : 7; /* W-element type */ + } bmask; +} actcapi_infonr; + +/* CAPI INFO element */ +typedef union actcapi_infoel { /* info element */ + __u8 len; /* length of info element */ + __u8 display[40]; /* display contents */ + __u8 uuinfo[40]; /* User-user info field */ + struct cause { /* Cause information */ + unsigned ext2 : 1; /* extension */ + unsigned cod : 2; /* coding standard */ + unsigned spare : 1; /* spare */ + unsigned loc : 4; /* location */ + unsigned ext1 : 1; /* extension */ + unsigned cval : 7; /* Cause value */ + } cause; + struct charge { /* Charging information */ + __u8 toc; /* type of charging info */ + __u8 unit[10]; /* charging units */ + } charge; + __u8 date[20]; /* date fields */ + __u8 stat; /* state of remote party */ +} actcapi_infoel; + +/* Message for EAZ<->MSN Mapping */ +typedef struct actcapi_msn { + __u8 eaz; + __u8 len; /* Length of MSN */ + __u8 msn[15] __attribute__ ((packed)); +} actcapi_msn; + +typedef struct actcapi_dlpd { + __u8 len; /* Length of structure */ + __u16 dlen __attribute__ ((packed)); /* Data Length */ + __u8 laa __attribute__ ((packed)); /* Link Address A */ + __u8 lab; /* Link Address B */ + __u8 modulo; /* Modulo Mode */ + __u8 win; /* Window size */ + __u8 xid[100]; /* XID Information */ +} actcapi_dlpd; + +typedef struct actcapi_ncpd { + __u8 len; /* Length of structure */ + __u16 lic __attribute__ ((packed)); + __u16 hic __attribute__ ((packed)); + __u16 ltc __attribute__ ((packed)); + __u16 htc __attribute__ ((packed)); + __u16 loc __attribute__ ((packed)); + __u16 hoc __attribute__ ((packed)); + __u8 modulo __attribute__ ((packed)); +} actcapi_ncpd; +#define actcapi_ncpi actcapi_ncpd + +/* + * Layout of NCCI field in a B3 DATA CAPI message is different from + * standard at act2000: + * + * Bit 0-4 = PLCI + * Bit 5-7 = Controller + * Bit 8-15 = NCCI + */ +#define MAKE_NCCI(plci,contr,ncci) \ + ((plci & 0x1f) | ((contr & 0x7) << 5) | ((ncci & 0xff) << 8)) + +#define EVAL_NCCI(fakencci,plci,contr,ncci) { \ + plci = fakencci & 0x1f; \ + contr = (fakencci >> 5) & 0x7; \ + ncci = (fakencci >> 8) & 0xff; \ +} + +/* + * Layout of PLCI field in a B3 DATA CAPI message is different from + * standard at act2000: + * + * Bit 0-4 = PLCI + * Bit 5-7 = Controller + * Bit 8-15 = reserved (must be 0) + */ +#define MAKE_PLCI(plci,contr) \ + ((plci & 0x1f) | ((contr & 0x7) << 5)) + +#define EVAL_PLCI(fakeplci,plci,contr) { \ + plci = fakeplci & 0x1f; \ + contr = (fakeplci >> 5) & 0x7; \ +} + +typedef struct actcapi_msg { + actcapi_msghdr hdr; + union msg { + __u16 manuf_msg; + struct manufacturer_req_net { + __u16 manuf_msg; + __u16 controller; + __u8 nettype; + } manufacturer_req_net; + struct manufacturer_req_v42 { + __u16 manuf_msg; + __u16 controller; + __u32 v42control; + } manufacturer_req_v42; + struct manufacturer_conf_v42 { + __u16 manuf_msg; + __u16 controller; + } manufacturer_conf_v42; + struct manufacturer_req_err { + __u16 manuf_msg; + __u16 controller; + } manufacturer_req_err; + struct manufacturer_ind_err { + __u16 manuf_msg; + __u16 controller; + __u32 errcode; + __u8 errstring; /* actually up to 160 */ + } manufacturer_ind_err; + struct manufacturer_req_msn { + __u16 manuf_msg; + __u16 controller; + actcapi_msn msnmap; + } manufacturer_req_msn; + /* TODO: TraceInit-req/conf/ind/resp and + * TraceDump-req/conf/ind/resp + */ + struct connect_req { + __u8 controller; + __u8 bchan; + __u32 infomask __attribute__ ((packed)); + __u8 si1; + __u8 si2; + __u8 eaz; + actcapi_addr addr; + } connect_req; + struct connect_conf { + __u16 plci; + __u16 info; + } connect_conf; + struct connect_ind { + __u16 plci; + __u8 controller; + __u8 si1; + __u8 si2; + __u8 eaz; + actcapi_addr addr; + } connect_ind; + struct connect_resp { + __u16 plci; + __u8 rejectcause; + } connect_resp; + struct connect_active_ind { + __u16 plci; + actcapi_addr addr; + } connect_active_ind; + struct connect_active_resp { + __u16 plci; + } connect_active_resp; + struct connect_b3_req { + __u16 plci; + actcapi_ncpi ncpi; + } connect_b3_req; + struct connect_b3_conf { + __u16 plci; + __u16 ncci; + __u16 info; + } connect_b3_conf; + struct connect_b3_ind { + __u16 ncci; + __u16 plci; + actcapi_ncpi ncpi; + } connect_b3_ind; + struct connect_b3_resp { + __u16 ncci; + __u8 rejectcause; + actcapi_ncpi ncpi __attribute__ ((packed)); + } connect_b3_resp; + struct disconnect_req { + __u16 plci; + __u8 cause; + } disconnect_req; + struct disconnect_conf { + __u16 plci; + __u16 info; + } disconnect_conf; + struct disconnect_ind { + __u16 plci; + __u16 info; + } disconnect_ind; + struct disconnect_resp { + __u16 plci; + } disconnect_resp; + struct connect_b3_active_ind { + __u16 ncci; + actcapi_ncpi ncpi; + } connect_b3_active_ind; + struct connect_b3_active_resp { + __u16 ncci; + } connect_b3_active_resp; + struct disconnect_b3_req { + __u16 ncci; + actcapi_ncpi ncpi; + } disconnect_b3_req; + struct disconnect_b3_conf { + __u16 ncci; + __u16 info; + } disconnect_b3_conf; + struct disconnect_b3_ind { + __u16 ncci; + __u16 info; + actcapi_ncpi ncpi; + } disconnect_b3_ind; + struct disconnect_b3_resp { + __u16 ncci; + } disconnect_b3_resp; + struct info_ind { + __u16 plci; + actcapi_infonr nr; + actcapi_infoel el; + } info_ind; + struct info_resp { + __u16 plci; + } info_resp; + struct listen_b3_req { + __u16 plci; + } listen_b3_req; + struct listen_b3_conf { + __u16 plci; + __u16 info; + } listen_b3_conf; + struct select_b2_protocol_req { + __u16 plci; + __u8 protocol; + actcapi_dlpd dlpd __attribute__ ((packed)); + } select_b2_protocol_req; + struct select_b2_protocol_conf { + __u16 plci; + __u16 info; + } select_b2_protocol_conf; + struct select_b3_protocol_req { + __u16 plci; + __u8 protocol; + actcapi_ncpd ncpd __attribute__ ((packed)); + } select_b3_protocol_req; + struct select_b3_protocol_conf { + __u16 plci; + __u16 info; + } select_b3_protocol_conf; +#if 0 + struct listen_req { + __u32 controller; + __u32 infomask; + __u32 cipmask; + __u32 cipmask2; + __u16 dummy; /* 2 Length-bytes of 2 Structs MUST always be 0!!! */ + } listen_req; + struct listen_conf { + __u32 controller; + __u16 info; + } listen_conf; +#else + struct listen_req { + __u8 controller; + __u32 infomask __attribute__ ((packed)); + __u16 eazmask __attribute__ ((packed)); + __u16 simask __attribute__ ((packed)); + } listen_req; + struct listen_conf { + __u8 controller; + __u16 info __attribute__ ((packed)); + } listen_conf; +#endif + struct data_b3_req { + __u16 fakencci; + __u16 datalen; + __u32 unused; + __u8 blocknr; + __u16 flags __attribute__ ((packed)); + } data_b3_req; + struct data_b3_ind { + __u16 fakencci; + __u16 datalen; + __u32 unused; + __u8 blocknr; + __u16 flags __attribute__ ((packed)); + } data_b3_ind; + struct data_b3_resp { + __u16 ncci; + __u8 blocknr; + } data_b3_resp; + struct data_b3_conf { + __u16 ncci; + __u8 blocknr; + __u16 info __attribute__ ((packed)); + } data_b3_conf; + } msg; +} actcapi_msg; + +extern __inline__ unsigned short +actcapi_nextsmsg(act2000_card *card) +{ + unsigned long flags; + unsigned short n; + + save_flags(flags); + cli(); + n = card->msgnum; + card->msgnum++; + card->msgnum &= 0x7fff; + restore_flags(flags); + return n; +} +#define DEBUG_MSG +#undef DEBUG_DATA_MSG +#undef DEBUG_DUMP_SKB + +extern int actcapi_chkhdr(act2000_card *, actcapi_msghdr *); +extern int actcapi_listen_req(act2000_card *); +extern int actcapi_manufacturer_req_net(act2000_card *); +extern int actcapi_manufacturer_req_v42(act2000_card *, ulong); +extern int actcapi_manufacturer_req_errh(act2000_card *); +extern int actcapi_manufacturer_req_msn(act2000_card *); +extern int actcapi_connect_req(act2000_card *, act2000_chan *, char *, char, int, int); +extern void actcapi_select_b2_protocol_req(act2000_card *, act2000_chan *); +extern void actcapi_disconnect_b3_req(act2000_card *, act2000_chan *); +extern void actcapi_connect_resp(act2000_card *, act2000_chan *, __u8); +extern void actcapi_dispatch(act2000_card *); +#ifdef DEBUG_MSG +extern void actcapi_debug_msg(struct sk_buff *skb, int); +#else +#define actcapi_debug_msg(skb, len) +#endif +#endif diff --git a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c new file mode 100644 index 000000000000..76be187f22ce --- /dev/null +++ b/drivers/isdn/act2000/module.c @@ -0,0 +1,953 @@ +/* $Id: module.c,v 1.7 1998/02/12 23:06:52 keil Exp $ + * + * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. + * + * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de) + * Thanks to Friedemann Baitinger and IBM Germany + * + * 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: module.c,v $ + * Revision 1.7 1998/02/12 23:06:52 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.6 1998/01/31 22:10:42 keil + * changes for 2.1.82 + * + * Revision 1.5 1997/10/09 22:23:04 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * + * Revision 1.4 1997/09/25 17:25:43 fritz + * Support for adding cards at runtime. + * Support for new Firmware. + * + * Revision 1.3 1997/09/24 23:11:45 fritz + * Optimized IRQ load and polling-mode. + * + * Revision 1.2 1997/09/24 19:44:17 fritz + * Added MSN mapping support, some cleanup. + * + * Revision 1.1 1997/09/23 18:00:13 fritz + * New driver for IBM Active 2000. + * + */ + +#include "act2000.h" +#include "act2000_isa.h" +#include "capi.h" + +static unsigned short isa_ports[] = +{ + 0x0200, 0x0240, 0x0280, 0x02c0, 0x0300, 0x0340, 0x0380, + 0xcfe0, 0xcfa0, 0xcf60, 0xcf20, 0xcee0, 0xcea0, 0xce60, +}; +#define ISA_NRPORTS (sizeof(isa_ports)/sizeof(unsigned short)) + +act2000_card *cards = (act2000_card *) NULL; + +/* Parameters to be set by insmod */ +static int act_bus = 0; +static int act_port = -1; /* -1 = Autoprobe */ +static int act_irq = -1; /* -1 = Autoselect */ +static char *act_id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + +MODULE_DESCRIPTION( "Driver for IBM Active 2000 ISDN card"); +MODULE_AUTHOR( "Fritz Elfert"); +MODULE_SUPPORTED_DEVICE( "ISDN subsystem"); +MODULE_PARM_DESC(act_bus, "BusType of first card, 1=ISA, 2=MCA, 3=PCMCIA, currently only ISA"); +MODULE_PARM_DESC(membase, "Base port address of first card"); +MODULE_PARM_DESC(act_irq, "IRQ of first card (-1 = grab next free IRQ)"); +MODULE_PARM_DESC(act_id, "ID-String of first card"); +MODULE_PARM(act_bus, "i"); +MODULE_PARM(act_port, "i"); +MODULE_PARM(act_irq, "i"); +MODULE_PARM(act_id, "s"); + +static int act2000_addcard(int, int, int, char *); + +static act2000_chan * +find_channel(act2000_card *card, int channel) +{ + if ((channel >= 0) && (channel < ACT2000_BCH)) + return &(card->bch[channel]); + printk(KERN_WARNING "act2000: Invalid channel %d\n", channel); + return NULL; +} + +/* + * Free MSN list + */ +static void +act2000_clear_msn(act2000_card *card) +{ + struct msn_entry *p = card->msn_list; + struct msn_entry *q; + unsigned long flags; + + save_flags(flags); + cli(); + card->msn_list = NULL; + restore_flags(flags); + while (p) { + q = p->next; + kfree(p); + p = q; + } +} + +/* + * Find an MSN entry in the list. + * If ia5 != 0, return IA5-encoded EAZ, else + * return a bitmask with corresponding bit set. + */ +static __u16 +act2000_find_msn(act2000_card *card, char *msn, int ia5) +{ + struct msn_entry *p = card->msn_list; + __u8 eaz = '0'; + + while (p) { + if (!strcmp(p->msn, msn)) { + eaz = p->eaz; + break; + } + p = p->next; + } + if (!ia5) + return (1 << (eaz - '0')); + else + return eaz; +} + +/* + * Find an EAZ entry in the list. + * return a string with corresponding msn. + */ +char * +act2000_find_eaz(act2000_card *card, char eaz) +{ + struct msn_entry *p = card->msn_list; + + while (p) { + if (p->eaz == eaz) + return(p->msn); + p = p->next; + } + return("\0"); +} + +/* + * Add or delete an MSN to the MSN list + * + * First character of msneaz is EAZ, rest is MSN. + * If length of eazmsn is 1, delete that entry. + */ +static int +act2000_set_msn(act2000_card *card, char *eazmsn) +{ + struct msn_entry *p = card->msn_list; + struct msn_entry *q = NULL; + unsigned long flags; + int i; + + if (!strlen(eazmsn)) + return 0; + if (strlen(eazmsn) > 16) + return -EINVAL; + for (i = 0; i < strlen(eazmsn); i++) + if (!isdigit(eazmsn[i])) + return -EINVAL; + if (strlen(eazmsn) == 1) { + /* Delete a single MSN */ + while (p) { + if (p->eaz == eazmsn[0]) { + save_flags(flags); + cli(); + if (q) + q->next = p->next; + else + card->msn_list = p->next; + restore_flags(flags); + kfree(p); + printk(KERN_DEBUG + "Mapping for EAZ %c deleted\n", + eazmsn[0]); + return 0; + } + q = p; + p = p->next; + } + return 0; + } + /* Add a single MSN */ + while (p) { + /* Found in list, replace MSN */ + if (p->eaz == eazmsn[0]) { + save_flags(flags); + cli(); + strcpy(p->msn, &eazmsn[1]); + restore_flags(flags); + printk(KERN_DEBUG + "Mapping for EAZ %c changed to %s\n", + eazmsn[0], + &eazmsn[1]); + return 0; + } + p = p->next; + } + /* Not found in list, add new entry */ + p = kmalloc(sizeof(msn_entry), GFP_KERNEL); + if (!p) + return -ENOMEM; + p->eaz = eazmsn[0]; + strcpy(p->msn, &eazmsn[1]); + p->next = card->msn_list; + save_flags(flags); + cli(); + card->msn_list = p; + restore_flags(flags); + printk(KERN_DEBUG + "Mapping %c -> %s added\n", + eazmsn[0], + &eazmsn[1]); + return 0; +} + +static void +act2000_transmit(struct act2000_card *card) +{ + switch (card->bus) { + case ACT2000_BUS_ISA: + isa_send(card); + break; + case ACT2000_BUS_PCMCIA: + case ACT2000_BUS_MCA: + default: + printk(KERN_WARNING + "act2000_transmit: Illegal bustype %d\n", card->bus); + } +} + +static void +act2000_receive(struct act2000_card *card) +{ + switch (card->bus) { + case ACT2000_BUS_ISA: + isa_receive(card); + break; + case ACT2000_BUS_PCMCIA: + case ACT2000_BUS_MCA: + default: + printk(KERN_WARNING + "act2000_receive: Illegal bustype %d\n", card->bus); + } +} + +static void +act2000_poll(unsigned long data) +{ + act2000_card * card = (act2000_card *)data; + unsigned long flags; + + act2000_receive(card); + save_flags(flags); + cli(); + del_timer(&card->ptimer); + card->ptimer.expires = jiffies + 3; + add_timer(&card->ptimer); + restore_flags(flags); +} + +static int +act2000_command(act2000_card * card, isdn_ctrl * c) +{ + ulong a; + act2000_chan *chan; + act2000_cdef cdef; + isdn_ctrl cmd; + char tmp[17]; + int ret; + unsigned long flags; + + switch (c->command) { + case ISDN_CMD_IOCTL: + memcpy(&a, c->parm.num, sizeof(ulong)); + switch (c->arg) { + case ACT2000_IOCTL_LOADBOOT: + switch (card->bus) { + case ACT2000_BUS_ISA: + ret = isa_download(card, + (act2000_ddef *)a); + if (!ret) { + card->flags |= ACT2000_FLAGS_LOADED; + if (!(card->flags & ACT2000_FLAGS_IVALID)) { + card->ptimer.expires = jiffies + 3; + card->ptimer.function = act2000_poll; + card->ptimer.data = (unsigned long)card; + add_timer(&card->ptimer); + } + actcapi_manufacturer_req_errh(card); + } + break; + default: + printk(KERN_WARNING + "act2000: Illegal BUS type %d\n", + card->bus); + ret = -EIO; + } + return ret; + case ACT2000_IOCTL_SETPROTO: + card->ptype = a?ISDN_PTYPE_EURO:ISDN_PTYPE_1TR6; + if (!(card->flags & ACT2000_FLAGS_RUNNING)) + return 0; + actcapi_manufacturer_req_net(card); + return 0; + case ACT2000_IOCTL_SETMSN: + if ((ret = copy_from_user(tmp, (char *)a, sizeof(tmp)))) + return ret; + if ((ret = act2000_set_msn(card, tmp))) + return ret; + if (card->flags & ACT2000_FLAGS_RUNNING) + return(actcapi_manufacturer_req_msn(card)); + return 0; + case ACT2000_IOCTL_ADDCARD: + if ((ret = copy_from_user(&cdef, (char *)a, sizeof(cdef)))) + return ret; + if (act2000_addcard(cdef.bus, cdef.port, cdef.irq, cdef.id)) + return -EIO; + return 0; + case ACT2000_IOCTL_TEST: + if (!(card->flags & ACT2000_FLAGS_RUNNING)) + return -ENODEV; + return 0; + default: + return -EINVAL; + } + break; + case ISDN_CMD_DIAL: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + save_flags(flags); + cli(); + if (chan->fsm_state != ACT2000_STATE_NULL) { + restore_flags(flags); + printk(KERN_WARNING "Dial on channel with state %d\n", + chan->fsm_state); + return -EBUSY; + } + if (card->ptype == ISDN_PTYPE_EURO) + tmp[0] = act2000_find_msn(card, c->parm.setup.eazmsn, 1); + else + tmp[0] = c->parm.setup.eazmsn[0]; + chan->fsm_state = ACT2000_STATE_OCALL; + chan->callref = 0xffff; + restore_flags(flags); + ret = actcapi_connect_req(card, chan, c->parm.setup.phone, + tmp[0], c->parm.setup.si1, + c->parm.setup.si2); + if (ret) { + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg &= 0x0f; + card->interface.statcallb(&cmd); + } + return ret; + case ISDN_CMD_ACCEPTD: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + if (chan->fsm_state == ACT2000_STATE_ICALL) + actcapi_select_b2_protocol_req(card, chan); + return 0; + case ISDN_CMD_ACCEPTB: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + return 0; + case ISDN_CMD_HANGUP: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + switch (chan->fsm_state) { + case ACT2000_STATE_ICALL: + case ACT2000_STATE_BSETUP: + actcapi_connect_resp(card, chan, 0x15); + break; + case ACT2000_STATE_ACTIVE: + actcapi_disconnect_b3_req(card, chan); + break; + } + return 0; + case ISDN_CMD_SETEAZ: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + if (strlen(c->parm.num)) { + if (card->ptype == ISDN_PTYPE_EURO) { + chan->eazmask = act2000_find_msn(card, c->parm.num, 0); + } + if (card->ptype == ISDN_PTYPE_1TR6) { + int i; + chan->eazmask = 0; + for (i = 0; i < strlen(c->parm.num); i++) + if (isdigit(c->parm.num[i])) + chan->eazmask |= (1 << (c->parm.num[i] - '0')); + } + } else + chan->eazmask = 0x3ff; + actcapi_listen_req(card); + return 0; + case ISDN_CMD_CLREAZ: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + chan->eazmask = 0; + actcapi_listen_req(card); + return 0; + case ISDN_CMD_SETL2: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + chan->l2prot = (c->arg >> 8); + return 0; + case ISDN_CMD_GETL2: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + return chan->l2prot; + case ISDN_CMD_SETL3: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg >> 8) != ISDN_PROTO_L3_TRANS) { + printk(KERN_WARNING "L3 protocol unknown\n"); + return -1; + } + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + chan->l3prot = (c->arg >> 8); + return 0; + case ISDN_CMD_GETL3: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + return chan->l3prot; + case ISDN_CMD_GETEAZ: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + printk(KERN_DEBUG "act2000 CMD_GETEAZ not implemented\n"); + return 0; + case ISDN_CMD_SETSIL: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + printk(KERN_DEBUG "act2000 CMD_SETSIL not implemented\n"); + return 0; + case ISDN_CMD_GETSIL: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + printk(KERN_DEBUG "act2000 CMD_GETSIL not implemented\n"); + return 0; + case ISDN_CMD_LOCK: + MOD_INC_USE_COUNT; + return 0; + case ISDN_CMD_UNLOCK: + MOD_DEC_USE_COUNT; + return 0; + } + + return -EINVAL; +} + +static int +act2000_sendbuf(act2000_card *card, int channel, int ack, struct sk_buff *skb) +{ + struct sk_buff *xmit_skb; + int len; + act2000_chan *chan; + actcapi_msg *msg; + + if (!(chan = find_channel(card, channel))) + return -1; + if (chan->fsm_state != ACT2000_STATE_ACTIVE) + return -1; + len = skb->len; + if ((chan->queued + len) >= ACT2000_MAX_QUEUED) + return 0; + if (!len) + return 0; + if (skb_headroom(skb) < 19) { + printk(KERN_WARNING "act2000_sendbuf: Headroom only %d\n", + skb_headroom(skb)); + xmit_skb = alloc_skb(len + 19, GFP_ATOMIC); + if (!xmit_skb) { + printk(KERN_WARNING "act2000_sendbuf: Out of memory\n"); + return 0; + } + skb_reserve(xmit_skb, 19); + memcpy(skb_put(xmit_skb, len), skb->data, len); + } else { + xmit_skb = skb_clone(skb, GFP_ATOMIC); + if (!xmit_skb) { + printk(KERN_WARNING "act2000_sendbuf: Out of memory\n"); + return 0; + } + } + dev_kfree_skb(skb); + msg = (actcapi_msg *)skb_push(xmit_skb, 19); + msg->hdr.len = 19 + len; + msg->hdr.applicationID = 1; + msg->hdr.cmd.cmd = 0x86; + msg->hdr.cmd.subcmd = 0x00; + msg->hdr.msgnum = actcapi_nextsmsg(card); + msg->msg.data_b3_req.datalen = len; + msg->msg.data_b3_req.blocknr = (msg->hdr.msgnum & 0xff); + msg->msg.data_b3_req.fakencci = MAKE_NCCI(chan->plci, 0, chan->ncci); + msg->msg.data_b3_req.flags = ack; /* Will be set to 0 on actual sending */ + actcapi_debug_msg(xmit_skb, 1); + chan->queued += len; + skb_queue_tail(&card->sndq, xmit_skb); + act2000_schedule_tx(card); + return len; +} + + +/* Read the Status-replies from the Interface */ +static int +act2000_readstatus(u_char * buf, int len, int user, act2000_card * card) +{ + int count; + u_char *p; + + for (p = buf, count = 0; count < len; p++, count++) { + if (card->status_buf_read == card->status_buf_write) + return count; + if (user) + put_user(*card->status_buf_read++, p); + else + *p = *card->status_buf_read++; + if (card->status_buf_read > card->status_buf_end) + card->status_buf_read = card->status_buf; + } + return count; +} + +static void +act2000_putmsg(act2000_card *card, char c) +{ + ulong flags; + + save_flags(flags); + cli(); + *card->status_buf_write++ = c; + if (card->status_buf_write == card->status_buf_read) { + if (++card->status_buf_read > card->status_buf_end) + card->status_buf_read = card->status_buf; + } + if (card->status_buf_write > card->status_buf_end) + card->status_buf_write = card->status_buf; + restore_flags(flags); +} + +static void +act2000_logstat(struct act2000_card *card, char *str) +{ + char *p = str; + isdn_ctrl c; + + while (*p) + act2000_putmsg(card, *p++); + c.command = ISDN_STAT_STAVAIL; + c.driver = card->myid; + c.arg = strlen(str); + card->interface.statcallb(&c); +} + +/* + * Find card with given driverId + */ +static inline act2000_card * +act2000_findcard(int driverid) +{ + act2000_card *p = cards; + + while (p) { + if (p->myid == driverid) + return p; + p = p->next; + } + return (act2000_card *) 0; +} + +/* + * Wrapper functions for interface to linklevel + */ +static int +if_command(isdn_ctrl * c) +{ + act2000_card *card = act2000_findcard(c->driver); + + if (card) + return (act2000_command(card, c)); + printk(KERN_ERR + "act2000: 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) +{ + act2000_card *card = act2000_findcard(id); + + if (card) { + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + return (len); + } + printk(KERN_ERR + "act2000: if_writecmd called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_readstatus(u_char * buf, int len, int user, int id, int channel) +{ + act2000_card *card = act2000_findcard(id); + + if (card) { + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + return (act2000_readstatus(buf, len, user, card)); + } + printk(KERN_ERR + "act2000: if_readstatus called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_sendbuf(int id, int channel, int ack, struct sk_buff *skb) +{ + act2000_card *card = act2000_findcard(id); + + if (card) { + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + return (act2000_sendbuf(card, channel, ack, skb)); + } + printk(KERN_ERR + "act2000: if_sendbuf called with invalid driverId!\n"); + return -ENODEV; +} + + +/* + * Allocate a new card-struct, initialize it + * link it into cards-list. + */ +static void +act2000_alloccard(int bus, int port, int irq, char *id) +{ + int i; + act2000_card *card; + if (!(card = (act2000_card *) kmalloc(sizeof(act2000_card), GFP_KERNEL))) { + printk(KERN_WARNING + "act2000: (%s) Could not allocate card-struct.\n", id); + return; + } + memset((char *) card, 0, sizeof(act2000_card)); + skb_queue_head_init(&card->sndq); + skb_queue_head_init(&card->rcvq); + skb_queue_head_init(&card->ackq); + card->snd_tq.routine = (void *) (void *) act2000_transmit; + card->snd_tq.data = card; + card->rcv_tq.routine = (void *) (void *) actcapi_dispatch; + card->rcv_tq.data = card; + card->poll_tq.routine = (void *) (void *) act2000_receive; + card->poll_tq.data = card; + init_timer(&card->ptimer); + card->interface.channels = ACT2000_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 | +#if 0 +/* Not yet! New Firmware is on the way ... */ + ISDN_FEATURE_L2_TRANS | +#endif + ISDN_FEATURE_L3_TRANS | + ISDN_FEATURE_P_UNKNOWN; + card->interface.hl_hdrlen = 20; + card->ptype = ISDN_PTYPE_EURO; + strncpy(card->interface.id, id, sizeof(card->interface.id) - 1); + for (i=0; ibch[i].plci = 0x8000; + card->bch[i].ncci = 0x8000; + card->bch[i].l2prot = ISDN_PROTO_L2_X75I; + card->bch[i].l3prot = ISDN_PROTO_L3_TRANS; + } + card->myid = -1; + card->bus = bus; + card->port = port; + card->irq = irq; + card->next = cards; + cards = card; +} + +/* + * register card at linklevel + */ +static int +act2000_registercard(act2000_card * card) +{ + switch (card->bus) { + case ACT2000_BUS_ISA: + break; + case ACT2000_BUS_MCA: + case ACT2000_BUS_PCMCIA: + default: + printk(KERN_WARNING + "act2000: Illegal BUS type %d\n", + card->bus); + return -1; + } + if (!register_isdn(&card->interface)) { + printk(KERN_WARNING + "act2000: Unable to register %s\n", + card->interface.id); + return -1; + } + card->myid = card->interface.channels; + sprintf(card->regname, "act2000-isdn (%s)", card->interface.id); + return 0; +} + +static void +unregister_card(act2000_card * card) +{ + isdn_ctrl cmd; + + cmd.command = ISDN_STAT_UNLOAD; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + switch (card->bus) { + case ACT2000_BUS_ISA: + isa_release(card); + break; + case ACT2000_BUS_MCA: + case ACT2000_BUS_PCMCIA: + default: + printk(KERN_WARNING + "act2000: Invalid BUS type %d\n", + card->bus); + break; + } +} + +static int +act2000_addcard(int bus, int port, int irq, char *id) +{ + act2000_card *p; + act2000_card *q = NULL; + int initialized; + int added = 0; + int failed = 0; + int i; + + if (!bus) + bus = ACT2000_BUS_ISA; + if (port != -1) { + /* Port defined, do fixed setup */ + act2000_alloccard(bus, port, irq, id); + } else { + /* No port defined, perform autoprobing. + * This may result in more than one card detected. + */ + switch (bus) { + case ACT2000_BUS_ISA: + for (i = 0; i < ISA_NRPORTS; i++) + if (isa_detect(isa_ports[i])) { + printk(KERN_INFO + "act2000: Detected ISA card at port 0x%x\n", + isa_ports[i]); + act2000_alloccard(bus, isa_ports[i], irq, id); + } + break; + case ACT2000_BUS_MCA: + case ACT2000_BUS_PCMCIA: + default: + printk(KERN_WARNING + "act2000: addcard: Invalid BUS type %d\n", + bus); + } + } + if (!cards) + return 1; + p = cards; + while (p) { + initialized = 0; + if (!p->interface.statcallb) { + /* Not yet registered. + * Try to register and activate it. + */ + added++; + switch (p->bus) { + case ACT2000_BUS_ISA: + if (isa_detect(p->port)) { + if (act2000_registercard(p)) + break; + if (isa_config_port(p, p->port)) { + printk(KERN_WARNING + "act2000: Could not request port 0x%04x\n", + p->port); + unregister_card(p); + p->interface.statcallb = NULL; + break; + } + if (isa_config_irq(p, p->irq)) { + printk(KERN_INFO + "act2000: No IRQ available, fallback to polling\n"); + /* Fall back to polled operation */ + p->irq = 0; + } + printk(KERN_INFO + "act2000: ISA" + "-type card at port " + "0x%04x ", + p->port); + if (p->irq) + printk("irq %d\n", p->irq); + else + printk("polled\n"); + initialized = 1; + } + break; + case ACT2000_BUS_MCA: + case ACT2000_BUS_PCMCIA: + default: + printk(KERN_WARNING + "act2000: addcard: Invalid BUS type %d\n", + p->bus); + } + } else + /* Card already initialized */ + initialized = 1; + if (initialized) { + /* Init OK, next card ... */ + q = p; + p = p->next; + } else { + /* Init failed, remove card from list, free memory */ + printk(KERN_WARNING + "act2000: Initialization of %s failed\n", + p->interface.id); + if (q) { + q->next = p->next; + kfree(p); + p = q->next; + } else { + cards = p->next; + kfree(p); + p = cards; + } + failed++; + } + } + return (added - failed); +} + +#define DRIVERNAME "IBM Active 2000 ISDN driver" + +#ifdef MODULE +#define act2000_init init_module +#endif + +int +act2000_init(void) +{ + printk(KERN_INFO "%s\n", DRIVERNAME); + if (!cards) + act2000_addcard(act_bus, act_port, act_irq, act_id); + if (!cards) + printk(KERN_INFO "act2000: No cards defined yet\n"); + /* No symbols to export, hide all symbols */ + EXPORT_NO_SYMBOLS; + return 0; +} + +#ifdef MODULE +void +cleanup_module(void) +{ + act2000_card *card = cards; + act2000_card *last; + while (card) { + unregister_card(card); + del_timer(&card->ptimer); + card = card->next; + } + card = cards; + while (card) { + last = card; + card = card->next; + act2000_clear_msn(last); + kfree(last); + } + printk(KERN_INFO "%s unloaded\n", DRIVERNAME); +} + +#else +void +act2000_setup(char *str, int *ints) +{ + int i, j, argc, port, irq, bus; + + argc = ints[0]; + i = 1; + if (argc) + while (argc) { + port = irq = -1; + bus = 0; + if (argc) { + bus = ints[i]; + i++; + argc--; + } + if (argc) { + port = ints[i]; + i++; + argc--; + } + if (argc) { + irq = ints[i]; + i++; + argc--; + } + act2000_addcard(bus, port, irq, act_id); + } +} +#endif diff --git a/drivers/isdn/avmb1/b1capi.c b/drivers/isdn/avmb1/b1capi.c index 00be6fef778c..648f19fc2372 100644 --- a/drivers/isdn/avmb1/b1capi.c +++ b/drivers/isdn/avmb1/b1capi.c @@ -1,11 +1,39 @@ /* - * $Id: b1capi.c,v 1.4 1997/05/27 15:17:45 fritz Exp $ + * $Id: b1capi.c,v 1.10 1998/02/13 07:09:10 calle 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.10 1998/02/13 07:09:10 calle + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.9 1998/01/31 11:14:39 calle + * merged changes to 2.0 tree, prepare 2.1.82 to work. + * + * Revision 1.8 1997/12/10 20:00:46 calle + * get changes from 2.0 version + * + * Revision 1.4.2.5 1997/12/07 19:59:54 calle + * more changes for M1/T1/B1 + config + * + * Revision 1.4.2.4 1997/11/26 16:57:20 calle + * more changes for B1/M1/T1. + * + * Revision 1.7 1997/10/19 14:45:40 calle + * fixed capi_get_version. + * + * Revision 1.6 1997/10/01 09:21:09 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.5 1997/07/12 08:22:26 calle + * Correct bug in CARD_NR macro, so now more than one card will work. + * Allow card reset, even if card is in running state. + * + * * Revision 1.4 1997/05/27 15:17:45 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -48,24 +76,16 @@ #include "capicmd.h" #include "capiutil.h" -static char *revision = "$Revision: 1.4 $"; +static char *revision = "$Revision: 1.10 $"; /* ------------------------------------------------------------- */ int showcapimsgs = 0; /* used in lli.c */ int loaddebug = 0; -static int portbase = 0x150; -#ifdef MODULE -static int irq = 15; -#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 -#endif /* ------------------------------------------------------------- */ @@ -97,7 +117,7 @@ typedef struct avmb1_appl { /* ------------------------------------------------------------- */ -static struct capi_version driver_version = {2, 0, 0, 9}; +static struct capi_version driver_version = {2, 0, 1, 1<<4}; static char driver_serial[CAPI_SERIAL_LEN] = "4711"; static char capi_manufakturer[64] = "AVM Berlin"; @@ -109,9 +129,9 @@ static char capi_manufakturer[64] = "AVM Berlin"; #define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f) -#define VALID_CARD(c) ((c) > 0 && (c) <= ncards) +#define VALID_CARD(c) ((c) > 0 && (c) <= CAPI_MAXCONTR) #define CARD(c) (&cards[(c)-1]) -#define CARDNR(cp) ((cards-(cp))+1) +#define CARDNR(cp) (((cp)-cards)+1) static avmb1_appl applications[CAPI_MAXAPPL]; static avmb1_card cards[CAPI_MAXCONTR]; @@ -126,6 +146,17 @@ static struct tq_struct tq_recv_notify; /* -------- util functions ------------------------------------ */ +static char *cardtype2str(int cardtype) +{ + switch (cardtype) { + default: + case AVM_CARDTYPE_B1: return "B1"; + case AVM_CARDTYPE_M1: return "M1"; + case AVM_CARDTYPE_M2: return "M2"; + case AVM_CARDTYPE_T1: return "T1"; + } +} + static inline int capi_cmd_valid(__u8 cmd) { switch (cmd) { @@ -287,6 +318,8 @@ static avmb1_ncci *find_ncci(avmb1_appl * app, __u32 ncci) return 0; } + + /* -------- Receiver ------------------------------------------ */ @@ -367,6 +400,7 @@ static void notify_up(__u16 contr) { struct capi_interface_user *p; + printk(KERN_NOTICE "b1capi: notify up contr %d\n", contr); for (p = capi_users; p; p = p->next) { if (p->callback) (*p->callback) (KCI_CONTRUP, contr, @@ -378,6 +412,7 @@ static void notify_up(__u16 contr) static void notify_down(__u16 contr) { struct capi_interface_user *p; + printk(KERN_NOTICE "b1capi: notify down contr %d\n", contr); for (p = capi_users; p; p = p->next) { if (p->callback) (*p->callback) (KCI_CONTRDOWN, contr, 0); @@ -400,18 +435,22 @@ static void notify_handler(void *dummy) void avmb1_card_ready(avmb1_card * card) { + struct capi_profile *profp = + (struct capi_profile *)card->version[VER_PROFILE]; + char *dversion = card->version[VER_DRIVER]; __u16 appl; + char *cardname, cname[20]; + __u32 flag; card->cversion.majorversion = 2; card->cversion.minorversion = 0; - card->cversion.majormanuversion = (card->version[VER_DRIVER][0] - '0') << 4; - card->cversion.majormanuversion |= (card->version[VER_DRIVER][2] - '0'); - card->cversion.minormanuversion = (card->version[VER_DRIVER][3] - '0') << 4; - card->cversion.minormanuversion |= (card->version[VER_DRIVER][5] - '0') * 10; - card->cversion.minormanuversion |= (card->version[VER_DRIVER][6] - '0'); + card->cversion.majormanuversion = (((dversion[0] - '0') & 0xf) << 4); + card->cversion.majormanuversion |= ((dversion[2] - '0') & 0xf); + card->cversion.minormanuversion = (dversion[3] - '0') << 4; + card->cversion.minormanuversion |= + (dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf); card->cardstate = CARD_RUNNING; - for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { if (VALID_APPLID(appl) && !APPL(appl)->releasing) { B1_send_register(card->port, appl, @@ -424,56 +463,190 @@ void avmb1_card_ready(avmb1_card * card) set_bit(CARDNR(card), ¬ify_up_set); queue_task(&tq_state_notify, &tq_scheduler); + + flag = ((__u8 *)(profp->manu))[1]; + switch (flag) { + case 0: cardname = cardtype2str(card->cardtype); break; + case 3: cardname = "PCMCIA B"; break; + case 4: cardname = "PCMCIA M1"; break; + case 5: cardname = "PCMCIA M2"; break; + case 6: cardname = "B1 V3.0"; break; + case 7: cardname = "B1 PCI"; break; + default: cardname = cname; break; + sprintf(cname, "AVM?%u", (unsigned int)flag); + break; + } + printk(KERN_NOTICE "b1capi: card %d \"%s\" ready.\n", + CARDNR(card), cardname); + flag = ((__u8 *)(profp->manu))[3]; + if (flag) + printk(KERN_NOTICE "b1capi: card %d Protocol:%s%s%s%s%s%s%s\n", + CARDNR(card), + (flag & 0x01) ? " DSS1" : "", + (flag & 0x02) ? " CT1" : "", + (flag & 0x04) ? " VN3" : "", + (flag & 0x08) ? " NI1" : "", + (flag & 0x10) ? " AUSTEL" : "", + (flag & 0x20) ? " ESS" : "", + (flag & 0x40) ? " 1TR6" : "" + ); + flag = ((__u8 *)(profp->manu))[5]; + if (flag) + printk(KERN_NOTICE "b1capi: card %d Linetype:%s%s%s%s\n", + CARDNR(card), + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); +} + +static void avmb1_card_down(avmb1_card * card, int notify) +{ + __u16 appl; + + card->cardstate = CARD_DETECTED; + + for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { + 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 forced down!\n", appl, np->ncci); + kfree(np); + nextpp = pp; + } else { + nextpp = &(*pp)->next; + } + } + } + set_bit(CARDNR(card), ¬ify_down_set); + queue_task(&tq_state_notify, &tq_scheduler); + printk(KERN_NOTICE "b1capi: card %d down.\n", CARDNR(card)); } /* ------------------------------------------------------------- */ -int avmb1_addcard(int port, int irq) + +int avmb1_registercard(int port, int irq, int cardtype, int allocio) { struct avmb1_card *card; - int irqval; + int irqval,i; + + for (i=0; i < CAPI_MAXCONTR && cards[i].cardstate != CARD_FREE; i++) ; + + if (i == CAPI_MAXCONTR) { + printk(KERN_ERR "b1capi: out of controller slots\n"); + return -ENFILE; + } - card = &cards[ncards]; + card = &cards[i]; memset(card, 0, sizeof(avmb1_card)); - sprintf(card->name, "avmb1-%d", ncards + 1); + sprintf(card->name, "avmb1-%d", CARDNR(card)); - request_region(port, AVMB1_PORTLEN, card->name); + if (allocio) + request_region(port, AVMB1_PORTLEN, card->name); - if ((irqval = request_irq(irq, avmb1_interrupt, SA_SHIRQ, card->name, card)) != 0) { + if ((irqval = request_irq(irq, avmb1_interrupt, + SA_SHIRQ, card->name, card)) != 0) { printk(KERN_ERR "b1capi: unable to get IRQ %d (irqval=%d).\n", irq, irqval); release_region((unsigned short) port, AVMB1_PORTLEN); return -EIO; } + + card->cardstate = CARD_DETECTED; ncards++; - card->cnr = ncards; + card->cnr = CARDNR(card); card->port = port; card->irq = irq; - card->cardstate = CARD_DETECTED; - return 0; + card->cardtype = cardtype; + return card->cnr; +} + +int avmb1_addcard(int port, int irq, int cardtype) +{ + return avmb1_registercard(port, irq, cardtype, 1); } -int avmb1_probecard(int port, int irq) +int avmb1_detectcard(int port, int irq, int cardtype) { int rc; - if (check_region((unsigned short) port, AVMB1_PORTLEN)) { - printk(KERN_WARNING - "b1capi: ports 0x%03x-0x%03x in use.\n", - portbase, portbase + AVMB1_PORTLEN); + if (!B1_valid_irq(irq, cardtype)) { + printk(KERN_WARNING "b1capi: irq %d not valid for %s-card.\n", + irq, cardtype2str(cardtype)); return -EIO; } - if (!B1_valid_irq(irq)) { - printk(KERN_WARNING "b1capi: irq %d not valid.\n", irq); + if ((rc = B1_detect(port, cardtype)) != 0) { + printk(KERN_NOTICE "b1capi: NO %s-card at 0x%x (%d)\n", + cardtype2str(cardtype), port, rc); return -EIO; } - if ((rc = B1_detect(port)) != 0) { - printk(KERN_NOTICE "b1capi: NO card at 0x%x (%d)\n", port, rc); + B1_reset(port); + switch (cardtype) { + default: + case AVM_CARDTYPE_M1: + case AVM_CARDTYPE_M2: + case AVM_CARDTYPE_B1: + printk(KERN_NOTICE "b1capi: AVM-%s-Controller detected at 0x%x\n", cardtype2str(cardtype), port); + break; + case AVM_CARDTYPE_T1: + printk(KERN_NOTICE "b1capi: AVM-%s-Controller may be at 0x%x\n", cardtype2str(cardtype), port); + break; + } + + return 0; +} + +int avmb1_probecard(int port, int irq, int cardtype) +{ + if (check_region((unsigned short) port, AVMB1_PORTLEN)) { + printk(KERN_WARNING + "b1capi: ports 0x%03x-0x%03x in use.\n", + port, port + AVMB1_PORTLEN); return -EIO; } - B1_reset(port); - printk(KERN_NOTICE "b1capi: AVM-B1-Controller detected at 0x%x\n", port); + return avmb1_detectcard(port, irq, cardtype); +} + +int avmb1_unregistercard(int cnr, int freeio) +{ + avmb1_card * card; + if (!VALID_CARD(cnr)) + return -ESRCH; + card = CARD(cnr); + if (card->cardstate == CARD_FREE) + return -ESRCH; + if (card->cardstate == CARD_RUNNING) + avmb1_card_down(card, freeio); + + free_irq(card->irq, card); + if (freeio) + release_region(card->port, AVMB1_PORTLEN); + card->cardstate = CARD_FREE; + return 0; +} + +int avmb1_resetcard(int cnr) +{ + avmb1_card * card; + + if (!VALID_CARD(cnr)) + return -ESRCH; + card = CARD(cnr); + if (card->cardstate == CARD_FREE) + return -ESRCH; + + if (card->cardstate == CARD_RUNNING) + avmb1_card_down(card, 0); + + B1_reset(card->port); + B1_reset(card->port); + + card->cardstate = CARD_DETECTED; return 0; } @@ -485,7 +658,7 @@ int avmb1_probecard(int port, int irq) static int capi_installed(void) { int i; - for (i = 0; i < ncards; i++) { + for (i = 0; i < CAPI_MAXCONTR; i++) { if (cards[i].cardstate == CARD_RUNNING) return 1; } @@ -512,7 +685,7 @@ static __u16 capi_register(capi_register_params * rparam, __u16 * applidp) memcpy(&APPL(appl)->rparam, rparam, sizeof(capi_register_params)); - for (i = 0; i < ncards; i++) { + for (i = 0; i < CAPI_MAXCONTR; i++) { if (cards[i].cardstate != CARD_RUNNING) continue; B1_send_register(cards[i].port, appl, @@ -538,9 +711,10 @@ static __u16 capi_release(__u16 applid) return CAPI_ILLAPPNR; while ((skb = skb_dequeue(&APPL(applid)->recv_queue)) != 0) kfree_skb(skb); - for (i = 0; i < ncards; i++) { - if (cards[i].cardstate != CARD_RUNNING) + for (i = 0; i < CAPI_MAXCONTR; i++) { + if (cards[i].cardstate != CARD_RUNNING) { continue; + } APPL(applid)->releasing++; B1_send_release(cards[i].port, applid); } @@ -628,7 +802,7 @@ static __u16 capi_get_version(__u16 contr, struct capi_version *verp) if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) return 0x2002; - memcpy((void *) verp, CARD(contr)->version[VER_SERIAL], + memcpy((void *) verp, &CARD(contr)->cversion, sizeof(capi_version)); return CAPI_NOERROR; } @@ -665,33 +839,52 @@ static __u16 capi_get_profile(__u16 contr, struct capi_profile *profp) static int capi_manufacturer(unsigned int cmd, void *data) { unsigned long flags; - avmb1_loaddef ldef; - avmb1_carddef cdef; + avmb1_loadandconfigdef ldef; + avmb1_extcarddef cdef; avmb1_resetdef rdef; + avmb1_getdef gdef; avmb1_card *card; int rc; switch (cmd) { case AVMB1_ADDCARD: - if ((rc = copy_from_user((void *) &cdef, data, - sizeof(avmb1_carddef)))) - return rc; - if (!B1_valid_irq(cdef.irq)) - return -EINVAL; + case AVMB1_ADDCARD_WITH_TYPE: + if (cmd == AVMB1_ADDCARD) { + if ((rc = copy_from_user((void *) &cdef, data, + sizeof(avmb1_carddef)))) + return rc; + cdef.cardtype = AVM_CARDTYPE_B1; + } else { + if ((rc = copy_from_user((void *) &cdef, data, + sizeof(avmb1_extcarddef)))) + return rc; + } - if ((rc = avmb1_probecard(cdef.port, cdef.irq)) != 0) + if ((rc = avmb1_probecard(cdef.port, cdef.irq, cdef.cardtype)) != 0) return rc; - return avmb1_addcard(cdef.port, cdef.irq); + return avmb1_addcard(cdef.port, cdef.irq, cdef.cardtype); case AVMB1_LOAD: + case AVMB1_LOAD_AND_CONFIG: + + if (cmd == AVMB1_LOAD) { + if ((rc = copy_from_user((void *) &ldef, data, + sizeof(avmb1_loaddef)))) + return rc; + ldef.t4config.len = 0; + ldef.t4config.data = 0; + } else { + if ((rc = copy_from_user((void *) &ldef, data, + sizeof(avmb1_loadandconfigdef)))) + return rc; + } + if (!VALID_CARD(ldef.contr)) + return -ESRCH; - if ((rc = copy_from_user((void *) &ldef, data, - sizeof(avmb1_loaddef)))) - return rc; - if (!VALID_CARD(ldef.contr) || ldef.t4file.len <= 0) { + if (ldef.t4file.len <= 0) { if (loaddebug) - printk(KERN_DEBUG "b1capi: load: invalid parameter contr=%d len=%d\n", ldef.contr, ldef.t4file.len); + printk(KERN_DEBUG "b1capi: load: invalid parameter length of t4file is %d ?\n", ldef.t4file.len); return -EINVAL; } @@ -720,6 +913,20 @@ static int capi_manufacturer(unsigned int cmd, void *data) return rc; } B1_disable_irq(card->port); + + if (ldef.t4config.len > 0) { /* load config */ + if (loaddebug) { + printk(KERN_DEBUG "b1capi: loading config to contr %d\n", + ldef.contr); + } + if ((rc = B1_load_config(card->port, &ldef.t4config))) { + B1_reset(card->port); + printk(KERN_ERR "b1capi: failed to load config!!\n"); + card->cardstate = CARD_DETECTED; + return rc; + } + } + if (loaddebug) { printk(KERN_DEBUG "b1capi: load: ready contr %d: checking\n", ldef.contr); @@ -737,7 +944,7 @@ static int capi_manufacturer(unsigned int cmd, void *data) card->cardstate = CARD_INITSTATE; save_flags(flags); cli(); - B1_assign_irq(card->port, card->irq); + B1_assign_irq(card->port, card->irq, card->cardtype); B1_enable_irq(card->port); restore_flags(flags); @@ -766,23 +973,31 @@ static int capi_manufacturer(unsigned int cmd, void *data) return -EINTR; } return 0; + case AVMB1_RESETCARD: if ((rc = copy_from_user((void *) &rdef, data, sizeof(avmb1_resetdef)))) return rc; - if (!VALID_CARD(rdef.contr)) - return -EINVAL; + return avmb1_resetcard(rdef.contr); + + case AVMB1_GET_CARDINFO: + if ((rc = copy_from_user((void *) &gdef, data, + sizeof(avmb1_getdef)))) + return rc; - card = CARD(rdef.contr); + if (!VALID_CARD(gdef.contr)) + return -ESRCH; - if (card->cardstate == CARD_RUNNING) - return -EBUSY; + card = CARD(gdef.contr); - B1_reset(card->port); - B1_reset(card->port); + gdef.cardstate = card->cardstate; + gdef.cardtype = card->cardtype; + + if ((rc = copy_to_user(data, (void *) &gdef, + sizeof(avmb1_getdef)))) + return rc; - card->cardstate = CARD_DETECTED; return 0; } return -EINVAL; @@ -845,22 +1060,14 @@ int detach_capi_interface(struct capi_interface_user *userp) /* -------- 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 +EXPORT_SYMBOL(avmb1_registercard); +EXPORT_SYMBOL(avmb1_unregistercard); +EXPORT_SYMBOL(avmb1_resetcard); +EXPORT_SYMBOL(avmb1_detectcard); /* @@ -876,11 +1083,6 @@ 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); */ @@ -899,15 +1101,7 @@ int avmb1_init(void) strcpy(rev, " ??? "); #ifdef MODULE - if (portbase) { - int rc; - if ((rc = avmb1_probecard(portbase, irq)) != 0) - return rc; - if ((rc = avmb1_addcard(portbase, irq)) != 0) - return rc; - } else { - printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: loaded\n", rev); - } + printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: loaded\n", rev); #else printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: started\n", rev); #endif @@ -929,20 +1123,20 @@ void cleanup_module(void) strcpy(rev, " ??? "); } - for (i = 0; i < ncards; i++) { - /* - * disable card - */ - B1_disable_irq(cards[i].port); - B1_reset(cards[i].port); - B1_reset(cards[i].port); - /* - * free kernel resources - */ - free_irq(cards[i].irq, &cards[i]); - release_region(cards[i].port, AVMB1_PORTLEN); - + for (i = 0; i < CAPI_MAXCONTR; i++) { + if (cards[i].cardstate != CARD_FREE) { + /* + * disable card + */ + B1_disable_irq(cards[i].port); + avmb1_resetcard(i+1); + /* + * free kernel resources + */ + avmb1_unregistercard(i+1, 1); + } } + schedule(); /* execute queued tasks .... */ printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: unloaded\n", rev); } #endif diff --git a/drivers/isdn/avmb1/b1lli.c b/drivers/isdn/avmb1/b1lli.c index 64292ef2189e..1c4c0b80671e 100644 --- a/drivers/isdn/avmb1/b1lli.c +++ b/drivers/isdn/avmb1/b1lli.c @@ -1,11 +1,35 @@ /* - * $Id: b1lli.c,v 1.1 1997/03/04 21:50:28 calle Exp $ + * $Id: b1lli.c,v 1.6 1998/02/13 07:09:11 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.6 1998/02/13 07:09:11 calle + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.5 1998/01/31 11:14:41 calle + * merged changes to 2.0 tree, prepare 2.1.82 to work. + * + * Revision 1.4 1997/12/10 20:00:48 calle + * get changes from 2.0 version + * + * Revision 1.1.2.2 1997/11/26 10:46:55 calle + * prepared for M1 (Mobile) and T1 (PMX) cards. + * prepared to set configuration after load to support other D-channel + * protocols, point-to-point and leased lines. + * + * Revision 1.3 1997/10/01 09:21:13 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.2 1997/07/13 12:22:42 calle + * bug fix for more than one controller in connect_req. + * debugoutput now with contrnr. + * + * * Revision 1.1 1997/03/04 21:50:28 calle * Frirst version in isdn4linux * @@ -66,6 +90,9 @@ * B3Length data .... */ +#define SEND_CONFIG 0x21 /* + */ + /* * LLI Messages from the ISDN-ControllerISDN Controller */ @@ -110,6 +137,9 @@ * int32 AppllID int32 0xffffffff */ +#define WRITE_REGISTER 0x00 +#define READ_REGISTER 0x01 + /* * port offsets */ @@ -120,7 +150,11 @@ #define B1_OUTSTAT 0x03 #define B1_RESET 0x10 #define B1_ANALYSE 0x04 +#define B1_IDENT 0x17 /* Hema card T1 */ +#define B1_IRQ_MASTER 0x12 /* Hema card T1 */ +#define B1_STAT0(cardtype) ((cardtype) == AVM_CARDTYPE_M1 ? 0x81200000l : 0x80A00000l) +#define B1_STAT1(cardtype) (0x80E00000l) static inline unsigned char b1outp(unsigned short base, @@ -131,6 +165,100 @@ static inline unsigned char b1outp(unsigned short base, return inb(base + B1_ANALYSE); } +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++); +} + +static void b1_wr_reg(unsigned short base, + unsigned int reg, + unsigned int value) +{ + B1_put_byte(base, WRITE_REGISTER); + B1_put_word(base, reg); + B1_put_word(base, value); +} + +static inline unsigned int b1_rd_reg(unsigned short base, + unsigned int reg) +{ + B1_put_byte(base, READ_REGISTER); + B1_put_word(base, reg); + return B1_get_word(base); + +} + +static inline void b1_set_test_bit(unsigned short base, + int cardtype, + int onoff) +{ + b1_wr_reg(base, B1_STAT0(cardtype), onoff ? 0x21 : 0x20); +} + +static inline int b1_get_test_bit(unsigned short base, + int cardtype) +{ + return (b1_rd_reg(base, B1_STAT0(cardtype)) & 0x01) != 0; +} + static int irq_table[16] = {0, 0, @@ -150,14 +278,30 @@ static int irq_table[16] = 112, /* irq 15 */ }; -int B1_valid_irq(unsigned irq) +int B1_valid_irq(unsigned irq, int cardtype) { - return irq_table[irq] != 0; + switch (cardtype) { + default: + case AVM_CARDTYPE_M1: + case AVM_CARDTYPE_M2: + case AVM_CARDTYPE_B1: + return irq_table[irq] != 0; + case AVM_CARDTYPE_T1: + return irq == 5; + } } -unsigned char B1_assign_irq(unsigned short base, unsigned irq) +unsigned char B1_assign_irq(unsigned short base, unsigned irq, int cardtype) { - return b1outp(base, B1_RESET, irq_table[irq]); + switch (cardtype) { + case AVM_CARDTYPE_T1: + return b1outp(base, B1_IRQ_MASTER, 0x08); + default: + case AVM_CARDTYPE_M1: + case AVM_CARDTYPE_M2: + case AVM_CARDTYPE_B1: + return b1outp(base, B1_RESET, irq_table[irq]); + } } unsigned char B1_enable_irq(unsigned short base) @@ -182,24 +326,27 @@ void B1_reset(unsigned short base) udelay(55 * 2 * 1000); /* 2 TIC's */ } -int B1_detect(unsigned short base) +int B1_detect(unsigned short base, int cardtype) { + int onoff, i; + + if (cardtype == AVM_CARDTYPE_T1) + return 0; + /* * Statusregister 0000 00xx */ if ((inb(base + B1_INSTAT) & 0xfc) || (inb(base + B1_OUTSTAT) & 0xfc)) return 1; - /* * Statusregister 0000 001x */ b1outp(base, B1_INSTAT, 0x2); /* enable irq */ - b1outp(base, B1_OUTSTAT, 0x2); + /* b1outp(base, B1_OUTSTAT, 0x2); */ if ((inb(base + B1_INSTAT) & 0xfe) != 0x2 - || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2) + /* || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2 */) return 2; - /* * Statusregister 0000 000x */ @@ -208,72 +355,23 @@ int B1_detect(unsigned short base) if ((inb(base + B1_INSTAT) & 0xfe) || (inb(base + B1_OUTSTAT) & 0xfe)) return 3; + + for (onoff = !0, i= 0; i < 10 ; i++) { + b1_set_test_bit(base, cardtype, onoff); + if (b1_get_test_bit(base, cardtype) != onoff) + return 4; + onoff = !onoff; + } - return 0; -} + if (cardtype == AVM_CARDTYPE_M1) + return 0; -static inline int B1_rx_full(unsigned short base) -{ - return inb(base + B1_INSTAT) & 0x1; -} + if ((b1_rd_reg(base, B1_STAT1(cardtype)) & 0x0f) != 0x01) + return 5; -static inline unsigned char B1_get_byte(unsigned short base) -{ - unsigned long i = jiffies + 5 * HZ; /* maximum wait time 5 sec */ - while (!B1_rx_full(base) && i > jiffies); - if (B1_rx_full(base)) - return inb(base + B1_READ); - printk(KERN_CRIT "b1lli: rx not full after 5 second\n"); return 0; } -static inline unsigned int B1_get_word(unsigned short base) -{ - unsigned int val = 0; - val |= B1_get_byte(base); - val |= (B1_get_byte(base) << 8); - val |= (B1_get_byte(base) << 16); - val |= (B1_get_byte(base) << 24); - return val; -} - -static inline int B1_tx_empty(unsigned short base) -{ - return inb(base + B1_OUTSTAT) & 0x1; -} - -static inline void B1_put_byte(unsigned short base, unsigned char val) -{ - 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; @@ -316,6 +414,62 @@ int B1_load_t4file(unsigned short base, avmb1_t4file * t4file) return 0; } +int B1_load_config(unsigned short base, avmb1_t4file * config) +{ + /* + * Data is in user space !!! + */ + unsigned char buf[256]; + unsigned char *dp; + int i, j, left, retval; + + + dp = config->data; + left = config->len; + if (left) { + B1_put_byte(base, SEND_CONFIG); + B1_put_word(base, 1); + B1_put_byte(base, SEND_CONFIG); + B1_put_word(base, left); + } + while (left > sizeof(buf)) { + retval = copy_from_user(buf, dp, sizeof(buf)); + if (retval) + return -EFAULT; + if (loaddebug) + printk(KERN_DEBUG "b1capi: conf load: %d bytes ..", sizeof(buf)); + for (i = 0; i < sizeof(buf); ) { + B1_put_byte(base, SEND_CONFIG); + for (j=0; j < 4; j++) { + B1_put_byte(base, buf[i++]); + } + } + if (loaddebug) + printk("ok\n"); + left -= sizeof(buf); + dp += sizeof(buf); + } + if (left) { + retval = copy_from_user(buf, dp, left); + if (retval) + return -EFAULT; + if (loaddebug) + printk(KERN_DEBUG "b1capi: conf load: %d bytes ..", left); + for (i = 0; i < left; ) { + B1_put_byte(base, SEND_CONFIG); + for (j=0; j < 4; j++) { + if (i < left) + B1_put_byte(base, buf[i++]); + else + B1_put_byte(base, 0); + } + } + if (loaddebug) + printk("ok\n"); + } + return 0; +} + int B1_loaded(unsigned short base) { int i; @@ -428,7 +582,9 @@ void B1_send_message(unsigned short port, struct sk_buff *skb) CAPIMSG_APPID(skb->data), capi_cmd2str(cmd, subcmd), len); } else { - printk(KERN_DEBUG "b1lli: Put %s\n", capi_message2str(skb->data)); + printk(KERN_DEBUG "b1lli: Put [0x%lx] %s\n", + (unsigned long) contr, + capi_message2str(skb->data)); } } @@ -447,7 +603,7 @@ void B1_send_message(unsigned short port, struct sk_buff *skb) CAPIMSG_APPID(skb->data), capi_cmd2str(cmd, subcmd), len); } else { - printk(KERN_DEBUG "b1lli: Put %s\n", capi_message2str(skb->data)); + printk(KERN_DEBUG "b1lli: Put [0x%lx] %s\n", (unsigned long)contr, capi_message2str(skb->data)); } } save_flags(flags); @@ -499,13 +655,12 @@ void B1_handle_interrupt(avmb1_card * card) capi_cmd2str(cmd, subcmd), MsgLen, DataB3Len); } else { - printk(KERN_DEBUG "b1lli: Got %s\n", capi_message2str(card->msgbuf)); + printk(KERN_DEBUG "b1lli: Got [0x%lx] %s\n", (unsigned long)contr, 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); @@ -528,14 +683,15 @@ void B1_handle_interrupt(avmb1_card * card) capi_cmd2str(cmd, subcmd), MsgLen); } else { - printk(KERN_DEBUG "b1lli: Got %s\n", capi_message2str(card->msgbuf)); + printk(KERN_DEBUG "b1lli: Got [0x%lx] %s\n", + (unsigned long) contr, + 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); } @@ -581,10 +737,9 @@ void B1_handle_interrupt(avmb1_card * card) card->versionlen = B1_get_slice(card->port, card->versionbuf); card->cardstate = CARD_ACTIVE; parse_version(card); - printk(KERN_INFO "b1lli: %s-card (%s) with %s now active\n", + printk(KERN_INFO "b1lli: %s-card (%s) now active\n", card->version[VER_CARDTYPE], - card->version[VER_DRIVER], - card->version[VER_PROTO]); + card->version[VER_DRIVER]); avmb1_card_ready(card); break; default: diff --git a/drivers/isdn/avmb1/b1pci.c b/drivers/isdn/avmb1/b1pci.c index af7b58407795..5a31d7992bf8 100644 --- a/drivers/isdn/avmb1/b1pci.c +++ b/drivers/isdn/avmb1/b1pci.c @@ -1,11 +1,22 @@ /* - * $Id: b1pci.c,v 1.2 1997/05/18 09:24:13 calle Exp $ + * $Id: b1pci.c,v 1.5 1998/01/31 11:14:43 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.5 1998/01/31 11:14:43 calle + * merged changes to 2.0 tree, prepare 2.1.82 to work. + * + * Revision 1.4 1997/12/10 20:00:50 calle + * get changes from 2.0 version + * + * Revision 1.3 1997/10/01 09:21:14 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * * Revision 1.2 1997/05/18 09:24:13 calle * added verbose disconnect reason reporting to avmb1. * some fixes in capi20 interface. @@ -19,7 +30,6 @@ #include #include #include -#include #include #include #include "compat.h" @@ -34,13 +44,11 @@ #define PCI_DEVICE_ID_AVM_B1 0x700 #endif -static char *revision = "$Revision: 1.2 $"; +static char *revision = "$Revision: 1.5 $"; /* ------------------------------------------------------------- */ -#ifdef HAS_NEW_SYMTAB MODULE_AUTHOR("Carsten Paeth "); -#endif /* ------------------------------------------------------------- */ @@ -61,7 +69,7 @@ int b1pci_init(void) char *p; char rev[10]; int rc; - int pci_index; + struct pci_dev *dev = NULL; if ((p = strchr(revision, ':'))) { strcpy(rev, p + 1); @@ -72,39 +80,26 @@ int b1pci_init(void) #ifdef CONFIG_PCI - if (!pcibios_present()) { - printk(KERN_ERR "b1pci: no PCI-BIOS present\n"); + if (!pci_present()) { + printk(KERN_ERR "b1pci: no PCI bus 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; + while (dev = pci_find_device(PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_B1, dev)) { + unsigned int ioaddr = dev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK; + unsigned int irq = dev->irq; printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n", ioaddr, irq); - if ((rc = avmb1_probecard(ioaddr, irq)) != 0) { + if ((rc = avmb1_probecard(ioaddr, irq, AVM_CARDTYPE_B1)) != 0) { printk(KERN_ERR "b1pci: no AVM-B1 at i/o %#x, irq %d detected\n", ioaddr, irq); return rc; } - if ((rc = avmb1_addcard(ioaddr, irq)) != 0) + if ((rc = avmb1_addcard(ioaddr, irq, AVM_CARDTYPE_B1)) < 0) return rc; } return 0; diff --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c index 14a5e7d0f052..67c39c675de3 100644 --- a/drivers/isdn/avmb1/capi.c +++ b/drivers/isdn/avmb1/capi.c @@ -1,11 +1,34 @@ /* - * $Id: capi.c,v 1.4 1997/05/27 15:17:50 fritz Exp $ + * $Id: capi.c,v 1.10 1998/02/13 07:09:13 calle Exp $ * * CAPI 2.0 Interface for Linux * * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capi.c,v $ + * Revision 1.10 1998/02/13 07:09:13 calle + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.9 1998/01/31 11:14:44 calle + * merged changes to 2.0 tree, prepare 2.1.82 to work. + * + * Revision 1.8 1997/11/04 06:12:08 calle + * capi.c: new read/write in file_ops since 2.1.60 + * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware. + * capiutil.c: needs config.h (CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON) + * compat.h: added #define LinuxVersionCode + * + * Revision 1.7 1997/10/11 10:29:34 calle + * llseek() parameters changed in 2.1.56. + * + * Revision 1.6 1997/10/01 09:21:15 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.5 1997/08/21 23:11:55 fritz + * Added changes for kernels >= 2.1.45 + * * Revision 1.4 1997/05/27 15:17:50 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -45,26 +68,22 @@ #include #include #include +#include #include #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 ---------------------------------------- */ @@ -94,21 +113,25 @@ static void capi_signal(__u16 applid, __u32 minor) /* -------- file_operations ----------------------------------------- */ -static loff_t capi_llseek(struct file *file, loff_t offset, int origin) +static long long capi_llseek(struct file *file, + long long offset, int origin) { return -ESPIPE; } -static ssize_t capi_read(struct file *file, - char *buf, size_t count, - loff_t *off) +static ssize_t capi_read(struct file *file, char *buf, + size_t count, loff_t *ppos) { - unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + struct inode *inode = file->f_dentry->d_inode; + unsigned int minor = MINOR(inode->i_rdev); struct capidev *cdev; struct sk_buff *skb; int retval; size_t copied; + if (ppos != &file->f_pos) + return -ESPIPE; + if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) return -ENODEV; @@ -149,11 +172,11 @@ static ssize_t capi_read(struct file *file, return copied; } -static ssize_t capi_write(struct file *file, - const char *buf, size_t count, - loff_t *off) +static ssize_t capi_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) { - unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + struct inode *inode = file->f_dentry->d_inode; + unsigned int minor = MINOR(inode->i_rdev); struct capidev *cdev; struct sk_buff *skb; int retval; @@ -161,6 +184,9 @@ static ssize_t capi_write(struct file *file, __u8 subcmd; __u16 mlen; + if (ppos != &file->f_pos) + return -ESPIPE; + if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) return -ENODEV; @@ -200,7 +226,11 @@ static unsigned int capi_poll(struct file *file, poll_table * wait) { unsigned int mask = 0; +#if (LINUX_VERSION_CODE >= 0x02012d) unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); +#else + unsigned int minor = MINOR(file->f_inode->i_rdev); +#endif struct capidev *cdev; if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) @@ -394,7 +424,7 @@ static int capi_open(struct inode *inode, struct file *file) return 0; } -static CLOSETYPE +static int capi_release(struct inode *inode, struct file *file) { unsigned int minor = MINOR(inode->i_rdev); @@ -403,7 +433,7 @@ capi_release(struct inode *inode, struct file *file) if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open) { printk(KERN_ERR "capi20: release minor %d ???\n", minor); - return CLOSEVAL; + return 0; } cdev = &capidevs[minor]; @@ -421,7 +451,7 @@ capi_release(struct inode *inode, struct file *file) cdev->is_open = 0; MOD_DEC_USE_COUNT; - return CLOSEVAL; + return 0; } static struct file_operations capi_fops = diff --git a/drivers/isdn/avmb1/capidrv.c b/drivers/isdn/avmb1/capidrv.c index c1dfcdafe1a3..1d4a7c2e863c 100644 --- a/drivers/isdn/avmb1/capidrv.c +++ b/drivers/isdn/avmb1/capidrv.c @@ -1,11 +1,42 @@ /* - * $Id: capidrv.c,v 1.3 1997/05/18 09:24:15 calle Exp $ + * $Id: capidrv.c,v 1.11 1998/02/13 07:09: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.11 1998/02/13 07:09:15 calle + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.10 1998/02/02 19:52:23 calle + * Fixed vbox (audio) acceptb. + * + * Revision 1.9 1998/01/31 11:14:45 calle + * merged changes to 2.0 tree, prepare 2.1.82 to work. + * + * Revision 1.8 1997/11/04 06:12:09 calle + * capi.c: new read/write in file_ops since 2.1.60 + * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware. + * capiutil.c: needs config.h (CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON) + * compat.h: added #define LinuxVersionCode + * + * Revision 1.7 1997/10/11 10:36:34 calle + * Added isdnlog support. patch to isdnlog needed. + * + * Revision 1.6 1997/10/11 10:25:55 calle + * New interface for lowlevel drivers. BSENT with nr. of bytes sent, + * allow sending without ACK. + * + * Revision 1.5 1997/10/01 09:21:16 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.4 1997/07/13 12:22:43 calle + * bug fix for more than one controller in connect_req. + * debugoutput now with contrnr. + * * Revision 1.3 1997/05/18 09:24:15 calle * added verbose disconnect reason reporting to avmb1. * some fixes in capi20 interface. @@ -48,13 +79,11 @@ #include "capicmd.h" #include "capidrv.h" -static char *revision = "$Revision: 1.3 $"; +static char *revision = "$Revision: 1.11 $"; int debugmode = 0; -#ifdef HAS_NEW_SYMTAB MODULE_AUTHOR("Carsten Paeth "); MODULE_PARM(debugmode, "i"); -#endif /* -------- type definitions ----------------------------------------- */ @@ -116,14 +145,26 @@ struct capidrv_contr { int oldstate; /* */ __u16 datahandle; + struct ncci_datahandle_queue { + struct ncci_datahandle_queue *next; + __u16 datahandle; + int len; + } *ackqueue; } *ncci_list; } *plcip; struct capidrv_ncci *nccip; } *bchans; struct capidrv_plci *plci_list; + + /* for q931 data */ + __u8 q931_buf[4096]; + __u8 *q931_read; + __u8 *q931_write; + __u8 *q931_end; }; + struct capidrv_data { __u16 appid; int ncontr; @@ -141,6 +182,9 @@ typedef struct capidrv_bchan capidrv_bchan; static capidrv_data global; static struct capi_interface *capifuncs; +static void handle_dtrace_data(capidrv_contr *card, + int send, int level2, __u8 *data, __u16 len); + /* -------- convert functions ---------------------------------------- */ static inline __u32 b1prot(int l2, int l3) @@ -167,9 +211,8 @@ static inline __u32 b2prot(int l2, int l3) default: return 0; case ISDN_PROTO_L2_HDLC: - return 1; case ISDN_PROTO_L2_TRANS: - return 0; + return 1; } } @@ -410,6 +453,42 @@ static void free_ncci(capidrv_contr * card, struct capidrv_ncci *nccip) kfree(nccip); } +static int capidrv_add_ack(struct capidrv_ncci *nccip, + __u16 datahandle, int len) +{ + struct ncci_datahandle_queue *n, **pp; + + n = (struct ncci_datahandle_queue *) + kmalloc(sizeof(struct ncci_datahandle_queue), GFP_ATOMIC); + if (!n) { + printk(KERN_ERR "capidrv: kmalloc ncci_datahandle failed\n"); + return -1; + } + n->next = 0; + n->datahandle = datahandle; + n->len = len; + for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) ; + *pp = n; + return 0; +} + +static int capidrv_del_ack(struct capidrv_ncci *nccip, __u16 datahandle) +{ + struct ncci_datahandle_queue **pp, *p; + int len; + + for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) { + if ((*pp)->datahandle == datahandle) { + p = *pp; + len = p->len; + *pp = (*pp)->next; + kfree(p); + return len; + } + } + return -1; +} + /* -------- convert and send capi message ---------------------------- */ static void send_message(capidrv_contr * card, _cmsg * cmsg) @@ -419,7 +498,6 @@ static void send_message(capidrv_contr * card, _cmsg * cmsg) 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); } @@ -686,8 +764,53 @@ static void handle_controller(_cmsg * cmsg) break; case CAPI_MANUFACTURER_IND: /* Controller */ + if ( cmsg->ManuID == 0x214D5641 + && cmsg->Class == 0 + && cmsg->Function == 1) { + __u8 *data = cmsg->ManuData+3; + __u16 len = cmsg->ManuData[0]; + __u16 layer; + int direction; + if (len == 255) { + len = (cmsg->ManuData[1] | (cmsg->ManuData[2] << 8)); + data += 2; + } + len -= 2; + layer = ((*(data-1)) << 8) | *(data-2); + if (layer & 0x300) + direction = (layer & 0x200) ? 0 : 1; + else direction = (layer & 0x800) ? 0 : 1; + if (layer & 0x0C00) { + if ((layer & 0xff) == 0x80) { + handle_dtrace_data(card, direction, 1, data, len); + break; + } + } else if ((layer & 0xff) < 0x80) { + handle_dtrace_data(card, direction, 0, data, len); + break; + } + printk(KERN_INFO "capidrv: %s from controller 0x%x layer 0x%x, ignored\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrController, layer); + break; + } goto ignored; case CAPI_MANUFACTURER_CONF: /* Controller */ + if (cmsg->ManuID == 0x214D5641) { + char *s = 0; + switch (cmsg->Class) { + case 0: break; + case 1: s = "unknown class"; break; + case 2: s = "unknown function"; break; + default: s = "unkown error"; break; + } + if (s) + printk(KERN_INFO "capidrv: %s from controller 0x%x function %d: %s\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrController, + cmsg->Function, s); + break; + } goto ignored; case CAPI_FACILITY_IND: /* Controller/plci/ncci */ goto ignored; @@ -996,6 +1119,7 @@ static void handle_ncci(_cmsg * cmsg) capidrv_plci *plcip; capidrv_ncci *nccip; isdn_ctrl cmd; + int len; if (!card) { printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", @@ -1093,11 +1217,14 @@ static void handle_ncci(_cmsg * cmsg) 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); - + len = capidrv_del_ack(nccip, cmsg->DataHandle); + if (len < 0) + break; + cmd.command = ISDN_STAT_BSENT; + cmd.driver = card->myid; + cmd.arg = nccip->chan; + cmd.parm.length = len; + card->interface.statcallb(&cmd); break; case CAPI_DISCONNECT_B3_IND: /* ncci */ @@ -1206,6 +1333,60 @@ static void capidrv_signal(__u16 applid, __u32 dummy) /* ------------------------------------------------------------------- */ +#define PUTBYTE_TO_STATUS(card, byte) \ + do { \ + *(card)->q931_write++ = (byte); \ + if ((card)->q931_write > (card)->q931_end) \ + (card)->q931_write = (card)->q931_buf; \ + } while (0) + +static void handle_dtrace_data(capidrv_contr *card, + int send, int level2, __u8 *data, __u16 len) +{ + long flags; + __u8 *p, *end; + isdn_ctrl cmd; + + if (!len) { + printk(KERN_DEBUG "avmb1_q931_data: len == %d\n", len); + return; + } + + save_flags(flags); + cli(); + + if (level2) { + PUTBYTE_TO_STATUS(card, 'D'); + PUTBYTE_TO_STATUS(card, '2'); + PUTBYTE_TO_STATUS(card, send ? '>' : '<'); + PUTBYTE_TO_STATUS(card, ':'); + } else { + PUTBYTE_TO_STATUS(card, 'D'); + PUTBYTE_TO_STATUS(card, '3'); + PUTBYTE_TO_STATUS(card, send ? '>' : '<'); + PUTBYTE_TO_STATUS(card, ':'); + } + + for (p = data, end = data+len; p < end; p++) { + __u8 w; + PUTBYTE_TO_STATUS(card, ' '); + w = (*p >> 4) & 0xf; + PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w); + w = *p & 0xf; + PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w); + } + PUTBYTE_TO_STATUS(card, '\n'); + + restore_flags(flags); + + cmd.command = ISDN_STAT_STAVAIL; + cmd.driver = card->myid; + cmd.arg = len*3+5; + card->interface.statcallb(&cmd); +} + +/* ------------------------------------------------------------------- */ + static _cmsg cmdcmsg; static int capidrv_ioctl(isdn_ctrl * c, capidrv_contr * card) @@ -1270,7 +1451,7 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) capi_fill_CONNECT_REQ(&cmdcmsg, global.appid, card->msgid++, - 1, /* adr */ + card->contrnr, /* adr */ si2cip(bchan->si1, bchan->si2), /* cipvalue */ called, /* CalledPartyNumber */ calling, /* CallingPartyNumber */ @@ -1471,7 +1652,7 @@ static int if_command(isdn_ctrl * c) static _cmsg sendcmsg; -static int if_sendbuf(int id, int channel, struct sk_buff *skb) +static int if_sendbuf(int id, int channel, int doack, struct sk_buff *skb) { capidrv_contr *card = findcontrbydriverid(id); capidrv_bchan *bchan; @@ -1479,6 +1660,7 @@ static int if_sendbuf(int id, int channel, struct sk_buff *skb) int len = skb->len; size_t msglen; __u16 errcode; + __u16 datahandle; if (!card) { printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n", @@ -1492,51 +1674,128 @@ static int if_sendbuf(int id, int channel, struct sk_buff *skb) card->name, channel); return 0; } + datahandle = nccip->datahandle; capi_fill_DATA_B3_REQ(&sendcmsg, global.appid, card->msgid++, nccip->ncci, /* adr */ (__u32) skb->data, /* Data */ skb->len, /* DataLength */ - nccip->datahandle++, /* DataHandle */ + datahandle, /* DataHandle */ 0 /* Flags */ ); + + if (capidrv_add_ack(nccip, datahandle, doack ? skb->len : -1) < 0) + return 0; + 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"); + (void)capidrv_del_ack(nccip, datahandle); 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: + if (errcode == CAPI_NOERROR) { dev_kfree_skb(skb); + nccip->datahandle++; return len; - case CAPI_SENDQUEUEFULL: - dev_kfree_skb(nskb); - return 0; - default: - return -1; } + (void)capidrv_del_ack(nccip, datahandle); + dev_kfree_skb(nskb); + return errcode == CAPI_SENDQUEUEFULL ? 0 : -1; } else { memcpy(skb_push(skb, msglen), sendcmsg.buf, msglen); errcode = (*capifuncs->capi_put_message) (global.appid, skb); - switch (errcode) { - case CAPI_NOERROR: + if (errcode == CAPI_NOERROR) { + nccip->datahandle++; return len; - case CAPI_SENDQUEUEFULL: - return 0; - default: - return -1; } + (void)capidrv_del_ack(nccip, datahandle); + return errcode == CAPI_SENDQUEUEFULL ? 0 : -1; + } +} + +static int if_readstat(__u8 *buf, int len, int user, int id, int channel) +{ + capidrv_contr *card = findcontrbydriverid(id); + int count; + __u8 *p; + + if (!card) { + printk(KERN_ERR "capidrv: if_readstat called with invalid driverId %d!\n", + id); + return -ENODEV; + } + + for (p=buf, count=0; count < len; p++, count++) { + if (user) + put_user(*card->q931_read++, p); + else + *p = *card->q931_read++; + if (card->q931_read > card->q931_end) + card->q931_read = card->q931_buf; + } + return count; + +} + +static void enable_dchannel_trace(capidrv_contr *card) +{ + __u8 manufacturer[CAPI_MANUFACTURER_LEN]; + capi_version version; + __u16 contr = card->contrnr; + __u16 errcode; + __u16 avmversion[3]; + + errcode = (*capifuncs->capi_get_manufacturer)(contr, manufacturer); + if (errcode != CAPI_NOERROR) { + printk(KERN_ERR "%s: can't get manufacturer (0x%x)\n", + card->name, errcode); + return; + } + if (strstr(manufacturer, "AVM") == 0) { + printk(KERN_ERR "%s: not from AVM, no d-channel trace possible\n", + card->name); + return; + } + errcode = (*capifuncs->capi_get_version)(contr, &version); + if (errcode != CAPI_NOERROR) { + printk(KERN_ERR "%s: can't get version (0x%x)\n", + card->name, errcode); + return; + } + avmversion[0] = (version.majormanuversion >> 4) & 0x0f; + avmversion[1] = (version.majormanuversion << 4) & 0xf0; + avmversion[1] |= (version.minormanuversion >> 4) & 0x0f; + avmversion[2] |= version.minormanuversion & 0x0f; + + if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) { + printk(KERN_INFO "%s: D2 trace enabled\n", card->name); + capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid, + card->msgid++, + contr, + 0x214D5641, /* ManuID */ + 0, /* Class */ + 1, /* Function */ + (_cstruct)"\004\200\014\000\000"); + } else { + printk(KERN_INFO "%s: D3 trace enabled\n", card->name); + capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid, + card->msgid++, + contr, + 0x214D5641, /* ManuID */ + 0, /* Class */ + 1, /* Function */ + (_cstruct)"\004\002\003\000\000"); } + send_message(card, &cmdcmsg); } static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) @@ -1568,7 +1827,7 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) card->interface.command = if_command; card->interface.writebuf_skb = if_sendbuf; card->interface.writecmd = 0; - card->interface.readstat = 0; + card->interface.readstat = if_readstat; card->interface.features = ISDN_FEATURE_L2_X75I | ISDN_FEATURE_L2_X75UI | ISDN_FEATURE_L2_X75BUI | @@ -1581,6 +1840,9 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) card->next = global.contr_list; global.contr_list = card; global.ncontr++; + card->q931_read = card->q931_buf; + card->q931_write = card->q931_buf; + card->q931_end = card->q931_buf + sizeof(card->q931_buf) - 1; if (!register_isdn(&card->interface)) { global.contr_list = global.contr_list->next; @@ -1600,7 +1862,7 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) cmd.command = ISDN_STAT_RUN; card->interface.statcallb(&cmd); - card->cipmask = 1; /* any */ + card->cipmask = 0x1FFF03FF; /* any */ card->cipmask2 = 0; capi_fill_LISTEN_REQ(&cmdcmsg, global.appid, @@ -1616,6 +1878,8 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) printk(KERN_INFO "%s: now up (%d B channels)\n", card->name, card->nbchan); + enable_dchannel_trace(card); + return 0; } @@ -1694,11 +1958,6 @@ int capidrv_init(void) 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, '$'); diff --git a/drivers/isdn/avmb1/capiutil.c b/drivers/isdn/avmb1/capiutil.c index 9eb60afed999..8eb7d3ae15d9 100644 --- a/drivers/isdn/avmb1/capiutil.c +++ b/drivers/isdn/avmb1/capiutil.c @@ -1,5 +1,5 @@ /* - * $Id: capiutil.c,v 1.3 1997/05/18 09:24:18 calle Exp $ + * $Id: capiutil.c,v 1.6 1997/11/04 06:12:12 calle Exp $ * * CAPI 2.0 convert capi message to capi message struct * @@ -7,6 +7,20 @@ * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capiutil.c,v $ + * Revision 1.6 1997/11/04 06:12:12 calle + * capi.c: new read/write in file_ops since 2.1.60 + * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware. + * capiutil.c: needs config.h (CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON) + * compat.h: added #define LinuxVersionCode + * + * Revision 1.5 1997/10/01 09:21:19 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.4 1997/08/10 07:43:55 calle + * forgot to export symbol capi_info2str for 2.1.x + * * Revision 1.3 1997/05/18 09:24:18 calle * added verbose disconnect reason reporting to avmb1. * some fixes in capi20 interface. @@ -26,13 +40,13 @@ * */ #include -#include #include #include #include #include #include #include +#include #include "compat.h" #include "capiutil.h" @@ -936,35 +950,18 @@ char *capi_cmsg2str(_cmsg * cmsg) } -#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 +EXPORT_SYMBOL(capi_info2str); #ifdef MODULE int init_module(void) { -#ifndef HAS_NEW_SYMTAB - register_symtab(&capifunc_syms); -#endif return 0; } diff --git a/drivers/isdn/avmb1/compat.h b/drivers/isdn/avmb1/compat.h index 551b20d604e1..41ad2b626312 100644 --- a/drivers/isdn/avmb1/compat.h +++ b/drivers/isdn/avmb1/compat.h @@ -1,11 +1,22 @@ /* - * $Id: compat.h,v 1.1 1997/03/04 21:50:36 calle Exp $ + * $Id: compat.h,v 1.3 1997/11/04 06:12:15 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.3 1997/11/04 06:12:15 calle + * capi.c: new read/write in file_ops since 2.1.60 + * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware. + * capiutil.c: needs config.h (CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON) + * compat.h: added #define LinuxVersionCode + * + * Revision 1.2 1997/10/01 09:21:22 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * * Revision 1.1 1997/03/04 21:50:36 calle * Frirst version in isdn4linux * @@ -23,8 +34,8 @@ #include #include -#if LINUX_VERSION_CODE >= 0x020112 /* 2.1.18 */ -#define HAS_NEW_SYMTAB +#ifndef LinuxVersionCode +#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) #endif #endif /* __COMPAT_H__ */ diff --git a/drivers/isdn/hisax/Makefile b/drivers/isdn/hisax/Makefile index 2cc325b1f246..a8e1f83effa5 100644 --- a/drivers/isdn/hisax/Makefile +++ b/drivers/isdn/hisax/Makefile @@ -1,7 +1,14 @@ L_OBJS := M_OBJS := -O_OBJS := isdnl1.o config.o tei.o isdnl2.o isdnl3.o \ - q931.o callc.o fsm.o +LX_OBJS := +MX_OBJS := +O_OBJS := +OX_OBJS := +L_TARGET := +O_TARGET := + +O_OBJS := isdnl1.o tei.o isdnl2.o isdnl3.o \ + lmgr.o q931.o callc.o fsm.o # EXTRA_CFLAGS += -S @@ -17,31 +24,105 @@ ifeq ($(CONFIG_HISAX_1TR6),y) O_OBJS += l3_1tr6.o endif +ISAC_OBJ := +ARCOFI_OBJ := +HSCX_OBJ := +HFC_OBJ := +HFC_2BDS0 := +RAWHDLC_OBJ := + ifeq ($(CONFIG_HISAX_16_0),y) O_OBJS += teles0.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o endif ifeq ($(CONFIG_HISAX_16_3),y) O_OBJS += teles3.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o endif ifeq ($(CONFIG_HISAX_AVM_A1),y) O_OBJS += avm_a1.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o endif -ifeq ($(CONFIG_HISAX_ELSA_PCC),y) - O_OBJS += elsa.o -endif - -ifeq ($(CONFIG_HISAX_ELSA_PCMCIA),y) +ifeq ($(CONFIG_HISAX_ELSA),y) O_OBJS += elsa.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o + ARCOFI_OBJ := arcofi.o endif ifeq ($(CONFIG_HISAX_IX1MICROR2),y) O_OBJS += ix1_micro.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_DIEHLDIVA),y) + O_OBJS += diva.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_ASUSCOM),y) + O_OBJS += asuscom.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_TELEINT),y) + O_OBJS += teleint.o + ISAC_OBJ := isac.o + HFC_OBJ := hfc_2bs0.o +endif + +ifeq ($(CONFIG_HISAX_SEDLBAUER),y) + O_OBJS += sedlbauer.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o endif +ifeq ($(CONFIG_HISAX_SPORTSTER),y) + O_OBJS += sportster.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_MIC),y) + O_OBJS += mic.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_NETJET),y) + O_OBJS += netjet.o + ISAC_OBJ := isac.o +endif + +ifeq ($(CONFIG_HISAX_TELES3C),y) + O_OBJS += teles3c.o + HFC_2BDS0 := hfc_2bds0.o +endif +ifeq ($(CONFIG_HISAX_AMD7930),y) + O_OBJS += amd7930.o + RAWHDLC_OBJ := rawhdlc.o +endif + +ifeq ($(CONFIG_HISAX_NICCY),y) + O_OBJS += niccy.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +O_OBJS += $(ISAC_OBJ) $(HSCX_OBJ) $(HFC_OBJ) $(ARCOFI_OBJ) $(HFC_2BDS0) $(RAWHDLC_OBJ) +OX_OBJS += config.o + O_TARGET := + ifeq ($(CONFIG_ISDN_DRV_HISAX),y) O_TARGET += hisax.o else diff --git a/drivers/isdn/hisax/amd7930.c b/drivers/isdn/hisax/amd7930.c new file mode 100644 index 000000000000..20259994877b --- /dev/null +++ b/drivers/isdn/hisax/amd7930.c @@ -0,0 +1,768 @@ +/* $Id: amd7930.c,v 1.2 1998/02/12 23:07:10 keil Exp $ + * + * HiSax ISDN driver - chip specific routines for AMD 7930 + * + * Author Brent Baccala (baccala@FreeSoft.org) + * + * + * + * $Log: amd7930.c,v $ + * Revision 1.2 1998/02/12 23:07:10 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.1 1998/02/03 23:20:51 keil + * New files for SPARC isdn support + * + * Revision 1.1 1998/01/08 04:17:12 baccala + * ISDN comes to the Sparc. Key points: + * + * - Existing ISDN HiSax driver provides all the smarts + * - it compiles, runs, talks to an isolated phone switch, connects + * to a Cisco, pings go through + * - AMD 7930 support only (no DBRI yet) + * - no US NI-1 support (may not work on US phone system - untested) + * - periodic packet loss, apparently due to lost interrupts + * - ISDN sometimes freezes, requiring reboot before it will work again + * + * The code is unreliable enough to be consider alpha + * + * + * Advanced Micro Devices' Am79C30A is an ISDN/audio chip used in the + * SparcStation 1+. The chip provides microphone and speaker interfaces + * which provide mono-channel audio at 8K samples per second via either + * 8-bit A-law or 8-bit mu-law encoding. Also, the chip features an + * ISDN BRI Line Interface Unit (LIU), I.430 S/T physical interface, + * which performs basic D channel LAPD processing and provides raw + * B channel data. The digital audio channel, the two ISDN B channels, + * and two 64 Kbps channels to the microprocessor are all interconnected + * via a multiplexer. + * + * This driver interfaces to the Linux HiSax ISDN driver, which performs + * all high-level Q.921 and Q.931 ISDN functions. The file is not + * itself a hardware driver; rather it uses functions exported by + * the AMD7930 driver in the sparcaudio subsystem (drivers/sbus/audio), + * allowing the chip to be simultaneously used for both audio and ISDN data. + * The hardware driver does _no_ buffering, but provides several callbacks + * which are called during interrupt service and should therefore run quickly. + * + * D channel transmission is performed by passing the hardware driver the + * address and size of an skb's data area, then waiting for a callback + * to signal successful transmission of the packet. A task is then + * queued to notify the HiSax driver that another packet may be transmitted. + * + * D channel reception is quite simple, mainly because of: + * 1) the slow speed of the D channel - 16 kbps, and + * 2) the presence of an 8- or 32-byte (depending on chip version) FIFO + * to buffer the D channel data on the chip + * Worst case scenario of back-to-back packets with the 8 byte buffer + * at 16 kbps yields an service time of 4 ms - long enough to preclude + * the need for fancy buffering. We queue a background task that copies + * data out of the receive buffer into an skb, and the hardware driver + * simply does nothing until we're done with the receive buffer and + * reset it for a new packet. + * + * B channel processing is more complex, because of: + * 1) the faster speed - 64 kbps, + * 2) the lack of any on-chip buffering (it interrupts for every byte), and + * 3) the lack of any chip support for HDLC encapsulation + * + * The HiSax driver can put each B channel into one of three modes - + * L1_MODE_NULL (channel disabled), L1_MODE_TRANS (transparent data relay), + * and L1_MODE_HDLC (HDLC encapsulation by low-level driver). + * L1_MODE_HDLC is the most common, used for almost all "pure" digital + * data sessions. L1_MODE_TRANS is used for ISDN audio. + * + * HDLC B channel transmission is performed via a large buffer into + * which the skb is copied while performing HDLC bit-stuffing. A CRC + * is computed and attached to the end of the buffer, which is then + * passed to the low-level routines for raw transmission. Once + * transmission is complete, the hardware driver is set to enter HDLC + * idle by successive transmission of mark (all 1) bytes, waiting for + * the ISDN driver to prepare another packet for transmission and + * deliver it. + * + * HDLC B channel reception is performed via an X-byte ring buffer + * divided into N sections of X/N bytes each. Defaults: X=256 bytes, N=4. + * As the hardware driver notifies us that each section is full, we + * hand it the next section and schedule a background task to peruse + * the received section, bit-by-bit, with an HDLC decoder. As + * packets are detected, they are copied into a large buffer while + * decoding HDLC bit-stuffing. The ending CRC is verified, and if + * it is correct, we alloc a new skb of the correct length (which we + * now know), copy the packet into it, and hand it to the upper layers. + * Optimization: for large packets, we hand the buffer (which also + * happens to be an skb) directly to the upper layer after an skb_trim, + * and alloc a new large buffer for future packets, thus avoiding a copy. + * Then we return to HDLC processing; state is saved between calls. + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "../../sbus/audio/amd7930.h" +#include "isac.h" +#include "isdnl1.h" +#include "rawhdlc.h" +#include + +static const char *amd7930_revision = "$Revision: 1.2 $"; + +#define RCV_BUFSIZE 1024 /* Size of raw receive buffer in bytes */ +#define RCV_BUFBLKS 4 /* Number of blocks to divide buffer into + * (must divide RCV_BUFSIZE) */ + +static void Bchan_fill_fifo(struct BCState *, struct sk_buff *); + +static void +Bchan_xmt_bh(struct BCState *bcs) +{ + struct sk_buff *skb; + + if (bcs->hw.amd7930.tx_skb != NULL) { + dev_kfree_skb(bcs->hw.amd7930.tx_skb); + bcs->hw.amd7930.tx_skb = NULL; + } + + if ((skb = skb_dequeue(&bcs->squeue))) { + Bchan_fill_fifo(bcs, skb); + } else { + clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event |= 1 << B_XMTBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } +} + +static void +Bchan_xmit_callback(struct BCState *bcs) +{ + queue_task(&bcs->hw.amd7930.tq_xmt, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +/* B channel transmission: two modes (three, if you count L1_MODE_NULL) + * + * L1_MODE_HDLC - We need to do HDLC encapsulation before transmiting + * the packet (i.e. make_raw_hdlc_data). Since this can be a + * time-consuming operation, our completion callback just schedules + * a bottom half to do encapsulation for the next packet. In between, + * the link will just idle + * + * L1_MODE_TRANS - Data goes through, well, transparent. No HDLC encap, + * and we can't just let the link idle, so the "bottom half" actually + * gets called during the top half (it's our callback routine in this case), + * but it's a lot faster now since we don't call make_raw_hdlc_data + */ + +static void +Bchan_fill_fifo(struct BCState *bcs, struct sk_buff *skb) +{ + struct IsdnCardState *cs = bcs->cs; + int len; + + if ((cs->debug & L1_DEB_HSCX) || (cs->debug & L1_DEB_HSCX_FIFO)) { + char tmp[1024]; + char *t = tmp; + + t += sprintf(t, "amd7930_fill_fifo %c cnt %d", + bcs->channel ? 'B' : 'A', skb->len); + if (cs->debug & L1_DEB_HSCX_FIFO) + QuickHex(t, skb->data, skb->len); + debugl1(cs, tmp); + } + + if (bcs->mode == L1_MODE_HDLC) { + len = make_raw_hdlc_data(skb->data, skb->len, + bcs->hw.amd7930.tx_buff, RAW_BUFMAX); + if (len > 0) + amd7930_bxmit(0, bcs->channel, + bcs->hw.amd7930.tx_buff, len, + (void *) &Bchan_xmit_callback, + (void *) bcs); + dev_kfree_skb(skb); + } else if (bcs->mode == L1_MODE_TRANS) { + amd7930_bxmit(0, bcs->channel, + bcs->hw.amd7930.tx_buff, skb->len, + (void *) &Bchan_xmt_bh, + (void *) bcs); + bcs->hw.amd7930.tx_skb = skb; + } else { + dev_kfree_skb(skb); + } +} + +static void +Bchan_mode(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + + if (cs->debug & L1_DEB_HSCX) { + char tmp[40]; + sprintf(tmp, "AMD 7930 mode %d bchan %d/%d", + mode, bc, bcs->channel); + debugl1(cs, tmp); + } + bcs->mode = mode; +} + +/* Bchan_l2l1 is the entry point for upper layer routines that want to + * transmit on the B channel. PH_DATA_REQ is a normal packet that + * we either start transmitting (if idle) or queue (if busy). + * PH_PULL_REQ can be called to request a callback message (PH_PULL_CNF) + * once the link is idle. After a "pull" callback, the upper layer + * routines can use PH_PULL_IND to send data. + */ + +static void +Bchan_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + + switch (pr) { + case (PH_DATA_REQ): + if (test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + } else { + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + Bchan_fill_fifo(st->l1.bcs, skb); + } + break; + case (PH_PULL_IND): + if (test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) { + printk(KERN_WARNING "amd7930: this shouldn't happen\n"); + break; + } + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + Bchan_fill_fifo(st->l1.bcs, skb); + break; + case (PH_PULL_REQ): + if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) { + clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL_CNF, NULL); + } else + set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } +} + +/* Receiver callback and bottom half - decodes HDLC at leisure (if + * L1_MODE_HDLC) and passes newly received skb on via bcs->rqueue. If + * a large packet is received, stick rv_skb (the buffer that the + * packet has been decoded into) on the receive queue and alloc a new + * (large) skb to act as buffer for future receives. If a small + * packet is received, leave rv_skb alone, alloc a new skb of the + * correct size, and copy the packet into it + */ + +static void +Bchan_recv_callback(struct BCState *bcs) +{ + struct amd7930_hw *hw = &bcs->hw.amd7930; + + hw->rv_buff_in += RCV_BUFSIZE/RCV_BUFBLKS; + hw->rv_buff_in %= RCV_BUFSIZE; + + if (hw->rv_buff_in != hw->rv_buff_out) { + amd7930_brecv(0, bcs->channel, + hw->rv_buff + hw->rv_buff_in, + RCV_BUFSIZE/RCV_BUFBLKS, + (void *) &Bchan_recv_callback, (void *) bcs); + } + + queue_task(&hw->tq_rcv, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void +Bchan_rcv_bh(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + struct amd7930_hw *hw = &bcs->hw.amd7930; + struct sk_buff *skb; + int len; + + if (cs->debug & L1_DEB_HSCX) { + char tmp[1024]; + + sprintf(tmp, "amd7930_Bchan_rcv (%d/%d)", + hw->rv_buff_in, hw->rv_buff_out); + debugl1(cs, tmp); + QuickHex(tmp, hw->rv_buff + hw->rv_buff_out, + RCV_BUFSIZE/RCV_BUFBLKS); + debugl1(cs, tmp); + } + + do { + if (bcs->mode == L1_MODE_HDLC) { + while ((len = read_raw_hdlc_data(hw->hdlc_state, + hw->rv_buff + hw->rv_buff_out, RCV_BUFSIZE/RCV_BUFBLKS, + hw->rv_skb->tail, HSCX_BUFMAX))) { + if (len > 0 && (cs->debug & L1_DEB_HSCX_FIFO)) { + char tmp[1024]; + char *t = tmp; + + t += sprintf(t, "amd7930_Bchan_rcv %c cnt %d", bcs->channel ? 'B' : 'A', len); + QuickHex(t, hw->rv_skb->tail, len); + debugl1(cs, tmp); + } + + if (len > HSCX_BUFMAX/2) { + /* Large packet received */ + + if (!(skb = dev_alloc_skb(HSCX_BUFMAX))) { + printk(KERN_WARNING "amd7930: receive out of memory"); + } else { + skb_put(hw->rv_skb, len); + skb_queue_tail(&bcs->rqueue, hw->rv_skb); + hw->rv_skb = skb; + bcs->event |= 1 << B_RCVBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + } + } else if (len > 0) { + /* Small packet received */ + + if (!(skb = dev_alloc_skb(len))) { + printk(KERN_WARNING "amd7930: receive out of memory\n"); + } else { + memcpy(skb_put(skb, len), hw->rv_skb->tail, len); + skb_queue_tail(&bcs->rqueue, skb); + bcs->event |= 1 << B_RCVBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + } else { + /* Reception Error */ + /* printk("amd7930: B channel receive error\n"); */ + } + } + } else if (bcs->mode == L1_MODE_TRANS) { + if (!(skb = dev_alloc_skb(RCV_BUFSIZE/RCV_BUFBLKS))) { + printk(KERN_WARNING "amd7930: receive out of memory\n"); + } else { + memcpy(skb_put(skb, RCV_BUFSIZE/RCV_BUFBLKS), + hw->rv_buff + hw->rv_buff_out, + RCV_BUFSIZE/RCV_BUFBLKS); + skb_queue_tail(&bcs->rqueue, skb); + bcs->event |= 1 << B_RCVBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + } + + if (hw->rv_buff_in == hw->rv_buff_out) { + /* Buffer was filled up - need to restart receiver */ + amd7930_brecv(0, bcs->channel, + hw->rv_buff + hw->rv_buff_in, + RCV_BUFSIZE/RCV_BUFBLKS, + (void *) &Bchan_recv_callback, + (void *) bcs); + } + + hw->rv_buff_out += RCV_BUFSIZE/RCV_BUFBLKS; + hw->rv_buff_out %= RCV_BUFSIZE; + + } while (hw->rv_buff_in != hw->rv_buff_out); +} + +static void +Bchan_close(struct BCState *bcs) +{ + struct sk_buff *skb; + + Bchan_mode(bcs, 0, 0); + amd7930_bclose(0, bcs->channel); + + if (test_bit(BC_FLG_INIT, &bcs->Flag)) { + while ((skb = skb_dequeue(&bcs->rqueue))) { + dev_kfree_skb(skb); + } + while ((skb = skb_dequeue(&bcs->squeue))) { + dev_kfree_skb(skb); + } + } + test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); +} + +static int +Bchan_open(struct BCState *bcs) +{ + struct amd7930_hw *hw = &bcs->hw.amd7930; + + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + + amd7930_bopen(0, bcs->channel, 0xff); + hw->rv_buff_in = 0; + hw->rv_buff_out = 0; + hw->tx_skb = NULL; + init_hdlc_state(hw->hdlc_state, 0); + amd7930_brecv(0, bcs->channel, + hw->rv_buff + hw->rv_buff_in, RCV_BUFSIZE/RCV_BUFBLKS, + (void *) &Bchan_recv_callback, (void *) bcs); + + bcs->event = 0; + bcs->tx_cnt = 0; + return (0); +} + +static void +Bchan_init(struct BCState *bcs) +{ + if (!(bcs->hw.amd7930.tx_buff = kmalloc(RAW_BUFMAX, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for amd7930.tx_buff\n"); + return; + } + if (!(bcs->hw.amd7930.rv_buff = kmalloc(RCV_BUFSIZE, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for amd7930.rv_buff\n"); + return; + } + if (!(bcs->hw.amd7930.rv_skb = dev_alloc_skb(HSCX_BUFMAX))) { + printk(KERN_WARNING + "HiSax: No memory for amd7930.rv_skb\n"); + return; + } + if (!(bcs->hw.amd7930.hdlc_state = kmalloc(sizeof(struct hdlc_state), + GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for amd7930.hdlc_state\n"); + return; + } + + bcs->hw.amd7930.tq_rcv.sync = 0; + bcs->hw.amd7930.tq_rcv.routine = (void (*)(void *)) &Bchan_rcv_bh; + bcs->hw.amd7930.tq_rcv.data = (void *) bcs; + + bcs->hw.amd7930.tq_xmt.sync = 0; + bcs->hw.amd7930.tq_xmt.routine = (void (*)(void *)) &Bchan_xmt_bh; + bcs->hw.amd7930.tq_xmt.data = (void *) bcs; +} + +static void +Bchan_manl1(struct PStack *st, int pr, + void *arg) +{ + switch (pr) { + case (PH_ACTIVATE_REQ): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + Bchan_mode(st->l1.bcs, st->l1.mode, st->l1.bc); + st->l1.l1man(st, PH_ACTIVATE_CNF, NULL); + break; + case (PH_DEACTIVATE_REQ): + if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) + Bchan_mode(st->l1.bcs, 0, 0); + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + break; + } +} + +int +setstack_amd7930(struct PStack *st, struct BCState *bcs) +{ + if (Bchan_open(bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = Bchan_l2l1; + st->ma.manl1 = Bchan_manl1; + setstack_manager(st); + bcs->st = st; + return (0); +} + + +static void +amd7930_drecv_callback(void *arg, int error, unsigned int count) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) arg; + static struct tq_struct task; + struct sk_buff *skb; + + /* NOTE: This function is called directly from an interrupt handler */ + + if (1) { + if (!(skb = alloc_skb(count, GFP_ATOMIC))) + printk(KERN_WARNING "HiSax: D receive out of memory\n"); + else { + memcpy(skb_put(skb, count), cs->rcvbuf, count); + skb_queue_tail(&cs->rq, skb); + } + + task.routine = (void *) DChannel_proc_rcv; + task.data = (void *) cs; + queue_task(&task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + + if (cs->debug & L1_DEB_ISAC_FIFO) { + char tmp[128]; + char *t = tmp; + + t += sprintf(t, "amd7930 Drecv cnt %d", count); + if (error) t += sprintf(t, " ERR %x", error); + QuickHex(t, cs->rcvbuf, count); + debugl1(cs, tmp); + } + + amd7930_drecv(0, cs->rcvbuf, MAX_DFRAME_LEN, + &amd7930_drecv_callback, cs); +} + +static void +amd7930_dxmit_callback(void *arg, int error) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) arg; + static struct tq_struct task; + + /* NOTE: This function is called directly from an interrupt handler */ + + /* may wish to do retransmission here, if error indicates collision */ + + if (cs->debug & L1_DEB_ISAC_FIFO) { + char tmp[128]; + char *t = tmp; + + t += sprintf(t, "amd7930 Dxmit cnt %d", cs->tx_skb->len); + if (error) t += sprintf(t, " ERR %x", error); + QuickHex(t, cs->tx_skb->data, cs->tx_skb->len); + debugl1(cs, tmp); + } + + cs->tx_skb = NULL; + + task.routine = (void *) DChannel_proc_xmt; + task.data = (void *) cs; + queue_task(&task, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void +amd7930_Dchan_l2l1(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + struct sk_buff *skb = arg; + char str[64]; + + switch (pr) { + case (PH_DATA_REQ): + if (cs->tx_skb) { + skb_queue_tail(&cs->sq, skb); +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA Queued", 0); +#endif + } else { + if ((cs->dlogflag) && (!(skb->data[2] & 1))) { + /* I-FRAME */ + LogFrame(cs, skb->data, skb->len); + sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); + dlogframe(cs, skb->data+4, skb->len-4, + str); + } + cs->tx_skb = skb; + cs->tx_cnt = 0; +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA", 0); +#endif + amd7930_dxmit(0, skb->data, skb->len, + &amd7930_dxmit_callback, cs); + } + break; + case (PH_PULL_IND): + if (cs->tx_skb) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, " l2l1 tx_skb exist this shouldn't happen"); + skb_queue_tail(&cs->sq, skb); + break; + } + if ((cs->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ + LogFrame(cs, skb->data, skb->len); + sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); + dlogframe(cs, skb->data + 4, skb->len - 4, + str); + } + cs->tx_skb = skb; + cs->tx_cnt = 0; +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA_PULLED", 0); +#endif + amd7930_dxmit(0, cs->tx_skb->data, cs->tx_skb->len, + &amd7930_dxmit_callback, cs); + break; + case (PH_PULL_REQ): +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + debugl1(cs, "-> PH_REQUEST_PULL"); +#endif + if (!cs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL_CNF, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } +} + +int +setDstack_amd7930(struct PStack *st, struct IsdnCardState *cs) +{ + st->l2.l2l1 = amd7930_Dchan_l2l1; + if (! cs->rcvbuf) { + printk("setDstack_amd7930: No cs->rcvbuf!\n"); + } else { + amd7930_drecv(0, cs->rcvbuf, MAX_DFRAME_LEN, + &amd7930_drecv_callback, cs); + } + return (0); +} + +static void +manl1_msg(struct IsdnCardState *cs, int msg, void *arg) { + struct PStack *st; + + st = cs->stlist; + while (st) { + st->ma.manl1(st, msg, arg); + st = st->next; + } +} + +static void +amd7930_new_ph(struct IsdnCardState *cs) +{ + switch (amd7930_get_liu_state(0)) { + case 3: + manl1_msg(cs, PH_POWERUP_CNF, NULL); + break; + + case 7: + manl1_msg(cs, PH_I4_P8_IND, NULL); + break; + + case 8: + manl1_msg(cs, PH_RSYNC_IND, NULL); + break; + } +} + +/* amd7930 LIU state change callback */ + +static void +amd7930_liu_callback(struct IsdnCardState *cs) +{ + static struct tq_struct task; + + if (!cs) + return; + + if (cs->debug & L1_DEB_ISAC) { + char tmp[32]; + sprintf(tmp, "amd7930_liu state %d", amd7930_get_liu_state(0)); + debugl1(cs, tmp); + } + + task.sync = 0; + task.routine = (void *) &amd7930_new_ph; + task.data = (void *) cs; + queue_task(&task, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +void +amd7930_l1cmd(struct IsdnCardState *cs, int msg, void *arg) +{ + u_char val; + char tmp[32]; + + if (cs->debug & L1_DEB_ISAC) { + char tmp[32]; + sprintf(tmp, "amd7930_l1cmd msg %x", msg); + debugl1(cs, tmp); + } + + switch(msg) { + case PH_RESET_REQ: + if (amd7930_get_liu_state(0) <= 3) + amd7930_liu_activate(0,0); + else + amd7930_liu_deactivate(0); + break; + case PH_ENABLE_REQ: + break; + case PH_INFO3_REQ: + amd7930_liu_activate(0,0); + break; + case PH_TESTLOOP_REQ: + break; + default: + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "amd7930_l1cmd unknown %4x", msg); + debugl1(cs, tmp); + } + break; + } +} + +static void init_amd7930(struct IsdnCardState *cs) +{ + Bchan_init(&cs->bcs[0]); + Bchan_init(&cs->bcs[1]); + cs->bcs[0].BC_SetStack = setstack_amd7930; + cs->bcs[1].BC_SetStack = setstack_amd7930; + cs->bcs[0].BC_Close = Bchan_close; + cs->bcs[1].BC_Close = Bchan_close; + Bchan_mode(cs->bcs, 0, 0); + Bchan_mode(cs->bcs + 1, 0, 0); +} + +void +release_amd7930(struct IsdnCardState *cs) +{ +} + +static int +amd7930_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + return(0); + case CARD_RELEASE: + release_amd7930(cs); + return(0); + case CARD_SETIRQ: + return(0); + case CARD_INIT: + cs->l1cmd = amd7930_l1cmd; + amd7930_liu_init(0, &amd7930_liu_callback, (void *)cs); + init_amd7930(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_amd7930(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, amd7930_revision); + printk(KERN_INFO "HiSax: AMD7930 driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_AMD7930) + return (0); + + cs->irq = amd7930_get_irqnum(0); + if (cs->irq == 0) + return (0); + + cs->cardmsg = &amd7930_card_msg; + + return (1); +} diff --git a/drivers/isdn/hisax/arcofi.c b/drivers/isdn/hisax/arcofi.c new file mode 100644 index 000000000000..dad1711c3bd3 --- /dev/null +++ b/drivers/isdn/hisax/arcofi.c @@ -0,0 +1,51 @@ +/* $Id: arcofi.c,v 1.1 1997/10/29 18:51:20 keil Exp $ + + * arcofi.h Ansteuerung ARCOFI 2165 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * + * $Log: arcofi.c,v $ + * Revision 1.1 1997/10/29 18:51:20 keil + * New files + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isdnl1.h" +#include "isac.h" + +int +send_arcofi(struct IsdnCardState *cs, const u_char *msg) { + u_char val; + char tmp[32]; + long flags; + int cnt=2; + + cs->mon_txp = 0; + cs->mon_txc = msg[0]; + memcpy(cs->mon_tx, &msg[1], cs->mon_txc); + cs->mocr &= 0x0f; + cs->mocr |= 0xa0; + test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags); + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + val = cs->readisac(cs, ISAC_MOSR); + cs->writeisac(cs, ISAC_MOX1, cs->mon_tx[cs->mon_txp++]); + cs->mocr |= 0x10; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + save_flags(flags); + sti(); + while (cnt && !test_bit(HW_MON1_TX_END, &cs->HW_Flags)) { + cnt--; + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + } + restore_flags(flags); + sprintf(tmp, "arcofi tout %d", cnt); + debugl1(cs, tmp); + return(cnt); +} + diff --git a/drivers/isdn/hisax/arcofi.h b/drivers/isdn/hisax/arcofi.h new file mode 100644 index 000000000000..5e1bb9e993e5 --- /dev/null +++ b/drivers/isdn/hisax/arcofi.h @@ -0,0 +1,17 @@ +/* $Id: arcofi.h,v 1.1 1997/10/29 18:51:20 keil Exp $ + + * arcofi.h Ansteuerung ARCOFI 2165 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * + * $Log: arcofi.h,v $ + * Revision 1.1 1997/10/29 18:51:20 keil + * New files + * + */ + +#define ARCOFI_USE 1 + +extern int send_arcofi(struct IsdnCardState *cs, const u_char *msg); diff --git a/drivers/isdn/hisax/asuscom.c b/drivers/isdn/hisax/asuscom.c new file mode 100644 index 000000000000..c8491744642e --- /dev/null +++ b/drivers/isdn/hisax/asuscom.c @@ -0,0 +1,292 @@ +/* $Id: asuscom.c,v 1.2 1998/02/02 13:27:06 keil Exp $ + + * asuscom.c low level stuff for ASUSCOM NETWORK INC. ISDNLink cards + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * Thanks to ASUSCOM NETWORK INC. Taiwan and Dynalink NL for informations + * + * + * $Log: asuscom.c,v $ + * Revision 1.2 1998/02/02 13:27:06 keil + * New + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *Asuscom_revision = "$Revision: 1.2 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ASUS_ISAC 0 +#define ASUS_HSCX 1 +#define ASUS_ADR 2 +#define ASUS_CTRL_U7 3 +#define ASUS_CTRL_POTS 5 + +/* CARD_ADR (Write) */ +#define ASUS_RESET 0x80 /* Bit 7 Reset-Leitung */ + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.asus.adr, + cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.asus.adr, + cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.asus.adr, \ + cs->hw.asus.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.asus.adr, \ + cs->hw.asus.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.asus.adr, \ + cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.asus.adr, \ + cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +asuscom_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 1) { + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0x0); + } + if (stat & 2) { + writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0x0); + } +} + +void +release_io_asuscom(struct IsdnCardState *cs) +{ + int bytecnt = 8; + + if (cs->hw.asus.cfg_reg) + release_region(cs->hw.asus.cfg_reg, bytecnt); +} + +static void +reset_asuscom(struct IsdnCardState *cs) +{ + long flags; + + byteout(cs->hw.asus.adr, ASUS_RESET); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + byteout(cs->hw.asus.adr, 0); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + restore_flags(flags); +} + +static int +Asus_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_asuscom(cs); + return(0); + case CARD_RELEASE: + release_io_asuscom(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &asuscom_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_asuscom(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, Asuscom_revision); + printk(KERN_INFO "HiSax: Asuscom ISDNLink driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_ASUSCOM) + return (0); + + bytecnt = 8; + cs->hw.asus.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_ADR; + cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_ISAC; + cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_HSCX; + cs->hw.asus.u7 = cs->hw.asus.cfg_reg + ASUS_CTRL_U7; + cs->hw.asus.pots = cs->hw.asus.cfg_reg + ASUS_CTRL_POTS; + + if (check_region((cs->hw.asus.cfg_reg), bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.asus.cfg_reg, + cs->hw.asus.cfg_reg + bytecnt); + return (0); + } else { + request_region(cs->hw.asus.cfg_reg, bytecnt, "asuscom isdn"); + } + + printk(KERN_INFO + "ISDNLink: defined at 0x%x IRQ %d\n", + cs->hw.asus.cfg_reg, + cs->irq); + printk(KERN_INFO "ISDNLink: resetting card\n"); + reset_asuscom(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Asus_card_msg; + ISACVersion(cs, "ISDNLink:"); + if (HscxVersion(cs, "ISDNLink:")) { + printk(KERN_WARNING + "ISDNLink: wrong HSCX versions check IO address\n"); + release_io_asuscom(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/avm_a1.c b/drivers/isdn/hisax/avm_a1.c index e6c7755462cb..464bc33fd4dc 100644 --- a/drivers/isdn/hisax/avm_a1.c +++ b/drivers/isdn/hisax/avm_a1.c @@ -1,4 +1,4 @@ -/* $Id: avm_a1.c,v 1.6 1997/04/13 19:54:07 keil Exp $ +/* $Id: avm_a1.c,v 2.7 1998/02/02 13:29:37 keil Exp $ * avm_a1.c low level stuff for AVM A1 (Fritz) isdn cards * @@ -6,6 +6,30 @@ * * * $Log: avm_a1.c,v $ + * Revision 2.7 1998/02/02 13:29:37 keil + * fast io + * + * Revision 2.6 1998/01/13 23:09:46 keil + * really disable timer + * + * Revision 2.5 1998/01/02 06:50:29 calle + * Perodic timer of A1 now disabled, no need for linux driver. + * + * Revision 2.4 1997/11/08 21:35:42 keil + * new l1 init + * + * Revision 2.3 1997/11/06 17:13:32 keil + * New 2.1 init code + * + * Revision 2.2 1997/10/29 18:55:48 keil + * changes for 2.1.60 (irq2dev_map) + * + * Revision 2.1 1997/07/27 21:47:13 keil + * new interface structures + * + * Revision 2.0 1997/06/26 11:02:48 keil + * New Layer and card interface + * * Revision 1.6 1997/04/13 19:54:07 keil * Change in IRQ check delay for SMP * @@ -27,17 +51,20 @@ * */ #define __NO_VERSION__ -#include "siemens.h" #include "hisax.h" -#include "avm_a1.h" +#include "isac.h" +#include "hscx.h" #include "isdnl1.h" -#include extern const char *CardType[]; -const char *avm_revision = "$Revision: 1.6 $"; +const char *avm_revision = "$Revision: 2.7 $"; + +#define AVM_A1_STAT_ISAC 0x01 +#define AVM_A1_STAT_HSCX 0x02 +#define AVM_A1_STAT_TIMER 0x04 -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) static inline u_char readreg(unsigned int adr, u_char off) @@ -55,906 +82,303 @@ writereg(unsigned int adr, u_char off, u_char data) static inline void read_fifo(unsigned int adr, u_char * data, int size) { - insb(adr - 0x400, data, size); + insb(adr, data, size); } static void write_fifo(unsigned int adr, u_char * data, int size) { - outsb(adr - 0x400, data, size); -} - -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"); + outsb(adr, data, size); } +/* Interface functions */ -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) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - long flags; - - save_flags(flags); - cli(); - waitforCEC(adr); - writereg(adr, HSCX_CMDR, data); - restore_flags(flags); + return (readreg(cs->hw.avm.isac, offset)); } -/* - * fast interrupt here - */ - static void -hscxreport(struct IsdnCardState *sp, int hscx) +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - printk(KERN_DEBUG "HSCX %d\n", hscx); - printk(KERN_DEBUG "ISTA %x\n", readreg(sp->hscx[hscx], HSCX_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readreg(sp->hscx[hscx], HSCX_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readreg(sp->hscx[hscx], HSCX_EXIR)); + writereg(cs->hw.avm.isac, offset, value); } -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) +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo(sp->hscx[hsp->hscx], ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + read_fifo(cs->hw.avm.isacfifo, data, size); } static void -hscx_fill_fifo(struct HscxState *hsp) +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - struct IsdnCardState *sp = hsp->sp; - int more, count; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_fill_fifo"); - - if (!hsp->tx_skb) - return; - if (hsp->tx_skb->len <= 0) - return; - - more = (hsp->mode == 1) ? 1 : 0; - if (hsp->tx_skb->len > 32) { - more = !0; - count = 32; - } else - count = hsp->tx_skb->len; - - waitforXFW(sp->hscx[hsp->hscx]); - save_flags(flags); - cli(); - ptr = hsp->tx_skb->data; - skb_pull(hsp->tx_skb, count); - hsp->tx_cnt -= count; - hsp->count += count; - write_fifo(sp->hscx[hsp->hscx], ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_fill_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + write_fifo(cs->hw.avm.isacfifo, data, size); } -static inline void -hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = readreg(sp->hscx[hsp->hscx], HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!r & 0x80) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX invalid frame"); - if ((r & 0x40) && hsp->mode) - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX RDO mode=%d", - hsp->mode); - debugl1(sp, tmp); - } - if (!r & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX CRC error"); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - } else { - count = readreg(sp->hscx[hsp->hscx], HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - if ((count = hsp->rcvidx - 1) > 0) { - if (!(skb = dev_alloc_skb(count))) - printk(KERN_WARNING "AVM: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "AVM: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - SET_SKB_FREE(hsp->tx_skb); - dev_kfree_skb(hsp->tx_skb); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } + return (readreg(cs->hw.avm.hscx[hscx], offset)); } -/* - * ISAC stuff goes here - */ - static void -isac_empty_fifo(struct IsdnCardState *sp, int count) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo(sp->isac, ptr, count); - writereg(sp->isac, ISAC_CMDR, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writereg(cs->hw.avm.hscx[hscx], offset, value); } -static void -isac_fill_fifo(struct IsdnCardState *sp) -{ - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - write_fifo(sp->isac, ptr, count); - writereg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_fill_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } -} - -static void -ph_command(struct IsdnCardState *sp, unsigned int command) -{ - if (sp->debug & L1_DEB_ISAC) { - char tmp[32]; - sprintf(tmp, "ph_command %d", command); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_CIX0, (command << 2) | 3); -} - - -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; - - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = readreg(sp->isac, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!exval & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - writereg(sp->isac, ISAC_CMDR, 0x80); - } else { - count = readreg(sp->isac, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "AVM: D receive out of memory\n"); - else { - 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 { - SET_SKB_FREE(sp->tx_skb); - dev_kfree_skb(sp->tx_skb); - 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]; +/* + * fast interrupt HSCX stuff goes here + */ +#define READHSCX(cs, nr, reg) readreg(cs->hw.avm.hscx[nr], reg) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.avm.hscx[nr], reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt) - if (val & 0x01) { - hsp = sp->hs + 1; - exval = readreg(sp->hscx[1], HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = readreg(sp->hscx[0], HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = readreg(sp->hscx[0], HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void avm_a1_interrupt(int intno, void *dev_id, struct pt_regs *regs) { - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val, sval, stat = 0; char tmp[32]; - sp = (struct IsdnCardState *) dev_id; - - if (!sp) { + if (!cs) { printk(KERN_WARNING "AVM A1: Spurious interrupt!\n"); return; } - while (((sval = bytein(sp->cfg_reg)) & 0xf) != 0x7) { + while (((sval = bytein(cs->hw.avm.cfg_reg)) & 0xf) != 0x7) { if (!(sval & AVM_A1_STAT_TIMER)) { - byteout(sp->cfg_reg, 0x14); - byteout(sp->cfg_reg, 0x18); - sval = bytein(sp->cfg_reg); - } else if (sp->debug & L1_DEB_INTSTAT) { + byteout(cs->hw.avm.cfg_reg, 0x1E); + sval = bytein(cs->hw.avm.cfg_reg); + } else if (cs->debug & L1_DEB_INTSTAT) { sprintf(tmp, "avm IntStatus %x", sval); - debugl1(sp, tmp); + debugl1(cs, tmp); } if (!(sval & AVM_A1_STAT_HSCX)) { - val = readreg(sp->hscx[1], HSCX_ISTA); + val = readreg(cs->hw.avm.hscx[1], HSCX_ISTA); if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); stat |= 1; } } if (!(sval & AVM_A1_STAT_ISAC)) { - val = readreg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.avm.isac, ISAC_ISTA); if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); stat |= 2; } } } if (stat & 1) { - writereg(sp->hscx[0], HSCX_MASK, 0xFF); - writereg(sp->hscx[1], HSCX_MASK, 0xFF); - writereg(sp->hscx[0], HSCX_MASK, 0x0); - writereg(sp->hscx[1], HSCX_MASK, 0x0); + writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0xFF); + writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0xFF); + writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0x0); + writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0x0); } if (stat & 2) { - writereg(sp->isac, ISAC_MASK, 0xFF); - writereg(sp->isac, ISAC_MASK, 0x0); + writereg(cs->hw.avm.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.avm.isac, ISAC_MASK, 0x0); } } - -static void -initisac(struct IsdnCardState *sp) -{ - unsigned int adr = sp->isac; - - /* 16.3 IOM 2 Mode */ - writereg(adr, ISAC_MASK, 0xff); - writereg(adr, ISAC_ADF2, 0x80); - writereg(adr, ISAC_SQXR, 0x2f); - writereg(adr, ISAC_SPCR, 0x0); - writereg(adr, ISAC_ADF1, 0x2); - writereg(adr, ISAC_STCR, 0x70); - writereg(adr, ISAC_MODE, 0xc9); - writereg(adr, ISAC_TIMR, 0x0); - writereg(adr, ISAC_ADF1, 0x0); - writereg(adr, ISAC_CMDR, 0x41); - writereg(adr, ISAC_CIX0, (1 << 2) | 3); - writereg(adr, ISAC_MASK, 0xff); - writereg(adr, ISAC_MASK, 0x0); -} - -static void -modehscx(struct HscxState *hs, int mode, int ichan) -{ - struct IsdnCardState *sp = hs->sp; - int hscx = hs->hscx; - - if (sp->debug & L1_DEB_HSCX) { - char tmp[40]; - sprintf(tmp, "hscx %c mode %d ichan %d", - 'A' + hscx, mode, ichan); - debugl1(sp, tmp); - } - hs->mode = mode; - writereg(sp->hscx[hscx], HSCX_CCR1, 0x85); - writereg(sp->hscx[hscx], HSCX_XAD1, 0xFF); - writereg(sp->hscx[hscx], HSCX_XAD2, 0xFF); - writereg(sp->hscx[hscx], HSCX_RAH2, 0xFF); - writereg(sp->hscx[hscx], HSCX_XBCH, 0x0); - writereg(sp->hscx[hscx], HSCX_RLCR, 0x0); - - switch (mode) { - case (0): - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0xff); - writereg(sp->hscx[hscx], HSCX_TSAR, 0xff); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - writereg(sp->hscx[hscx], HSCX_MODE, 0x84); - break; - case (1): - if (ichan == 0) { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } else { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x3); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x3); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } - writereg(sp->hscx[hscx], HSCX_MODE, 0xe4); - writereg(sp->hscx[hscx], HSCX_CMDR, 0x41); - break; - case (2): - if (ichan == 0) { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } else { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x3); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x3); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } - writereg(sp->hscx[hscx], HSCX_MODE, 0x8c); - writereg(sp->hscx[hscx], HSCX_CMDR, 0x41); - break; - } - writereg(sp->hscx[hscx], HSCX_ISTA, 0x00); -} - inline static void -release_ioregs(struct IsdnCard *card, int mask) +release_ioregs(struct IsdnCardState *cs, int mask) { - release_region(card->sp->cfg_reg, 8); + release_region(cs->hw.avm.cfg_reg, 8); if (mask & 1) - release_region(card->sp->isac, 32); + release_region(cs->hw.avm.isac + 32, 32); if (mask & 2) - release_region(card->sp->isac - 0x400, 1); + release_region(cs->hw.avm.isacfifo, 1); if (mask & 4) - release_region(card->sp->hscx[0], 32); + release_region(cs->hw.avm.hscx[0] + 32, 32); if (mask & 8) - release_region(card->sp->hscx[0] - 0x400, 1); + release_region(cs->hw.avm.hscxfifo[0], 1); if (mask & 0x10) - release_region(card->sp->hscx[1], 32); + release_region(cs->hw.avm.hscx[1] + 32, 32); if (mask & 0x20) - release_region(card->sp->hscx[1] - 0x400, 1); + release_region(cs->hw.avm.hscxfifo[1], 1); } -void -release_io_avm_a1(struct IsdnCard *card) +static int +AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - release_ioregs(card, 0x3f); + switch (mt) { + case CARD_RESET: + return(0); + case CARD_RELEASE: + release_ioregs(cs, 0x3f); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &avm_a1_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); } -static void -clear_pending_ints(struct IsdnCardState *sp) +__initfunc(int +setup_avm_a1(struct IsdnCard *card)) { - 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_irqs(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_irqs(sp->irq) > sp->counter) - break; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 1; - schedule(); - } - sprintf(tmp, "IRQ %d count %d", sp->irq, - kstat_irqs(sp->irq)); - debugl1(sp, tmp); - if (kstat_irqs(sp->irq) == sp->counter) { - printk(KERN_WARNING - "AVM A1: IRQ(%d) getting no interrupts during init\n", - sp->irq); - free_irq(sp->irq, sp); - return (0); - } - } - return (ret); -} - -int -setup_avm_a1(struct IsdnCard *card) -{ - u_char val, verA, verB; - struct IsdnCardState *sp = card->sp; + u_char val; + struct IsdnCardState *cs = card->cs; long flags; char tmp[64]; strcpy(tmp, avm_revision); - printk(KERN_NOTICE "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp)); - if (sp->typ != ISDN_CTYPE_A1) + printk(KERN_INFO "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_A1) return (0); - sp->cfg_reg = card->para[1] + 0x1800; - sp->isac = card->para[1] + 0x1400; - sp->hscx[0] = card->para[1] + 0x400; - sp->hscx[1] = card->para[1] + 0xc00; - sp->irq = card->para[0]; - if (check_region((sp->cfg_reg), 8)) { + cs->hw.avm.cfg_reg = card->para[1] + 0x1800; + cs->hw.avm.isac = card->para[1] + 0x1400 - 0x20; + cs->hw.avm.hscx[0] = card->para[1] + 0x400 - 0x20; + cs->hw.avm.hscx[1] = card->para[1] + 0xc00 - 0x20; + cs->hw.avm.isacfifo = card->para[1] + 0x1000; + cs->hw.avm.hscxfifo[0] = card->para[1]; + cs->hw.avm.hscxfifo[1] = card->para[1] + 0x800; + cs->irq = card->para[0]; + if (check_region((cs->hw.avm.cfg_reg), 8)) { printk(KERN_WARNING "HiSax: %s config port %x-%x already in use\n", CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + 8); + cs->hw.avm.cfg_reg, + cs->hw.avm.cfg_reg + 8); return (0); } else { - request_region(sp->cfg_reg, 8, "avm cfg"); + request_region(cs->hw.avm.cfg_reg, 8, "avm cfg"); } - if (check_region((sp->isac), 32)) { + if (check_region((cs->hw.avm.isac + 32), 32)) { printk(KERN_WARNING "HiSax: %s isac ports %x-%x already in use\n", - CardType[sp->typ], - sp->isac, - sp->isac + 32); - release_ioregs(card, 0); + CardType[cs->typ], + cs->hw.avm.isac + 32, + cs->hw.avm.isac + 64); + release_ioregs(cs, 0); return (0); } else { - request_region(sp->isac, 32, "HiSax isac"); + request_region(cs->hw.avm.isac + 32, 32, "HiSax isac"); } - if (check_region((sp->isac - 0x400), 1)) { + if (check_region((cs->hw.avm.isacfifo), 1)) { printk(KERN_WARNING "HiSax: %s isac fifo port %x already in use\n", - CardType[sp->typ], - sp->isac - 0x400); - release_ioregs(card, 1); + CardType[cs->typ], + cs->hw.avm.isacfifo); + release_ioregs(cs, 1); return (0); } else { - request_region(sp->isac - 0x400, 1, "HiSax isac fifo"); + request_region(cs->hw.avm.isacfifo, 1, "HiSax isac fifo"); } - if (check_region((sp->hscx[0]), 32)) { + if (check_region((cs->hw.avm.hscx[0]) + 32, 32)) { printk(KERN_WARNING "HiSax: %s hscx A ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[0], - sp->hscx[0] + 32); - release_ioregs(card, 3); + CardType[cs->typ], + cs->hw.avm.hscx[0] + 32, + cs->hw.avm.hscx[0] + 64); + release_ioregs(cs, 3); return (0); } else { - request_region(sp->hscx[0], 32, "HiSax hscx A"); + request_region(cs->hw.avm.hscx[0] + 32, 32, "HiSax hscx A"); } - if (check_region((sp->hscx[0] - 0x400), 1)) { + if (check_region(cs->hw.avm.hscxfifo[0], 1)) { printk(KERN_WARNING "HiSax: %s hscx A fifo port %x already in use\n", - CardType[sp->typ], - sp->hscx[0] - 0x400); - release_ioregs(card, 7); + CardType[cs->typ], + cs->hw.avm.hscxfifo[0]); + release_ioregs(cs, 7); return (0); } else { - request_region(sp->hscx[0] - 0x400, 1, "HiSax hscx A fifo"); + request_region(cs->hw.avm.hscxfifo[0], 1, "HiSax hscx A fifo"); } - if (check_region((sp->hscx[1]), 32)) { + if (check_region(cs->hw.avm.hscx[1] + 32, 32)) { printk(KERN_WARNING "HiSax: %s hscx B ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[1], - sp->hscx[1] + 32); - release_ioregs(card, 0xf); + CardType[cs->typ], + cs->hw.avm.hscx[1] + 32, + cs->hw.avm.hscx[1] + 64); + release_ioregs(cs, 0xf); return (0); } else { - request_region(sp->hscx[1], 32, "HiSax hscx B"); + request_region(cs->hw.avm.hscx[1] + 32, 32, "HiSax hscx B"); } - if (check_region((sp->hscx[1] - 0x400), 1)) { + if (check_region(cs->hw.avm.hscxfifo[1], 1)) { printk(KERN_WARNING "HiSax: %s hscx B fifo port %x already in use\n", - CardType[sp->typ], - sp->hscx[1] - 0x400); - release_ioregs(card, 0x1f); + CardType[cs->typ], + cs->hw.avm.hscxfifo[1]); + release_ioregs(cs, 0x1f); return (0); } else { - request_region(sp->hscx[1] - 0x400, 1, "HiSax hscx B fifo"); + request_region(cs->hw.avm.hscxfifo[1], 1, "HiSax hscx B fifo"); } save_flags(flags); - byteout(sp->cfg_reg, 0x0); + byteout(cs->hw.avm.cfg_reg, 0x0); sti(); HZDELAY(HZ / 5 + 1); - byteout(sp->cfg_reg, 0x1); + byteout(cs->hw.avm.cfg_reg, 0x1); HZDELAY(HZ / 5 + 1); - byteout(sp->cfg_reg, 0x0); + byteout(cs->hw.avm.cfg_reg, 0x0); HZDELAY(HZ / 5 + 1); - val = sp->irq; + val = cs->irq; if (val == 9) val = 2; - byteout(sp->cfg_reg + 1, val); + byteout(cs->hw.avm.cfg_reg + 1, val); HZDELAY(HZ / 5 + 1); - byteout(sp->cfg_reg, 0x0); + byteout(cs->hw.avm.cfg_reg, 0x0); HZDELAY(HZ / 5 + 1); restore_flags(flags); - val = bytein(sp->cfg_reg); + val = bytein(cs->hw.avm.cfg_reg); printk(KERN_INFO "AVM A1: Byte at %x is %x\n", - sp->cfg_reg, val); - val = bytein(sp->cfg_reg + 3); + cs->hw.avm.cfg_reg, val); + val = bytein(cs->hw.avm.cfg_reg + 3); printk(KERN_INFO "AVM A1: Byte at %x is %x\n", - sp->cfg_reg + 3, val); - val = bytein(sp->cfg_reg + 2); + cs->hw.avm.cfg_reg + 3, val); + val = bytein(cs->hw.avm.cfg_reg + 2); printk(KERN_INFO "AVM A1: Byte at %x is %x\n", - sp->cfg_reg + 2, val); - byteout(sp->cfg_reg, 0x14); - byteout(sp->cfg_reg, 0x18); - val = bytein(sp->cfg_reg); + cs->hw.avm.cfg_reg + 2, val); + byteout(cs->hw.avm.cfg_reg, 0x1E); + val = bytein(cs->hw.avm.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)) { + cs->hw.avm.cfg_reg, val); + + printk(KERN_INFO + "HiSax: %s config irq:%d cfg:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.avm.cfg_reg); + printk(KERN_INFO + "HiSax: isac:0x%X/0x%X\n", + cs->hw.avm.isac + 32, cs->hw.avm.isacfifo); + printk(KERN_INFO + "HiSax: hscx A:0x%X/0x%X hscx B:0x%X/0x%X\n", + cs->hw.avm.hscx[0] + 32, cs->hw.avm.hscxfifo[0], + cs->hw.avm.hscx[1] + 32, cs->hw.avm.hscxfifo[1]); + + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &AVM_card_msg; + ISACVersion(cs, "AVM A1:"); + if (HscxVersion(cs, "AVM A1:")) { printk(KERN_WARNING "AVM A1: wrong HSCX versions check IO address\n"); - release_io_avm_a1(card); + release_ioregs(cs, 0x3f); return (0); } - sp->modehscx = &modehscx; - sp->ph_command = &ph_command; - sp->hscx_fill_fifo = &hscx_fill_fifo; - sp->isac_fill_fifo = &isac_fill_fifo; return (1); } diff --git a/drivers/isdn/hisax/avm_a1.h b/drivers/isdn/hisax/avm_a1.h deleted file mode 100644 index 85f44675e0a8..000000000000 --- a/drivers/isdn/hisax/avm_a1.h +++ /dev/null @@ -1,25 +0,0 @@ -/* $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/buffers.c b/drivers/isdn/hisax/buffers.c deleted file mode 100644 index 621385464192..000000000000 --- a/drivers/isdn/hisax/buffers.c +++ /dev/null @@ -1,326 +0,0 @@ -/* $Id: buffers.c,v 1.1 1996/10/13 20:04:49 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: buffers.c,v $ - * Revision 1.1 1996/10/13 20:04:49 keil - * Initial revision - * - * - */ -#define __NO_VERSION__ -#include "hisax.h" -#include -#include - -#undef SMALLOC_DEBUG - -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); - 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) -{ -#ifdef SMALLOC_DEBUG - printk(KERN_DEBUG "Sfree %x\n", (unsigned int)ptr); -#endif - kfree(ptr); -} - -byte * -Smalloc(int size, int pr, char *why) -{ - byte *p; - - p = (byte *) kmalloc(size, pr); -#ifdef SMALLOC_DEBUG - printk(KERN_DEBUG "Smalloc %s size %d res %x\n", why, size, (unsigned int)p); -#endif - return (p); -} diff --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c index fbf045574774..6924e3f9e547 100644 --- a/drivers/isdn/hisax/callc.c +++ b/drivers/isdn/hisax/callc.c @@ -1,4 +1,4 @@ -/* $Id: callc.c,v 1.30 1997/05/29 10:40:43 keil Exp $ +/* $Id: callc.c,v 2.13 1998/02/12 23:07:16 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,96 +7,54 @@ * Fritz Elfert * * $Log: callc.c,v $ - * Revision 1.30 1997/05/29 10:40:43 keil - * chanp->impair was uninitialised + * Revision 2.13 1998/02/12 23:07:16 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() * - * Revision 1.29 1997/04/23 20:09:49 fritz - * Removed tmp, used by removed debugging code. + * Revision 2.12 1998/02/09 10:55:54 keil + * New leased line mode * - * Revision 1.28 1997/04/21 13:42:25 keil - * Remove unneeded debug + * Revision 2.11 1998/02/02 13:35:19 keil + * config B-channel delay * - * Revision 1.27 1997/04/16 14:21:01 keil - * remove unused variable + * Revision 2.10 1997/11/06 17:09:15 keil + * New 2.1 init code * - * Revision 1.26 1997/04/13 19:55:21 keil - * Changes in debugging code + * Revision 2.9 1997/10/29 19:01:58 keil + * new LL interface * - * Revision 1.25 1997/04/06 22:54:08 keil - * Using SKB's + * Revision 2.8 1997/10/10 20:56:44 fritz + * New HL interface. * - * Revision 1.24 1997/03/05 11:28:03 keil - * fixed undefined l2tei procedure - * a layer1 release delete now the drel timer + * Revision 2.7 1997/10/01 09:21:28 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. * - * Revision 1.23 1997/03/04 23:07:42 keil - * bugfix dial parameter + * Revision 2.6 1997/09/11 17:26:58 keil + * Open B-channel if here are incomming packets * - * Revision 1.22 1997/02/27 13:51:55 keil - * Reset B-channel (dlc) statemachine in every release + * Revision 2.5 1997/08/07 17:46:05 keil + * Fix Incomming Call without broadcast * - * Revision 1.21 1997/02/19 09:24:27 keil - * Bugfix: Hangup to LL if a ttyI rings + * Revision 2.4 1997/08/03 14:37:58 keil + * Activate Layer2 in PtP mode * - * Revision 1.20 1997/02/17 00:32:47 keil - * Bugfix: No Busy reported to LL + * Revision 2.3 1997/07/31 19:23:40 keil + * LAYER2_WATCHING for PtP * - * Revision 1.19 1997/02/14 12:23:10 fritz - * Added support for new insmod parameter handling. + * Revision 2.2 1997/07/31 11:48:18 keil + * experimental REJECT after ALERTING * - * Revision 1.18 1997/02/11 01:36:58 keil - * Changed setup-interface (incoming and outgoing), cause reporting + * Revision 2.1 1997/07/30 17:12:59 keil + * more changes for 'One TEI per card' * - * 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 2.0 1997/07/27 21:12:21 keil + * CRef based L3; new channel handling; many other stuff * - * Revision 1.16 1997/01/27 23:17:03 keil - * delete timers while unloading + * Revision 1.31 1997/06/26 11:09:23 keil + * New managment and minor changes * - * 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 + * old logs removed /KKe * */ @@ -104,53 +62,63 @@ #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 $"; +const char *lli_revision = "$Revision: 2.13 $"; extern struct IsdnCard cards[]; extern int nrcards; extern void HiSax_mod_dec_use_count(void); extern void HiSax_mod_inc_use_count(void); -static int init_ds(struct Channel *chanp, int incoming); -static void release_ds(struct Channel *chanp); +static int init_b_st(struct Channel *chanp, int incoming); +static void release_b_st(struct Channel *chanp); static struct Fsm callcfsm = -{NULL, 0, 0}; +{NULL, 0, 0, NULL, NULL}; static struct Fsm lcfsm = -{NULL, 0, 0}; +{NULL, 0, 0, NULL, NULL}; 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) +/* experimental REJECT after ALERTING for CALLBACK to beat the 4s delay */ +#define ALERT_REJECT 1 + +/* Value to delay the sending of the first B-channel paket after CONNECT + * here is no value given by ITU, but experience shows that 300 ms will + * work on many networks, if you or your other side is behind local exchanges + * a greater value may be recommented. If the delay is to short the first paket + * will be lost and autodetect on many comercial routers goes wrong ! + * You can adjust this value on runtime with + * hisaxctrl 2 + * value is in milliseconds + */ +#define DEFAULT_B_DELAY 300 + +/* Flags for remembering action done in lli */ + +#define FLG_START_D 0 +#define FLG_ESTAB_D 1 +#define FLG_CALL_SEND 2 +#define FLG_CALL_REC 3 +#define FLG_CALL_ALERT 4 +#define FLG_START_B 5 +#define FLG_CONNECT_B 6 +#define FLG_LL_DCONN 7 +#define FLG_LL_BCONN 8 +#define FLG_DISC_SEND 9 +#define FLG_DISC_REC 10 +#define FLG_REL_REC 11 +#define FLG_DO_ALERT 12 +#define FLG_DO_HANGUP 13 +#define FLG_DO_CONNECT 14 +#define FLG_DO_ESTAB 15 /* * Because of callback it's a good idea to delay the shutdown of the d-channel */ -#define DREL_TIMER_VALUE 30000 +#define DREL_TIMER_VALUE 10000 /* * Find card with given driverId @@ -162,9 +130,9 @@ 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); + if (cards[i].cs) + if (cards[i].cs->myid == driverid) + return (cards[i].cs); return (struct IsdnCardState *) 0; } @@ -176,7 +144,7 @@ link_debug(struct Channel *chanp, char *s, int direction) 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); + HiSax_putstatus(chanp->cs, tmp); } @@ -233,7 +201,7 @@ enum { EV_SETUP_CMPL_IND, /* 10 */ EV_BC_EST, /* 11 */ EV_WRITEBUF, /* 12 */ - EV_DATAIN, /* 13 */ + EV_ESTABLISH, /* 13 */ EV_HANGUP, /* 14 */ EV_BC_REL, /* 15 */ EV_CINF, /* 16 */ @@ -263,7 +231,7 @@ static char *strEvent[] = "EV_SETUP_CMPL_IND", "EV_BC_EST", "EV_WRITEBUF", - "EV_DATAIN", + "EV_ESTABLISH", "EV_HANGUP", "EV_BC_REL", "EV_CINF", @@ -283,7 +251,6 @@ enum { ST_LC_ESTABLISH_WAIT, ST_LC_CONNECTED, ST_LC_FLUSH_WAIT, - ST_LC_FLUSH_DELAY, ST_LC_RELEASE_WAIT, }; @@ -297,7 +264,6 @@ static char *strLcState[] = "ST_LC_ESTABLISH_WAIT", "ST_LC_CONNECTED", "ST_LC_FLUSH_WAIT", - "ST_LC_FLUSH_DELAY", "ST_LC_RELEASE_WAIT", }; @@ -307,9 +273,7 @@ enum { 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, }; @@ -322,9 +286,7 @@ static char *strLcEvent[] = "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", }; @@ -332,788 +294,962 @@ static char *strLcEvent[] = #define LC_B 1 static inline void -l4_deliver_cause(struct Channel *chanp) +lli_deliver_cause(struct Channel *chanp) { isdn_ctrl ic; - if (chanp->para.cause < 0) + if (chanp->proc->para.cause < 0) return; - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->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); + if (chanp->cs->protocol == ISDN_PTYPE_EURO) + sprintf(ic.parm.num, "E%02X%02X", chanp->proc->para.loc & 0x7f, + chanp->proc->para.cause & 0x7f); else - sprintf(ic.parm.num, "%02X%02X", chanp->para.loc & 0x7f, - chanp->para.cause & 0x7f); - chanp->sp->iif.statcallb(&ic); + sprintf(ic.parm.num, "%02X%02X", chanp->proc->para.loc & 0x7f, + chanp->proc->para.cause & 0x7f); + chanp->cs->iif.statcallb(&ic); +} + +static void +lli_d_established(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); + if (chanp->leased) { + isdn_ctrl ic; + int ret; + char txt[32]; + + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) chanp->chan); + FsmChangeState(fi, ST_IN_WAIT_LL); + test_and_set_bit(FLG_CALL_REC, &chanp->Flags); + if (chanp->debug & 1) + link_debug(chanp, "STAT_ICALL_LEASED", 0); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_ICALL; + ic.arg = chanp->chan; + ic.parm.setup.si1 = 7; + ic.parm.setup.si2 = 0; + ic.parm.setup.plan = 0; + ic.parm.setup.screen = 0; + sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1); + sprintf(ic.parm.setup.phone,"LEASED%d", chanp->cs->myid); + ret = chanp->cs->iif.statcallb(&ic); + if (chanp->debug & 1) { + sprintf(txt, "statcallb ret=%d", ret); + link_debug(chanp, txt, 1); + } + if (!ret) { + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan); + FsmChangeState(fi, ST_NULL); + } + } else if (fi->state == ST_WAIT_DSHUTDOWN) + FsmChangeState(fi, ST_NULL); +} + +static void +lli_d_released(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + test_and_clear_bit(FLG_START_D, &chanp->Flags); } /* * Dial out */ static void -l4_prep_dialout(struct FsmInst *fi, int event, void *arg) +lli_prep_dialout(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_OUT_WAIT_D); FsmDelTimer(&chanp->drel_timer, 60); FsmDelTimer(&chanp->dial_timer, 73); - chanp->l2_active_protocol = chanp->l2_protocol; chanp->incoming = 0; - chanp->lc_b.l2_start = !0; + chanp->lc_b->l2_start = !0; switch (chanp->l2_active_protocol) { case (ISDN_PROTO_L2_X75I): - chanp->lc_b.l2_establish = !0; + chanp->lc_b->l2_establish = !0; break; case (ISDN_PROTO_L2_HDLC): case (ISDN_PROTO_L2_TRANS): - chanp->lc_b.l2_establish = 0; + chanp->lc_b->l2_establish = 0; break; default: - printk(KERN_WARNING "l4_prep_dialout unknown protocol\n"); + printk(KERN_WARNING "lli_prep_dialout unknown protocol\n"); break; } - if (chanp->Flags & FLG_ESTAB_D) { + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) { FsmEvent(fi, EV_DLEST, NULL); } else { - chanp->Flags = FLG_START_D; + chanp->Flags = 0; + test_and_set_bit(FLG_START_D, &chanp->Flags); if (chanp->leased) { - chanp->lc_d.l2_establish = 0; + chanp->lc_d->l2_establish = 0; } - FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL); } } static void -l4_do_dialout(struct FsmInst *fi, int event, void *arg) +lli_do_dialout(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_OUT_DIAL); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) chanp->chan); if (chanp->leased) { - chanp->para.bchannel = (chanp->chan & 1) + 1; FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL); } else { - SETBIT(chanp->Flags, FLG_ESTAB_D); - chanp->para.callref = chanp->outcallref; - chanp->outcallref++; - if (chanp->outcallref == 128) - chanp->outcallref = 64; - chanp->is.l4.l4l3(&chanp->is, CC_SETUP_REQ, NULL); - SETBIT(chanp->Flags, FLG_CALL_SEND); + test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP_REQ, chanp); + test_and_set_bit(FLG_CALL_SEND, &chanp->Flags); } } static void -l4_init_bchan_out(struct FsmInst *fi, int event, void *arg) +lli_init_bchan_out(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_BCONN); - SETBIT(chanp->Flags, FLG_LL_DCONN); + test_and_set_bit(FLG_LL_DCONN, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, "STAT_DCONN", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DCONN; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - init_ds(chanp, 0); - SETBIT(chanp->Flags, FLG_START_B); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL); + chanp->cs->iif.statcallb(&ic); + init_b_st(chanp, 0); + test_and_set_bit(FLG_START_B, &chanp->Flags); + FsmEvent(&chanp->lc_b->lcfi, EV_LC_ESTABLISH, NULL); } static void -l4_go_active(struct FsmInst *fi, int event, void *arg) +lli_go_active(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_ACTIVE); chanp->data_open = !0; - SETBIT(chanp->Flags, FLG_CONNECT_B); + test_and_set_bit(FLG_CONNECT_B, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, "STAT_BCONN", 0); - SETBIT(chanp->Flags, FLG_LL_BCONN); - ic.driver = chanp->sp->myid; + test_and_set_bit(FLG_LL_BCONN, &chanp->Flags); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BCONN; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_CONN, (void *) chanp->chan); } /* incomming call */ static void -l4_start_dchan(struct FsmInst *fi, int event, void *arg) +lli_start_dchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_IN_WAIT_D); FsmDelTimer(&chanp->drel_timer, 61); - if (chanp->Flags & FLG_ESTAB_D) { + if (event == EV_ACCEPTD) + test_and_set_bit(FLG_DO_CONNECT, &chanp->Flags); + else if (event == EV_HANGUP) { + test_and_set_bit(FLG_DO_HANGUP, &chanp->Flags); +#ifdef ALERT_REJECT + test_and_set_bit(FLG_DO_ALERT, &chanp->Flags); +#endif + } + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) { FsmEvent(fi, EV_DLEST, NULL); - } else { - chanp->Flags = FLG_START_D; - FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL); - } + } else if (!test_and_set_bit(FLG_START_D, &chanp->Flags)) + FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL); } static void -l4_deliver_call(struct FsmInst *fi, int event, void *arg) +lli_deliver_call(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; int ret; char txt[32]; + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) chanp->chan); /* * Report incoming calls only once to linklevel, use CallFlags * which is set to 3 with each broadcast message in isdnl1.c * and resetted if a interface answered the STAT_ICALL. */ - if ((chanp->sp) && (chanp->sp->CallFlags == 3)) { + if (1) { /* for only one TEI */ FsmChangeState(fi, ST_IN_WAIT_LL); - SETBIT(chanp->Flags, FLG_ESTAB_D); - SETBIT(chanp->Flags, FLG_CALL_REC); + test_and_set_bit(FLG_CALL_REC, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, "STAT_ICALL", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_ICALL; ic.arg = chanp->chan; /* * No need to return "unknown" for calls without OAD, * cause that's handled in linklevel now (replaced by '0') */ - ic.parm.setup = chanp->para.setup; - ret = chanp->sp->iif.statcallb(&ic); + ic.parm.setup = chanp->proc->para.setup; + ret = chanp->cs->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); + FsmDelTimer(&chanp->drel_timer, 61); + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) { + FsmChangeState(fi, ST_IN_ALERT_SEND); + test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING_REQ, chanp->proc); + } else { + test_and_set_bit(FLG_DO_ALERT, &chanp->Flags); + FsmChangeState(fi, ST_IN_WAIT_D); + test_and_set_bit(FLG_START_D, &chanp->Flags); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL); + } break; case 2: /* Rejecting Call */ - RESBIT(chanp->Flags, FLG_CALL_REC); + test_and_clear_bit(FLG_CALL_REC, &chanp->Flags); break; case 0: /* OK, nobody likes this call */ default: /* statcallb problems */ - chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE, chanp->proc); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan); FsmChangeState(fi, ST_NULL); - chanp->Flags = FLG_ESTAB_D; - FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61); +#ifndef LAYER2_WATCHING + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) + FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61); +#endif break; } } else { - chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE, chanp->proc); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan); + FsmChangeState(fi, ST_NULL); +#ifndef LAYER2_WATCHING + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) + FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62); +#endif + } +} + +static void +lli_establish_d(struct FsmInst *fi, int event, void *arg) +{ + /* This establish the D-channel for pending L3 messages + * without blocking th channel + */ + struct Channel *chanp = fi->userdata; + + test_and_set_bit(FLG_DO_ESTAB, &chanp->Flags); + FsmChangeState(fi, ST_IN_WAIT_D); + test_and_set_bit(FLG_START_D, &chanp->Flags); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL); +} + +static void +lli_do_action(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); + if (chanp->leased) { + FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); + test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags); + test_and_clear_bit(FLG_DO_CONNECT, &chanp->Flags); + FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL); + } else if (test_and_clear_bit(FLG_DO_CONNECT, &chanp->Flags) && + !test_bit(FLG_DO_HANGUP, &chanp->Flags)) { + FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); + test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP_RSP, chanp->proc); + } else if (test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags)) { + if (test_bit(FLG_DO_HANGUP, &chanp->Flags)) + FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63); + FsmChangeState(fi, ST_IN_ALERT_SEND); + test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING_REQ, chanp->proc); + } else if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags)) { + FsmChangeState(fi, ST_WAIT_DRELEASE); + chanp->proc->para.cause = 0x15; /* Call Rejected */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT_REQ, chanp->proc); + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); + } else if (test_and_clear_bit(FLG_DO_ESTAB, &chanp->Flags)) { FsmChangeState(fi, ST_NULL); - chanp->Flags = FLG_ESTAB_D; - FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62); + chanp->Flags = 0; + test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ESTABLISH, chanp->proc); + chanp->proc = NULL; +#ifndef LAYER2_WATCHING + FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60); +#endif } } static void -l4_send_dconnect(struct FsmInst *fi, int event, void *arg) +lli_send_dconnect(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); - chanp->is.l4.l4l3(&chanp->is, CC_SETUP_RSP, NULL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP_RSP, chanp->proc); } static void -l4_init_bchan_in(struct FsmInst *fi, int event, void *arg) +lli_init_bchan_in(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_BCONN); - SETBIT(chanp->Flags, FLG_LL_DCONN); + test_and_set_bit(FLG_LL_DCONN, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, "STAT_DCONN", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DCONN; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); chanp->l2_active_protocol = chanp->l2_protocol; chanp->incoming = !0; - chanp->lc_b.l2_start = 0; + chanp->lc_b->l2_start = 0; switch (chanp->l2_active_protocol) { case (ISDN_PROTO_L2_X75I): - chanp->lc_b.l2_establish = !0; + chanp->lc_b->l2_establish = !0; break; case (ISDN_PROTO_L2_HDLC): case (ISDN_PROTO_L2_TRANS): - chanp->lc_b.l2_establish = 0; + chanp->lc_b->l2_establish = 0; break; default: - printk(KERN_WARNING "r9 unknown protocol\n"); + printk(KERN_WARNING "bchannel unknown protocol\n"); break; } - init_ds(chanp, !0); - SETBIT(chanp->Flags, FLG_START_B); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL); + init_b_st(chanp, !0); + test_and_set_bit(FLG_START_B, &chanp->Flags); + 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) +lli_cancel_call(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_DRELEASE); - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - RESBIT(chanp->Flags, FLG_LL_BCONN); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->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->cs->iif.statcallb(&ic); } - chanp->para.cause = 0x10; /* Normal Call Clearing */ - chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL); - SETBIT(chanp->Flags, FLG_DISC_SEND); + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT_REQ, chanp->proc); + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); } static void -l4_shutdown_d(struct FsmInst *fi, int event, void *arg) +lli_shutdown_d(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_WAIT_DSHUTDOWN); FsmDelTimer(&chanp->drel_timer, 62); - RESBIT(chanp->Flags, FLG_ESTAB_D); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); +#ifdef LAYER2_WATCHING + FsmChangeState(fi, ST_NULL); +#else + if (!test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags)) { + if (chanp->chan) { + if (chanp->cs->channel[0].fi.state != ST_NULL) + return; + } else { + if (chanp->cs->channel[1].fi.state != ST_NULL) + return; + } + } + FsmChangeState(fi, ST_WAIT_DSHUTDOWN); + test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL); +#endif } static void -l4_timeout_d(struct FsmInst *fi, int event, void *arg) +lli_timeout_d(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - if (chanp->Flags & FLG_LL_DCONN) { + if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - RESBIT(chanp->Flags, FLG_LL_DCONN); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; + lli_deliver_cause(chanp); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } FsmChangeState(fi, ST_NULL); - chanp->Flags = FLG_ESTAB_D; + chanp->Flags = 0; + test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); +#ifndef LAYER2_WATCHING FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60); +#endif + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan); } static void -l4_go_null(struct FsmInst *fi, int event, void *arg) +lli_go_null(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_NULL); chanp->Flags = 0; FsmDelTimer(&chanp->drel_timer, 63); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan); } static void -l4_disconn_bchan(struct FsmInst *fi, int event, void *arg) +lli_disconn_bchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; chanp->data_open = 0; FsmChangeState(fi, ST_WAIT_BRELEASE); - RESBIT(chanp->Flags, FLG_CONNECT_B); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); + test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags); + FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL); } static void -l4_send_d_disc(struct FsmInst *fi, int event, void *arg) +lli_send_d_disc(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - - if (chanp->Flags & (FLG_DISC_REC | FLG_REL_REC)) + if (test_bit(FLG_DISC_REC, &chanp->Flags) || + test_bit(FLG_REL_REC, &chanp->Flags)) return; FsmChangeState(fi, ST_WAIT_DRELEASE); - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - RESBIT(chanp->Flags, FLG_LL_BCONN); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + if (chanp->leased) { + ic.command = ISDN_STAT_CAUSE; + ic.arg = chanp->chan; + sprintf(ic.parm.num, "L0010"); + chanp->cs->iif.statcallb(&ic); + if (chanp->debug & 1) + link_debug(chanp, "STAT_DHUP", 0); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); + FsmChangeState(fi, ST_WAIT_DSHUTDOWN); + test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL); + } else { + if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags)) + chanp->proc->para.cause = 0x15; /* Call Reject */ + else + chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT_REQ, chanp->proc); + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); } - chanp->para.cause = 0x10; /* Normal Call Clearing */ - chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL); - SETBIT(chanp->Flags, FLG_DISC_SEND); } static void -l4_released_bchan(struct FsmInst *fi, int event, void *arg) +lli_released_bchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_DCOMMAND); chanp->data_open = 0; - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - RESBIT(chanp->Flags, FLG_LL_BCONN); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); + release_b_st(chanp); + test_and_clear_bit(FLG_START_B, &chanp->Flags); } static void -l4_release_bchan(struct FsmInst *fi, int event, void *arg) +lli_release_bchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; chanp->data_open = 0; - SETBIT(chanp->Flags, FLG_DISC_REC); + test_and_set_bit(FLG_DISC_REC, &chanp->Flags); FsmChangeState(fi, ST_WAIT_BREL_DISC); - RESBIT(chanp->Flags, FLG_CONNECT_B); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); + test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags); + FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL); } static void -l4_received_d_rel(struct FsmInst *fi, int event, void *arg) +lli_received_d_rel(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; chanp->data_open = 0; - FsmChangeState(fi, ST_WAIT_DSHUTDOWN); - SETBIT(chanp->Flags, FLG_REL_REC); - if (chanp->Flags & FLG_CONNECT_B) { - chanp->lc_b.l2_establish = 0; /* direct reset in lc_b.lcfi */ - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); - RESBIT(chanp->Flags, FLG_CONNECT_B); + FsmChangeState(fi, ST_NULL); + test_and_set_bit(FLG_REL_REC, &chanp->Flags); + if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) { + chanp->lc_b->l2_establish = 0; /* direct reset in lc_b->lcfi */ + FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL); } - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - RESBIT(chanp->Flags, FLG_LL_BCONN); - } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); + chanp->cs->iif.statcallb(&ic); } - if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND | FLG_CALL_ALERT)) { + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + if (test_bit(FLG_LL_DCONN, &chanp->Flags) || + test_bit(FLG_CALL_SEND, &chanp->Flags) || + test_bit(FLG_CALL_ALERT, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; + lli_deliver_cause(chanp); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - FsmEvent(&chanp->lc_d.lcfi, EV_LC_FLUSH, NULL); - RESBIT(chanp->Flags, FLG_ESTAB_D); - RESBIT(chanp->Flags, FLG_DISC_SEND); - RESBIT(chanp->Flags, FLG_CALL_REC); - RESBIT(chanp->Flags, FLG_CALL_ALERT); - RESBIT(chanp->Flags, FLG_LL_DCONN); - RESBIT(chanp->Flags, FLG_CALL_SEND); + test_and_clear_bit(FLG_DISC_SEND, &chanp->Flags); + test_and_clear_bit(FLG_CALL_REC, &chanp->Flags); + test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags); + test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); + test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); + lli_timeout_d(fi, event, arg); } static void -l4_received_d_relcnf(struct FsmInst *fi, int event, void *arg) +lli_received_d_relcnf(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; chanp->data_open = 0; - FsmChangeState(fi, ST_WAIT_DSHUTDOWN); - if (chanp->Flags & FLG_CONNECT_B) { - chanp->lc_b.l2_establish = 0; /* direct reset in lc_b.lcfi */ - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); - RESBIT(chanp->Flags, FLG_CONNECT_B); + FsmChangeState(fi, ST_NULL); + if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) { + chanp->lc_b->l2_establish = 0; /* direct reset in lc_b->lcfi */ + FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL); } - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - RESBIT(chanp->Flags, FLG_LL_BCONN); + chanp->cs->iif.statcallb(&ic); } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); - } - if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND | FLG_CALL_ALERT)) { + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + if (test_bit(FLG_LL_DCONN, &chanp->Flags) || + test_bit(FLG_CALL_SEND, &chanp->Flags) || + test_bit(FLG_CALL_ALERT, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; + lli_deliver_cause(chanp); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - FsmEvent(&chanp->lc_d.lcfi, EV_LC_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); + test_and_clear_bit(FLG_DISC_SEND, &chanp->Flags); + test_and_clear_bit(FLG_CALL_REC, &chanp->Flags); + test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags); + test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); + test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); + lli_timeout_d(fi, event, arg); } static void -l4_received_d_disc(struct FsmInst *fi, int event, void *arg) +lli_received_d_disc(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; chanp->data_open = 0; FsmChangeState(fi, ST_WAIT_D_REL_CNF); - SETBIT(chanp->Flags, FLG_DISC_REC); - if (chanp->Flags & FLG_LL_BCONN) { + test_and_set_bit(FLG_DISC_REC, &chanp->Flags); + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - RESBIT(chanp->Flags, FLG_LL_BCONN); - } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); + chanp->cs->iif.statcallb(&ic); } - if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND | FLG_CALL_ALERT)) { + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + if (test_bit(FLG_LL_DCONN, &chanp->Flags) || + test_bit(FLG_CALL_SEND, &chanp->Flags) || + test_bit(FLG_CALL_ALERT, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; + lli_deliver_cause(chanp); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->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); + test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags); + test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); + test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE_REQ, chanp->proc); } /* processing charge info */ static void -l4_charge_info(struct FsmInst *fi, int event, void *arg) +lli_charge_info(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_CINF; ic.arg = chanp->chan; - sprintf(ic.parm.num, "%d", chanp->para.chargeinfo); - chanp->sp->iif.statcallb(&ic); + sprintf(ic.parm.num, "%d", chanp->proc->para.chargeinfo); + chanp->cs->iif.statcallb(&ic); } /* error procedures */ static void -l4_no_dchan(struct FsmInst *fi, int event, void *arg) +lli_no_dchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; if (chanp->debug & 1) link_debug(chanp, "STAT_NODCH", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_NODCH; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); chanp->Flags = 0; FsmChangeState(fi, ST_NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL); } static void -l4_no_dchan_ready(struct FsmInst *fi, int event, void *arg) +lli_no_dchan_ready(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } static void -l4_no_dchan_in(struct FsmInst *fi, int event, void *arg) +lli_no_dchan_in(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; + isdn_ctrl ic; - chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL); + if (chanp->debug & 1) + link_debug(chanp, "STAT_DHUP", 0); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); + chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL, chanp->proc); chanp->Flags = 0; FsmChangeState(fi, ST_NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL); } static void -l4_no_setup_rsp(struct FsmInst *fi, int event, void *arg) +lli_no_setup_rsp(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - chanp->Flags = 0; FsmChangeState(fi, ST_NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); + test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); + lli_shutdown_d(fi, event, arg); } static void -l4_setup_err(struct FsmInst *fi, int event, void *arg) +lli_setup_err(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_DRELEASE); - if (chanp->Flags & FLG_LL_DCONN) { + if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - RESBIT(chanp->Flags, FLG_LL_DCONN); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; + lli_deliver_cause(chanp); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - SETBIT(chanp->Flags, FLG_DISC_SEND); /* DISCONN was sent from L3 */ + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); /* DISCONN was sent from L3 */ } static void -l4_connect_err(struct FsmInst *fi, int event, void *arg) +lli_connect_err(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_DRELEASE); - if (chanp->Flags & FLG_LL_DCONN) { + if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - RESBIT(chanp->Flags, FLG_LL_DCONN); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; + lli_deliver_cause(chanp); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - SETBIT(chanp->Flags, FLG_DISC_SEND); /* DISCONN was sent from L3 */ + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); /* DISCONN was sent from L3 */ } static void -l4_active_dlrl(struct FsmInst *fi, int event, void *arg) +lli_got_dlrl(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; chanp->data_open = 0; FsmChangeState(fi, ST_NULL); - if (chanp->Flags & FLG_CONNECT_B) { - chanp->lc_b.l2_establish = 0; /* direct reset in lc_b.lcfi */ - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); - RESBIT(chanp->Flags, FLG_CONNECT_B); + if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) { + chanp->lc_b->l2_establish = 0; /* direct reset in lc_b->lcfi */ + FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL); } - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - RESBIT(chanp->Flags, FLG_LL_BCONN); - } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); + chanp->cs->iif.statcallb(&ic); } - if (chanp->Flags & FLG_LL_DCONN) { - 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; + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + if (chanp->leased) { + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_CAUSE; + ic.arg = chanp->chan; + sprintf(ic.parm.num, "L%02X%02X", 0, 0x2f); + chanp->cs->iif.statcallb(&ic); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); + chanp->Flags = 0; + } else { + if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) { + if (chanp->debug & 1) + link_debug(chanp, "STAT_DHUP", 0); + if (chanp->cs->protocol == ISDN_PTYPE_EURO) { + chanp->proc->para.cause = 0x2f; + chanp->proc->para.loc = 0; + } else { + chanp->proc->para.cause = 0x70; + chanp->proc->para.loc = 0; + } + lli_deliver_cause(chanp); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); + } + chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL, chanp->proc); + chanp->Flags = 0; + FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL); } - chanp->Flags = 0; - chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan); } + /* *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}, +static struct FsmNode fnlist[] HISAX_INITDATA = +{ + {ST_NULL, EV_DIAL, lli_prep_dialout}, + {ST_NULL, EV_SETUP_IND, lli_deliver_call}, + {ST_NULL, EV_SHUTDOWN_D, lli_shutdown_d}, + {ST_NULL, EV_DLRL, lli_go_null}, + {ST_NULL, EV_DLEST, lli_d_established}, + {ST_NULL, EV_ESTABLISH, lli_establish_d}, + {ST_OUT_WAIT_D, EV_DLEST, lli_do_dialout}, + {ST_OUT_WAIT_D, EV_DLRL, lli_no_dchan}, + {ST_OUT_WAIT_D, EV_HANGUP, lli_no_dchan}, + {ST_IN_WAIT_D, EV_DLEST, lli_do_action}, + {ST_IN_WAIT_D, EV_DLRL, lli_no_dchan_in}, + {ST_IN_WAIT_D, EV_ACCEPTD, lli_start_dchan}, + {ST_IN_WAIT_D, EV_HANGUP, lli_start_dchan}, + {ST_OUT_DIAL, EV_SETUP_CNF, lli_init_bchan_out}, + {ST_OUT_DIAL, EV_HANGUP, lli_cancel_call}, + {ST_OUT_DIAL, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_OUT_DIAL, EV_RELEASE_IND, lli_received_d_rel}, + {ST_OUT_DIAL, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_OUT_DIAL, EV_NOSETUP_RSP, lli_no_setup_rsp}, + {ST_OUT_DIAL, EV_SETUP_ERR, lli_setup_err}, + {ST_OUT_DIAL, EV_DLRL, lli_got_dlrl}, + {ST_IN_WAIT_LL, EV_DLEST, lli_d_established}, + {ST_IN_WAIT_LL, EV_DLRL, lli_d_released}, + {ST_IN_WAIT_LL, EV_ACCEPTD, lli_start_dchan}, + {ST_IN_WAIT_LL, EV_HANGUP, lli_start_dchan}, + {ST_IN_WAIT_LL, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_IN_WAIT_LL, EV_RELEASE_IND, lli_received_d_rel}, + {ST_IN_WAIT_LL, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_IN_ALERT_SEND, EV_SETUP_CMPL_IND, lli_init_bchan_in}, + {ST_IN_ALERT_SEND, EV_ACCEPTD, lli_send_dconnect}, + {ST_IN_ALERT_SEND, EV_HANGUP, lli_send_d_disc}, + {ST_IN_ALERT_SEND, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_IN_ALERT_SEND, EV_RELEASE_IND, lli_received_d_rel}, + {ST_IN_ALERT_SEND, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_IN_ALERT_SEND, EV_DLRL, lli_got_dlrl}, + {ST_IN_WAIT_CONN_ACK, EV_SETUP_CMPL_IND, lli_init_bchan_in}, + {ST_IN_WAIT_CONN_ACK, EV_HANGUP, lli_send_d_disc}, + {ST_IN_WAIT_CONN_ACK, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_IN_WAIT_CONN_ACK, EV_RELEASE_IND, lli_received_d_rel}, + {ST_IN_WAIT_CONN_ACK, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_IN_WAIT_CONN_ACK, EV_CONNECT_ERR, lli_connect_err}, + {ST_IN_WAIT_CONN_ACK, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_BCONN, EV_BC_EST, lli_go_active}, + {ST_WAIT_BCONN, EV_BC_REL, lli_send_d_disc}, + {ST_WAIT_BCONN, EV_HANGUP, lli_send_d_disc}, + {ST_WAIT_BCONN, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_WAIT_BCONN, EV_RELEASE_IND, lli_received_d_rel}, + {ST_WAIT_BCONN, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_WAIT_BCONN, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_BCONN, EV_CINF, lli_charge_info}, + {ST_ACTIVE, EV_CINF, lli_charge_info}, + {ST_ACTIVE, EV_BC_REL, lli_released_bchan}, + {ST_ACTIVE, EV_HANGUP, lli_disconn_bchan}, + {ST_ACTIVE, EV_DISCONNECT_IND, lli_release_bchan}, + {ST_ACTIVE, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_ACTIVE, EV_RELEASE_IND, lli_received_d_rel}, + {ST_ACTIVE, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_BRELEASE, EV_BC_REL, lli_send_d_disc}, + {ST_WAIT_BRELEASE, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_WAIT_BRELEASE, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_WAIT_BRELEASE, EV_RELEASE_IND, lli_received_d_rel}, + {ST_WAIT_BRELEASE, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_BREL_DISC, EV_BC_REL, lli_received_d_disc}, + {ST_WAIT_BREL_DISC, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_WAIT_BREL_DISC, EV_RELEASE_IND, lli_received_d_rel}, + {ST_WAIT_BREL_DISC, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_DCOMMAND, EV_HANGUP, lli_send_d_disc}, + {ST_WAIT_DCOMMAND, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_WAIT_DCOMMAND, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_WAIT_DCOMMAND, EV_RELEASE_IND, lli_received_d_rel}, + {ST_WAIT_DCOMMAND, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_DRELEASE, EV_RELEASE_IND, lli_timeout_d}, + {ST_WAIT_DRELEASE, EV_RELEASE_CNF, lli_timeout_d}, + {ST_WAIT_DRELEASE, EV_RELEASE_ERR, lli_timeout_d}, + {ST_WAIT_DRELEASE, EV_DIAL, lli_no_dchan_ready}, + {ST_WAIT_DRELEASE, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_D_REL_CNF, EV_RELEASE_CNF, lli_timeout_d}, + {ST_WAIT_D_REL_CNF, EV_RELEASE_ERR, lli_timeout_d}, +/* ETS 300-104 16.1 */ + {ST_WAIT_D_REL_CNF, EV_RELEASE_IND, lli_timeout_d}, + {ST_WAIT_D_REL_CNF, EV_DIAL, lli_no_dchan_ready}, + {ST_WAIT_D_REL_CNF, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_DSHUTDOWN, EV_DLRL, lli_go_null}, + {ST_WAIT_DSHUTDOWN, EV_DLEST, lli_d_established}, + {ST_WAIT_DSHUTDOWN, EV_DIAL, lli_prep_dialout}, + {ST_WAIT_DSHUTDOWN, EV_SETUP_IND, lli_deliver_call}, }; /* *INDENT-ON* */ - - #define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode)) static void -lc_r1(struct FsmInst *fi, int event, void *arg) +lc_activate_l1(struct FsmInst *fi, int event, void *arg) { struct LcFsm *lf = fi->userdata; + FsmDelTimer(&lf->act_timer, 50); 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); + /* This timeout is to avoid a hang if no L1 activation is possible */ + FsmAddTimer(&lf->act_timer, 30000, EV_LC_TIMER, NULL, 50); + lf->st->ma.manl1(lf->st, PH_ACTIVATE_REQ, NULL); +} +static void +lc_activated_from_l1(struct FsmInst *fi, int event, void *arg) +{ + struct LcFsm *lf = fi->userdata; + + if (lf->l2_establish) + FsmChangeState(fi, ST_LC_DELAY); + else { + FsmChangeState(fi, ST_LC_CONNECTED); + lf->lccall(lf, LC_ESTABLISH, NULL); + } } static void -lc_r6(struct FsmInst *fi, int event, void *arg) +lc_l1_activated(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); + /* This timer is needed for delay the first paket on a channel + to be shure that the other side is ready too */ + if (lf->delay) + FsmAddTimer(&lf->act_timer, lf->delay, EV_LC_TIMER, NULL, 51); + else + FsmEvent(fi, EV_LC_TIMER, NULL); } static void -lc_r2(struct FsmInst *fi, int event, void *arg) +lc_start_l2(struct FsmInst *fi, int event, void *arg) { struct LcFsm *lf = fi->userdata; - if (lf->l2_establish) { +/* if (!lf->st->l1.act_state) + lf->st->l1.act_state = 2; +*/ if (lf->l2_establish) { FsmChangeState(fi, ST_LC_ESTABLISH_WAIT); if (lf->l2_start) lf->st->ma.manl2(lf->st, DL_ESTABLISH, NULL); @@ -1124,107 +1260,97 @@ lc_r2(struct FsmInst *fi, int event, void *arg) } static void -lc_r3(struct FsmInst *fi, int event, void *arg) +lc_connected(struct FsmInst *fi, int event, void *arg) { struct LcFsm *lf = fi->userdata; + FsmDelTimer(&lf->act_timer, 50); 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) +lc_release_l2(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->st->ma.manl1(lf->st, PH_DEACTIVATE_REQ, NULL); lf->lccall(lf, LC_RELEASE, NULL); } } static void -lc_r4_1(struct FsmInst *fi, int event, void *arg) +lc_l2_released(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); + FsmChangeState(fi, ST_LC_RELEASE_WAIT); + FsmDelTimer(&lf->act_timer, 51); + /* This delay is needed for send out the UA frame before + * PH_DEACTIVATE the interface + */ + FsmAddTimer(&lf->act_timer, 20, EV_LC_TIMER, NULL, 54); } static void -lc_r5_1(struct FsmInst *fi, int event, void *arg) +lc_release_l1(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); + FsmDelTimer(&lf->act_timer, 54); + FsmChangeState(fi, ST_LC_NULL); + lf->st->ma.manl1(lf->st, PH_DEACTIVATE_REQ, NULL); + lf->lccall(lf, LC_RELEASE, NULL); } static void -lc_r5(struct FsmInst *fi, int event, void *arg) +lc_l1_deactivated(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}, +static struct FsmNode LcFnList[] HISAX_INITDATA = +{ + {ST_LC_NULL, EV_LC_ESTABLISH, lc_activate_l1}, + {ST_LC_NULL, EV_LC_PH_ACTIVATE, lc_activated_from_l1}, + {ST_LC_NULL, EV_LC_DL_ESTABLISH, lc_connected}, + {ST_LC_ACTIVATE_WAIT, EV_LC_PH_ACTIVATE, lc_l1_activated}, + {ST_LC_ACTIVATE_WAIT, EV_LC_TIMER, lc_release_l1}, + {ST_LC_ACTIVATE_WAIT, EV_LC_PH_DEACTIVATE, lc_l1_deactivated}, + {ST_LC_DELAY, EV_LC_ESTABLISH, lc_start_l2}, + {ST_LC_DELAY, EV_LC_TIMER, lc_start_l2}, + {ST_LC_DELAY, EV_LC_DL_ESTABLISH, lc_connected}, + {ST_LC_DELAY, EV_LC_PH_DEACTIVATE, lc_l1_deactivated}, + {ST_LC_ESTABLISH_WAIT, EV_LC_DL_ESTABLISH, lc_connected}, + {ST_LC_ESTABLISH_WAIT, EV_LC_RELEASE, lc_release_l1}, + {ST_LC_ESTABLISH_WAIT, EV_LC_DL_RELEASE, lc_release_l1}, + {ST_LC_ESTABLISH_WAIT, EV_LC_PH_DEACTIVATE, lc_l1_deactivated}, + {ST_LC_CONNECTED, EV_LC_ESTABLISH, lc_connected}, + {ST_LC_CONNECTED, EV_LC_RELEASE, lc_release_l2}, + {ST_LC_CONNECTED, EV_LC_DL_RELEASE, lc_l2_released}, + {ST_LC_CONNECTED, EV_LC_PH_DEACTIVATE, lc_l1_deactivated}, + {ST_LC_FLUSH_WAIT, EV_LC_TIMER, lc_release_l2}, + {ST_LC_FLUSH_WAIT, EV_LC_PH_DEACTIVATE, lc_l1_deactivated}, + {ST_LC_RELEASE_WAIT, EV_LC_DL_RELEASE, lc_release_l1}, + {ST_LC_RELEASE_WAIT, EV_LC_TIMER, lc_release_l1}, + {ST_LC_FLUSH_WAIT, EV_LC_PH_DEACTIVATE, lc_l1_deactivated}, }; /* *INDENT-ON* */ - - - - - - - - #define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode)) -void -CallcNew(void) +HISAX_INITFUNC(void +CallcNew(void)) { callcfsm.state_count = STATE_COUNT; callcfsm.event_count = EVENT_COUNT; @@ -1247,17 +1373,11 @@ CallcFree(void) } static void -release_ds(struct Channel *chanp) +release_b_st(struct Channel *chanp) { - struct PStack *st = &chanp->ds; - struct IsdnCardState *sp; - struct HscxState *hsp; - - sp = st->l1.hardware; - hsp = sp->hs + chanp->hscx; - - close_hscxstate(hsp); + struct PStack *st = chanp->b_st; + chanp->bcs->BC_Close(chanp->bcs); switch (chanp->l2_active_protocol) { case (ISDN_PROTO_L2_X75I): releasestack_isdnl2(st); @@ -1268,90 +1388,145 @@ release_ds(struct Channel *chanp) break; } /* Reset B-Channel Statemachine */ - FsmDelTimer(&chanp->lc_b.act_timer, 79); - FsmChangeState(&chanp->lc_b.lcfi, ST_LC_NULL); + 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) +dc_l1man(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp; + chanp = (struct Channel *) st->lli.userdata; switch (pr) { - case (PH_ACTIVATE): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_ACTIVATE, NULL); + case (PH_ACTIVATE_CNF): + case (PH_ACTIVATE_IND): + FsmEvent(&chanp->lc_d->lcfi, EV_LC_PH_ACTIVATE, NULL); break; - case (PH_DEACTIVATE): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_DEACTIVATE, NULL); + case (PH_DEACTIVATE_IND): + FsmEvent(&chanp->lc_d->lcfi, EV_LC_PH_DEACTIVATE, NULL); break; } } static void -cc_l2man(struct PStack *st, int pr, void *arg) +dc_l2man(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp = (struct Channel *) st->lli.userdata; switch (pr) { case (DL_ESTABLISH): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_ESTABLISH, NULL); + 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); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_DL_RELEASE, NULL); break; } } static void -dcc_l1man(struct PStack *st, int pr, void *arg) +bc_l1man(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp = (struct Channel *) st->lli.userdata; switch (pr) { - case (PH_ACTIVATE): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_ACTIVATE, NULL); + case (PH_ACTIVATE_IND): + case (PH_ACTIVATE_CNF): + FsmEvent(&chanp->lc_b->lcfi, EV_LC_PH_ACTIVATE, NULL); break; - case (PH_DEACTIVATE): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_DEACTIVATE, NULL); + case (PH_DEACTIVATE_IND): + FsmEvent(&chanp->lc_b->lcfi, EV_LC_PH_DEACTIVATE, NULL); break; } } static void -dcc_l2man(struct PStack *st, int pr, void *arg) +bc_l2man(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp = (struct Channel *) st->lli.userdata; switch (pr) { case (DL_ESTABLISH): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_ESTABLISH, NULL); + FsmEvent(&chanp->lc_b->lcfi, EV_LC_DL_ESTABLISH, NULL); break; case (DL_RELEASE): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_RELEASE, NULL); + FsmEvent(&chanp->lc_b->lcfi, EV_LC_DL_RELEASE, NULL); break; } } -static void -l2tei_dummy(struct PStack *st, int pr, void *arg) +struct Channel +*selectfreechannel(struct PStack *st) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; - char tmp[64], tm[32]; + struct IsdnCardState *cs = st->l1.hardware; + struct Channel *chanp = st->lli.userdata; + int i; - jiftime(tm, jiffies); - sprintf(tmp, "%s Channel %d Warning! Dummy l2tei called pr=%d\n", tm, chanp->chan, pr); - HiSax_putstatus(chanp->sp, tmp); + if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) + i=1; + else + i=0; + while (i<2) { + if (chanp->fi.state == ST_NULL) + return (chanp); + chanp++; + i++; + } + return (NULL); +} + +int +is_activ(struct PStack *st) +{ + struct IsdnCardState *cs = st->l1.hardware; + struct Channel *chanp = st->lli.userdata; + int i; + + if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) + i=1; + else + i=0; + while (i<2) { + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) + return (1); + chanp++; + i++; + } + return (0); } static void -ll_handler(struct PStack *st, int pr, void *arg) +ll_handler(struct l3_process *pc, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp; char tmp[64], tm[32]; + if (pr == CC_SETUP_IND) { + if (!(chanp = selectfreechannel(pc->st))) { + pc->st->lli.l4l3(pc->st, CC_DLRL, pc); + return; + } else { + chanp->proc = pc; + pc->chan = chanp; + FsmEvent(&chanp->fi, EV_SETUP_IND, NULL); + return; + } + } else if (pr == CC_ESTABLISH) { + if (is_activ(pc->st)) { + pc->st->lli.l4l3(pc->st, CC_ESTABLISH, pc); + return; + } else if (!(chanp = selectfreechannel(pc->st))) { + pc->st->lli.l4l3(pc->st, CC_DLRL, pc); + return; + } else { + chanp->proc = pc; + FsmEvent(&chanp->fi, EV_ESTABLISH, NULL); + return; + } + + + } + chanp = pc->chan; switch (pr) { case (CC_DISCONNECT_IND): FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL); @@ -1359,9 +1534,6 @@ ll_handler(struct PStack *st, int pr, void *arg) 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; @@ -1386,49 +1558,51 @@ ll_handler(struct PStack *st, int pr, void *arg) case (CC_RELEASE_ERR): FsmEvent(&chanp->fi, EV_RELEASE_ERR, NULL); break; + case (CC_PROCEEDING_IND): + case (CC_ALERTING_IND): + break; default: - if (chanp->debug & 2048) { + if (chanp->debug & 0x800) { jiftime(tm, jiffies); sprintf(tmp, "%s Channel %d L3->L4 unknown primitiv %d\n", tm, chanp->chan, pr); - HiSax_putstatus(chanp->sp, tmp); + HiSax_putstatus(chanp->cs, tmp); } } } static void -init_is(struct Channel *chanp, unsigned int ces) +init_d_st(struct Channel *chanp) { - struct PStack *st = &chanp->is; - struct IsdnCardState *sp = chanp->sp; + struct PStack *st = chanp->d_st; + struct IsdnCardState *cs = chanp->cs; char tmp[128]; - setstack_HiSax(st, sp); + HiSax_addlist(cs, st); + setstack_HiSax(st, cs); st->l2.sap = 0; - st->l2.tei = 255; - st->l2.ces = ces; - st->l2.extended = !0; - st->l2.laptype = LAPD; + st->l2.tei = -1; + st->l2.flag = 0; + test_and_set_bit(FLG_MOD128, &st->l2.flag); + test_and_set_bit(FLG_LAPD, &st->l2.flag); + test_and_set_bit(FLG_ORIG, &st->l2.flag); + st->l2.maxlen = MAX_DFRAME_LEN; st->l2.window = 1; - st->l2.orig = !0; - st->l2.t200 = 1000; /* 1000 milliseconds */ - if (st->protocol == ISDN_PTYPE_1TR6) { - st->l2.n200 = 3; /* try 3 times */ - st->l2.t203 = 10000; /* 10000 milliseconds */ - } else { - st->l2.n200 = 4; /* try 4 times */ - st->l2.t203 = 5000; /* 5000 milliseconds */ - } + st->l2.T200 = 1000; /* 1000 milliseconds */ + st->l2.N200 = 3; /* try 3 times */ + if (st->protocol == ISDN_PTYPE_1TR6) + st->l2.T203 = 10000; /* 10000 milliseconds */ + else + st->l2.T203 = 10000; /* 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->lli.userdata = chanp; + st->lli.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); + st->l1.l1man = dc_l1man; + st->l2.l2man = dc_l2man; } static void @@ -1439,7 +1613,7 @@ callc_debug(struct FsmInst *fi, char *s) jiftime(tm, jiffies); sprintf(str, "%s Channel %d callc %s\n", tm, chanp->chan, s); - HiSax_putstatus(chanp->sp, str); + HiSax_putstatus(chanp->cs, str); } static void @@ -1449,8 +1623,8 @@ lc_debug(struct FsmInst *fi, char *s) 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); + sprintf(str, "%s Channel %d dc %s\n", tm, lf->ch->chan, s); + HiSax_putstatus(lf->ch->cs, str); } static void @@ -1460,22 +1634,35 @@ dlc_debug(struct FsmInst *fi, char *s) 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); + sprintf(str, "%s Channel %d bc %s\n", tm, lf->ch->chan, s); + HiSax_putstatus(lf->ch->cs, str); } static void lccall_d(struct LcFsm *lf, int pr, void *arg) { - struct Channel *chanp = lf->ch; + struct IsdnCardState *cs = lf->st->l1.hardware; + struct Channel *chanp; + int i; - switch (pr) { - case (LC_ESTABLISH): - FsmEvent(&chanp->fi, EV_DLEST, NULL); - break; - case (LC_RELEASE): - FsmEvent(&chanp->fi, EV_DLRL, NULL); - break; + if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) { + chanp = lf->ch; + i = 1; + } else { + chanp = cs->channel; + i = 0; + } + while (i < 2) { + switch (pr) { + case (LC_ESTABLISH): + FsmEvent(&chanp->fi, EV_DLEST, NULL); + break; + case (LC_RELEASE): + FsmEvent(&chanp->fi, EV_DLRL, NULL); + break; + } + chanp++; + i++; } } @@ -1495,20 +1682,19 @@ lccall_b(struct LcFsm *lf, int pr, void *arg) } static void -init_chan(int chan, struct IsdnCardState *csta, int hscx, - unsigned int ces) +init_chan(int chan, struct IsdnCardState *csta) { struct Channel *chanp = csta->channel + chan; - chanp->sp = csta; - chanp->hscx = hscx; + chanp->cs = csta; + chanp->bcs = csta->bcs + chan; chanp->chan = chan; chanp->incoming = 0; chanp->debug = 0; chanp->Flags = 0; chanp->leased = 0; - chanp->impair = 0; - init_is(chanp, ces); + chanp->b_st = kmalloc(sizeof(struct PStack), GFP_ATOMIC); + chanp->b_st->next = NULL; chanp->fi.fsm = &callcfsm; chanp->fi.state = ST_NULL; @@ -1517,58 +1703,72 @@ init_chan(int chan, struct IsdnCardState *csta, int hscx, chanp->fi.printdebug = callc_debug; FsmInitTimer(&chanp->fi, &chanp->dial_timer); FsmInitTimer(&chanp->fi, &chanp->drel_timer); - - chanp->lc_d.lcfi.fsm = &lcfsm; - chanp->lc_d.lcfi.state = ST_LC_NULL; - chanp->lc_d.lcfi.debug = 0; - chanp->lc_d.lcfi.userdata = &chanp->lc_d; - chanp->lc_d.lcfi.printdebug = lc_debug; - chanp->lc_d.type = LC_D; - chanp->lc_d.ch = chanp; - chanp->lc_d.st = &chanp->is; - chanp->lc_d.l2_establish = !0; - chanp->lc_d.l2_start = !0; - chanp->lc_d.lccall = lccall_d; - FsmInitTimer(&chanp->lc_d.lcfi, &chanp->lc_d.act_timer); - - chanp->lc_b.lcfi.fsm = &lcfsm; - chanp->lc_b.lcfi.state = ST_LC_NULL; - chanp->lc_b.lcfi.debug = 0; - chanp->lc_b.lcfi.userdata = &chanp->lc_b; - chanp->lc_b.lcfi.printdebug = dlc_debug; - chanp->lc_b.type = LC_B; - chanp->lc_b.ch = chanp; - chanp->lc_b.st = &chanp->ds; - chanp->lc_b.l2_establish = !0; - chanp->lc_b.l2_start = !0; - chanp->lc_b.lccall = lccall_b; - FsmInitTimer(&chanp->lc_b.lcfi, &chanp->lc_b.act_timer); - chanp->outcallref = 64; + if (!chan || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) { + chanp->d_st = kmalloc(sizeof(struct PStack), GFP_ATOMIC); + chanp->d_st->next = NULL; + init_d_st(chanp); + chanp->lc_d = kmalloc(sizeof(struct LcFsm), GFP_ATOMIC); + 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->delay = 0; + chanp->lc_d->ch = chanp; + chanp->lc_d->st = chanp->d_st; + 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); + } else { + chanp->d_st = csta->channel->d_st; + chanp->lc_d = csta->channel->lc_d; + } + chanp->lc_b = kmalloc(sizeof(struct LcFsm), GFP_ATOMIC); + 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->delay = DEFAULT_B_DELAY; + chanp->lc_b->ch = chanp; + chanp->lc_b->st = chanp->b_st; + 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->data_open = 0; } int CallcNewChan(struct IsdnCardState *csta) { - int ces; - chancount += 2; - ces = randomces(); - init_chan(0, csta, 1, ces++); - ces %= 0xffff; - init_chan(1, csta, 0, ces++); + init_chan(0, csta); + init_chan(1, csta); printk(KERN_INFO "HiSax: 2 channels added\n"); +#ifdef LAYER2_WATCHING + printk(KERN_INFO "LAYER2 ESTABLISH\n"); + test_and_set_bit(FLG_START_D, &csta->channel->Flags); + FsmEvent(&csta->channel->lc_d->lcfi, EV_LC_ESTABLISH, NULL); +#endif return (2); } static void -release_is(struct Channel *chanp) +release_d_st(struct Channel *chanp) { - struct PStack *st = &chanp->is; + struct PStack *st = chanp->d_st; + if (!st) + return; releasestack_isdnl2(st); releasestack_isdnl3(st); HiSax_rmlist(st->l1.hardware, st); + kfree(st); + chanp->d_st = NULL; } void @@ -1579,27 +1779,45 @@ CallcFreeChan(struct IsdnCardState *csta) 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); + FsmDelTimer(&csta->channel[i].lc_d->act_timer, 77); + FsmDelTimer(&csta->channel[i].lc_b->act_timer, 76); + if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) + release_d_st(csta->channel + i); + if (csta->channel[i].b_st) { + if (test_and_clear_bit(FLG_START_B, &csta->channel[i].Flags)) + release_b_st(csta->channel + i); + kfree(csta->channel[i].b_st); + csta->channel[i].b_st = NULL; + } else + printk(KERN_WARNING "CallcFreeChan b_st ch%d allready freed\n", i); + if (csta->channel[i].lc_b) { + kfree(csta->channel[i].lc_b); + csta->channel[i].b_st = NULL; } - release_is(csta->channel + i); + if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) { + release_d_st(csta->channel + i); + FsmDelTimer(&csta->channel[i].lc_d->act_timer, 77); + if (csta->channel[i].lc_d) { + kfree(csta->channel[i].lc_d); + csta->channel[i].d_st = NULL; + } else + printk(KERN_WARNING "CallcFreeChan lc_d ch%d allready freed\n", i); + } else + csta->channel[i].d_st = NULL; } } static void lldata_handler(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp = (struct Channel *) st->lli.userdata; struct sk_buff *skb = arg; switch (pr) { case (DL_DATA): if (chanp->data_open) - chanp->sp->iif.rcvcallb_skb(chanp->sp->myid, chanp->chan, skb); + chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb); else { - SET_SKB_FREE(skb); dev_kfree_skb(skb); } break; @@ -1613,16 +1831,22 @@ lldata_handler(struct PStack *st, int pr, void *arg) static void lltrans_handler(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp = (struct Channel *) st->lli.userdata; struct sk_buff *skb = arg; switch (pr) { - case (PH_DATA): + case (PH_DATA_IND): if (chanp->data_open) - chanp->sp->iif.rcvcallb_skb(chanp->sp->myid, chanp->chan, skb); + chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb); else { - SET_SKB_FREE(skb); - dev_kfree_skb(skb); + if (chanp->lc_b->lcfi.state == ST_LC_DELAY) + FsmEvent(&chanp->lc_b->lcfi, EV_LC_DL_ESTABLISH, NULL); + if (chanp->data_open) { + link_debug(chanp, "channel now open", 0); + chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, + chanp->chan, skb); + } else + dev_kfree_skb(skb); } break; default: @@ -1633,73 +1857,79 @@ lltrans_handler(struct PStack *st, int pr, void *arg) } static void -ll_writewakeup(struct PStack *st) +ll_writewakeup(struct PStack *st, int len) { - struct Channel *chanp = st->l4.userdata; + struct Channel *chanp = st->lli.userdata; isdn_ctrl ic; - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BSENT; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + ic.parm.length = len; + chanp->cs->iif.statcallb(&ic); } static int -init_ds(struct Channel *chanp, int incoming) +init_b_st(struct Channel *chanp, int incoming) { - struct PStack *st = &chanp->ds; - struct IsdnCardState *sp = chanp->sp; - struct HscxState *hsp = sp->hs + chanp->hscx; + struct PStack *st = chanp->b_st; + struct IsdnCardState *cs = chanp->cs; char tmp[128]; - st->l1.hardware = sp; - - hsp->mode = 2; - - if (setstack_hscx(st, hsp)) + st->l1.hardware = cs; + chanp->bcs->mode = 2; + if (chanp->bcs->BC_SetStack(st, chanp->bcs)) return (-1); - - st->l2.extended = 0; - st->l2.laptype = LAPB; - st->l2.orig = !incoming; - st->l2.t200 = 1000; /* 1000 milliseconds */ + st->l2.flag = 0; + test_and_set_bit(FLG_LAPB, &st->l2.flag); + st->l2.maxlen = MAX_DATA_SIZE; + if (!incoming) + test_and_set_bit(FLG_ORIG, &st->l2.flag); + st->l2.T200 = 1000; /* 1000 milliseconds */ st->l2.window = 7; - st->l2.n200 = 4; /* try 4 times */ - st->l2.t203 = 5000; /* 5000 milliseconds */ - + st->l2.N200 = 4; /* try 4 times */ + st->l2.T203 = 5000; /* 5000 milliseconds */ st->l3.debug = 0; switch (chanp->l2_active_protocol) { case (ISDN_PROTO_L2_X75I): sprintf(tmp, "Channel %d x.75", chanp->chan); 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->l1.l1man = bc_l1man; + st->l2.l2man = bc_l2man; + st->lli.userdata = chanp; + st->lli.l1writewakeup = NULL; + st->lli.l2writewakeup = ll_writewakeup; st->l2.l2m.debug = chanp->debug & 16; st->l2.debug = chanp->debug & 64; st->ma.manl2(st, MDL_NOTEIPROC, NULL); - st->l1.hscxmode = 2; /* Packet-Mode ? */ - st->l1.hscxchannel = chanp->para.bchannel - 1; + st->l1.mode = L1_MODE_HDLC; + if (chanp->leased) + st->l1.bc = chanp->chan & 1; + else + st->l1.bc = chanp->proc->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; + st->l1.l1man = bc_l1man; + st->lli.userdata = chanp; + st->lli.l1writewakeup = ll_writewakeup; + st->l1.mode = L1_MODE_HDLC; + if (chanp->leased) + st->l1.bc = chanp->chan & 1; + else + st->l1.bc = chanp->proc->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; + st->l1.l1man = bc_l1man; + st->lli.userdata = chanp; + st->lli.l1writewakeup = ll_writewakeup; + st->l1.mode = L1_MODE_TRANS; + if (chanp->leased) + st->l1.bc = chanp->chan & 1; + else + st->l1.bc = chanp->proc->para.bchannel - 1; break; } return (0); @@ -1719,15 +1949,17 @@ distr_debug(struct IsdnCardState *csta, int debugflags) for (i = 0; i < 2; i++) { chanp[i].debug = debugflags; chanp[i].fi.debug = debugflags & 2; - chanp[i].is.l2.l2m.debug = debugflags & 8; - chanp[i].ds.l2.l2m.debug = debugflags & 16; - chanp[i].is.l2.debug = debugflags & 32; - chanp[i].ds.l2.debug = debugflags & 64; - chanp[i].lc_d.lcfi.debug = debugflags & 128; - chanp[i].lc_b.lcfi.debug = debugflags & 256; + chanp[i].d_st->l2.l2m.debug = debugflags & 8; + chanp[i].b_st->l2.l2m.debug = debugflags & 0x10; + chanp[i].d_st->l2.debug = debugflags & 0x20; + chanp[i].b_st->l2.debug = debugflags & 0x40; + chanp[i].lc_d->lcfi.debug = debugflags & 0x80; + chanp[i].lc_b->lcfi.debug = debugflags & 0x100; + chanp[i].b_st->ma.tei_m.debug = debugflags & 0x200; + chanp[i].b_st->ma.debug = debugflags & 0x200; + chanp[i].d_st->l1.l1m.debug = debugflags & 0x1000; } csta->dlogflag = debugflags & 4; - csta->teistack->l2.l2m.debug = debugflags & 512; } int @@ -1748,8 +1980,11 @@ HiSax_command(isdn_ctrl * ic) switch (ic->command) { case (ISDN_CMD_SETEAZ): chanp = csta->channel + ic->arg; - if (chanp->debug & 1) - link_debug(chanp, "SETEAZ", 1); + if (chanp->debug & 1) { + sprintf(tmp, "SETEAZ card %d %s", csta->cardnr + 1, + ic->parm.num); + link_debug(chanp, tmp, 1); + } break; case (ISDN_CMD_SETL2): chanp = csta->channel + (ic->arg & 0xff); @@ -1768,9 +2003,9 @@ HiSax_command(isdn_ctrl * ic) 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'; + chanp->setup = ic->parm.setup; + if (!strcmp(chanp->setup.eazmsn, "0")) + chanp->setup.eazmsn[0] = '\0'; /* this solution is dirty and may be change, if * we make a callreference based callmanager */ if (chanp->fi.state == ST_NULL) { @@ -1817,7 +2052,7 @@ HiSax_command(isdn_ctrl * ic) case (ISDN_CMD_LOCK): HiSax_mod_inc_use_count(); #ifdef MODULE - if (csta->channel[0].debug & 1024) { + if (csta->channel[0].debug & 0x400) { jiftime(tmp, jiffies); i = strlen(tmp); sprintf(tmp + i, " LOCK modcnt %lx\n", MOD_USE_COUNT); @@ -1828,7 +2063,7 @@ HiSax_command(isdn_ctrl * ic) case (ISDN_CMD_UNLOCK): HiSax_mod_dec_use_count(); #ifdef MODULE - if (csta->channel[0].debug & 1024) { + if (csta->channel[0].debug & 0x400) { jiftime(tmp, jiffies); i = strlen(tmp); sprintf(tmp + i, " UNLOCK modcnt %lx\n", MOD_USE_COUNT); @@ -1852,16 +2087,13 @@ HiSax_command(isdn_ctrl * ic) 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); - } + num = *(unsigned int *) ic->parm.num; + csta->channel[0].lc_b->delay = num; + csta->channel[1].lc_b->delay = num; + sprintf(tmp, "delay card %d set to %d ms\n", + csta->cardnr + 1, num); + HiSax_putstatus(csta, tmp); + printk(KERN_DEBUG "HiSax: %s", tmp); break; case (3): for (i = 0; i < *(unsigned int *) ic->parm.num; i++) @@ -1872,35 +2104,47 @@ HiSax_command(isdn_ctrl * ic) HiSax_mod_inc_use_count(); break; case (5): /* set card in leased mode */ - csta->channel[0].leased = 1; - csta->channel[1].leased = 1; - sprintf(tmp, "card %d set into leased mode\n", - csta->cardnr + 1); - HiSax_putstatus(csta, tmp); + num = *(unsigned int *) ic->parm.num; + if ((num <1) || (num > 2)) { + sprintf(tmp, "Set LEASED wrong channel %d\n", + num); + HiSax_putstatus(csta, tmp); + printk(KERN_WARNING "HiSax: %s", tmp); + } else { + num--; + csta->channel[num].leased = 1; + csta->channel[num].lc_d->l2_establish = 0; + sprintf(tmp, "card %d channel %d set leased mode\n", + csta->cardnr + 1, num + 1); + HiSax_putstatus(csta, tmp); + FsmEvent(&csta->channel[num].lc_d->lcfi, EV_LC_ESTABLISH, NULL); + } + break; + case (6): /* set B-channel test loop */ + num = *(unsigned int *) ic->parm.num; + if (csta->stlist) + csta->stlist->ma.manl1(csta->stlist, + PH_TESTLOOP_REQ, (void *) num); 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); + csta->cardnr + 1, csta->debug); + HiSax_putstatus(cards[0].cs, 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; + csta->channel[0].d_st->l3.debug = *(unsigned int *) ic->parm.num; + csta->channel[1].d_st->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); + HiSax_putstatus(cards[0].cs, tmp); printk(KERN_DEBUG "HiSax: %s", tmp); break; default: @@ -1917,7 +2161,7 @@ HiSax_command(isdn_ctrl * ic) } int -HiSax_writebuf_skb(int id, int chan, struct sk_buff *skb) +HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb) { struct IsdnCardState *csta = hisax_findcard(id); struct Channel *chanp; @@ -1933,7 +2177,7 @@ HiSax_writebuf_skb(int id, int chan, struct sk_buff *skb) return -ENODEV; } chanp = csta->channel + chan; - st = &chanp->ds; + st = chanp->b_st; if (!chanp->data_open) { link_debug(chanp, "writebuf: channel not open", 1); return -EIO; @@ -1945,11 +2189,11 @@ HiSax_writebuf_skb(int id, int chan, struct sk_buff *skb) return -EINVAL; } if (len) { - if ((len + csta->hs[chanp->hscx].tx_cnt) > MAX_DATA_MEM) { + if ((len + chanp->bcs->tx_cnt) > MAX_DATA_MEM) { /* Must return 0 here, since this is not an error * but a temporary lack of resources. */ - if (chanp->debug & 2048) { + if (chanp->debug & 0x800) { sprintf(tmp, "writebuf: no buffers for %d bytes", len); link_debug(chanp, tmp, 1); } @@ -1959,12 +2203,13 @@ HiSax_writebuf_skb(int id, int chan, struct sk_buff *skb) cli(); nskb = skb_clone(skb, GFP_ATOMIC); if (nskb) { - if (chanp->lc_b.l2_establish) { - csta->hs[chanp->hscx].tx_cnt += len + st->l2.ihsize; - chanp->ds.l3.l3l2(&chanp->ds, DL_DATA, nskb); - } else { - csta->hs[chanp->hscx].tx_cnt += len; - chanp->ds.l2.l2l1(&chanp->ds, PH_DATA, nskb); + if (!ack) + nskb->pkt_type = PACKET_NOACK; + if (chanp->lc_b->l2_establish) + st->l3.l3l2(st, DL_DATA, nskb); + else { + chanp->bcs->tx_cnt += len; + st->l2.l2l1(st, PH_DATA_REQ, nskb); } dev_kfree_skb(skb); } else diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c index 7379c8a779e5..67e308e43d1b 100644 --- a/drivers/isdn/hisax/config.c +++ b/drivers/isdn/hisax/config.c @@ -1,58 +1,53 @@ -/* $Id: config.c,v 1.15 1997/04/06 22:57:24 keil Exp $ +/* $Id: config.c,v 2.12 1998/02/11 17:28:02 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 2.12 1998/02/11 17:28:02 keil + * Niccy PnP/PCI support * - * Revision 1.14 1997/03/25 23:11:22 keil - * US NI-1 protocol + * Revision 2.11 1998/02/09 21:26:13 keil + * fix export module for 2.1 * - * Revision 1.13 1997/03/23 21:45:49 keil - * Add support for ELSA PCMCIA + * Revision 2.10 1998/02/09 18:46:05 keil + * Support for Sedlbauer PCMCIA (Marcus Niemann) * - * Revision 1.12 1997/03/11 21:01:43 keil - * nzproto is only used with modules + * Revision 2.9 1998/02/03 23:31:28 keil + * add AMD7930 support * - * Revision 1.11 1997/02/14 12:23:12 fritz - * Added support for new insmod parameter handling. + * Revision 2.8 1998/02/02 13:32:59 keil + * New card support * - * Revision 1.10 1997/02/14 09:22:09 keil - * Final 2.0 version + * Revision 2.7 1998/01/31 21:41:44 keil + * changes for newer 2.1 kernels * - * Revision 1.9 1997/02/10 11:45:09 fritz - * More changes for Kernel 2.1.X compatibility. + * Revision 2.6 1997/11/08 21:35:43 keil + * new l1 init * - * Revision 1.8 1997/02/09 00:28:05 keil - * new interface handling, one interface per card - * default protocol now works again + * Revision 2.5 1997/11/06 17:15:08 keil + * New 2.1 init; PCMCIA wrapper changes * - * Revision 1.7 1997/01/27 15:56:57 keil - * Teles PCMCIA ITK ix1 micro added + * Revision 2.4 1997/10/29 19:07:52 keil + * changes for 2.1 * - * Revision 1.6 1997/01/21 22:17:56 keil - * new module load syntax + * Revision 2.3 1997/10/01 09:21:33 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. * - * Revision 1.5 1997/01/09 18:28:20 keil - * cosmetic cleanups + * Revision 2.2 1997/09/11 17:24:46 keil + * Add new cards * - * 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 + * Revision 2.1 1997/07/27 21:41:35 keil + * version change * + * Revision 2.0 1997/06/26 11:06:28 keil + * New card and L1 interface. + * Eicon.Diehl Diva and Dynalink IS64PH support * + * old changes removed /KKe * */ #include @@ -68,16 +63,30 @@ * { type, protocol, p0, p1, p2, NULL } * * type - * 1 Teles 16.0 p0=irq p1=membase p2=iobase - * 2 Teles 8.0 p0=irq p1=membase - * 3 Teles 16.3 p0=irq p1=iobase - * 4 Creatix PNP p0=irq p1=IO0 (ISAC) p2=IO1 (HSCX) - * 5 AVM A1 (Fritz) p0=irq p1=iobase - * 6 ELSA PC [p0=iobase] or nothing (autodetect) - * 7 ELSA Quickstep p0=irq p1=iobase - * ELSA PCMCIA p0=irq p1=iobase - * 8 Teles PCMCIA p0=irq p1=iobase - * 9 ITK ix1-micro p0=irq p1=iobase + * 1 Teles 16.0 p0=irq p1=membase p2=iobase + * 2 Teles 8.0 p0=irq p1=membase + * 3 Teles 16.3 p0=irq p1=iobase + * 4 Creatix PNP p0=irq p1=IO0 (ISAC) p2=IO1 (HSCX) + * 5 AVM A1 (Fritz) p0=irq p1=iobase + * 6 ELSA PC [p0=iobase] or nothing (autodetect) + * 7 ELSA Quickstep p0=irq p1=iobase + * 8 Teles PCMCIA p0=irq p1=iobase + * 9 ITK ix1-micro p0=irq p1=iobase + * 10 ELSA PCMCIA p0=irq p1=iobase + * 11 Eicon.Diehl Diva p0=irq p1=iobase + * 12 Asuscom ISDNLink p0=irq p1=iobase + * 13 Teleint p0=irq p1=iobase + * 14 Teles 16.3c p0=irq p1=iobase + * 15 Sedlbauer speed p0=irq p1=iobase + * 16 USR Sportster internal p0=irq p1=iobase + * 17 MIC card p0=irq p1=iobase + * 18 ELSA Quickstep 1000PCI no parameter + * 19 Compaq ISDN S0 ISA card p0=irq p1=IO0 (HSCX) p2=IO1 (ISAC) p3=IO2 + * 20 Travers Technologies NETjet PCI card + * 21 reserved TELES PCI + * 22 Sedlbauer Speed Star p0=irq p1=iobase + * 23 reserved + * 24 Dr Neuhaus Niccy PnP/PCI card p0=irq p1=IO0 p2=IO1 (PnP only) * * * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1 @@ -85,38 +94,108 @@ * */ -#ifdef CONFIG_HISAX_ELSA_PCC +#ifdef CONFIG_HISAX_ELSA #define DEFAULT_CARD ISDN_CTYPE_ELSA -#define DEFAULT_CFG {0,0,0} -#endif -#ifdef CONFIG_HISAX_ELSA_PCMCIA -#define DEFAULT_CARD ISDN_CTYPE_ELSA_QS1000 -#define DEFAULT_CFG {3,0x2f8,0} +#define DEFAULT_CFG {0,0,0,0} +int elsa_init_pcmcia(void*, int, int*, int); +EXPORT_SYMBOL(elsa_init_pcmcia); #endif #ifdef CONFIG_HISAX_AVM_A1 #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_A1 -#define DEFAULT_CFG {10,0x340,0} +#define DEFAULT_CFG {10,0x340,0,0} #endif #ifdef CONFIG_HISAX_16_3 #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_16_3 -#define DEFAULT_CFG {15,0x180,0} +#define DEFAULT_CFG {15,0x180,0,0} #endif #ifdef CONFIG_HISAX_16_0 #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_16_0 -#define DEFAULT_CFG {15,0xd0000,0xd80} +#define DEFAULT_CFG {15,0xd0000,0xd80,0} #endif #ifdef CONFIG_HISAX_IX1MICROR2 #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_IX1MICROR2 -#define DEFAULT_CFG {5,0x390,0} +#define DEFAULT_CFG {5,0x390,0,0} +#endif + +#ifdef CONFIG_HISAX_DIEHLDIVA +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_DIEHLDIVA +#define DEFAULT_CFG {0,0x0,0,0} +#endif + +#ifdef CONFIG_HISAX_ASUSCOM +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_ASUSCOM +#define DEFAULT_CFG {5,0x200,0,0} +#endif + +#ifdef CONFIG_HISAX_TELEINT +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_TELEINT +#define DEFAULT_CFG {5,0x300,0,0} +#endif + +#ifdef CONFIG_HISAX_SEDLBAUER +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_SEDLBAUER +#define DEFAULT_CFG {11,0x270,0,0} +int sedl_init_pcmcia(void*, int, int*, int); +EXPORT_SYMBOL(sedl_init_pcmcia); +#endif + +#ifdef CONFIG_HISAX_SPORTSTER +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_SPORTSTER +#define DEFAULT_CFG {7,0x268,0,0} +#endif + +#ifdef CONFIG_HISAX_MIC +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_MIC +#define DEFAULT_CFG {12,0x3e0,0,0} +#endif + +#ifdef CONFIG_HISAX_NETJET +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_NETJET +#define DEFAULT_CFG {0,0,0,0} +#endif + +#ifdef CONFIG_HISAX_TELES3C +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_TELES3C +#define DEFAULT_CFG {5,0x500,0,0} +#endif + +#ifdef CONFIG_HISAX_AMD7930 +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_AMD7930 +#define DEFAULT_CFG {12,0x3e0,0,0} +#endif + +#ifdef CONFIG_HISAX_NICCY +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_NICCY +#define DEFAULT_CFG {0,0x0,0,0} #endif #ifdef CONFIG_HISAX_1TR6 @@ -150,7 +229,7 @@ NULL, \ } -#define EMPTY_CARD {0, DEFAULT_PROTO, {0, 0, 0}, NULL} +#define EMPTY_CARD {0, DEFAULT_PROTO, {0, 0, 0, 0}, NULL} struct IsdnCard cards[] = { @@ -172,55 +251,63 @@ struct IsdnCard cards[] = 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" \ +static char HiSaxID[96] HISAX_INITDATA = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ "\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; +char *HiSax_id HISAX_INITDATA = HiSaxID; #ifdef MODULE /* Variables for insmod */ -static int type[] = +static int type[] HISAX_INITDATA = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -static int protocol[] = +static int protocol[] HISAX_INITDATA = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -static int io[] = +static int io[] HISAX_INITDATA = {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 */ -static int io0[] = +#undef IO0_IO1 +#ifdef CONFIG_HISAX_16_3 +#define IO0_IO1 +#endif +#ifdef CONFIG_HISAX_NICCY +#undef IO0_IO1 +#define IO0_IO1 +#endif +#ifdef IO0_IO1 +static int io0[] HISAX_INITDATA = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -static int io1[] = +static int io1[] HISAX_INITDATA = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; #endif -static int irq[] = +static int irq[] HISAX_INITDATA = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -static int mem[] = +static int mem[] HISAX_INITDATA = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -static char *id = HiSaxID; +static char *id HISAX_INITDATA = HiSaxID; -#if (LINUX_VERSION_CODE > 0x020111) MODULE_AUTHOR("Karsten Keil"); -MODULE_PARM(type, "1-16i"); -MODULE_PARM(protocol, "1-16i"); -MODULE_PARM(io, "1-16i"); -MODULE_PARM(irq, "1-16i"); -MODULE_PARM(mem, "1-16i"); +MODULE_PARM(type, "1-3i"); +MODULE_PARM(protocol, "1-2i"); +MODULE_PARM(io, "1-8i"); +MODULE_PARM(irq, "1-2i"); +MODULE_PARM(mem, "1-12i"); MODULE_PARM(id, "s"); #ifdef CONFIG_HISAX_16_3 /* For Creatix/Teles PnP */ -MODULE_PARM(io0, "1-16i"); -MODULE_PARM(io1, "1-16i"); -#endif +MODULE_PARM(io0, "1-8i"); +MODULE_PARM(io1, "1-8i"); #endif #endif +int nrcards; + extern char *l1_revision; extern char *l2_revision; extern char *l3_revision; -extern char *l4_revision; +extern char *lli_revision; extern char *tei_revision; -char * -HiSax_getrev(const char *revision) +HISAX_INITFUNC(char * +HiSax_getrev(const char *revision)) { char *rev; char *p; @@ -234,7 +321,27 @@ HiSax_getrev(const char *revision) return rev; } -int nrcards; +HISAX_INITFUNC(void +HiSaxVersion(void)) +{ + char tmp[64], rev[64]; + char *r = rev; + + 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, lli_revision); + r += sprintf(r, "%s/", HiSax_getrev(tmp)); + strcpy(tmp, tei_revision); + r += sprintf(r, "%s", HiSax_getrev(tmp)); + + printk(KERN_INFO "HiSax: Driver for Siemens chip set ISDN cards\n"); + printk(KERN_INFO "HiSax: Version 2.8\n"); + printk(KERN_INFO "HiSax: Revisions %s\n", rev); +} void HiSax_mod_dec_use_count(void) @@ -251,8 +358,8 @@ HiSax_mod_inc_use_count(void) #ifdef MODULE #define HiSax_init init_module #else -void -HiSax_setup(char *str, int *ints) +__initfunc(void +HiSax_setup(char *str, int *ints)) { int i, j, argc; @@ -297,31 +404,28 @@ HiSax_setup(char *str, int *ints) } #endif -int -HiSax_init(void) +__initfunc(int +HiSax_init(void)) { int i; - char tmp[64], rev[64]; - char *r = rev; + #ifdef MODULE int nzproto = 0; +#ifdef CONFIG_HISAX_ELSA + if (type[0] == ISDN_CTYPE_ELSA_PCMCIA) { + /* we have exported and return in this case */ + return 0; + } +#endif +#ifdef CONFIG_HISAX_SEDLBAUER + if (type[0] == ISDN_CTYPE_SEDLBAUER_PCMCIA) { + /* we have to export and return in this case */ + return 0; + } +#endif #endif + HiSaxVersion(); 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; @@ -343,37 +447,44 @@ HiSax_init(void) cards[i].para[1] = mem[i]; break; - case ISDN_CTYPE_16_3: - case ISDN_CTYPE_TELESPCMCIA: - cards[i].para[0] = irq[i]; - cards[i].para[1] = io[i]; - break; - -#ifdef CONFIG_HISAX_16_3 /* For Creatix/Teles PnP */ +#ifdef IO0_IO1 case ISDN_CTYPE_PNP: + case ISDN_CTYPE_NICCY: cards[i].para[0] = irq[i]; cards[i].para[1] = io0[i]; cards[i].para[2] = io1[i]; break; -#endif - case ISDN_CTYPE_A1: + case ISDN_CTYPE_COMPAQ_ISA: cards[i].para[0] = irq[i]; - cards[i].para[1] = io[i]; + cards[i].para[1] = io0[i]; + cards[i].para[2] = io1[i]; + cards[i].para[3] = io[i]; break; - +#endif case ISDN_CTYPE_ELSA: cards[i].para[0] = io[i]; break; - case ISDN_CTYPE_ELSA_QS1000: - cards[i].para[0] = irq[i]; - cards[i].para[1] = io[i]; - break; - + case ISDN_CTYPE_16_3: + case ISDN_CTYPE_TELESPCMCIA: + case ISDN_CTYPE_A1: + case ISDN_CTYPE_ELSA_PNP: + case ISDN_CTYPE_ELSA_PCMCIA: case ISDN_CTYPE_IX1MICROR2: + case ISDN_CTYPE_DIEHLDIVA: + case ISDN_CTYPE_ASUSCOM: + case ISDN_CTYPE_TELEINT: + case ISDN_CTYPE_SEDLBAUER: + case ISDN_CTYPE_SEDLBAUER_PCMCIA: + case ISDN_CTYPE_SPORTSTER: + case ISDN_CTYPE_MIC: + case ISDN_CTYPE_TELES3C: cards[i].para[0] = irq[i]; cards[i].para[1] = io[i]; break; - + case ISDN_CTYPE_ELSA_PCI: + case ISDN_CTYPE_NETJET: + case ISDN_CTYPE_AMD7930: + break; } } if (!nzproto) { @@ -394,20 +505,20 @@ HiSax_init(void) CallcNew(); Isdnl2New(); - if (HiSax_inithardware()) { + TeiNew(); + Isdnl1New(); + if (HiSax_inithardware(NULL)) { /* Install only, if at least one card found */ /* No symbols to export, hide all symbols */ #ifdef MODULE -#if (LINUX_VERSION_CODE < 0x020111) - register_symtab(NULL); -#else EXPORT_NO_SYMBOLS; -#endif - printk(KERN_NOTICE "HiSax: module installed\n"); + printk(KERN_INFO "HiSax: module installed\n"); #endif return (0); } else { + Isdnl1Free(); + TeiFree(); Isdnl2Free(); CallcFree(); return -EIO; @@ -419,7 +530,101 @@ void cleanup_module(void) { HiSax_closehardware(); - printk(KERN_NOTICE "HiSax module removed\n"); + printk(KERN_INFO "HiSax module removed\n"); } +#ifdef CONFIG_HISAX_ELSA +int elsa_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) +{ + int i; + int nzproto = 0; + + nrcards = 0; + HiSaxVersion(); + if (id) /* If id= string used */ + HiSax_id = id; + /* Initialize all 16 structs, even though we only accept + two pcmcia cards + */ + for (i = 0; i < 16; i++) { + cards[i].para[0] = irq[i]; + cards[i].para[1] = io[i]; + cards[i].typ = type[i]; + if (protocol[i]) { + cards[i].protocol = protocol[i]; + nzproto++; + } + } + cards[0].para[0] = pcm_irq; + cards[0].para[1] = (int)pcm_iob; + cards[0].protocol = prot; + cards[0].typ = 10; + nzproto = 1; + + 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" : ""); + + Isdnl1New(); + CallcNew(); + Isdnl2New(); + TeiNew(); + HiSax_inithardware(busy_flag); + printk(KERN_NOTICE "HiSax: module installed\n"); + return (0); +} +#endif +#ifdef CONFIG_HISAX_SEDLBAUER +int sedl_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) +{ + int i; + int nzproto = 0; + + nrcards = 0; + HiSaxVersion(); + if (id) /* If id= string used */ + HiSax_id = id; + /* Initialize all 16 structs, even though we only accept + two pcmcia cards + */ + for (i = 0; i < 16; i++) { + cards[i].para[0] = irq[i]; + cards[i].para[1] = io[i]; + cards[i].typ = type[i]; + if (protocol[i]) { + cards[i].protocol = protocol[i]; + nzproto++; + } + } + cards[0].para[0] = pcm_irq; + cards[0].para[1] = (int)pcm_iob; + cards[0].protocol = prot; + cards[0].typ = ISDN_CTYPE_SEDLBAUER_PCMCIA; + nzproto = 1; + + 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" : ""); + + Isdnl1New(); + CallcNew(); + Isdnl2New(); + TeiNew(); + HiSax_inithardware(busy_flag); + printk(KERN_NOTICE "HiSax: module installed\n"); + return (0); +} #endif +#endif diff --git a/drivers/isdn/hisax/diva.c b/drivers/isdn/hisax/diva.c new file mode 100644 index 000000000000..fa3a45b72d35 --- /dev/null +++ b/drivers/isdn/hisax/diva.c @@ -0,0 +1,465 @@ +/* $Id: diva.c,v 1.5 1998/02/02 13:29:38 keil Exp $ + + * diva.c low level stuff for Eicon.Diehl Diva Family ISDN cards + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * Thanks to Eicon Technology Diehl GmbH & Co. oHG for documents and informations + * + * + * $Log: diva.c,v $ + * Revision 1.5 1998/02/02 13:29:38 keil + * fast io + * + * Revision 1.4 1997/11/08 21:35:44 keil + * new l1 init + * + * Revision 1.3 1997/11/06 17:13:33 keil + * New 2.1 init code + * + * Revision 1.2 1997/10/29 18:55:55 keil + * changes for 2.1.60 (irq2dev_map) + * + * Revision 1.1 1997/09/18 17:11:20 keil + * first version + * + * + */ + +#define __NO_VERSION__ +#include +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" +#include +#include + +extern const char *CardType[]; + +const char *Diva_revision = "$Revision: 1.5 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define DIVA_HSCX_DATA 0 +#define DIVA_HSCX_ADR 4 +#define DIVA_ISA_ISAC_DATA 2 +#define DIVA_ISA_ISAC_ADR 6 +#define DIVA_ISA_CTRL 7 + +#define DIVA_PCI_ISAC_DATA 8 +#define DIVA_PCI_ISAC_ADR 0xc +#define DIVA_PCI_CTRL 0x10 + +/* SUB Types */ +#define DIVA_ISA 1 +#define DIVA_PCI 2 + +/* PCI stuff */ +#define PCI_VENDOR_EICON_DIEHL 0x1133 +#define PCI_DIVA20PRO_ID 0xe001 +#define PCI_DIVA20_ID 0xe002 +#define PCI_DIVA20PRO_U_ID 0xe003 +#define PCI_DIVA20_U_ID 0xe004 + +/* CTRL (Read) */ +#define DIVA_IRQ_STAT 0x01 +#define DIVA_EEPROM_SDA 0x02 +/* CTRL (Write) */ +#define DIVA_IRQ_REQ 0x01 +#define DIVA_RESET 0x08 +#define DIVA_EEPROM_CLK 0x40 +#define DIVA_PCI_LED_A 0x10 +#define DIVA_PCI_LED_B 0x20 +#define DIVA_ISA_LED_A 0x20 +#define DIVA_ISA_LED_B 0x40 +#define DIVA_IRQ_CLR 0x80 + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return(readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return(readreg(cs->hw.diva.hscx_adr, + cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.diva.hscx_adr, + cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.diva.hscx_adr, \ + cs->hw.diva.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.diva.hscx_adr, \ + cs->hw.diva.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.diva.hscx_adr, \ + cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.diva.hscx_adr, \ + cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +diva_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, sval, stat = 0; + int cnt=8; + + if (!cs) { + printk(KERN_WARNING "Diva: Spurious interrupt!\n"); + return; + } + while (((sval = bytein(cs->hw.diva.ctrl)) & DIVA_IRQ_REQ) && cnt) { + val = readreg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_ISTA + 0x40); + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA); + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + cnt--; + } + if (!cnt) + printk(KERN_WARNING "Diva: IRQ LOOP\n"); + if (stat & 1) { + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0x0); + } + if (stat & 2) { + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0x0); + } +} + +void +release_io_diva(struct IsdnCardState *cs) +{ + int bytecnt; + + del_timer(&cs->hw.diva.tl); + if (cs->subtyp == DIVA_ISA) + bytecnt = 8; + else + bytecnt = 32; + if (cs->hw.diva.cfg_reg) { + byteout(cs->hw.diva.ctrl, 0); /* LED off, Reset */ + release_region(cs->hw.diva.cfg_reg, bytecnt); + } +} + +static void +reset_diva(struct IsdnCardState *cs) +{ + long flags; + + save_flags(flags); + sti(); + cs->hw.diva.ctrl_reg = 0; /* Reset On */ + byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + cs->hw.diva.ctrl_reg |= DIVA_RESET; /* Reset Off */ + byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + if (cs->subtyp == DIVA_ISA) + cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A; + else + cs->hw.diva.ctrl_reg |= DIVA_PCI_LED_A; + byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); +} + +#define DIVA_ASSIGN 1 + +static void +diva_led_handler(struct IsdnCardState *cs) +{ + int blink = 0; + + del_timer(&cs->hw.diva.tl); + if (cs->hw.diva.status & DIVA_ASSIGN) + cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_A : DIVA_PCI_LED_A; + else { + cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_A : DIVA_PCI_LED_A; + blink = 250; + } + if (cs->hw.diva.status & 0xf000) + cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_B : DIVA_PCI_LED_B; + else if (cs->hw.diva.status & 0x0f00) { + cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_B : DIVA_PCI_LED_B; + blink = 500; + } else + cs->hw.diva.ctrl_reg &= ~((DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_B : DIVA_PCI_LED_B); + + byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); + if (blink) { + init_timer(&cs->hw.diva.tl); + cs->hw.diva.tl.expires = jiffies + ((blink * HZ) / 1000); + add_timer(&cs->hw.diva.tl); + } +} + +static int +Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_diva(cs); + return(0); + case CARD_RELEASE: + release_io_diva(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &diva_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + case MDL_REMOVE_REQ: + cs->hw.diva.status = 0; + break; + case MDL_ASSIGN_REQ: + cs->hw.diva.status |= DIVA_ASSIGN; + break; + case MDL_INFO_SETUP: + if ((int)arg) + cs->hw.diva.status |= 0x0200; + else + cs->hw.diva.status |= 0x0100; + break; + case MDL_INFO_CONN: + if ((int)arg) + cs->hw.diva.status |= 0x2000; + else + cs->hw.diva.status |= 0x1000; + break; + case MDL_INFO_REL: + if ((int)arg) { + cs->hw.diva.status &= ~0x2000; + cs->hw.diva.status &= ~0x0200; + } else { + cs->hw.diva.status &= ~0x1000; + cs->hw.diva.status &= ~0x0100; + } + break; + } + diva_led_handler(cs); + return(0); +} + + + +static int pci_index __initdata = 0; + +__initfunc(int +setup_diva(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, Diva_revision); + printk(KERN_INFO "HiSax: Eicon.Diehl Diva driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_DIEHLDIVA) + return(0); + cs->hw.diva.status = 0; + if (card->para[1]) { + cs->subtyp = DIVA_ISA; + cs->hw.diva.ctrl_reg = 0; + cs->hw.diva.cfg_reg = card->para[1]; + cs->hw.diva.ctrl = card->para[1] + DIVA_ISA_CTRL; + cs->hw.diva.isac = card->para[1] + DIVA_ISA_ISAC_DATA; + cs->hw.diva.hscx = card->para[1] + DIVA_HSCX_DATA; + cs->hw.diva.isac_adr = card->para[1] + DIVA_ISA_ISAC_ADR; + cs->hw.diva.hscx_adr = card->para[1] + DIVA_HSCX_ADR; + cs->irq = card->para[0]; + bytecnt = 8; + } else { +#if CONFIG_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr; + + cs->subtyp = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL, + PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = DIVA_PCI; + else if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL, + PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = DIVA_PCI; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_2, &pci_ioaddr); + if (cs->subtyp) + break; + } + if (!cs->subtyp) { + printk(KERN_WARNING "Diva: No PCI card found\n"); + return(0); + } + if (!pci_irq) { + printk(KERN_WARNING "Diva: No IRQ for PCI card found\n"); + return(0); + } + + if (!pci_ioaddr) { + printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.diva.cfg_reg = pci_ioaddr; + cs->hw.diva.ctrl = pci_ioaddr + DIVA_PCI_CTRL; + cs->hw.diva.isac = pci_ioaddr + DIVA_PCI_ISAC_DATA; + cs->hw.diva.hscx = pci_ioaddr + DIVA_HSCX_DATA; + cs->hw.diva.isac_adr = pci_ioaddr + DIVA_PCI_ISAC_ADR; + cs->hw.diva.hscx_adr = pci_ioaddr + DIVA_HSCX_ADR; + cs->irq = pci_irq; + bytecnt = 32; +#else + printk(KERN_WARNING "Diva: cfgreg 0 and NO_PCI_BIOS\n"); + printk(KERN_WARNING "Diva: unable to config DIVA PCI\n"); + return (0); +#endif /* CONFIG_PCI */ + } + + printk(KERN_INFO + "Diva: %s card configured at 0x%x IRQ %d\n", + (cs->subtyp == DIVA_ISA) ? "ISA" : "PCI", + cs->hw.diva.cfg_reg, cs->irq); + if (check_region(cs->hw.diva.cfg_reg, bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.diva.cfg_reg, + cs->hw.diva.cfg_reg + bytecnt); + return (0); + } else { + request_region(cs->hw.diva.cfg_reg, bytecnt, "diva isdn"); + } + + reset_diva(cs); + cs->hw.diva.tl.function = (void *) diva_led_handler; + cs->hw.diva.tl.data = (long) cs; + init_timer(&cs->hw.diva.tl); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Diva_card_msg; + + ISACVersion(cs, "Diva:"); + if (HscxVersion(cs, "Diva:")) { + printk(KERN_WARNING + "Diva: wrong HSCX versions check IO address\n"); + release_io_diva(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c index 5cea59d9dd93..6a9966431637 100644 --- a/drivers/isdn/hisax/elsa.c +++ b/drivers/isdn/hisax/elsa.c @@ -1,4 +1,4 @@ -/* $Id: elsa.c,v 1.14 1997/04/13 19:53:25 keil Exp $ +/* $Id: elsa.c,v 2.6 1998/02/02 13:29:40 keil Exp $ * elsa.c low level stuff for Elsa isdn cards * @@ -8,1241 +8,726 @@ * * * $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 2.6 1998/02/02 13:29:40 keil + * fast io * - * 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 2.5 1998/01/31 21:41:45 keil + * changes for newer 2.1 kernels * - * Revision 1.11 1997/03/23 21:45:46 keil - * Add support for ELSA PCMCIA + * Revision 2.4 1997/11/08 21:35:46 keil + * new l1 init * - * Revision 1.10 1997/03/12 21:42:19 keil - * Bugfix: IRQ hangs with QS1000 + * Revision 2.3 1997/11/06 17:15:09 keil + * New 2.1 init; PCMCIA wrapper changes * - * Revision 1.9 1997/03/04 15:57:39 keil - * bugfix IRQ reset Quickstep, ELSA PC changes, some stuff for new cards + * Revision 2.2 1997/10/29 18:57:09 keil + * changes for 2.1.60, arcofi support * - * Revision 1.8 1997/01/27 15:51:48 keil - * SMP proof,cosmetics + * Revision 2.1 1997/07/27 21:47:08 keil + * new interface structures * - * Revision 1.7 1997/01/21 22:20:48 keil - * Elsa Quickstep support + * Revision 2.0 1997/06/26 11:02:40 keil + * New Layer and card interface * - * 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.14 1997/04/13 19:53:25 keil + * Fixed QS1000 init, change in IRQ check delay for SMP * - * Revision 1.2 1996/10/27 22:08:03 keil - * cosmetic changes + * Revision 1.13 1997/04/07 22:58:07 keil + * need include config.h * - * Revision 1.1 1996/10/13 20:04:52 keil - * Initial revision + * Revision 1.12 1997/04/06 22:54:14 keil + * Using SKB's * + * old changes removed KKe * */ -#define ARCOFI_USE 0 - #define __NO_VERSION__ #include -#include "siemens.h" #include "hisax.h" -#include "elsa.h" +#include "arcofi.h" +#include "isac.h" +#include "ipac.h" +#include "hscx.h" #include "isdnl1.h" -#include +#include +#include extern const char *CardType[]; -const char *Elsa_revision = "$Revision: 1.14 $"; +const char *Elsa_revision = "$Revision: 2.6 $"; const char *Elsa_Types[] = {"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro", - "PCMCIA", "QS 1000", "QS 3000"}; + "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI"}; 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) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ELSA_ISAC 0 +#define ELSA_ISAC_PCM 1 +#define ELSA_ITAC 1 +#define ELSA_HSCX 2 +#define ELSA_ALE 3 +#define ELSA_ALE_PCM 4 +#define ELSA_CONTROL 4 +#define ELSA_CONFIG 5 +#define ELSA_START_TIMER 6 +#define ELSA_TRIG_IRQ 7 + +#define ELSA_PC 1 +#define ELSA_PCC8 2 +#define ELSA_PCC16 3 +#define ELSA_PCF 4 +#define ELSA_PCFPRO 5 +#define ELSA_PCMCIA 6 +#define ELSA_QS1000 7 +#define ELSA_QS3000 8 +#define ELSA_QS1000PCI 9 + +/* PCI stuff */ +#define PCI_VENDOR_ELSA 0x1048 +#define PCI_QS1000_ID 0x1000 + + +/* ITAC Registeradressen (only Microlink PC) */ +#define ITAC_SYS 0x34 +#define ITAC_ISEN 0x48 +#define ITAC_RFIE 0x4A +#define ITAC_XFIE 0x4C +#define ITAC_SCIE 0x4E +#define ITAC_STIE 0x46 + +/*** *** + *** Makros als Befehle fuer die Kartenregister *** + *** (mehrere Befehle werden durch Bit-Oderung kombiniert) *** + *** ***/ + +/* Config-Register (Read) */ +#define ELSA_TIMER_RUN 0x02 /* Bit 1 des Config-Reg */ +#define ELSA_TIMER_RUN_PCC8 0x01 /* Bit 0 des Config-Reg bei PCC */ +#define ELSA_IRQ_IDX 0x38 /* Bit 3,4,5 des Config-Reg */ +#define ELSA_IRQ_IDX_PCC8 0x30 /* Bit 4,5 des Config-Reg */ +#define ELSA_IRQ_IDX_PC 0x0c /* Bit 2,3 des Config-Reg */ + +/* Control-Register (Write) */ +#define ELSA_LINE_LED 0x02 /* Bit 1 Gelbe LED */ +#define ELSA_STAT_LED 0x08 /* Bit 3 Gruene LED */ +#define ELSA_ISDN_RESET 0x20 /* Bit 5 Reset-Leitung */ +#define ELSA_ENA_TIMER_INT 0x80 /* Bit 7 Freigabe Timer Interrupt */ + +/* ALE-Register (Read) */ +#define ELSA_HW_RELEASE 0x07 /* Bit 0-2 Hardwarerkennung */ +#define ELSA_S0_POWER_BAD 0x08 /* Bit 3 S0-Bus Spannung fehlt */ + +/* Status Flags */ +#define ELSA_TIMER_AKTIV 1 +#define ELSA_BAD_PWR 2 +#define ELSA_ASSIGN 4 static inline u_char -readhscx(unsigned int adr, int hscx, u_char off) +readreg(unsigned int ale, unsigned int adr, u_char off) { register u_char ret; long flags; save_flags(flags); cli(); - byteout(adr + CARD_ALE, off + (hscx ? 0x60 : 0x20)); - ret = bytein(adr + CARD_HSCX); + byteout(ale, off); + ret = bytein(adr); restore_flags(flags); return (ret); } static inline void -read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) { /* fifo read without cli because it's allready done */ - byteout(adr + CARD_ALE, (hscx ? 0x40 : 0)); - insb(adr + CARD_HSCX, data, size); + byteout(ale, off); + insb(adr, data, size); } static inline void -writehscx(unsigned int adr, int hscx, u_char off, u_char data) +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) { long flags; save_flags(flags); cli(); - byteout(adr + CARD_ALE, off + (hscx ? 0x60 : 0x20)); - byteout(adr + CARD_HSCX, data); + byteout(ale, off); + byteout(adr, data); restore_flags(flags); } static inline void -write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) { /* fifo write without cli because it's allready done */ - byteout(adr + CARD_ALE, (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); + byteout(ale, off); + outsb(adr, data, size); } -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; +/* Interface functions */ - 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) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - 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); + return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset)); } -static inline void -writeitac(unsigned int adr, u_char off, u_char data) +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - long flags; - - save_flags(flags); - cli(); - byteout(adr + CARD_ALE, off); - byteout(adr + CARD_ITAC, data); - restore_flags(flags); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset, value); } -static inline int -TimerRun(struct IsdnCardState *sp) +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - register u_char val; - - val = bytein(sp->cfg_reg + CARD_CONFIG); - if (sp->subtyp == ELSA_QS1000) - return (0 == (val & TIMER_RUN)); - else if (sp->subtyp == ELSA_PCC8) - return (val & TIMER_RUN_PCC8); - return (val & TIMER_RUN); + readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size); } -static inline void -elsa_led_handler(struct IsdnCardState *sp) +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - - u_char outval = 0xf0; - int stat = 0, cval; - - - if ((sp->ph_state == 0) || (sp->ph_state == 15)) { - stat = 1; - } else { - if (sp->hs[0].mode != 0) - stat |= 2; - if (sp->hs[1].mode != 0) - stat |= 4; - } - cval = (sp->counter >> 6) & 3; - switch (cval) { - case 0: - if (!stat) - outval |= STAT_LED; - else if (stat == 1) - outval |= LINE_LED | STAT_LED; - else { - if (stat & 2) - outval |= STAT_LED; - if (stat & 4) - outval |= LINE_LED; - } - break; - case 1: - if (!stat) - outval |= LINE_LED; - else if (stat == 1) - outval |= LINE_LED | STAT_LED; - else { - if (stat & 2) - outval |= STAT_LED; - if (stat & 4) - outval |= LINE_LED; - } - break; - case 2: - if (!stat) - outval |= STAT_LED; - else if (stat == 1) - outval |= 0; - else { - if (stat & 2) - outval |= STAT_LED; - if (stat & 4) - outval |= LINE_LED; - } - break; - case 3: - if (!stat) - outval |= LINE_LED; - break; - } - byteout(sp->cfg_reg + CARD_CONTROL, outval); + writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size); } -#endif -static inline void -waitforCEC(int adr, int hscx) +static u_char +ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset) { - int to = 50; - - while ((readhscx(adr, hscx, HSCX_STAR) & 0x04) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Elsa: waitforCEC timeout\n"); + return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset+0x80)); } - -static inline void -waitforXFW(int adr, int hscx) +static void +WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value) { - int to = 50; - - while ((!(readhscx(adr, hscx, HSCX_STAR) & 0x44) == 0x40) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Elsa: waitforXFW timeout\n"); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset|0x80, value); } -static inline void -writehscxCMDR(int adr, int hscx, u_char data) +static void +ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) { - long flags; - - save_flags(flags); - cli(); - waitforCEC(adr, hscx); - writehscx(adr, hscx, HSCX_CMDR, data); - restore_flags(flags); + readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size); } -/* - * fast interrupt here - */ - - static void -hscxreport(struct IsdnCardState *sp, int hscx) +WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) { - printk(KERN_DEBUG "HSCX %d\n", hscx); - printk(KERN_DEBUG "ISTA %x\n", readhscx(sp->cfg_reg, hscx, HSCX_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readhscx(sp->cfg_reg, hscx, HSCX_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readhscx(sp->cfg_reg, hscx, HSCX_EXIR)); + writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size); } -void -elsa_report(struct IsdnCardState *sp) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - printk(KERN_DEBUG "ISAC\n"); - printk(KERN_DEBUG "ISTA %x\n", readisac(sp->cfg_reg, ISAC_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readisac(sp->cfg_reg, ISAC_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readisac(sp->cfg_reg, ISAC_EXIR)); - hscxreport(sp, 0); - hscxreport(sp, 1); + return (readreg(cs->hw.elsa.ale, + cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0))); } -/* - * HSCX stuff goes here - */ - static void -hscx_empty_fifo(struct HscxState *hsp, int count) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo_hscx(sp->cfg_reg, hsp->hscx, ptr, count); - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writereg(cs->hw.elsa.ale, + cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0), value); } -static void -hscx_fill_fifo(struct HscxState *hsp) +static inline u_char +readitac(struct IsdnCardState *cs, u_char off) { - struct IsdnCardState *sp = hsp->sp; - int more, count; - u_char *ptr; + register u_char ret; long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_fill_fifo"); - - if (!hsp->tx_skb) - return; - if (hsp->tx_skb->len <= 0) - return; - - more = (hsp->mode == 1) ? 1 : 0; - if (hsp->tx_skb->len > 32) { - more = !0; - count = 32; - } else - count = hsp->tx_skb->len; - - waitforXFW(sp->cfg_reg, hsp->hscx); save_flags(flags); cli(); - ptr = hsp->tx_skb->data; - skb_pull(hsp->tx_skb, count); - hsp->tx_cnt -= count; - hsp->count += count; - write_fifo_hscx(sp->cfg_reg, hsp->hscx, ptr, count); - writehscxCMDR(sp->cfg_reg, hsp->hscx, more ? 0x8 : 0xa); + byteout(cs->hw.elsa.ale, off); + ret = bytein(cs->hw.elsa.itac); restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_fill_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (ret); } static inline void -hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx) -{ - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = readhscx(sp->cfg_reg, hsp->hscx, HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!r & 0x80) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX invalid frame"); - if ((r & 0x40) && hsp->mode) - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX RDO mode=%d", - hsp->mode); - debugl1(sp, tmp); - } - if (!r & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX CRC error"); - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80); - } else { - count = readhscx(sp->cfg_reg, hsp->hscx, HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - if ((count = hsp->rcvidx - 1) > 0) { - if (sp->debug & L1_DEB_HSCX_FIFO) { - sprintf(tmp, "HX Frame %d", count); - debugl1(sp, tmp); - } - if (!(skb = dev_alloc_skb(count))) - printk(KERN_WARNING "Elsa: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "elsa: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - SET_SKB_FREE(hsp->tx_skb); - dev_kfree_skb(hsp->tx_skb); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } -} - -/* - * ISAC stuff goes here - */ - -static void -isac_empty_fifo(struct IsdnCardState *sp, int count) +writeitac(struct IsdnCardState *cs, u_char off, u_char data) { - u_char *ptr; long flags; - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - writeisac(sp->cfg_reg, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; save_flags(flags); cli(); - read_fifo_isac(sp->cfg_reg, ptr, count); - writeisac(sp->cfg_reg, ISAC_CMDR, 0x80); + byteout(cs->hw.elsa.ale, off); + byteout(cs->hw.elsa.itac, data); restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } } -static void -isac_fill_fifo(struct IsdnCardState *sp) +static inline int +TimerRun(struct IsdnCardState *cs) { - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - 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); + register u_char v; + + v = bytein(cs->hw.elsa.cfg); + if ((cs->subtyp == ELSA_QS1000) || (cs->subtyp == ELSA_QS3000)) + return (0 == (v & ELSA_TIMER_RUN)); + else if (cs->subtyp == ELSA_PCC8) + return (v & ELSA_TIMER_RUN_PCC8); + return (v & ELSA_TIMER_RUN); } +/* + * fast interrupt HSCX stuff goes here + */ -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval, v1; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; -#if ARCOFI_USE - struct BufHeader *ibh; - u_char *ptr; -#endif +#define READHSCX(cs, nr, reg) readreg(cs->hw.elsa.ale, \ + cs->hw.elsa.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.elsa.ale, \ + cs->hw.elsa.hscx, reg + (nr ? 0x40 : 0), data) - 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 { - 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 { - SET_SKB_FREE(sp->tx_skb); - dev_kfree_skb(sp->tx_skb); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (readisac(sp->cfg_reg, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = readisac(sp->cfg_reg, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - if (exval & 0x08) { - v1 = readisac(sp->cfg_reg, ISAC_MOSR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC MOSR %02x", v1); - debugl1(sp, tmp); - } -#if ARCOFI_USE - if (v1 & 0x08) { - if (!sp->mon_rx) - if (BufPoolGet(&(sp->mon_rx), &(sp->rbufpool), - GFP_ATOMIC, (void *) 1, 3)) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON RX out of buffers!"); - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a); - goto afterMONR0; - } else - sp->mon_rxp = 0; - ibh = sp->mon_rx; - ptr = DATAPTR(ibh); - ptr += sp->mon_rxp; - sp->mon_rxp++; - if (sp->mon_rxp >= 3072) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a); - sp->mon_rxp = 0; - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON RX overflow!"); - goto afterMONR0; - } - *ptr = readisac(sp->cfg_reg, ISAC_MOR0); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC MOR0 %02x", *ptr); - debugl1(sp, tmp); - } - } - afterMONR0: - if (v1 & 0x80) { - if (!sp->mon_rx) - if (BufPoolGet(&(sp->mon_rx), &(sp->rbufpool), - GFP_ATOMIC, (void *) 1, 3)) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON RX out of buffers!"); - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - goto afterMONR1; - } else - sp->mon_rxp = 0; - ibh = sp->mon_rx; - ptr = DATAPTR(ibh); - ptr += sp->mon_rxp; - sp->mon_rxp++; - if (sp->mon_rxp >= 3072) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - sp->mon_rxp = 0; - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON RX overflow!"); - goto afterMONR1; - } - *ptr = readisac(sp->cfg_reg, ISAC_MOR1); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC MOR1 %02x", *ptr); - debugl1(sp, tmp); - } - } - afterMONR1: - if (v1 & 0x04) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a); - sp->mon_rx->datasize = sp->mon_rxp; - sp->mon_flg |= MON0_RX; - } - if (v1 & 0x40) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - sp->mon_rx->datasize = sp->mon_rxp; - sp->mon_flg |= MON1_RX; - } - if (v1 == 0x02) { - ibh = sp->mon_tx; - if (!ibh) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a); - goto AfterMOX0; - } - count = ibh->datasize - sp->mon_txp; - if (count <= 0) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0f); - BufPoolRelease(sp->mon_tx); - sp->mon_tx = NULL; - sp->mon_txp = 0; - sp->mon_flg |= MON0_TX; - goto AfterMOX0; - } - ptr = DATAPTR(ibh); - ptr += sp->mon_txp; - sp->mon_txp++; - writeisac(sp->cfg_reg, ISAC_MOX0, *ptr); - } - AfterMOX0: - if (v1 == 0x20) { - ibh = sp->mon_tx; - if (!ibh) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - goto AfterMOX1; - } - count = ibh->datasize - sp->mon_txp; - if (count <= 0) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0xf0); - BufPoolRelease(sp->mon_tx); - sp->mon_tx = NULL; - sp->mon_txp = 0; - sp->mon_flg |= MON1_TX; - goto AfterMOX1; - } - ptr = DATAPTR(ibh); - ptr += sp->mon_txp; - sp->mon_txp++; - writeisac(sp->cfg_reg, ISAC_MOX1, *ptr); - } - AfterMOX1: -#endif - } - } -} +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.elsa.ale, \ + cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt) -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.elsa.ale, \ + cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt) - u_char exval; - struct HscxState *hsp; - char tmp[32]; - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = readhscx(sp->cfg_reg, 1, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = readhscx(sp->cfg_reg, 0, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = readhscx(sp->cfg_reg, 0, HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs) { - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val; + int icnt=20; - sp = (struct IsdnCardState *) dev_id; - - if (!sp) { + if (!cs) { printk(KERN_WARNING "Elsa: Spurious interrupt!\n"); return; } -#ifdef CONFIG_HISAX_ELSA_PCC - INT_RESTART: - if (!TimerRun(sp)) { - /* Timer Restart */ - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - if (!(sp->counter++ & 0x3f)) { - /* Call LEDs all 64 tics */ - elsa_led_handler(sp); - } - } -#endif - val = readhscx(sp->cfg_reg, 1, HSCX_ISTA); + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40); Start_HSCX: if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); } - val = readisac(sp->cfg_reg, ISAC_ISTA); + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA); Start_ISAC: if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); } -#ifdef CONFIG_HISAX_ELSA_PCC - if (!TimerRun(sp)) - goto INT_RESTART; -#endif - val = readhscx(sp->cfg_reg, 1, HSCX_ISTA); - if (val) { - if (sp->debug & L1_DEB_HSCX) - debugl1(sp, "HSCX IntStat after IntRoutine"); + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40); + if (val && icnt) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + icnt--; goto Start_HSCX; } - val = readisac(sp->cfg_reg, ISAC_ISTA); - if (val) { - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "ISAC IntStat after IntRoutine"); + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA); + if (val && icnt) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + icnt--; goto Start_ISAC; } - writehscx(sp->cfg_reg, 0, HSCX_MASK, 0xFF); - writehscx(sp->cfg_reg, 1, HSCX_MASK, 0xFF); - writeisac(sp->cfg_reg, ISAC_MASK, 0xFF); -#ifdef CONFIG_HISAX_ELSA_PCC - if (sp->subtyp == ELSA_QS1000) { - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff); + if (!icnt) + printk(KERN_WARNING"ELSA IRQ LOOP\n"); + writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0xFF); + if (cs->hw.elsa.status & ELSA_TIMER_AKTIV) { + if (!TimerRun(cs)) { + /* Timer Restart */ + byteout(cs->hw.elsa.timer, 0); + cs->hw.elsa.counter++; + } } -#endif - writehscx(sp->cfg_reg, 0, HSCX_MASK, 0x0); - writehscx(sp->cfg_reg, 1, HSCX_MASK, 0x0); - writeisac(sp->cfg_reg, ISAC_MASK, 0x0); + if (cs->hw.elsa.trig) + byteout(cs->hw.elsa.trig, 0x00); + writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0x0); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0x0); } - static void -initisac(struct IsdnCardState *sp) +elsa_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) { - unsigned int adr = sp->cfg_reg; - - /* 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); -} + struct IsdnCardState *cs = dev_id; + u_char ista,val; + char tmp[64]; + int icnt=20; -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); + if (!cs) { + printk(KERN_WARNING "Elsa: Spurious interrupt!\n"); + return; } - hs->mode = mode; - writehscx(sp->cfg_reg, hscx, HSCX_CCR1, 0x85); - writehscx(sp->cfg_reg, hscx, HSCX_XAD1, 0xFF); - writehscx(sp->cfg_reg, hscx, HSCX_XAD2, 0xFF); - writehscx(sp->cfg_reg, hscx, HSCX_RAH2, 0xFF); - writehscx(sp->cfg_reg, hscx, HSCX_XBCH, 0x0); - writehscx(sp->cfg_reg, hscx, HSCX_RLCR, 0x0); - writehscx(sp->cfg_reg, hscx, HSCX_CCR2, 0x30); - - switch (mode) { - case (0): - writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0xff); - writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0xff); - writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0x84); - break; - case (1): - if (ichan == 0) { - writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x2f); - writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x2f); - } else { - writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x3); - writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x3); - } - writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0xe4); - writehscx(sp->cfg_reg, hscx, HSCX_CMDR, 0x41); - break; - case (2): - if (ichan == 0) { - writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x2f); - writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x2f); - } else { - writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x3); - writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x3); - } - writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0x8c); - writehscx(sp->cfg_reg, hscx, HSCX_CMDR, 0x41); - break; + if ((cs->typ == ISDN_CTYPE_ELSA_PCMCIA) && (*cs->busy_flag == 1)) { + /* The card tends to generate interrupts while being removed + causing us to just crash the kernel. bad. */ + printk(KERN_WARNING "Elsa: card not available!\n"); + return; + } + ista = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA); +Start_IPAC: + if (cs->debug & L1_DEB_IPAC) { + sprintf(tmp, "IPAC ISTA %02X", ista); + debugl1(cs, tmp); + } + if (ista & 0x0f) { + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40); + if (ista & 0x01) + val |= 0x01; + if (ista & 0x04) + val |= 0x02; + if (ista & 0x08) + val |= 0x04; + if (val) + hscx_int_main(cs, val); + } + if (ista & 0x20) { + val = 0xfe & readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA + 0x80); + if (val) { + isac_interrupt(cs, val); + } + } + if (ista & 0x10) { + val = 0x01; + isac_interrupt(cs, val); } - writehscx(sp->cfg_reg, hscx, HSCX_ISTA, 0x00); + ista = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA); + if ((ista & 0x3f) && icnt) { + icnt--; + goto Start_IPAC; + } + if (!icnt) + printk(KERN_WARNING "ELSA IRQ LOOP\n"); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xFF); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xC0); } void -release_io_elsa(struct IsdnCard *card) +release_io_elsa(struct IsdnCardState *cs) { int bytecnt = 8; - if (card->sp->subtyp == ELSA_PCFPRO) + del_timer(&cs->hw.elsa.tl); + if (cs->hw.elsa.ctrl) + byteout(cs->hw.elsa.ctrl, 0); /* LEDs Out */ + if ((cs->subtyp == ELSA_PCFPRO) || + (cs->subtyp == ELSA_QS3000) || + (cs->subtyp == ELSA_PCF)) 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 + if (cs->subtyp == ELSA_QS1000PCI) { + byteout(cs->hw.elsa.cfg + 0x4c, 0x01); /* disable IRQ */ + bytecnt = 2; + release_region(cs->hw.elsa.cfg, 0x80); + } + if (cs->hw.elsa.base) + release_region(cs->hw.elsa.base, bytecnt); } static void -clear_pending_ints(struct IsdnCardState *sp) +reset_elsa(struct IsdnCardState *cs) { -#ifdef CONFIG_HISAX_ELSA_PCMCIA - int val; - char tmp[64]; + long flags; - val = readhscx(sp->cfg_reg, 1, HSCX_ISTA); - sprintf(tmp, "HSCX B ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readhscx(sp->cfg_reg, 1, HSCX_EXIR); - sprintf(tmp, "HSCX B EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x02) { - val = readhscx(sp->cfg_reg, 0, HSCX_EXIR); - sprintf(tmp, "HSCX A EXIR %x", val); - debugl1(sp, tmp); + if (cs->hw.elsa.timer) { + /* Wait 1 Timer */ + byteout(cs->hw.elsa.timer, 0); + while (TimerRun(cs)); + cs->hw.elsa.ctrl_reg |= 0x50; + cs->hw.elsa.ctrl_reg &= ~ELSA_ISDN_RESET; /* Reset On */ + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + /* Wait 1 Timer */ + byteout(cs->hw.elsa.timer, 0); + while (TimerRun(cs)); + cs->hw.elsa.ctrl_reg |= ELSA_ISDN_RESET; /* Reset Off */ + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + /* Wait 1 Timer */ + byteout(cs->hw.elsa.timer, 0); + while (TimerRun(cs)); + if (cs->hw.elsa.trig) + byteout(cs->hw.elsa.trig, 0xff); } - 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); + if (cs->subtyp == ELSA_QS1000PCI) { + save_flags(flags); + sti(); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x00); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0); + schedule(); + restore_flags(flags); + byteout(cs->hw.elsa.cfg + 0x4c, 0x41); /* enable ELSA PCI IRQ */ } -#endif - writehscx(sp->cfg_reg, 0, HSCX_MASK, 0x0); - writehscx(sp->cfg_reg, 1, HSCX_MASK, 0x0); - writeisac(sp->cfg_reg, ISAC_MASK, 0x0); - writeisac(sp->cfg_reg, ISAC_CMDR, 0x41); +} + +const u_char ARCOFI_VERSION[] = {2,0xa0,0}; +const u_char ARCOFI_COP_5[] = {4,0xa1,0x25,0xbb,0x4a}; /* GTX */ +const u_char ARCOFI_COP_6[] = {6,0xa1,0x26,0,0,0x82,0x7c}; /* GRL GRH */ +const u_char ARCOFI_COP_7[] = {4,0xa1,0x27,0x80,0x80}; /* GZ */ +const u_char ARCOFI_COP_8[] = {10,0xa1,0x28,0x49,0x31,0x8,0x13,0x6e,0x88,0x2a,0x61}; /* TX */ +const u_char ARCOFI_COP_9[] = {10,0xa1,0x29,0x80,0xcb,0x9e,0x88,0x00,0xc8,0xd8,0x80}; /* RX */ +const u_char ARCOFI_XOP_0[] = {2,0xa1,0x30}; /* PWR Down */ +const u_char ARCOFI_XOP_1[] = {2,0xa1,0x31}; /* PWR Down */ +const u_char ARCOFI_XOP_F[] = {2,0xa1,0x3f}; /* PWR Down */ +const u_char ARCOFI_SOP_F[] = {10,0xa1,0x1f,0x00,0x50,0x10,0x00,0x00,0x80,0x02,0x12}; + +static void +init_arcofi(struct IsdnCardState *cs) { + send_arcofi(cs, ARCOFI_COP_5); + send_arcofi(cs, ARCOFI_COP_6); + send_arcofi(cs, ARCOFI_COP_7); + send_arcofi(cs, ARCOFI_COP_8); + send_arcofi(cs, ARCOFI_COP_9); + send_arcofi(cs, ARCOFI_SOP_F); + send_arcofi(cs, ARCOFI_XOP_F); } static void -check_arcofi(struct IsdnCardState *sp) +check_arcofi(struct IsdnCardState *cs) { -#if 0 - u_char val; +#if ARCOFI_USE + int arcofi_present = 0; 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); + if (!cs->mon_tx) + if (!(cs->mon_tx=kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC MON TX out of buffers!"); + return; + } + send_arcofi(cs, ARCOFI_VERSION); + if (test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags)) { + if (test_and_clear_bit(HW_MON1_RX_END, &cs->HW_Flags)) { + sprintf(tmp, "Arcofi response received %d bytes", cs->mon_rxp); + debugl1(cs, tmp); + p = cs->mon_rx; t = tmp; t += sprintf(tmp, "Arcofi data"); - QuickHex(t, p, sp->mon_rx->datasize); - debugl1(sp, tmp); - BufPoolRelease(sp->mon_rx); - sp->mon_rx = NULL; - sp->mon_rxp = 0; - sp->mon_flg = 0; + QuickHex(t, p, cs->mon_rxp); + debugl1(cs, tmp); + if ((cs->mon_rxp == 2) && (cs->mon_rx[0] == 0xa0)) { + switch(cs->mon_rx[1]) { + case 0x80: + debugl1(cs, "Arcofi 2160 detected"); + arcofi_present = 1; + break; + case 0x82: + debugl1(cs, "Arcofi 2165 detected"); + arcofi_present = 2; + break; + case 0x84: + debugl1(cs, "Arcofi 2163 detected"); + arcofi_present = 3; + break; + default: + debugl1(cs, "unknown Arcofi response"); + break; + } + } else + debugl1(cs, "undefined Monitor response"); + cs->mon_rxp = 0; } - } else if (sp->mon_tx) { - BufPoolRelease(sp->mon_tx); - sp->mon_tx = NULL; - sp->mon_txp = 0; + } else if (cs->mon_tx) { sprintf(tmp, "Arcofi not detected"); - debugl1(sp, tmp); + debugl1(cs, tmp); + } + if (arcofi_present) { + if (cs->subtyp==ELSA_QS1000) { + cs->subtyp = ELSA_QS3000; + printk(KERN_INFO + "Elsa: %s detected modem at 0x%x\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base+8); + release_region(cs->hw.elsa.base, 8); + if (check_region(cs->hw.elsa.base, 16)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base + 8, + cs->hw.elsa.base + 16); + } else + request_region(cs->hw.elsa.base, 16, + "elsa isdn modem"); + } else if (cs->subtyp==ELSA_PCC16) { + cs->subtyp = ELSA_PCF; + printk(KERN_INFO + "Elsa: %s detected modem at 0x%x\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base+8); + release_region(cs->hw.elsa.base, 8); + if (check_region(cs->hw.elsa.base, 16)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base + 8, + cs->hw.elsa.base + 16); + } else + request_region(cs->hw.elsa.base, 16, + "elsa isdn modem"); + } else + printk(KERN_INFO + "Elsa: %s detected modem at 0x%x\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base+8); + init_arcofi(cs); } - sp->mon_flg = 0; #endif } -int -initelsa(struct IsdnCardState *sp) +static void +elsa_led_handler(struct IsdnCardState *cs) { - int ret, irq_cnt, cnt = 3; - long flags; + int blink = 0; - irq_cnt = kstat_irqs(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_irqs(sp->irq)); - if (kstat_irqs(sp->irq) == irq_cnt) { - printk(KERN_WARNING - "Elsa: IRQ(%d) getting no interrupts during init %d\n", - sp->irq, 4 - cnt); - if (cnt == 1) { - free_irq(sp->irq, sp); - return (0); + if ((cs->subtyp == ELSA_PCMCIA) && + (cs->subtyp == ELSA_QS1000PCI)) + return; + del_timer(&cs->hw.elsa.tl); + if (cs->hw.elsa.status & ELSA_ASSIGN) + cs->hw.elsa.ctrl_reg |= ELSA_STAT_LED; + else if (cs->hw.elsa.status & ELSA_BAD_PWR) + cs->hw.elsa.ctrl_reg &= ~ELSA_STAT_LED; + else { + cs->hw.elsa.ctrl_reg ^= ELSA_STAT_LED; + blink = 250; + } + if (cs->hw.elsa.status & 0xf000) + cs->hw.elsa.ctrl_reg |= ELSA_LINE_LED; + else if (cs->hw.elsa.status & 0x0f00) { + cs->hw.elsa.ctrl_reg ^= ELSA_LINE_LED; + blink = 500; + } else + cs->hw.elsa.ctrl_reg &= ~ELSA_LINE_LED; + + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + if (blink) { + init_timer(&cs->hw.elsa.tl); + cs->hw.elsa.tl.expires = jiffies + ((blink * HZ) / 1000); + add_timer(&cs->hw.elsa.tl); + } +} + +static int +Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + int pwr, ret = 0; + long flags; + + switch (mt) { + case CARD_RESET: + reset_elsa(cs); + return(0); + case CARD_RELEASE: + release_io_elsa(cs); + return(0); + case CARD_SETIRQ: + if (cs->subtyp == ELSA_QS1000PCI) + ret = request_irq(cs->irq, &elsa_interrupt_ipac, + I4L_IRQ_FLAG, "HiSax", cs); + else + ret = request_irq(cs->irq, &elsa_interrupt, + I4L_IRQ_FLAG, "HiSax", cs); + return(ret); + case CARD_INIT: + if (cs->hw.elsa.trig) + byteout(cs->hw.elsa.trig, 0xff); + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + if (cs->subtyp == ELSA_QS1000) { + byteout(cs->hw.elsa.timer, 0); + byteout(cs->hw.elsa.trig, 0xff); + } + return(0); + case CARD_TEST: + if ((cs->subtyp != ELSA_PCMCIA) && + (cs->subtyp != ELSA_QS1000PCI)) { + save_flags(flags); + cs->hw.elsa.counter = 0; + sti(); + cs->hw.elsa.ctrl_reg |= ELSA_ENA_TIMER_INT; + cs->hw.elsa.status |= ELSA_TIMER_AKTIV; + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + byteout(cs->hw.elsa.timer, 0); + } else + return(0); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (110 * HZ) / 1000; /* Timeout 110ms */ + schedule(); + restore_flags(flags); + cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT; + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + cs->hw.elsa.status &= ~ELSA_TIMER_AKTIV; + printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n", + cs->hw.elsa.counter); + if (abs(cs->hw.elsa.counter - 13) < 3) { + printk(KERN_INFO "Elsa: timer and irq OK\n"); + ret = 0; } else { - reset_elsa(sp); - cnt--; + printk(KERN_WARNING + "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n", + cs->hw.elsa.counter, cs->irq); + ret = 1; } - } else { - check_arcofi(sp); - cnt = 0; - } + check_arcofi(cs); + elsa_led_handler(cs); + return(ret); + case MDL_REMOVE_REQ: + cs->hw.elsa.status &= 0; + break; + case MDL_ASSIGN_REQ: + cs->hw.elsa.status |= ELSA_ASSIGN; + break; + case MDL_INFO_SETUP: + if ((int) arg) + cs->hw.elsa.status |= 0x0200; + else + cs->hw.elsa.status |= 0x0100; + break; + case MDL_INFO_CONN: + if ((int) arg) + cs->hw.elsa.status |= 0x2000; + else + cs->hw.elsa.status |= 0x1000; + break; + case MDL_INFO_REL: + if ((int) arg) { + cs->hw.elsa.status &= ~0x2000; + cs->hw.elsa.status &= ~0x0200; + } else { + cs->hw.elsa.status &= ~0x1000; + cs->hw.elsa.status &= ~0x0100; + } + break; + case CARD_AUX_IND: + break; } - sp->counter = 0; - return (ret); + pwr = bytein(cs->hw.elsa.ale); + if (pwr & 0x08) + cs->hw.elsa.status |= ELSA_BAD_PWR; + else + cs->hw.elsa.status &= ~ELSA_BAD_PWR; + elsa_led_handler(cs); + return(ret); } -#ifdef CONFIG_HISAX_ELSA_PCC static unsigned char -probe_elsa_adr(unsigned int adr) +probe_elsa_adr(unsigned int adr, int typ) { int i, in1, in2, p16_1 = 0, p16_2 = 0, p8_1 = 0, p8_2 = 0, pc_1 = 0, pc_2 = 0, pfp_1 = 0, pfp_2 = 0; long flags; - if (check_region(adr, 8)) { + /* In case of the elsa pcmcia card, this region is in use, + reserved for us by the card manager. So we do not check it + here, it would fail. */ + if (typ != ISDN_CTYPE_ELSA_PCMCIA && check_region(adr, 8)) { printk(KERN_WARNING "Elsa: Probing Port 0x%x: already in use\n", adr); @@ -1251,8 +736,8 @@ probe_elsa_adr(unsigned int adr) save_flags(flags); cli(); for (i = 0; i < 16; i++) { - in1 = inb(adr + CARD_CONFIG); /* 'toggelt' bei */ - in2 = inb(adr + CARD_CONFIG); /* jedem Zugriff */ + in1 = inb(adr + ELSA_CONFIG); /* 'toggelt' bei */ + in2 = inb(adr + ELSA_CONFIG); /* jedem Zugriff */ p16_1 += 0x04 & in1; p16_2 += 0x04 & in2; p8_1 += 0x02 & in1; @@ -1283,205 +768,295 @@ probe_elsa_adr(unsigned int adr) } static unsigned int -probe_elsa(struct IsdnCardState *sp) +probe_elsa(struct IsdnCardState *cs) { int i; unsigned int CARD_portlist[] = {0x160, 0x170, 0x260, 0x360, 0}; for (i = 0; CARD_portlist[i]; i++) { - if ((sp->subtyp = probe_elsa_adr(CARD_portlist[i]))) + if ((cs->subtyp = probe_elsa_adr(CARD_portlist[i], cs->typ))) break; } return (CARD_portlist[i]); } -#endif + +static int pci_index __initdata = 0; int setup_elsa(struct IsdnCard *card) { -#ifdef CONFIG_HISAX_ELSA_PCC long flags; -#endif int bytecnt; - u_char val, verA, verB; - struct IsdnCardState *sp = card->sp; + u_char val; + struct IsdnCardState *cs = card->cs; char tmp[64]; strcpy(tmp, Elsa_revision); - printk(KERN_NOTICE "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp)); -#ifdef CONFIG_HISAX_ELSA_PCC - if (sp->typ == ISDN_CTYPE_ELSA) { - sp->cfg_reg = card->para[0]; + printk(KERN_INFO "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp)); + cs->hw.elsa.ctrl_reg = 0; + cs->hw.elsa.status = 0; + if (cs->typ == ISDN_CTYPE_ELSA) { + cs->hw.elsa.base = card->para[0]; printk(KERN_INFO "Elsa: Microlink IO probing\n"); - if (sp->cfg_reg) { - if (!(sp->subtyp = probe_elsa_adr(sp->cfg_reg))) { + if (cs->hw.elsa.base) { + if (!(cs->subtyp = probe_elsa_adr(cs->hw.elsa.base, + cs->typ))) { printk(KERN_WARNING "Elsa: no Elsa Microlink at 0x%x\n", - sp->cfg_reg); + cs->hw.elsa.base); return (0); } } else - sp->cfg_reg = probe_elsa(sp); - if (sp->cfg_reg) { - val = bytein(sp->cfg_reg + CARD_CONFIG); - if (sp->subtyp == ELSA_PC) { + cs->hw.elsa.base = probe_elsa(cs); + if (cs->hw.elsa.base) { + cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG; + cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL; + cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE; + cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC; + cs->hw.elsa.itac = cs->hw.elsa.base + ELSA_ITAC; + cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ; + cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER; + val = bytein(cs->hw.elsa.cfg); + if (cs->subtyp == ELSA_PC) { const u_char CARD_IrqTab[8] = {7, 3, 5, 9, 0, 0, 0, 0}; - sp->irq = CARD_IrqTab[(val & IRQ_INDEX_PC) >> 2]; - } else if (sp->subtyp == ELSA_PCC8) { + cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PC) >> 2]; + } else if (cs->subtyp == ELSA_PCC8) { const u_char CARD_IrqTab[8] = {7, 3, 5, 9, 0, 0, 0, 0}; - sp->irq = CARD_IrqTab[(val & IRQ_INDEX_PCC8) >> 4]; + cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PCC8) >> 4]; } else { const u_char CARD_IrqTab[8] = {15, 10, 15, 3, 11, 5, 11, 9}; - sp->irq = CARD_IrqTab[(val & IRQ_INDEX) >> 3]; + cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX) >> 3]; } - val = bytein(sp->cfg_reg + CARD_ALE) & 0x7; + val = bytein(cs->hw.elsa.ale) & ELSA_HW_RELEASE; if (val < 3) val |= 8; val += 'A' - 3; if (val == 'B' || val == 'C') val ^= 1; - if ((sp->subtyp == ELSA_PCFPRO) && (val = 'G')) + if ((cs->subtyp == ELSA_PCFPRO) && (val = 'G')) val = 'C'; printk(KERN_INFO "Elsa: %s found at 0x%x Rev.:%c IRQ %d\n", - Elsa_Types[sp->subtyp], - sp->cfg_reg, - val, sp->irq); - val = bytein(sp->cfg_reg + CARD_ALE) & 0x08; - if (val) + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + val, cs->irq); + val = bytein(cs->hw.elsa.ale) & ELSA_S0_POWER_BAD; + if (val) { printk(KERN_WARNING "Elsa: Microlink S0 bus power bad\n"); + cs->hw.elsa.status |= ELSA_BAD_PWR; + } } else { printk(KERN_WARNING "No Elsa Microlink found\n"); return (0); } - } else if (sp->typ == ISDN_CTYPE_ELSA_QS1000) { - sp->cfg_reg = card->para[1]; - sp->irq = card->para[0]; - sp->subtyp = ELSA_QS1000; + } else if (cs->typ == ISDN_CTYPE_ELSA_PNP) { + cs->hw.elsa.base = card->para[1]; + cs->irq = card->para[0]; + cs->subtyp = ELSA_QS1000; + cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG; + cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE; + cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC; + cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ; + cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER; + cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL; printk(KERN_INFO - "Elsa: %s found at 0x%x IRQ %d\n", - Elsa_Types[sp->subtyp], - sp->cfg_reg, - sp->irq); - } else - return (0); -#endif -#ifdef CONFIG_HISAX_ELSA_PCMCIA - if (sp->typ == ISDN_CTYPE_ELSA_QS1000) { - sp->cfg_reg = card->para[1]; - sp->irq = card->para[0]; - sp->subtyp = ELSA_PCMCIA; + "Elsa: %s defined at 0x%x IRQ %d\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + cs->irq); + } else if (cs->typ == ISDN_CTYPE_ELSA_PCMCIA) { + cs->hw.elsa.base = card->para[1]; + cs->irq = card->para[0]; + cs->subtyp = ELSA_PCMCIA; + cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE_PCM; + cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC_PCM; + cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + cs->hw.elsa.timer = 0; + cs->hw.elsa.trig = 0; + cs->hw.elsa.ctrl = 0; printk(KERN_INFO - "Elsa: %s found at 0x%x IRQ %d\n", - Elsa_Types[sp->subtyp], - sp->cfg_reg, - sp->irq); - } else + "Elsa: %s defined at 0x%x IRQ %d\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + cs->irq); + } else if (cs->typ == ISDN_CTYPE_ELSA_PCI) { +#if CONFIG_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr; + + cs->subtyp = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_ELSA, + PCI_QS1000_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = ELSA_QS1000PCI; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr); + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.elsa.cfg = pci_ioaddr; + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_3, &pci_ioaddr); + if (cs->subtyp) + break; + } + if (!cs->subtyp) { + printk(KERN_WARNING "Elsa: No PCI card found\n"); + return(0); + } + if (!pci_irq) { + printk(KERN_WARNING "Elsa: No IRQ for PCI card found\n"); + return(0); + } + + if (!pci_ioaddr) { + printk(KERN_WARNING "Elsa: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.elsa.base = pci_ioaddr; + cs->hw.elsa.ale = pci_ioaddr; + cs->hw.elsa.isac = pci_ioaddr +1; + cs->hw.elsa.hscx = pci_ioaddr +1; + cs->irq = pci_irq; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + cs->hw.elsa.timer = 0; + cs->hw.elsa.trig = 0; + printk(KERN_INFO + "Elsa: %s defined at 0x%x/0x%x IRQ %d\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + cs->hw.elsa.cfg, + cs->irq); +#else + printk(KERN_WARNING "Elsa: Elsa PCI and NO_PCI_BIOS\n"); + printk(KERN_WARNING "Elsa: unable to config Elsa PCI\n"); + return (0); +#endif /* CONFIG_PCI */ + } else return (0); -#endif - switch (sp->subtyp) { + switch (cs->subtyp) { case ELSA_PC: - bytecnt = 8; - break; case ELSA_PCC8: - bytecnt = 8; - break; - case ELSA_PCFPRO: - bytecnt = 16; - break; case ELSA_PCC16: + case ELSA_QS1000: + case ELSA_PCMCIA: bytecnt = 8; break; + case ELSA_PCFPRO: case ELSA_PCF: bytecnt = 16; break; - case ELSA_QS1000: - bytecnt = 8; - break; - case ELSA_PCMCIA: - bytecnt = 8; + case ELSA_QS1000PCI: + bytecnt = 2; break; default: printk(KERN_WARNING - "Unknown ELSA subtype %d\n", sp->subtyp); + "Unknown ELSA subtype %d\n", cs->subtyp); return (0); } - - if (check_region((sp->cfg_reg), bytecnt)) { + /* In case of the elsa pcmcia card, this region is in use, + reserved for us by the card manager. So we do not check it + here, it would fail. */ + if (cs->typ != ISDN_CTYPE_ELSA_PCMCIA && check_region(cs->hw.elsa.base, bytecnt)) { printk(KERN_WARNING "HiSax: %s config port %x-%x already in use\n", CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + bytecnt); + cs->hw.elsa.base, + cs->hw.elsa.base + bytecnt); return (0); } else { - request_region(sp->cfg_reg, bytecnt, "elsa isdn"); + request_region(cs->hw.elsa.base, bytecnt, "elsa isdn"); } - - /* Teste Timer */ -#ifdef CONFIG_HISAX_ELSA_PCC - byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff); - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - if (!TimerRun(sp)) { - byteout(sp->cfg_reg + CARD_START_TIMER, 0); /* 2. Versuch */ - if (!TimerRun(sp)) { + if (cs->subtyp == ELSA_QS1000PCI) { + if (check_region(cs->hw.elsa.cfg, 0x80)) { printk(KERN_WARNING - "Elsa: timer do not start\n"); - release_io_elsa(card); + "HiSax: %s pci port %x-%x already in use\n", + CardType[card->typ], + cs->hw.elsa.cfg, + cs->hw.elsa.cfg + 0x80); + release_region(cs->hw.elsa.base, bytecnt); return (0); + } else { + request_region(cs->hw.elsa.cfg, 0x80, "elsa isdn pci"); } } - save_flags(flags); - sti(); - HZDELAY(1); /* wait >=10 ms */ - restore_flags(flags); - if (TimerRun(sp)) { - printk(KERN_WARNING "Elsa: timer do not run down\n"); - release_io_elsa(card); - return (0); + cs->hw.elsa.tl.function = (void *) elsa_led_handler; + cs->hw.elsa.tl.data = (long) cs; + init_timer(&cs->hw.elsa.tl); + /* Teste Timer */ + if (cs->hw.elsa.timer) { + byteout(cs->hw.elsa.trig, 0xff); + byteout(cs->hw.elsa.timer, 0); + if (!TimerRun(cs)) { + byteout(cs->hw.elsa.timer, 0); /* 2. Versuch */ + if (!TimerRun(cs)) { + printk(KERN_WARNING + "Elsa: timer do not start\n"); + release_io_elsa(cs); + return (0); + } + } + save_flags(flags); + sti(); + HZDELAY(1); /* wait >=10 ms */ + restore_flags(flags); + if (TimerRun(cs)) { + printk(KERN_WARNING "Elsa: timer do not run down\n"); + release_io_elsa(cs); + return (0); + } + printk(KERN_INFO "Elsa: timer OK; resetting card\n"); } - printk(KERN_INFO "Elsa: timer OK; resetting card\n"); - reset_elsa(sp); -#endif - verA = readhscx(sp->cfg_reg, 0, HSCX_VSTR) & 0xf; - verB = readhscx(sp->cfg_reg, 1, HSCX_VSTR) & 0xf; - printk(KERN_INFO "Elsa: HSCX version A: %s B: %s\n", - HscxVersion(verA), HscxVersion(verB)); - val = readisac(sp->cfg_reg, ISAC_RBCH); - printk(KERN_INFO "Elsa: ISAC %s\n", - ISACVersion(val)); - -#ifdef CONFIG_HISAX_ELSA_PCMCIA - if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) { - printk(KERN_WARNING - "Elsa: wrong HSCX versions check IO address\n"); - release_io_elsa(card); - return (0); + reset_elsa(cs); + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Elsa_card_msg; + if (cs->subtyp == ELSA_QS1000PCI) { + cs->readisac = &ReadISAC_IPAC; + cs->writeisac = &WriteISAC_IPAC; + cs->readisacfifo = &ReadISACfifo_IPAC; + cs->writeisacfifo = &WriteISACfifo_IPAC; + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ID); + printk(KERN_INFO "Elsa: IPAC version %x\n", val); + } else { + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + ISACVersion(cs, "Elsa:"); + if (HscxVersion(cs, "Elsa:")) { + printk(KERN_WARNING + "Elsa: wrong HSCX versions check IO address\n"); + release_io_elsa(cs); + return (0); + } } -#endif - -#ifdef CONFIG_HISAX_ELSA_PCC - if (sp->subtyp == ELSA_PC) { - val = readitac(sp->cfg_reg, ITAC_SYS); + if (cs->subtyp == ELSA_PC) { + val = readitac(cs, ITAC_SYS); printk(KERN_INFO "Elsa: ITAC version %s\n", ITACVer[val & 7]); - writeitac(sp->cfg_reg, ITAC_ISEN, 0); - writeitac(sp->cfg_reg, ITAC_RFIE, 0); - writeitac(sp->cfg_reg, ITAC_XFIE, 0); - writeitac(sp->cfg_reg, ITAC_SCIE, 0); - writeitac(sp->cfg_reg, ITAC_STIE, 0); + writeitac(cs, ITAC_ISEN, 0); + writeitac(cs, ITAC_RFIE, 0); + writeitac(cs, ITAC_XFIE, 0); + writeitac(cs, ITAC_SCIE, 0); + writeitac(cs, ITAC_STIE, 0); } -#endif - sp->modehscx = &modehscx; - sp->ph_command = &ph_command; - sp->hscx_fill_fifo = &hscx_fill_fifo; - sp->isac_fill_fifo = &isac_fill_fifo; - return (1); } + diff --git a/drivers/isdn/hisax/elsa.h b/drivers/isdn/hisax/elsa.h deleted file mode 100644 index 5187e6abbf81..000000000000 --- a/drivers/isdn/hisax/elsa.h +++ /dev/null @@ -1,90 +0,0 @@ -/* $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/hisax/fsm.c b/drivers/isdn/hisax/fsm.c index d0bb2f14f0ea..ae0662f3f561 100644 --- a/drivers/isdn/hisax/fsm.c +++ b/drivers/isdn/hisax/fsm.c @@ -1,4 +1,4 @@ -/* $Id: fsm.c,v 1.4 1997/04/06 22:56:42 keil Exp $ +/* $Id: fsm.c,v 1.7 1997/11/06 17:09:13 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,6 +7,15 @@ * Fritz Elfert * * $Log: fsm.c,v $ + * Revision 1.7 1997/11/06 17:09:13 keil + * New 2.1 init code + * + * Revision 1.6 1997/07/27 21:42:25 keil + * proof Fsm routines + * + * Revision 1.5 1997/06/26 11:10:05 keil + * Restart timer function added + * * Revision 1.4 1997/04/06 22:56:42 keil * Some cosmetic changes * @@ -26,9 +35,9 @@ #define FSM_TIMER_DEBUG 0 -void +HISAX_INITFUNC(void FsmNew(struct Fsm *fsm, - struct FsmNode *fnlist, int fncount) + struct FsmNode *fnlist, int fncount)) { int i; @@ -36,9 +45,14 @@ FsmNew(struct Fsm *fsm, kmalloc(4L * fsm->state_count * fsm->event_count, GFP_KERNEL); memset(fsm->jumpmatrix, 0, 4L * fsm->state_count * fsm->event_count); - for (i = 0; i < fncount; i++) - fsm->jumpmatrix[fsm->state_count * fnlist[i].event + - fnlist[i].state] = (int) fnlist[i].routine; + for (i = 0; i < fncount; i++) + if ((fnlist[i].state>=fsm->state_count) || (fnlist[i].event>=fsm->event_count)) { + printk(KERN_ERR "FsmNew Error line %d st(%d/%d) ev(%d/%d)\n", + i,fnlist[i].state,fsm->state_count, + fnlist[i].event,fsm->event_count); + } else + fsm->jumpmatrix[fsm->state_count * fnlist[i].event + + fnlist[i].state] = (int) fnlist[i].routine; } void @@ -53,6 +67,11 @@ FsmEvent(struct FsmInst *fi, int event, void *arg) void (*r) (struct FsmInst *, int, void *); char str[80]; + if ((fi->state>=fi->fsm->state_count) || (event >= fi->fsm->event_count)) { + printk(KERN_ERR "FsmEvent Error st(%d/%d) ev(%d/%d)\n", + fi->state,fi->fsm->state_count,event,fi->fsm->event_count); + return(1); + } r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; if (r) { if (fi->debug) { @@ -155,10 +174,26 @@ FsmAddTimer(struct FsmTimer *ft, return 0; } -int -FsmTimerRunning(struct FsmTimer *ft) +void +FsmRestartTimer(struct FsmTimer *ft, + int millisec, int event, void *arg, int where) { - return (ft->tl.next != NULL); + +#if FSM_TIMER_DEBUG + if (ft->fi->debug) { + char str[40]; + sprintf(str, "FsmRestartTimer %lx %d %d", (long) ft, millisec, where); + ft->fi->printdebug(ft->fi, str); + } +#endif + + if (ft->tl.next || ft->tl.prev) + del_timer(&ft->tl); + init_timer(&ft->tl); + ft->event = event; + ft->arg = arg; + ft->tl.expires = jiffies + (millisec * HZ) / 1000; + add_timer(&ft->tl); } void diff --git a/drivers/isdn/hisax/hfc_2bds0.c b/drivers/isdn/hisax/hfc_2bds0.c new file mode 100644 index 000000000000..734ec8bd36b8 --- /dev/null +++ b/drivers/isdn/hisax/hfc_2bds0.c @@ -0,0 +1,1297 @@ +/* $Id: hfc_2bds0.c,v 1.3 1998/02/12 23:07:22 keil Exp $ + * + * specific routines for CCD's HFC 2BDS0 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hfc_2bds0.c,v $ + * Revision 1.3 1998/02/12 23:07:22 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.2 1998/02/02 13:26:13 keil + * New + * + * + * + */ +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_2bds0.h" +#include "isdnl1.h" +#include +/* +#define KDEBUG_DEF +#include "kdebug.h" +*/ + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +static void +dummyf(struct IsdnCardState *cs, u_char * data, int size) +{ + printk(KERN_WARNING "HiSax: hfcd dummy fifo called\n"); +} + +static inline u_char +ReadReg(struct IsdnCardState *cs, int data, u_char reg) +{ + register u_char ret; + + if (data) { + if (cs->hw.hfcD.cip != reg) { + cs->hw.hfcD.cip = reg; + byteout(cs->hw.hfcD.addr | 1, reg); + } + ret = bytein(cs->hw.hfcD.addr); +#if HFC_REG_DEBUG + if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) { + char tmp[32]; + sprintf(tmp, "t3c RD %02x %02x", reg, ret); + debugl1(cs, tmp); + } +#endif + } else + ret = bytein(cs->hw.hfcD.addr | 1); + return (ret); +} + +static inline void +WriteReg(struct IsdnCardState *cs, int data, u_char reg, u_char value) +{ + if (cs->hw.hfcD.cip != reg) { + cs->hw.hfcD.cip = reg; + byteout(cs->hw.hfcD.addr | 1, reg); + } + if (data) + byteout(cs->hw.hfcD.addr, value); +#if HFC_REG_DEBUG + if (cs->debug & L1_DEB_HSCX_FIFO && (data != HFCD_DATA_NODEB)) { + char tmp[16]; + sprintf(tmp, "t3c W%c %02x %02x", data ? 'D' : 'C', reg, value); + debugl1(cs, tmp); + } +#endif +} + +/* Interface functions */ + +static u_char +readreghfcd(struct IsdnCardState *cs, u_char offset) +{ + return(ReadReg(cs, HFCD_DATA, offset)); +} + +static void +writereghfcd(struct IsdnCardState *cs, u_char offset, u_char value) +{ + WriteReg(cs, HFCD_DATA, offset, value); +} + +void +set_cs_func(struct IsdnCardState *cs) +{ + cs->readisac = &readreghfcd; + cs->writeisac = &writereghfcd; + cs->readisacfifo = &dummyf; + cs->writeisacfifo = &dummyf; + cs->BC_Read_Reg = &ReadReg; + cs->BC_Write_Reg = &WriteReg; +} + +static inline int +WaitForBusy(struct IsdnCardState *cs) +{ + int to = 130; + + while (!(ReadReg(cs, HFCD_DATA, HFCD_STAT) & HFCD_BUSY) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: WaitForBusy timeout\n"); + return (to); +} + +static inline int +WaitNoBusy(struct IsdnCardState *cs) +{ + long flags; + int to = 130; + + while ((ReadReg(cs, HFCD_STATUS, HFCD_STATUS) & HFCD_BUSY) && to) { + save_flags(flags); + sti(); + udelay(1); + to--; + restore_flags(flags); + } + if (!to) + printk(KERN_WARNING "HiSax: WaitNoBusy timeout\n"); + return (to); +} + +static int +SelFiFo(struct IsdnCardState *cs, u_char FiFo) +{ + u_char cip; + long flags; + + + if (cs->hw.hfcD.fifo == FiFo) + return(1); + save_flags(flags); + cli(); + switch(FiFo) { + case 0: cip = HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_B1; + break; + case 1: cip = HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_B1; + break; + case 2: cip = HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_B2; + break; + case 3: cip = HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_B2; + break; + case 4: cip = HFCD_FIFO | HFCD_Z1 | HFCD_SEND; + break; + case 5: cip = HFCD_FIFO | HFCD_Z1 | HFCD_REC; + break; + default: + restore_flags(flags); + debugl1(cs, "SelFiFo Error"); + return(0); + } + cs->hw.hfcD.fifo = FiFo; + WaitNoBusy(cs); + cs->BC_Write_Reg(cs, HFCD_DATA, cip, 0); + sti(); + WaitForBusy(cs); + restore_flags(flags); + return(2); +} +static int +GetFreeFifoBytes_B(struct BCState *bcs) +{ + int s; + + if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2) + return (bcs->cs->hw.hfcD.bfifosize); + s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2]; + if (s <= 0) + s += bcs->cs->hw.hfcD.bfifosize; + s = bcs->cs->hw.hfcD.bfifosize - s; + return (s); +} + +static int +GetFreeFifoBytes_D(struct IsdnCardState *cs) +{ + int s; + + if (cs->hw.hfcD.f1 == cs->hw.hfcD.f2) + return (cs->hw.hfcD.dfifosize); + s = cs->hw.hfcD.send[cs->hw.hfcD.f1] - cs->hw.hfcD.send[cs->hw.hfcD.f2]; + if (s <= 0) + s += cs->hw.hfcD.dfifosize; + s = cs->hw.hfcD.dfifosize - s; + return (s); +} + +static int +ReadZReg(struct IsdnCardState *cs, u_char reg) +{ + int val; + + WaitNoBusy(cs); + val = 256 * ReadReg(cs, HFCD_DATA, reg | HFCB_Z_HIGH); + WaitNoBusy(cs); + val += ReadReg(cs, HFCD_DATA, reg | HFCB_Z_LOW); + return (val); +} + +static void +hfc_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static struct sk_buff +*hfc_empty_fifo(struct BCState *bcs, int count) +{ + u_char *ptr; + struct sk_buff *skb; + struct IsdnCardState *cs = bcs->cs; + int idx; + int chksum; + long flags; + u_char stat, cip; + char tmp[64]; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hfc_empty_fifo"); + idx = 0; + save_flags(flags); + if (count > HSCX_BUFMAX + 3) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfc_empty_fifo: incoming packet too large"); + cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel); + while (idx++ < count) { + cli(); + WaitNoBusy(cs); + ReadReg(cs, HFCD_DATA_NODEB, cip); + sti(); + } + skb = NULL; + } else if (count < 4) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfc_empty_fifo: incoming packet too small"); + cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel); + cli(); + while ((idx++ < count) && WaitNoBusy(cs)) + ReadReg(cs, HFCD_DATA_NODEB, cip); + skb = NULL; + } else if (!(skb = dev_alloc_skb(count - 3))) + printk(KERN_WARNING "HFC: receive out of memory\n"); + else { + ptr = skb_put(skb, count - 3); + idx = 0; + cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel); + cli(); + while (idx < (count - 3)) { + cli(); + if (!WaitNoBusy(cs)) + break; + *ptr = ReadReg(cs, HFCD_DATA_NODEB, cip); + sti(); + ptr++; + idx++; + } + if (idx != count - 3) { + sti(); + debugl1(cs, "RFIFO BUSY error"); + printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel); + dev_kfree_skb(skb); + skb = NULL; + } else { + cli(); + WaitNoBusy(cs); + chksum = (ReadReg(cs, HFCD_DATA, cip) << 8); + WaitNoBusy(cs); + chksum += ReadReg(cs, HFCD_DATA, cip); + WaitNoBusy(cs); + stat = ReadReg(cs, HFCD_DATA, cip); + sti(); + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc_empty_fifo %d chksum %x stat %x", + bcs->channel, chksum, stat); + debugl1(cs, tmp); + } + if (stat) { + debugl1(cs, "FIFO CRC error"); + dev_kfree_skb(skb); + skb = NULL; + } + } + } + sti(); + WaitForBusy(cs); + cli(); + WaitNoBusy(cs); + stat = ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F2_INC | + HFCB_REC | HFCB_CHANNEL(bcs->channel)); + sti(); + WaitForBusy(cs); + restore_flags(flags); + return (skb); +} + +static void +hfc_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + long flags; + int idx, fcnt; + int count; + u_char cip; + char tmp[64]; + + + if (!bcs->hw.hfc.tx_skb) + return; + if (bcs->hw.hfc.tx_skb->len <= 0) + return; + + save_flags(flags); + cli(); + SelFiFo(cs, HFCB_SEND | HFCB_CHANNEL(bcs->channel)); + cip = HFCB_FIFO | HFCB_F1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel); + WaitNoBusy(cs); + bcs->hw.hfc.f1 = ReadReg(cs, HFCD_DATA, cip); + WaitNoBusy(cs); + cip = HFCB_FIFO | HFCB_F2 | HFCB_SEND | HFCB_CHANNEL(bcs->channel); + WaitNoBusy(cs); + bcs->hw.hfc.f2 = ReadReg(cs, HFCD_DATA, cip); + bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel)); + sti(); + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)", + bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2, + bcs->hw.hfc.send[bcs->hw.hfc.f1]); + debugl1(cs, tmp); + } + fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2; + if (fcnt < 0) + fcnt += 32; + if (fcnt > 30) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo more as 30 frames"); + restore_flags(flags); + return; + } + count = GetFreeFifoBytes_B(bcs); + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc_fill_fifo %d count(%d/%d),%lx", + bcs->channel, bcs->hw.hfc.tx_skb->len, + count, current->state); + debugl1(cs, tmp); + } + if (count < bcs->hw.hfc.tx_skb->len) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo no fifo mem"); + restore_flags(flags); + return; + } + cip = HFCB_FIFO | HFCB_FIFO_IN | HFCB_SEND | HFCB_CHANNEL(bcs->channel); + idx = 0; + cli(); + WaitForBusy(cs); + WaitNoBusy(cs); + WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->hw.hfc.tx_skb->data[idx++]); + while (idx < bcs->hw.hfc.tx_skb->len) { + cli(); + if (!WaitNoBusy(cs)) + break; + WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->hw.hfc.tx_skb->data[idx]); + sti(); + idx++; + } + if (idx != bcs->hw.hfc.tx_skb->len) { + sti(); + debugl1(cs, "FIFO Send BUSY error"); + printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel); + } else { + bcs->tx_cnt -= bcs->hw.hfc.tx_skb->len; + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->hw.hfc.tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hfc.tx_skb->len); + dev_kfree_skb(bcs->hw.hfc.tx_skb); + bcs->hw.hfc.tx_skb = NULL; + } + WaitForBusy(cs); + cli(); + WaitNoBusy(cs); + ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F1_INC | HFCB_SEND | HFCB_CHANNEL(bcs->channel)); + sti(); + WaitForBusy(cs); + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + restore_flags(flags); + return; +} + +static void +hfc_send_data(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + char tmp[32]; + + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + sprintf(tmp,"send_data %d blocked", bcs->channel); + debugl1(cs, tmp); + } +} + +void +main_rec_2bds0(struct BCState *bcs) +{ + long flags; + struct IsdnCardState *cs = bcs->cs; + int z1, z2, rcnt; + u_char f1, f2, cip; + int receive, count = 5; + struct sk_buff *skb; + char tmp[64]; + + save_flags(flags); + Begin: + count--; + cli(); + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + sprintf(tmp,"rec_data %d blocked", bcs->channel); + debugl1(cs, tmp); + restore_flags(flags); + return; + } + SelFiFo(cs, HFCB_REC | HFCB_CHANNEL(bcs->channel)); + cip = HFCB_FIFO | HFCB_F1 | HFCB_REC | HFCB_CHANNEL(bcs->channel); + WaitNoBusy(cs); + f1 = ReadReg(cs, HFCD_DATA, cip); + cip = HFCB_FIFO | HFCB_F2 | HFCB_REC | HFCB_CHANNEL(bcs->channel); + WaitNoBusy(cs); + f2 = ReadReg(cs, HFCD_DATA, cip); + sti(); + if (f1 != f2) { + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc rec %d f1(%d) f2(%d)", + bcs->channel, f1, f2); + debugl1(cs, tmp); + } + cli(); + z1 = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_CHANNEL(bcs->channel)); + z2 = ReadZReg(cs, HFCB_FIFO | HFCB_Z2 | HFCB_REC | HFCB_CHANNEL(bcs->channel)); + sti(); + rcnt = z1 - z2; + if (rcnt < 0) + rcnt += cs->hw.hfcD.bfifosize; + rcnt++; + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc rec %d z1(%x) z2(%x) cnt(%d)", + bcs->channel, z1, z2, rcnt); + debugl1(cs, tmp); + } + if ((skb = hfc_empty_fifo(bcs, rcnt))) { + cli(); + skb_queue_tail(&bcs->rqueue, skb); + sti(); + hfc_sched_event(bcs, B_RCVBUFREADY); + } + rcnt = f1 -f2; + if (rcnt<0) + rcnt += 32; + if (rcnt>1) + receive = 1; + else + receive = 0; + } else + receive = 0; + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + if (count && receive) + goto Begin; + restore_flags(flags); + return; +} + +void +mode_2bs0(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + + if (cs->debug & L1_DEB_HSCX) { + char tmp[40]; + sprintf(tmp, "HFCD bchannel mode %d bchan %d/%d", + mode, bc, bcs->channel); + debugl1(cs, tmp); + } + bcs->mode = mode; + bcs->channel = bc; + switch (mode) { + case (L1_MODE_NULL): + if (bc) { + cs->hw.hfcD.conn |= 0x18; + cs->hw.hfcD.sctrl &= ~SCTRL_B2_ENA; + } else { + cs->hw.hfcD.conn |= 0x3; + cs->hw.hfcD.sctrl &= ~SCTRL_B1_ENA; + } + break; + case (L1_MODE_TRANS): + if (bc) { + cs->hw.hfcD.ctmt |= 2; + cs->hw.hfcD.conn &= ~0x18; + cs->hw.hfcD.sctrl |= SCTRL_B2_ENA; + } else { + cs->hw.hfcD.ctmt |= 1; + cs->hw.hfcD.conn &= ~0x3; + cs->hw.hfcD.sctrl |= SCTRL_B1_ENA; + } + break; + case (L1_MODE_HDLC): + if (bc) { + cs->hw.hfcD.ctmt &= ~2; + cs->hw.hfcD.conn &= ~0x18; + cs->hw.hfcD.sctrl |= SCTRL_B2_ENA; + } else { + cs->hw.hfcD.ctmt &= ~1; + cs->hw.hfcD.conn &= ~0x3; + cs->hw.hfcD.sctrl |= SCTRL_B1_ENA; + } + break; + } + WriteReg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl); + WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); + WriteReg(cs, HFCD_DATA, HFCD_CONN, cs->hw.hfcD.conn); +} + +static void +hfc_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA_REQ): + save_flags(flags); + cli(); + if (st->l1.bcs->hw.hfc.tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->hw.hfc.tx_skb = skb; +/* test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); +*/ st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + } + break; + case (PH_PULL_IND): + if (st->l1.bcs->hw.hfc.tx_skb) { + printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n"); + break; + } + save_flags(flags); + cli(); +/* test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); +*/ st->l1.bcs->hw.hfc.tx_skb = skb; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + break; + case (PH_PULL_REQ): + if (!st->l1.bcs->hw.hfc.tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL_CNF, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } +} + +void +close_2bs0(struct BCState *bcs) +{ + struct sk_buff *skb; + + mode_2bs0(bcs, 0, 0); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + while ((skb = skb_dequeue(&bcs->rqueue))) { + dev_kfree_skb(skb); + } + while ((skb = skb_dequeue(&bcs->squeue))) { + dev_kfree_skb(skb); + } + if (bcs->hw.hfc.tx_skb) { + dev_kfree_skb(bcs->hw.hfc.tx_skb); + bcs->hw.hfc.tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +static int +open_hfcstate(struct IsdnCardState *cs, + int bc) +{ + struct BCState *bcs = cs->bcs + bc; + + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->hw.hfc.tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->tx_cnt = 0; + return (0); +} + +static void +hfc_manl1(struct PStack *st, int pr, + void *arg) +{ + switch (pr) { + case (PH_ACTIVATE_REQ): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + mode_2bs0(st->l1.bcs, st->l1.mode, st->l1.bc); + st->l1.l1man(st, PH_ACTIVATE_CNF, NULL); + break; + case (PH_DEACTIVATE_REQ): + if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) + mode_2bs0(st->l1.bcs, 0, 0); + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + break; + } +} + +int +setstack_2b(struct PStack *st, struct BCState *bcs) +{ + if (open_hfcstate(st->l1.hardware, bcs->channel)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = hfc_l2l1; + st->ma.manl1 = hfc_manl1; + setstack_manager(st); + bcs->st = st; + return (0); +} + +static void +manl1_msg(struct IsdnCardState *cs, int msg, void *arg) { + struct PStack *st; + + st = cs->stlist; + while (st) { + st->ma.manl1(st, msg, arg); + st = st->next; + } +} + +static void +hfcd_bh(struct IsdnCardState *cs) +{ +/* struct PStack *stptr; +*/ + if (!cs) + return; +#if 0 + if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy cleared"); + stptr = cs->stlist; + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE_CNF, NULL); + stptr = stptr->next; + } + } +#endif + if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) { + switch (cs->ph_state) { + case (0): + manl1_msg(cs, PH_RESET_IND, NULL); + break; + case (3): + manl1_msg(cs, PH_DEACT_IND, NULL); + break; + case (8): + manl1_msg(cs, PH_RSYNC_IND, NULL); + break; + case (6): + manl1_msg(cs, PH_INFO2_IND, NULL); + break; + case (7): + manl1_msg(cs, PH_I4_P8_IND, NULL); + break; + default: + break; + } + } + if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) + DChannel_proc_rcv(cs); + if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) + DChannel_proc_xmt(cs); +} + +void +sched_event_D(struct IsdnCardState *cs, int event) +{ + test_and_set_bit(event, &cs->event); + queue_task(&cs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static +int receive_dmsg(struct IsdnCardState *cs) +{ + struct sk_buff *skb; + long flags; + int idx; + int rcnt, z1, z2; + u_char stat, cip, f1, f2; + int chksum; + int count=5; + u_char *ptr; + char tmp[64]; + + save_flags(flags); + cli(); + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + debugl1(cs, "rec_dmsg blocked"); + restore_flags(flags); + return(1); + } + SelFiFo(cs, 4 | HFCD_REC); + cip = HFCD_FIFO | HFCD_F1 | HFCD_REC; + WaitNoBusy(cs); + f1 = cs->readisac(cs, cip) & 0xf; + cip = HFCD_FIFO | HFCD_F2 | HFCD_REC; + WaitNoBusy(cs); + f2 = cs->readisac(cs, cip) & 0xf; + while ((f1 != f2) && count--) { + z1 = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_REC); + z2 = ReadZReg(cs, HFCD_FIFO | HFCD_Z2 | HFCD_REC); + rcnt = z1 - z2; + if (rcnt < 0) + rcnt += cs->hw.hfcD.dfifosize; + rcnt++; + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "hfcd recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)", + f1, f2, z1, z2, rcnt); + debugl1(cs, tmp); + } + sti(); + idx = 0; + cip = HFCD_FIFO | HFCD_FIFO_OUT | HFCD_REC; + if (rcnt > MAX_DFRAME_LEN + 3) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "empty_fifo d: incoming packet too large"); + while (idx < rcnt) { + cli(); + if (!(WaitNoBusy(cs))) + break; + ReadReg(cs, HFCD_DATA_NODEB, cip); + sti(); + idx++; + } + } else if (rcnt < 4) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "empty_fifo d: incoming packet too small"); + cli(); + while ((idx++ < rcnt) && WaitNoBusy(cs)) + ReadReg(cs, HFCD_DATA_NODEB, cip); + } else if ((skb = dev_alloc_skb(rcnt - 3))) { + ptr = skb_put(skb, rcnt - 3); + while (idx < (rcnt - 3)) { + cli(); + if (!(WaitNoBusy(cs))) + break; + *ptr = ReadReg(cs, HFCD_DATA_NODEB, cip); + sti(); + idx++; + ptr++; + } + if (idx != (rcnt - 3)) { + sti(); + debugl1(cs, "RFIFO D BUSY error"); + printk(KERN_WARNING "HFC DFIFO channel BUSY Error\n"); + dev_kfree_skb(skb); + skb = NULL; + } else { + cli(); + WaitNoBusy(cs); + chksum = (ReadReg(cs, HFCD_DATA, cip) << 8); + WaitNoBusy(cs); + chksum += ReadReg(cs, HFCD_DATA, cip); + WaitNoBusy(cs); + stat = ReadReg(cs, HFCD_DATA, cip); + sti(); + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "empty_dfifo chksum %x stat %x", + chksum, stat); + debugl1(cs, tmp); + } + if (stat) { + debugl1(cs, "FIFO CRC error"); + dev_kfree_skb(skb); + skb = NULL; + } else { + skb_queue_tail(&cs->rq, skb); + sched_event_D(cs, D_RCVBUFREADY); + } + } + } else + printk(KERN_WARNING "HFC: D receive out of memory\n"); + sti(); + WaitForBusy(cs); + cip = HFCD_FIFO | HFCD_F2_INC | HFCD_REC; + cli(); + WaitNoBusy(cs); + stat = ReadReg(cs, HFCD_DATA, cip); + sti(); + WaitForBusy(cs); + cip = HFCD_FIFO | HFCD_F2 | HFCD_REC; + cli(); + WaitNoBusy(cs); + f2 = cs->readisac(cs, cip) & 0xf; + } + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + restore_flags(flags); + return(1); +} + +static void +hfc_fill_dfifo(struct IsdnCardState *cs) +{ + long flags; + int idx, fcnt; + int count; + u_char cip; + char tmp[64]; + + if (!cs->tx_skb) + return; + if (cs->tx_skb->len <= 0) + return; + + save_flags(flags); + cli(); + SelFiFo(cs, 4 | HFCD_SEND); + cip = HFCD_FIFO | HFCD_F1 | HFCD_SEND; + WaitNoBusy(cs); + cs->hw.hfcD.f1 = ReadReg(cs, HFCD_DATA, cip) & 0xf; + WaitNoBusy(cs); + cip = HFCD_FIFO | HFCD_F2 | HFCD_SEND; + cs->hw.hfcD.f2 = ReadReg(cs, HFCD_DATA, cip) & 0xf; + cs->hw.hfcD.send[cs->hw.hfcD.f1] = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_SEND); + sti(); + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "hfc_fill_Dfifo f1(%d) f2(%d) z1(%x)", + cs->hw.hfcD.f1, cs->hw.hfcD.f2, + cs->hw.hfcD.send[cs->hw.hfcD.f1]); + debugl1(cs, tmp); + } + fcnt = cs->hw.hfcD.f1 - cs->hw.hfcD.f2; + if (fcnt < 0) + fcnt += 16; + if (fcnt > 14) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_Dfifo more as 14 frames"); + restore_flags(flags); + return; + } + count = GetFreeFifoBytes_D(cs); + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "hfc_fill_Dfifo count(%d/%d)", + cs->tx_skb->len, count); + debugl1(cs, tmp); + } + if (count < cs->tx_skb->len) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfc_fill_Dfifo no fifo mem"); + restore_flags(flags); + return; + } + cip = HFCD_FIFO | HFCD_FIFO_IN | HFCD_SEND; + idx = 0; + cli(); + WaitForBusy(cs); + WaitNoBusy(cs); + WriteReg(cs, HFCD_DATA_NODEB, cip, cs->tx_skb->data[idx++]); + while (idx < cs->tx_skb->len) { + cli(); + if (!(WaitNoBusy(cs))) + break; + WriteReg(cs, HFCD_DATA_NODEB, cip, cs->tx_skb->data[idx]); + sti(); + idx++; + } + if (idx != cs->tx_skb->len) { + sti(); + debugl1(cs, "DFIFO Send BUSY error"); + printk(KERN_WARNING "HFC S DFIFO channel BUSY Error\n"); + } + WaitForBusy(cs); + cli(); + WaitNoBusy(cs); + ReadReg(cs, HFCD_DATA, HFCD_FIFO | HFCD_F1_INC | HFCD_SEND); + dev_kfree_skb(cs->tx_skb); + cs->tx_skb = NULL; + sti(); + WaitForBusy(cs); + restore_flags(flags); + return; +} + +static +struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel) +{ + if (cs->bcs[0].mode && (cs->bcs[0].channel == channel)) + return(&cs->bcs[0]); + else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel)) + return(&cs->bcs[1]); + else + return(NULL); +} + +void +hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val) +{ + u_char exval; + struct BCState *bcs; + char tmp[32]; + int count=15; + long flags; + + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "HFCD irq %x %s", val, + test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ? + "locked" : "unlocked"); + debugl1(cs, tmp); + } + val &= cs->hw.hfcD.int_m1; + if (val & 0x40) { /* TE state machine irq */ + exval = cs->readisac(cs, HFCD_STATES) & 0xf; + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "ph_state chg %d->%d", cs->ph_state, + exval); + debugl1(cs, tmp); + } + cs->ph_state = exval; + sched_event_D(cs, D_L1STATECHANGE); + val &= ~0x40; + } + while (val) { + save_flags(flags); + cli(); + if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + cs->hw.hfcD.int_s1 |= val; + restore_flags(flags); + return; + } + if (cs->hw.hfcD.int_s1 & 0x18) { + exval = val; + val = cs->hw.hfcD.int_s1; + cs->hw.hfcD.int_s1 = exval; + } + if (val & 0x08) { + if (!(bcs=Sel_BCS(cs, 0))) { + if (cs->debug) + debugl1(cs, "hfcd spurious 0x08 IRQ"); + } else + main_rec_2bds0(bcs); + } + if (val & 0x10) { + if (!(bcs=Sel_BCS(cs, 1))) { + if (cs->debug) + debugl1(cs, "hfcd spurious 0x10 IRQ"); + } else + main_rec_2bds0(bcs); + } + if (val & 0x01) { + if (!(bcs=Sel_BCS(cs, 0))) { + if (cs->debug) + debugl1(cs, "hfcd spurious 0x01 IRQ"); + } else { + if (bcs->hw.hfc.tx_skb) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + sprintf(tmp,"fill_data %d blocked", bcs->channel); + debugl1(cs, tmp); + } + } else { + if ((bcs->hw.hfc.tx_skb = skb_dequeue(&bcs->squeue))) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + sprintf(tmp,"fill_data %d blocked", bcs->channel); + debugl1(cs, tmp); + } + } else { + hfc_sched_event(bcs, B_XMTBUFREADY); + } + } + } + } + if (val & 0x02) { + if (!(bcs=Sel_BCS(cs, 1))) { + if (cs->debug) + debugl1(cs, "hfcd spurious 0x02 IRQ"); + } else { + if (bcs->hw.hfc.tx_skb) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + sprintf(tmp,"fill_data %d blocked", bcs->channel); + debugl1(cs, tmp); + } + } else { + if ((bcs->hw.hfc.tx_skb = skb_dequeue(&bcs->squeue))) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + sprintf(tmp,"fill_data %d blocked", bcs->channel); + debugl1(cs, tmp); + } + } else { + hfc_sched_event(bcs, B_XMTBUFREADY); + } + } + } + } + if (val & 0x20) { /* receive dframe */ + receive_dmsg(cs); + } + if (val & 0x04) { /* dframe transmitted */ + if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) + del_timer(&cs->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + sched_event_D(cs, D_CLEARBUSY); + if (cs->tx_skb) + if (cs->tx_skb->len) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + debugl1(cs, "hfc_fill_dfifo irq blocked"); + } + goto afterXPR; + } else { + dev_kfree_skb(cs->tx_skb); + cs->tx_cnt = 0; + cs->tx_skb = NULL; + } + if ((cs->tx_skb = skb_dequeue(&cs->sq))) { + cs->tx_cnt = 0; + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + debugl1(cs, "hfc_fill_dfifo irq blocked"); + } + } else + sched_event_D(cs, D_XMTBUFREADY); + } + afterXPR: + if (cs->hw.hfcD.int_s1 && count--) { + val = cs->hw.hfcD.int_s1; + cs->hw.hfcD.int_s1 = 0; + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "HFCD irq %x loop %d", val, 15-count); + debugl1(cs, tmp); + } + } else + val = 0; + restore_flags(flags); + } +} + +static void +HFCD_l2l1(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + struct sk_buff *skb = arg; + char str[64]; + switch (pr) { + case (PH_DATA_REQ): + if (cs->tx_skb) { + skb_queue_tail(&cs->sq, skb); +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA Queued", 0); +#endif + } else { + if ((cs->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ + LogFrame(cs, skb->data, skb->len); + sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); + dlogframe(cs, skb->data + 4, skb->len - 4, + str); + } + cs->tx_skb = skb; + cs->tx_cnt = 0; +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA", 0); +#endif + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "hfc_fill_dfifo blocked"); + + } + break; + case (PH_PULL_IND): + if (cs->tx_skb) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, " l2l1 tx_skb exist this shouldn't happen"); + skb_queue_tail(&cs->sq, skb); + break; + } + if ((cs->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ + LogFrame(cs, skb->data, skb->len); + sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); + dlogframe(cs, skb->data + 4, skb->len - 4, + str); + } + cs->tx_skb = skb; + cs->tx_cnt = 0; +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA_PULLED", 0); +#endif + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "hfc_fill_dfifo blocked"); + break; + case (PH_PULL_REQ): +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + debugl1(cs, "-> PH_REQUEST_PULL"); +#endif + if (!cs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL_CNF, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } +} + +void +hfcd_l1cmd(struct IsdnCardState *cs, int msg, void *arg) +{ + char tmp[32]; + switch(msg) { + case PH_RESET_REQ: + cs->writeisac(cs, HFCD_STATES, HFCD_LOAD_STATE | 3); /* HFC ST 3 */ + udelay(6); + cs->writeisac(cs, HFCD_STATES, 3); /* HFC ST 2 */ + cs->hw.hfcD.mst_m |= HFCD_MASTER; + cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m); + cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION); + manl1_msg(cs, PH_POWERUP_CNF, NULL); + break; + case PH_ENABLE_REQ: + cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION); + break; + case PH_DEACT_ACK: + cs->hw.hfcD.mst_m &= ~HFCD_MASTER; + cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m); + break; + case PH_INFO3_REQ: + cs->hw.hfcD.mst_m |= HFCD_MASTER; + cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m); + break; +#if 0 + case PH_TESTLOOP_REQ: + u_char val = 0; + if (1 & (int) arg) + val |= 0x0c; + if (2 & (int) arg) + val |= 0x3; + if (test_bit(HW_IOM1, &cs->HW_Flags)) { + /* IOM 1 Mode */ + if (!val) { + cs->writeisac(cs, ISAC_SPCR, 0xa); + cs->writeisac(cs, ISAC_ADF1, 0x2); + } else { + cs->writeisac(cs, ISAC_SPCR, val); + cs->writeisac(cs, ISAC_ADF1, 0xa); + } + } else { + /* IOM 2 Mode */ + cs->writeisac(cs, ISAC_SPCR, val); + if (val) + cs->writeisac(cs, ISAC_ADF1, 0x8); + else + cs->writeisac(cs, ISAC_ADF1, 0x0); + } + break; +#endif + default: + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "hfcd_l1cmd unknown %4x", msg); + debugl1(cs, tmp); + } + break; + } +} + +void +setstack_hfcd(struct PStack *st, struct IsdnCardState *cs) +{ + st->l2.l2l1 = HFCD_l2l1; +} + +static void +hfc_dbusy_timer(struct IsdnCardState *cs) +{ +#if 0 + struct PStack *stptr; + if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy"); + test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); + stptr = cs->stlist; + + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE_IND, NULL); + stptr = stptr->next; + } + } +#endif +} + +__initfunc(unsigned int +*init_send_hfcd(int cnt)) +{ + int i, *send; + + if (!(send = kmalloc(cnt * sizeof(unsigned int), GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for hfcd.send\n"); + return(NULL); + } + for (i = 0; i < cnt; i++) + send[i] = 0x1fff; + return(send); +} + +__initfunc(void +init2bds0(struct IsdnCardState *cs)) +{ + cs->setstack_d = setstack_hfcd; + cs->l1cmd = hfcd_l1cmd; + cs->dbusytimer.function = (void *) hfc_dbusy_timer; + cs->dbusytimer.data = (long) cs; + init_timer(&cs->dbusytimer); + cs->tqueue.routine = (void *) (void *) hfcd_bh; + if (!cs->hw.hfcD.send) + cs->hw.hfcD.send = init_send_hfcd(16); + if (!cs->bcs[0].hw.hfc.send) + cs->bcs[0].hw.hfc.send = init_send_hfcd(32); + if (!cs->bcs[1].hw.hfc.send) + cs->bcs[1].hw.hfc.send = init_send_hfcd(32); + cs->BC_Send_Data = &hfc_send_data; + cs->bcs[0].BC_SetStack = setstack_2b; + cs->bcs[1].BC_SetStack = setstack_2b; + cs->bcs[0].BC_Close = close_2bs0; + cs->bcs[1].BC_Close = close_2bs0; + mode_2bs0(cs->bcs, 0, 0); + mode_2bs0(cs->bcs + 1, 0, 1); +} + +void +release2bds0(struct IsdnCardState *cs) +{ + if (cs->bcs[0].hw.hfc.send) { + kfree(cs->bcs[0].hw.hfc.send); + cs->bcs[0].hw.hfc.send = NULL; + } + if (cs->bcs[1].hw.hfc.send) { + kfree(cs->bcs[1].hw.hfc.send); + cs->bcs[1].hw.hfc.send = NULL; + } + if (cs->hw.hfcD.send) { + kfree(cs->hw.hfcD.send); + cs->hw.hfcD.send = NULL; + } +} diff --git a/drivers/isdn/hisax/hfc_2bds0.h b/drivers/isdn/hisax/hfc_2bds0.h new file mode 100644 index 000000000000..d11e8b50363e --- /dev/null +++ b/drivers/isdn/hisax/hfc_2bds0.h @@ -0,0 +1,131 @@ +/* $Id: hfc_2bds0.h,v 1.2 1998/02/02 13:26:15 keil Exp $ + + * specific defines for CCD's HFC 2BDS0 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hfc_2bds0.h,v $ + * Revision 1.2 1998/02/02 13:26:15 keil + * New + * + * + * + */ + +#define HFCD_CIRM 0x18 +#define HFCD_CTMT 0x19 +#define HFCD_INT_M1 0x1A +#define HFCD_INT_M2 0x1B +#define HFCD_INT_S1 0x1E +#define HFCD_STAT 0x1C +#define HFCD_STAT_DISB 0x1D +#define HFCD_STATES 0x30 +#define HFCD_SCTRL 0x31 +#define HFCD_TEST 0x32 +#define HFCD_SQ 0x34 +#define HFCD_CLKDEL 0x37 +#define HFCD_MST_MODE 0x2E +#define HFCD_CONN 0x2F + +#define HFCD_FIFO 0x80 +#define HFCD_Z1 0x10 +#define HFCD_Z2 0x18 +#define HFCD_Z_LOW 0x00 +#define HFCD_Z_HIGH 0x04 +#define HFCD_F1_INC 0x12 +#define HFCD_FIFO_IN 0x16 +#define HFCD_F1 0x1a +#define HFCD_F2 0x1e +#define HFCD_F2_INC 0x22 +#define HFCD_FIFO_OUT 0x26 +#define HFCD_REC 0x01 +#define HFCD_SEND 0x00 + +#define HFCB_FIFO 0x80 +#define HFCB_Z1 0x00 +#define HFCB_Z2 0x08 +#define HFCB_Z_LOW 0x00 +#define HFCB_Z_HIGH 0x04 +#define HFCB_F1_INC 0x28 +#define HFCB_FIFO_IN 0x2c +#define HFCB_F1 0x30 +#define HFCB_F2 0x34 +#define HFCB_F2_INC 0x38 +#define HFCB_FIFO_OUT 0x3c +#define HFCB_REC 0x01 +#define HFCB_SEND 0x00 +#define HFCB_B1 0x00 +#define HFCB_B2 0x02 +#define HFCB_CHANNEL(ch) (ch ? HFCB_B2 : HFCB_B1) + +#define HFCD_STATUS 0 +#define HFCD_DATA 1 +#define HFCD_DATA_NODEB 2 + +/* Status (READ) */ +#define HFCD_BUSY 0x01 +#define HFCD_BUSY_NBUSY 0x04 +#define HFCD_TIMER_ELAP 0x10 +#define HFCD_STATINT 0x20 +#define HFCD_FRAMEINT 0x40 +#define HFCD_ANYINT 0x80 + +/* CTMT (Write) */ +#define HFCD_CLTIMER 0x80 +#define HFCD_TIM25 0x00 +#define HFCD_TIM50 0x08 +#define HFCD_TIM400 0x10 +#define HFCD_TIM800 0x18 +#define HFCD_AUTO_TIMER 0x20 +#define HFCD_TRANSB2 0x02 +#define HFCD_TRANSB1 0x01 + +/* CIRM (Write) */ +#define HFCD_RESET 0x08 +#define HFCD_MEM8K 0x10 +#define HFCD_INTA 0x01 +#define HFCD_INTB 0x02 +#define HFCD_INTC 0x03 +#define HFCD_INTD 0x04 +#define HFCD_INTE 0x05 +#define HFCD_INTF 0x06 + +/* INT_M1;INT_S1 */ +#define HFCD_INTS_B1TRANS 0x01 +#define HFCD_INTS_B2TRANS 0x02 +#define HFCD_INTS_DTRANS 0x04 +#define HFCD_INTS_B1REC 0x08 +#define HFCD_INTS_B2REC 0x10 +#define HFCD_INTS_DREC 0x20 +#define HFCD_INTS_L1STATE 0x40 +#define HFCD_INTS_TIMER 0x80 + +/* INT_M2 */ +#define HFCD_IRQ_ENABLE 0x08 + +/* STATES */ +#define HFCD_LOAD_STATE 0x10 +#define HFCD_ACTIVATE 0x20 +#define HFCD_DO_ACTION 0x40 + +/* HFCD_MST_MODE */ +#define HFCD_MASTER 0x01 + +/* HFCD_SCTRL */ +#define SCTRL_B1_ENA 0x01 +#define SCTRL_B2_ENA 0x02 +#define SCTRL_LOW_PRIO 0x08 +#define SCTRL_SQ_ENA 0x10 +#define SCTRL_TEST 0x20 +#define SCTRL_NONE_CAP 0x40 +#define SCTRL_PWR_DOWN 0x80 + +/* HFCD_TEST */ +#define HFCD_AUTO_AWAKE 0x01 + +extern void main_irq_2bds0(struct BCState *bcs); +extern void init2bds0(struct IsdnCardState *cs); +extern void release2bds0(struct IsdnCardState *cs); +extern void hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val); +extern void set_cs_func(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/hfc_2bs0.c b/drivers/isdn/hisax/hfc_2bs0.c new file mode 100644 index 000000000000..2d9ce5bd52ea --- /dev/null +++ b/drivers/isdn/hisax/hfc_2bs0.c @@ -0,0 +1,615 @@ +/* $Id: hfc_2bs0.c,v 1.4 1998/02/12 23:07:29 keil Exp $ + + * specific routines for CCD's HFC 2BS0 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hfc_2bs0.c,v $ + * Revision 1.4 1998/02/12 23:07:29 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.3 1997/11/06 17:13:35 keil + * New 2.1 init code + * + * Revision 1.2 1997/10/29 19:04:47 keil + * changes for 2.1 + * + * Revision 1.1 1997/09/11 17:31:33 keil + * Common part for HFC 2BS0 based cards + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_2bs0.h" +#include "isac.h" +#include "isdnl1.h" +#include + +static inline int +WaitForBusy(struct IsdnCardState *cs) +{ + int to = 130; + long flags; + u_char val; + + save_flags(flags); + cli(); + while (!(cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) { + val = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2 | + (cs->hw.hfc.cip & 3)); + udelay(1); + to--; + } + restore_flags(flags); + if (!to) { + printk(KERN_WARNING "HiSax: waitforBusy timeout\n"); + return (0); + } else + return (to); +} + +static inline int +WaitNoBusy(struct IsdnCardState *cs) +{ + int to = 125; + + while ((cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) { + udelay(1); + to--; + } + if (!to) { + printk(KERN_WARNING "HiSax: waitforBusy timeout\n"); + return (0); + } else + return (to); +} + +int +GetFreeFifoBytes(struct BCState *bcs) +{ + int s; + + if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2) + return (bcs->cs->hw.hfc.fifosize); + s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2]; + if (s <= 0) + s += bcs->cs->hw.hfc.fifosize; + s = bcs->cs->hw.hfc.fifosize - s; + return (s); +} + +int +ReadZReg(struct BCState *bcs, u_char reg) +{ + int val; + + WaitNoBusy(bcs->cs); + val = 256 * bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_HIGH); + WaitNoBusy(bcs->cs); + val += bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_LOW); + return (val); +} + +void +hfc_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void +hfc_clear_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + long flags; + int idx, cnt; + int rcnt, z1, z2; + u_char cip, f1, f2; + char tmp[64]; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hfc_clear_fifo"); + save_flags(flags); + cli(); + cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel); + if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) { + cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip); + WaitForBusy(cs); + } + WaitNoBusy(cs); + f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel); + WaitNoBusy(cs); + f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel)); + z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel)); + cnt = 32; + while (((f1 != f2) || (z1 != z2)) && cnt--) { + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc clear %d f1(%d) f2(%d)", + bcs->channel, f1, f2); + debugl1(cs, tmp); + } + rcnt = z1 - z2; + if (rcnt < 0) + rcnt += cs->hw.hfc.fifosize; + if (rcnt) + rcnt++; + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc clear %d z1(%x) z2(%x) cnt(%d)", + bcs->channel, z1, z2, rcnt); + debugl1(cs, tmp); + } + cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel); + idx = 0; + while ((idx < rcnt) && WaitNoBusy(cs)) { + cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip); + idx++; + } + if (f1 != f2) { + WaitNoBusy(cs); + cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); + } + cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel); + WaitNoBusy(cs); + f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel); + WaitNoBusy(cs); + f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel)); + z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel)); + } + restore_flags(flags); + return; +} + + +static struct sk_buff +* +hfc_empty_fifo(struct BCState *bcs, int count) +{ + u_char *ptr; + struct sk_buff *skb; + struct IsdnCardState *cs = bcs->cs; + int idx; + int chksum; + u_char stat, cip; + char tmp[64]; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hfc_empty_fifo"); + idx = 0; + if (count > HSCX_BUFMAX + 3) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfc_empty_fifo: incoming packet too large"); + cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel); + while ((idx++ < count) && WaitNoBusy(cs)) + cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip); + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); + return (NULL); + } + if (count < 4) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfc_empty_fifo: incoming packet too small"); + cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel); + while ((idx++ < count) && WaitNoBusy(cs)) + cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip); + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); + return (NULL); + } + if (!(skb = dev_alloc_skb(count - 3))) + printk(KERN_WARNING "HFC: receive out of memory\n"); + else { + ptr = skb_put(skb, count - 3); + idx = 0; + cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel); + while ((idx < count - 3) && WaitNoBusy(cs)) { + *ptr++ = cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip); + idx++; + } + if (idx != count - 3) { + debugl1(cs, "RFIFO BUSY error"); + printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel); + dev_kfree_skb(skb); + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); + return (NULL); + } + WaitNoBusy(cs); + chksum = (cs->BC_Read_Reg(cs, HFC_DATA, cip) << 8); + WaitNoBusy(cs); + chksum += cs->BC_Read_Reg(cs, HFC_DATA, cip); + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, cip); + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc_empty_fifo %d chksum %x stat %x", + bcs->channel, chksum, stat); + debugl1(cs, tmp); + } + if (stat) { + debugl1(cs, "FIFO CRC error"); + dev_kfree_skb(skb); + skb = NULL; + } + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); + } + return (skb); +} + +static void +hfc_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + long flags; + int idx, fcnt; + int count; + u_char cip; + char tmp[64]; + + if (!bcs->hw.hfc.tx_skb) + return; + if (bcs->hw.hfc.tx_skb->len <= 0) + return; + + save_flags(flags); + cli(); + cip = HFC_CIP | HFC_F1 | HFC_SEND | HFC_CHANNEL(bcs->channel); + if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) { + cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip); + WaitForBusy(cs); + } + WaitNoBusy(cs); + bcs->hw.hfc.f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + cip = HFC_CIP | HFC_F2 | HFC_SEND | HFC_CHANNEL(bcs->channel); + WaitNoBusy(cs); + bcs->hw.hfc.f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(bcs, HFC_Z1 | HFC_SEND | HFC_CHANNEL(bcs->channel)); + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)", + bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2, + bcs->hw.hfc.send[bcs->hw.hfc.f1]); + debugl1(cs, tmp); + } + fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2; + if (fcnt < 0) + fcnt += 32; + if (fcnt > 30) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo more as 30 frames"); + restore_flags(flags); + return; + } + count = GetFreeFifoBytes(bcs); + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc_fill_fifo %d count(%d/%d)", + bcs->channel, bcs->hw.hfc.tx_skb->len, + count); + debugl1(cs, tmp); + } + if (count < bcs->hw.hfc.tx_skb->len) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo no fifo mem"); + restore_flags(flags); + return; + } + cip = HFC_CIP | HFC_FIFO_IN | HFC_SEND | HFC_CHANNEL(bcs->channel); + idx = 0; + while ((idx < bcs->hw.hfc.tx_skb->len) && WaitNoBusy(cs)) + cs->BC_Write_Reg(cs, HFC_DATA_NODEB, cip, bcs->hw.hfc.tx_skb->data[idx++]); + if (idx != bcs->hw.hfc.tx_skb->len) { + debugl1(cs, "FIFO Send BUSY error"); + printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel); + } else { + count = bcs->hw.hfc.tx_skb->len; + bcs->tx_cnt -= count; + if (PACKET_NOACK == bcs->hw.hfc.tx_skb->pkt_type) + count = -1; + dev_kfree_skb(bcs->hw.hfc.tx_skb); + bcs->hw.hfc.tx_skb = NULL; + WaitForBusy(cs); + WaitNoBusy(cs); + cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F1_INC | HFC_SEND | HFC_CHANNEL(bcs->channel)); + if (bcs->st->lli.l1writewakeup && (count >= 0)) + bcs->st->lli.l1writewakeup(bcs->st, count); + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + restore_flags(flags); + return; +} + +void +main_irq_hfc(struct BCState *bcs) +{ + long flags; + struct IsdnCardState *cs = bcs->cs; + int z1, z2, rcnt; + u_char f1, f2, cip; + int receive, transmit, count = 5; + struct sk_buff *skb; + char tmp[64]; + + save_flags(flags); + Begin: + cli(); + count--; + cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel); + if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) { + cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip); + WaitForBusy(cs); + } + WaitNoBusy(cs); + f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel); + WaitNoBusy(cs); + f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + if (f1 != f2) { + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc rec %d f1(%d) f2(%d)", + bcs->channel, f1, f2); + debugl1(cs, tmp); + } + WaitForBusy(cs); + z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel)); + z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel)); + rcnt = z1 - z2; + if (rcnt < 0) + rcnt += cs->hw.hfc.fifosize; + rcnt++; + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc rec %d z1(%x) z2(%x) cnt(%d)", + bcs->channel, z1, z2, rcnt); + debugl1(cs, tmp); + } +/* sti(); */ + if ((skb = hfc_empty_fifo(bcs, rcnt))) { + skb_queue_tail(&bcs->rqueue, skb); + hfc_sched_event(bcs, B_RCVBUFREADY); + } + receive = 1; + } else + receive = 0; + restore_flags(flags); + udelay(1); + cli(); + if (bcs->hw.hfc.tx_skb) { + transmit = 1; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + hfc_fill_fifo(bcs); + if (test_bit(BC_FLG_BUSY, &bcs->Flag)) + transmit = 0; + } else { + if ((bcs->hw.hfc.tx_skb = skb_dequeue(&bcs->squeue))) { + transmit = 1; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + hfc_fill_fifo(bcs); + if (test_bit(BC_FLG_BUSY, &bcs->Flag)) + transmit = 0; + } else { + transmit = 0; + hfc_sched_event(bcs, B_XMTBUFREADY); + } + } + restore_flags(flags); + if ((receive || transmit) && count) + goto Begin; + return; +} + +void +mode_hfc(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + + if (cs->debug & L1_DEB_HSCX) { + char tmp[40]; + sprintf(tmp, "HFC 2BS0 mode %d bchan %d/%d", + mode, bc, bcs->channel); + debugl1(cs, tmp); + } + bcs->mode = mode; + + switch (mode) { + case (L1_MODE_NULL): + if (bc) + cs->hw.hfc.isac_spcr &= ~0x03; + else + cs->hw.hfc.isac_spcr &= ~0x0c; + break; + case (L1_MODE_TRANS): + if (bc) { + cs->hw.hfc.ctmt |= 1; + cs->hw.hfc.isac_spcr &= ~0x03; + cs->hw.hfc.isac_spcr |= 0x02; + } else { + cs->hw.hfc.ctmt |= 2; + cs->hw.hfc.isac_spcr &= ~0x0c; + cs->hw.hfc.isac_spcr |= 0x08; + } + break; + case (L1_MODE_HDLC): + if (bc) { + cs->hw.hfc.ctmt &= ~1; + cs->hw.hfc.isac_spcr &= ~0x03; + cs->hw.hfc.isac_spcr |= 0x02; + } else { + cs->hw.hfc.ctmt &= ~2; + cs->hw.hfc.isac_spcr &= ~0x0c; + cs->hw.hfc.isac_spcr |= 0x08; + } + break; + } + cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt); + cs->writeisac(cs, ISAC_SPCR, cs->hw.hfc.isac_spcr); + if (mode) + hfc_clear_fifo(bcs); +} + +static void +hfc_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA_REQ): + save_flags(flags); + cli(); + if (st->l1.bcs->hw.hfc.tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->hw.hfc.tx_skb = skb; + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + } + break; + case (PH_PULL_IND): + if (st->l1.bcs->hw.hfc.tx_skb) { + printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n"); + break; + } + save_flags(flags); + cli(); + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->hw.hfc.tx_skb = skb; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + break; + case (PH_PULL_REQ): + if (!st->l1.bcs->hw.hfc.tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL_CNF, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } +} + +void +close_hfcstate(struct BCState *bcs) +{ + struct sk_buff *skb; + + mode_hfc(bcs, 0, 0); + if (test_bit(BC_FLG_INIT, &bcs->Flag)) { + while ((skb = skb_dequeue(&bcs->rqueue))) { + dev_kfree_skb(skb); + } + while ((skb = skb_dequeue(&bcs->squeue))) { + dev_kfree_skb(skb); + } + if (bcs->hw.hfc.tx_skb) { + dev_kfree_skb(bcs->hw.hfc.tx_skb); + bcs->hw.hfc.tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } + test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); +} + +static int +open_hfcstate(struct IsdnCardState *cs, + int bc) +{ + struct BCState *bcs = cs->bcs + bc; + + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->hw.hfc.tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->tx_cnt = 0; + return (0); +} + +static void +hfc_manl1(struct PStack *st, int pr, + void *arg) +{ + switch (pr) { + case (PH_ACTIVATE_REQ): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + mode_hfc(st->l1.bcs, st->l1.mode, st->l1.bc); + st->l1.l1man(st, PH_ACTIVATE_CNF, NULL); + break; + case (PH_DEACTIVATE_REQ): + if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) + mode_hfc(st->l1.bcs, 0, 0); + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + break; + } +} + +int +setstack_hfc(struct PStack *st, struct BCState *bcs) +{ + if (open_hfcstate(st->l1.hardware, bcs->channel)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = hfc_l2l1; + st->ma.manl1 = hfc_manl1; + setstack_manager(st); + bcs->st = st; + return (0); +} + +__initfunc(void +init_send(struct BCState *bcs)) +{ + int i; + + if (!(bcs->hw.hfc.send = kmalloc(32 * sizeof(unsigned int), GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for hfc.send\n"); + return; + } + for (i = 0; i < 32; i++) + bcs->hw.hfc.send[i] = 0x1fff; +} + +__initfunc(void +inithfc(struct IsdnCardState *cs)) +{ + init_send(&cs->bcs[0]); + init_send(&cs->bcs[1]); + cs->BC_Send_Data = &hfc_fill_fifo; + cs->bcs[0].BC_SetStack = setstack_hfc; + cs->bcs[1].BC_SetStack = setstack_hfc; + cs->bcs[0].BC_Close = close_hfcstate; + cs->bcs[1].BC_Close = close_hfcstate; + mode_hfc(cs->bcs, 0, 0); + mode_hfc(cs->bcs + 1, 0, 0); +} + +void +releasehfc(struct IsdnCardState *cs) +{ + if (cs->bcs[0].hw.hfc.send) { + kfree(cs->bcs[0].hw.hfc.send); + cs->bcs[0].hw.hfc.send = NULL; + } + if (cs->bcs[1].hw.hfc.send) { + kfree(cs->bcs[1].hw.hfc.send); + cs->bcs[1].hw.hfc.send = NULL; + } +} diff --git a/drivers/isdn/hisax/hfc_2bs0.h b/drivers/isdn/hisax/hfc_2bs0.h new file mode 100644 index 000000000000..cce8e4a35076 --- /dev/null +++ b/drivers/isdn/hisax/hfc_2bs0.h @@ -0,0 +1,62 @@ +/* $Id: hfc_2bs0.h,v 1.1 1997/09/11 17:31:34 keil Exp $ + + * specific defines for CCD's HFC 2BS0 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hfc_2bs0.h,v $ + * Revision 1.1 1997/09/11 17:31:34 keil + * Common part for HFC 2BS0 based cards + * + * + */ + +#define HFC_CTMT 0xe0 +#define HFC_CIRM 0xc0 +#define HFC_CIP 0x80 +#define HFC_Z1 0x00 +#define HFC_Z2 0x08 +#define HFC_Z_LOW 0x00 +#define HFC_Z_HIGH 0x04 +#define HFC_F1_INC 0x28 +#define HFC_FIFO_IN 0x2c +#define HFC_F1 0x30 +#define HFC_F2 0x34 +#define HFC_F2_INC 0x38 +#define HFC_FIFO_OUT 0x3c +#define HFC_B1 0x00 +#define HFC_B2 0x02 +#define HFC_REC 0x01 +#define HFC_SEND 0x00 +#define HFC_CHANNEL(ch) (ch ? HFC_B2 : HFC_B1) + +#define HFC_STATUS 0 +#define HFC_DATA 1 +#define HFC_DATA_NODEB 2 + +/* Status (READ) */ +#define HFC_BUSY 0x01 +#define HFC_TIMINT 0x02 +#define HFC_EXTINT 0x04 + +/* CTMT (Write) */ +#define HFC_CLTIMER 0x10 +#define HFC_TIM50MS 0x08 +#define HFC_TIMIRQE 0x04 +#define HFC_TRANSB2 0x02 +#define HFC_TRANSB1 0x01 + +/* CIRM (Write) */ +#define HFC_RESET 0x08 +#define HFC_MEM8K 0x10 +#define HFC_INTA 0x01 +#define HFC_INTB 0x02 +#define HFC_INTC 0x03 +#define HFC_INTD 0x04 +#define HFC_INTE 0x05 +#define HFC_INTF 0x06 + +extern void main_irq_hfc(struct BCState *bcs); +extern void inithfc(struct IsdnCardState *cs); +extern void releasehfc(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h index efe7d53290df..f5b15e2bf59e 100644 --- a/drivers/isdn/hisax/hisax.h +++ b/drivers/isdn/hisax/hisax.h @@ -1,52 +1,59 @@ -/* $Id: hisax.h,v 1.13 1997/04/06 22:54:12 keil Exp $ +/* $Id: hisax.h,v 2.14 1998/02/11 17:28:04 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 2.14 1998/02/11 17:28:04 keil + * Niccy PnP/PCI support * - * Revision 1.12 1997/03/23 21:45:45 keil - * Add support for ELSA PCMCIA + * Revision 2.13 1998/02/09 18:46:02 keil + * Support for Sedlbauer PCMCIA (Marcus Niemann) * - * Revision 1.11 1997/02/11 01:36:02 keil - * New Param structure + * Revision 2.12 1998/02/03 23:31:30 keil + * add AMD7930 support * - * Revision 1.10 1997/02/09 00:23:52 keil - * new interface handling, one interface per card + * Revision 2.11 1998/02/02 13:33:00 keil + * New card support * - * Revision 1.9 1997/01/27 23:18:44 keil - * prototype for releasestack_isdnl3 + * Revision 2.10 1997/11/08 21:37:52 keil + * new l1 init;new Compaq card * - * Revision 1.8 1997/01/27 16:02:37 keil - * new cards, callc timers, HZDELAY macro, HiSax_getrev prototype + * Revision 2.9 1997/11/06 17:09:09 keil + * New 2.1 init code * - * Revision 1.7 1997/01/21 22:22:14 keil - * changes for 2.0; Elsa Quickstep support + * Revision 2.8 1997/10/29 19:04:13 keil + * new L1; changes for 2.1 * - * Revision 1.6 1997/01/04 13:48:28 keil - * primitiv for MDL_REMOVE added + * Revision 2.7 1997/10/10 20:56:47 fritz + * New HL interface. * - * Revision 1.5 1996/12/08 19:49:19 keil - * Monitor channel support + * Revision 2.6 1997/09/11 17:25:51 keil + * Add new cards * - * Revision 1.4 1996/11/18 15:35:39 keil - * some changes for ELSA cards + * Revision 2.5 1997/08/03 14:36:31 keil + * Implement RESTART procedure * - * Revision 1.3 1996/11/05 19:37:23 keil - * using config.h + * Revision 2.4 1997/07/31 19:25:20 keil + * PTP_DATA_LINK support * - * Revision 1.2 1996/10/27 22:21:52 keil - * CallFlags for broadcast messages + * Revision 2.3 1997/07/31 11:50:17 keil + * ONE TEI and FIXED TEI handling * - * Revision 1.1 1996/10/13 20:03:46 keil - * Initial revision + * Revision 2.2 1997/07/30 17:13:02 keil + * more changes for 'One TEI per card' * + * Revision 2.1 1997/07/27 21:45:13 keil + * new main structures * + * Revision 2.0 1997/06/26 11:06:27 keil + * New card and L1 interface. + * Eicon.Diehl Diva and Dynalink IS64PH support + * + * old changes removed KKe * */ -#include #include +#include #include #include #include @@ -64,140 +71,136 @@ #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; -}; +#include + +#define PH_ACTIVATE_REQ 0x0010 +#define PH_ACTIVATE_CNF 0x0011 +#define PH_ACTIVATE_IND 0x0012 +#define PH_DEACTIVATE_REQ 0x0020 +#define PH_DEACTIVATE_CNF 0x0021 +#define PH_DEACTIVATE_IND 0x0022 +#define PH_DEACT_REQ 0x0024 +#define PH_DEACT_CNF 0x0025 +#define PH_DEACT_IND 0x0026 +#define PH_DEACT_ACK 0x0027 +#define PH_TESTLOOP_REQ 0x0030 +#define PH_PAUSE_CNF 0x0035 +#define PH_PAUSE_IND 0x0036 +#define PH_PULL_REQ 0x0038 +#define PH_PULL_CNF 0x0039 +#define PH_PULL_IND 0x003A +#define PH_DATA_REQ 0x0040 +#define PH_DATA_IND 0x0042 + +#define PH_INFO3_REQ 0x0008 +#define PH_INFO2_IND 0x000A +#define PH_ENABLE_REQ 0x0004 +#define PH_RSYNC_IND 0x0006 +#define PH_RESET_REQ 0x0000 +#define PH_RESET_IND 0x0002 +#define PH_POWERUP_CNF 0x0003 +#define PH_ACTIV_REQ 0x000C +#define PH_I4_P8_IND 0x000D +#define PH_I4_P10_IND 0x000F + +#define MDL_ASSIGN_REQ 0x0050 +#define MDL_ASSIGN_IND 0x0052 +#define MDL_REMOVE_REQ 0x0054 +#define MDL_ERROR_REQ 0x0058 +#define MDL_ERROR_IND 0x005A +#define CARD_AUX_IND 0x005E + +#define DL_UNIT_DATA 6 +#define CC_ESTABLISH 7 +#define DL_ESTABLISH 8 +#define DL_DATA 9 + +#define CC_CONNECT 15 +#define DL_RELEASE 20 +#define DL_FLUSH 21 + +#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_INFO_SETUP 42 +#define MDL_INFO_CONN 43 +#define MDL_INFO_REL 44 +#define MDL_NOTEIPROC 46 + +#define LC_ESTABLISH 47 +#define LC_RELEASE 48 + +#define CC_INFO_CHARGE 52 + +#define CC_MORE_INFO 53 +#define CC_IGNORE 54 +#define CC_RESTART 55 + + +#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 + +#define CARD_RESET 0x1001 +#define CARD_SETIRQ 0x1002 +#define CARD_INIT 0x1003 +#define CARD_RELEASE 0x1004 +#define CARD_TEST 0x1005 #ifdef __KERNEL__ -#undef DEBUG_MAGIC - -#define MAX_DFRAME_LEN 3072 +#define MAX_DFRAME_LEN 260 #define HSCX_BUFMAX 4096 #define MAX_DATA_SIZE (HSCX_BUFMAX - 4) -#define MAX_DATA_MEM (HSCX_BUFMAX * 2) +#define MAX_DATA_MEM (HSCX_BUFMAX + 64) +#define RAW_BUFMAX (((HSCX_BUFMAX*6)/5) + 5) #define MAX_HEADER_LEN 4 #define MAX_WINDOW 8 +#define MAX_MON_FRAME 32 + +/* #define I4L_IRQ_FLAG SA_INTERRUPT */ +#define I4L_IRQ_FLAG 0 /* * Statemachine */ + struct Fsm { int *jumpmatrix; int state_count, event_count; @@ -226,73 +229,107 @@ struct FsmTimer { }; struct L3Timer { - struct PStack *st; + struct l3_process *pc; struct timer_list tl; int event; }; +#define FLG_L1_ACTIVATING 1 +#define FLG_L1_ACTIVATED 2 +#define FLG_L1_DEACTTIMER 3 +#define FLG_L1_ACTTIMER 4 +#define FLG_L1_T3RUN 5 +#define FLG_L1_PULL_REQ 6 + struct Layer1 { void *hardware; - int hscx; + struct BCState *bcs; struct PStack **stlistp; - int act_state; + int Flags; + struct FsmInst l1m; + struct FsmTimer timer; void (*l1l2) (struct PStack *, int, void *); void (*l1man) (struct PStack *, int, void *); - int hscxmode, hscxchannel, requestpull; + void (*l1tei) (struct PStack *, int, void *); + int mode, bc; }; +#define GROUP_TEI 127 +#define TEI_SAPI 63 +#define CTRL_SAPI 0 +#define PACKET_NOACK 250 + +/* Layer2 Flags */ + +#define FLG_LAPB 0 +#define FLG_LAPD 1 +#define FLG_ORIG 2 +#define FLG_MOD128 3 +#define FLG_PEND_REL 4 +#define FLG_L3_INIT 5 +#define FLG_T200_RUN 6 +#define FLG_ACK_PEND 7 +#define FLG_REJEXC 8 +#define FLG_OWN_BUSY 9 +#define FLG_PEER_BUSY 10 +#define FLG_DCHAN_BUSY 11 + struct Layer2 { - int sap, tei, ces; - int extended, laptype; - int uihsize, ihsize; + int tei; + int tei_wanted; + int sap; + int maxlen; + unsigned int flag; int vs, va, vr; - struct sk_buff_head i_queue; - int window, orig; - int rejexp; - int debug; - struct sk_buff *windowar[MAX_WINDOW]; + int rc; + int window; int sow; - struct FsmInst l2m; + struct sk_buff *windowar[MAX_WINDOW]; + struct sk_buff_head i_queue; + struct sk_buff_head ui_queue; void (*l2l1) (struct PStack *, int, void *); - void (*l2l1discardq) (struct PStack *, int, void *, int); void (*l2man) (struct PStack *, int, void *); void (*l2l3) (struct PStack *, int, void *); void (*l2tei) (struct PStack *, int, void *); - struct FsmTimer t200_timer, t203_timer; - int t200, n200, t203; - int rc, t200_running; + struct FsmInst l2m; + struct FsmTimer t200, t203; + int T200, N200, T203; + int debug; char debug_id[32]; }; struct Layer3 { - void (*l3l4) (struct PStack *, int, void *); + void (*l3l4) (struct l3_process *, int, void *); void (*l3l2) (struct PStack *, int, void *); - int state, callref; - struct L3Timer timer; - int t303, t304, t305, t308, t310, t313, t318, t319; - int n_t303; + struct l3_process *proc; + struct l3_process *global; + int N303; int debug; - int channr; }; -struct Layer4 { +struct LLInterface { void (*l4l3) (struct PStack *, int, void *); void *userdata; - void (*l1writewakeup) (struct PStack *); - void (*l2writewakeup) (struct PStack *); + void (*l1writewakeup) (struct PStack *, int); + void (*l2writewakeup) (struct PStack *, int); }; + struct Management { + int ri; + struct FsmInst tei_m; + struct FsmTimer t202; + int T202, N202, debug; + void (*layer) (struct PStack *, int, void *); 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 @@ -300,39 +337,114 @@ struct Param { int spv; /* SPV Flag */ }; + struct PStack { struct PStack *next; struct Layer1 l1; struct Layer2 l2; struct Layer3 l3; - struct Layer4 l4; + struct LLInterface lli; struct Management ma; - struct Param *pa; int protocol; /* EDSS1 or 1TR6 */ }; -struct HscxState { - int inuse, init, active; - struct IsdnCardState *sp; - int hscx, mode; - u_char *rcvbuf; /* B-Channel receive Buffer */ - int rcvidx; /* B-Channel receive Buffer Index */ - struct sk_buff *tx_skb; /* B-Channel transmit Buffer */ +struct l3_process { + int callref; + int state; + struct L3Timer timer; + int N303; + int debug; + struct Param para; + struct Channel *chan; + struct PStack *st; + struct l3_process *next; +}; + +struct hscx_hw { + int rcvidx; + int count; /* Current skb sent count */ + u_char *rcvbuf; /* B-Channel receive Buffer */ + struct sk_buff *tx_skb; /* B-Channel transmit Buffer */ +}; + +struct hfcB_hw { + unsigned int *send; + int f1; + int f2; + struct sk_buff *tx_skb; /* B-Channel transmit Buffer */ +}; + +struct tiger_hw { + struct sk_buff *tx_skb; /* B-Channel transmit Buffer */ + u_int *send; + u_int *s_irq; + u_int *s_end; + u_int *sendp; + u_int *rec; + int free; + u_char *rcvbuf; + u_char *sendbuf; + u_char *sp; + int sendcnt; + u_int s_tot; + u_int r_bitcnt; + u_int r_tot; + u_int r_err; + u_int r_fcs; + u_char r_state; + u_char r_one; + u_char r_val; + u_char s_state; +}; + +struct amd7930_hw { + u_char *tx_buff; + u_char *rv_buff; + int rv_buff_in; + int rv_buff_out; + struct sk_buff *rv_skb; + struct hdlc_state *hdlc_state; + struct tq_struct tq_rcv; + struct tq_struct tq_xmt; + struct sk_buff *tx_skb; /* B-Channel transmit Buffer */ +}; + +#define BC_FLG_INIT 1 +#define BC_FLG_ACTIV 2 +#define BC_FLG_BUSY 3 +#define BC_FLG_NOFRAME 4 +#define BC_FLG_HALF 5 +#define BC_FLG_EMPTY 6 + +#define L1_MODE_NULL 0 +#define L1_MODE_TRANS 1 +#define L1_MODE_HDLC 2 + +struct BCState { + int channel; + int mode; + int Flag; + struct IsdnCardState *cs; int tx_cnt; /* B-Channel transmit counter */ - int count; /* Current skb sent count */ struct sk_buff_head rqueue; /* B-Channel receive Queue */ - struct sk_buff_head squeue; /* B-Channel receive Queue */ + struct sk_buff_head squeue; /* B-Channel send Queue */ struct PStack *st; struct tq_struct tqueue; int event; -#ifdef DEBUG_MAGIC - int magic; /* 301270 */ -#endif + int (*BC_SetStack) (struct PStack *, struct BCState *); + void (*BC_Close) (struct BCState *); + union { + struct hscx_hw hscx; + struct hfcB_hw hfc; + struct tiger_hw tiger; + struct amd7930_hw amd7930; + } hw; }; struct LcFsm { - struct FsmInst lcfi; int type; + int delay; + struct FsmInst lcfi; struct Channel *ch; void (*lccall) (struct LcFsm *, int, void *); struct PStack *st; @@ -343,52 +455,209 @@ struct LcFsm { }; struct Channel { - struct PStack ds, is; - struct IsdnCardState *sp; - int hscx; + struct PStack *b_st, *d_st; + struct IsdnCardState *cs; + struct BCState *bcs; int chan; int incoming; struct FsmInst fi; - struct LcFsm lc_d, lc_b; - struct Param para; + struct LcFsm *lc_d; + struct LcFsm *lc_b; 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; + struct l3_process *proc; + setup_parm setup; /* from isdnif.h numbers and Serviceindicator */ int Flags; /* for remembering action done in l4 */ int leased; }; -struct IsdnCardState { -#ifdef DEBUG_MAGIC - int magic; -#endif - unsigned char typ; - unsigned char subtyp; - int protocol; - unsigned int irq; +struct elsa_hw { + unsigned int base; + unsigned int cfg; + unsigned int ctrl; + unsigned int ale; + unsigned int isac; + unsigned int itac; + unsigned int hscx; + unsigned int trig; + unsigned int timer; + unsigned int counter; + unsigned int status; + struct timer_list tl; + u_char ctrl_reg; +}; + +struct teles3_hw { + unsigned int cfg_reg; + unsigned int isac; + unsigned int hscx[2]; + unsigned int isacfifo; + unsigned int hscxfifo[2]; +}; + +struct teles0_hw { unsigned int cfg_reg; unsigned int membase; +}; + +struct avm_hw { + unsigned int cfg_reg; unsigned int isac; unsigned int hscx[2]; + unsigned int isacfifo; + unsigned int hscxfifo[2]; unsigned int counter; +}; + +struct ix1_hw { + unsigned int cfg_reg; + unsigned int isac_ale; + unsigned int isac; + unsigned int hscx_ale; + unsigned int hscx; +}; + +struct diva_hw { + unsigned int cfg_reg; + unsigned int ctrl; + unsigned int isac_adr; + unsigned int isac; + unsigned int hscx_adr; + unsigned int hscx; + unsigned int status; + struct timer_list tl; + u_char ctrl_reg; +}; + +struct asus_hw { + unsigned int cfg_reg; + unsigned int adr; + unsigned int isac; + unsigned int hscx; + unsigned int u7; + unsigned int pots; +}; + + +struct hfc_hw { + unsigned int addr; + unsigned int fifosize; + unsigned char cirm; + unsigned char ctmt; + unsigned char cip; + u_char isac_spcr; + struct timer_list timer; +}; + +struct sedl_hw { + unsigned int cfg_reg; + unsigned int adr; + unsigned int isac; + unsigned int hscx; + unsigned int reset_on; + unsigned int reset_off; +}; + +struct spt_hw { + unsigned int cfg_reg; + unsigned int isac; + unsigned int hscx[2]; + unsigned char res_irq; +}; + +struct mic_hw { + unsigned int cfg_reg; + unsigned int adr; + unsigned int isac; + unsigned int hscx; +}; + +struct njet_hw { + unsigned int base; + unsigned int isac; + unsigned int auxa; + unsigned char auxd; + unsigned char dmactrl; + unsigned char ctrl_reg; + unsigned char irqmask0; + unsigned char irqstat0; + unsigned char last_is0; +}; + +struct hfcD_hw { + unsigned int addr; + unsigned int bfifosize; + unsigned int dfifosize; + unsigned char cirm; + unsigned char ctmt; + unsigned char cip; + unsigned char conn; + unsigned char mst_m; + unsigned char int_m1; + unsigned char int_m2; + unsigned char int_s1; + unsigned char sctrl; + unsigned char stat; + unsigned char fifo; + unsigned char f1; + unsigned char f2; + unsigned int *send; + struct timer_list timer; +}; + +#define HW_IOM1 0 +#define HW_IPAC 1 +#define FLG_TWO_DCHAN 4 +#define FLG_L1_DBUSY 5 +#define FLG_DBUSY_TIMER 6 +#define FLG_LOCK_ATOMIC 7 +#define HW_MON0_RX_END 8 +#define HW_MON1_RX_END 9 +#define HW_MON0_TX_END 10 +#define HW_MON1_TX_END 11 + +struct IsdnCardState { + unsigned char typ; + unsigned char subtyp; + int protocol; + unsigned int irq; + int HW_Flags; + int *busy_flag; + union { + struct elsa_hw elsa; + struct teles0_hw teles0; + struct teles3_hw teles3; + struct avm_hw avm; + struct ix1_hw ix1; + struct diva_hw diva; + struct asus_hw asus; + struct hfc_hw hfc; + struct sedl_hw sedl; + struct spt_hw spt; + struct mic_hw mic; + struct njet_hw njet; + struct hfcD_hw hfcD; + struct ix1_hw niccy; + } hw; int myid; isdn_if iif; u_char *status_buf; u_char *status_read; u_char *status_write; u_char *status_end; - void (*ph_command) (struct IsdnCardState *, unsigned int); - void (*modehscx) (struct HscxState *, int, int); - void (*hscx_fill_fifo) (struct HscxState *); - void (*isac_fill_fifo) (struct IsdnCardState *); + u_char (*readisac) (struct IsdnCardState *, u_char); + void (*writeisac) (struct IsdnCardState *, u_char, u_char); + void (*readisacfifo) (struct IsdnCardState *, u_char *, int); + void (*writeisacfifo) (struct IsdnCardState *, u_char *, int); + u_char (*BC_Read_Reg) (struct IsdnCardState *, int, u_char); + void (*BC_Write_Reg) (struct IsdnCardState *, int, u_char, u_char); + void (*BC_Send_Data) (struct BCState *); + int (*cardmsg) (struct IsdnCardState *, int, void *); + void (*l1cmd) (struct IsdnCardState *, int, void *); struct Channel channel[2]; + struct BCState bcs[2]; struct PStack *stlist; u_char *rcvbuf; int rcvidx; @@ -396,16 +665,20 @@ struct IsdnCardState { int tx_cnt; int event; struct tq_struct tqueue; - int ph_active; + struct timer_list dbusytimer; struct sk_buff_head rq, sq; /* D-channel queues */ - int cardnr; int ph_state; - struct PStack *teistack; - struct HscxState hs[2]; + int cardnr; int dlogflag; char *dlogspace; int debug; - unsigned int CallFlags; + u_char *mon_tx; + u_char *mon_rx; + int mon_txp; + int mon_txc; + int mon_rxp; + u_char mocr; + void (*setstack_d) (struct PStack *, struct IsdnCardState *); }; #define MON0_RX 1 @@ -419,99 +692,246 @@ struct IsdnCardState { #define ISDN_CTYPE_PNP 4 #define ISDN_CTYPE_A1 5 #define ISDN_CTYPE_ELSA 6 -#define ISDN_CTYPE_ELSA_QS1000 7 +#define ISDN_CTYPE_ELSA_PNP 7 #define ISDN_CTYPE_TELESPCMCIA 8 #define ISDN_CTYPE_IX1MICROR2 9 +#define ISDN_CTYPE_ELSA_PCMCIA 10 +#define ISDN_CTYPE_DIEHLDIVA 11 +#define ISDN_CTYPE_ASUSCOM 12 +#define ISDN_CTYPE_TELEINT 13 +#define ISDN_CTYPE_TELES3C 14 +#define ISDN_CTYPE_SEDLBAUER 15 +#define ISDN_CTYPE_SPORTSTER 16 +#define ISDN_CTYPE_MIC 17 +#define ISDN_CTYPE_ELSA_PCI 18 +#define ISDN_CTYPE_COMPAQ_ISA 19 +#define ISDN_CTYPE_NETJET 20 +#define ISDN_CTYPE_TELESPCI 21 +#define ISDN_CTYPE_SEDLBAUER_PCMCIA 22 +#define ISDN_CTYPE_AMD7930 23 +#define ISDN_CTYPE_NICCY 24 + +#define ISDN_CTYPE_COUNT 24 + +#ifdef ISDN_CHIP_ISAC +#undef ISDN_CHIP_ISAC +#endif -#define ISDN_CTYPE_COUNT 9 +#define HISAX_INITFUNC(__arginit) __initfunc(__arginit) +#define HISAX_INITDATA __initdata #ifdef CONFIG_HISAX_16_0 #define CARD_TELES0 (1<< ISDN_CTYPE_16_0) | (1<< ISDN_CTYPE_8_0) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else #define CARD_TELES0 0 #endif #ifdef CONFIG_HISAX_16_3 #define CARD_TELES3 (1<< ISDN_CTYPE_16_3) | (1<< ISDN_CTYPE_PNP) | \ - (1<< ISDN_CTYPE_TELESPCMCIA) + (1<< ISDN_CTYPE_TELESPCMCIA) | (1<< ISDN_CTYPE_COMPAQ_ISA) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else #define CARD_TELES3 0 #endif #ifdef CONFIG_HISAX_AVM_A1 #define CARD_AVM_A1 (1<< ISDN_CTYPE_A1) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else #define CARD_AVM_A1 0 #endif -#ifdef CONFIG_HISAX_ELSA_PCC -#define CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_QS1000) +#ifdef CONFIG_HISAX_ELSA +#define CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_PNP) | \ + (1<< ISDN_CTYPE_ELSA_PCMCIA) | (1<< ISDN_CTYPE_ELSA_PCI) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#undef HISAX_INITFUNC +#define HISAX_INITFUNC(__arginit) __arginit +#undef HISAX_INITDATA +#define HISAX_INITDATA #else #define CARD_ELSA 0 #endif -#ifdef CONFIG_HISAX_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) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else #define CARD_IX1MICROR2 0 #endif +#ifdef CONFIG_HISAX_DIEHLDIVA +#define CARD_DIEHLDIVA (1 << ISDN_CTYPE_DIEHLDIVA) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_DIEHLDIVA 0 +#endif + +#ifdef CONFIG_HISAX_ASUSCOM +#define CARD_ASUSCOM (1 << ISDN_CTYPE_ASUSCOM) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_ASUSCOM 0 +#endif + +#ifdef CONFIG_HISAX_TELEINT +#define CARD_TELEINT (1 << ISDN_CTYPE_TELEINT) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_TELEINT 0 +#endif + +#ifdef CONFIG_HISAX_SEDLBAUER +#define CARD_SEDLBAUER (1 << ISDN_CTYPE_SEDLBAUER) | (1 << ISDN_CTYPE_SEDLBAUER_PCMCIA) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_SEDLBAUER 0 +#endif + +#ifdef CONFIG_HISAX_SPORTSTER +#define CARD_SPORTSTER (1 << ISDN_CTYPE_SPORTSTER) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_SPORTSTER 0 +#endif + +#ifdef CONFIG_HISAX_MIC +#define CARD_MIC (1 << ISDN_CTYPE_MIC) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_MIC 0 +#endif + +#ifdef CONFIG_HISAX_NETJET +#define CARD_NETJET (1 << ISDN_CTYPE_NETJET) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_NETJET 0 +#endif + +#ifdef CONFIG_HISAX_TELES3C +#define CARD_TELES3C (1<< ISDN_CTYPE_TELES3C) +#else +#define CARD_TELES3C 0 +#endif + +#ifdef CONFIG_HISAX_AMD7930 +#define CARD_AMD7930 (1 << ISDN_CTYPE_AMD7930) +#else +#define CARD_AMD7930 0 +#endif + +#ifdef CONFIG_HISAX_NICCY +#define CARD_NICCY (1 << ISDN_CTYPE_NICCY) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_NICCY 0 +#endif + + #define SUPORTED_CARDS (CARD_TELES0 | CARD_TELES3 | CARD_AVM_A1 | CARD_ELSA \ - | CARD_IX1MICROR2) + | CARD_IX1MICROR2 | CARD_DIEHLDIVA | CARD_ASUSCOM \ + | CARD_TELEINT | CARD_SEDLBAUER | CARD_SPORTSTER \ + | CARD_MIC | CARD_NETJET | CARD_TELES3C | CARD_AMD7930 \ + | CARD_NICCY) + +#define TEI_PER_CARD 0 + +#ifdef CONFIG_HISAX_1TR6 +#undef TEI_PER_CARD +#define TEI_PER_CARD 1 +#endif + +#ifdef CONFIG_HISAX_EURO +#undef TEI_PER_CARD +#define TEI_PER_CARD 1 +#define HISAX_EURO_SENDCOMPLETE 1 +#ifdef CONFIG_HISAX_ML +#undef HISAX_EURO_SENDCOMPLETE +#endif +#undef HISAX_DE_AOC +#ifdef CONFIG_DE_AOC +#define HISAX_DE_AOC 1 +#endif +#endif + +#if TEI_PER_CARD +#undef TEI_FIXED +#endif + +#undef PTP_DATA_LINK + +#ifdef PTP_DATA_LINK +#undef TEI_FIXED +#define TEI_FIXED 0 +#define LAYER2_WATCHING +#endif struct IsdnCard { int typ; int protocol; /* EDSS1 or 1TR6 */ - unsigned int para[3]; - struct IsdnCardState *sp; + unsigned int para[4]; + struct IsdnCardState *cs; }; - -#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); +int HiSax_inithardware(int *); void HiSax_closehardware(void); -void setstack_HiSax(struct PStack *st, struct IsdnCardState *sp); -unsigned int randomces(void); +void setstack_HiSax(struct PStack *st, struct IsdnCardState *cs); +unsigned int random_ri(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); +int newcallref(void); void FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount); void FsmFree(struct Fsm *fsm); int FsmEvent(struct FsmInst *fi, int event, void *arg); void FsmChangeState(struct FsmInst *fi, int newstate); void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft); -int FsmAddTimer(struct FsmTimer *ft, int millisec, - int event, void *arg, int where); +int FsmAddTimer(struct FsmTimer *ft, int millisec, int event, + void *arg, int where); +void FsmRestartTimer(struct FsmTimer *ft, int millisec, int event, + void *arg, int where); void FsmDelTimer(struct FsmTimer *ft, int where); -int FsmTimerRunning(struct FsmTimer *ft); void jiftime(char *s, long mark); int HiSax_command(isdn_ctrl * ic); -int HiSax_writebuf_skb(int id, int chan, struct sk_buff *skb); +int HiSax_writebuf_skb(int id, int chan, int ack, 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); @@ -520,10 +940,12 @@ 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__ */ +void setstack_manager(struct PStack *st); +#ifdef ISDN_CHIP_ISAC +void setstack_isac(struct PStack *st, struct IsdnCardState *cs); +#endif /* ISDN_CHIP_ISAC */ +#endif /* __KERNEL__ */ #define HZDELAY(jiffs) {int tout = jiffs; while (tout--) udelay(1000000/HZ);} @@ -533,8 +955,12 @@ void CallcNew(void); void CallcFree(void); int CallcNewChan(struct IsdnCardState *csta); void CallcFreeChan(struct IsdnCardState *csta); +void Isdnl1New(void); +void Isdnl1Free(void); void Isdnl2New(void); void Isdnl2Free(void); void init_tei(struct IsdnCardState *sp, int protocol); void release_tei(struct IsdnCardState *sp); char *HiSax_getrev(const char *revision); +void TeiNew(void); +void TeiFree(void); diff --git a/drivers/isdn/hisax/hscx.c b/drivers/isdn/hisax/hscx.c new file mode 100644 index 000000000000..c44cc54dfbf6 --- /dev/null +++ b/drivers/isdn/hisax/hscx.c @@ -0,0 +1,280 @@ +/* $Id: hscx.c,v 1.7 1998/02/12 23:07:36 keil Exp $ + + * hscx.c HSCX specific routines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hscx.c,v $ + * Revision 1.7 1998/02/12 23:07:36 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.6 1998/02/02 13:41:12 keil + * new init + * + * Revision 1.5 1997/11/06 17:09:34 keil + * New 2.1 init code + * + * Revision 1.4 1997/10/29 19:01:06 keil + * changes for 2.1 + * + * Revision 1.3 1997/07/27 21:38:34 keil + * new B-channel interface + * + * Revision 1.2 1997/06/26 11:16:17 keil + * first version + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hscx.h" +#include "isdnl1.h" +#include + +static char *HSCXVer[] HISAX_INITDATA = +{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7", + "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"}; + +HISAX_INITFUNC(int +HscxVersion(struct IsdnCardState *cs, char *s)) +{ + int verA, verB; + + verA = cs->BC_Read_Reg(cs, 0, HSCX_VSTR) & 0xf; + verB = cs->BC_Read_Reg(cs, 1, HSCX_VSTR) & 0xf; + printk(KERN_INFO "%s HSCX version A: %s B: %s\n", s, + HSCXVer[verA], HSCXVer[verB]); + if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) + return (1); + else + return (0); +} + +void +modehscx(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + int hscx = bcs->channel; + + if (cs->debug & L1_DEB_HSCX) { + char tmp[40]; + sprintf(tmp, "hscx %c mode %d ichan %d", + 'A' + hscx, mode, bc); + debugl1(cs, tmp); + } + bcs->mode = mode; + cs->BC_Write_Reg(cs, hscx, HSCX_CCR1, 0x85); + cs->BC_Write_Reg(cs, hscx, HSCX_XAD1, 0xFF); + cs->BC_Write_Reg(cs, hscx, HSCX_XAD2, 0xFF); + cs->BC_Write_Reg(cs, hscx, HSCX_RAH2, 0xFF); + cs->BC_Write_Reg(cs, hscx, HSCX_XBCH, 0x0); + cs->BC_Write_Reg(cs, hscx, HSCX_RLCR, 0x0); + cs->BC_Write_Reg(cs, hscx, HSCX_CCR2, 0x30); + cs->BC_Write_Reg(cs, hscx, HSCX_XCCR, 7); + cs->BC_Write_Reg(cs, hscx, HSCX_RCCR, 7); + + /* Switch IOM 1 SSI */ + if (test_bit(HW_IOM1, &cs->HW_Flags) && (hscx == 0)) + bc = 1 - bc; + + if (bc == 0) { + cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, + test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : 0x2f); + cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, + test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : 0x2f); + } else { + cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0x3); + cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0x3); + } + switch (mode) { + case (L1_MODE_NULL): + cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0xff); + cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0xff); + cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x84); + break; + case (L1_MODE_TRANS): + cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0xe4); + break; + case (L1_MODE_HDLC): + cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x8c); + break; + } + if (mode) + cs->BC_Write_Reg(cs, hscx, HSCX_CMDR, 0x41); + cs->BC_Write_Reg(cs, hscx, HSCX_ISTA, 0x00); +} + +void +hscx_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void +hscx_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA_REQ): + save_flags(flags); + cli(); + if (st->l1.bcs->hw.hscx.tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->hw.hscx.tx_skb = skb; + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->hw.hscx.count = 0; + restore_flags(flags); + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + } + break; + case (PH_PULL_IND): + if (st->l1.bcs->hw.hscx.tx_skb) { + printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n"); + break; + } + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->hw.hscx.tx_skb = skb; + st->l1.bcs->hw.hscx.count = 0; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + break; + case (PH_PULL_REQ): + if (!st->l1.bcs->hw.hscx.tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL_CNF, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } + +} + +void +close_hscxstate(struct BCState *bcs) +{ + struct sk_buff *skb; + + modehscx(bcs, 0, 0); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + if (bcs->hw.hscx.rcvbuf) { + kfree(bcs->hw.hscx.rcvbuf); + bcs->hw.hscx.rcvbuf = NULL; + } + while ((skb = skb_dequeue(&bcs->rqueue))) { + dev_kfree_skb(skb); + } + while ((skb = skb_dequeue(&bcs->squeue))) { + dev_kfree_skb(skb); + } + if (bcs->hw.hscx.tx_skb) { + dev_kfree_skb(bcs->hw.hscx.tx_skb); + bcs->hw.hscx.tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +static int +open_hscxstate(struct IsdnCardState *cs, + int bc) +{ + struct BCState *bcs = cs->bcs + bc; + + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for hscx.rcvbuf\n"); + return (1); + } + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->hw.hscx.tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->hw.hscx.rcvidx = 0; + bcs->tx_cnt = 0; + return (0); +} + +static void +hscx_manl1(struct PStack *st, int pr, + void *arg) +{ + switch (pr) { + case (PH_ACTIVATE_REQ): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + modehscx(st->l1.bcs, st->l1.mode, st->l1.bc); + st->l1.l1man(st, PH_ACTIVATE_CNF, NULL); + break; + case (PH_DEACTIVATE_REQ): + if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) + modehscx(st->l1.bcs, 0, 0); + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + break; + } +} + +int +setstack_hscx(struct PStack *st, struct BCState *bcs) +{ + if (open_hscxstate(st->l1.hardware, bcs->channel)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = hscx_l2l1; + st->ma.manl1 = hscx_manl1; + setstack_manager(st); + bcs->st = st; + return (0); +} + +HISAX_INITFUNC(void +clear_pending_hscx_ints(struct IsdnCardState *cs)) +{ + int val; + char tmp[64]; + + val = cs->BC_Read_Reg(cs, 1, HSCX_ISTA); + sprintf(tmp, "HSCX B ISTA %x", val); + debugl1(cs, tmp); + if (val & 0x01) { + val = cs->BC_Read_Reg(cs, 1, HSCX_EXIR); + sprintf(tmp, "HSCX B EXIR %x", val); + debugl1(cs, tmp); + } else if (val & 0x02) { + val = cs->BC_Read_Reg(cs, 0, HSCX_EXIR); + sprintf(tmp, "HSCX A EXIR %x", val); + debugl1(cs, tmp); + } + val = cs->BC_Read_Reg(cs, 0, HSCX_ISTA); + sprintf(tmp, "HSCX A ISTA %x", val); + debugl1(cs, tmp); + val = cs->BC_Read_Reg(cs, 1, HSCX_STAR); + sprintf(tmp, "HSCX B STAR %x", val); + debugl1(cs, tmp); + val = cs->BC_Read_Reg(cs, 0, HSCX_STAR); + sprintf(tmp, "HSCX A STAR %x", val); + debugl1(cs, tmp); + cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0xFF); + cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0xFF); + cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0); + cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0); +} + +HISAX_INITFUNC(void +inithscx(struct IsdnCardState *cs)) +{ + cs->bcs[0].BC_SetStack = setstack_hscx; + cs->bcs[1].BC_SetStack = setstack_hscx; + cs->bcs[0].BC_Close = close_hscxstate; + cs->bcs[1].BC_Close = close_hscxstate; + modehscx(cs->bcs, 0, 0); + modehscx(cs->bcs + 1, 0, 0); +} diff --git a/drivers/isdn/hisax/hscx.h b/drivers/isdn/hisax/hscx.h new file mode 100644 index 000000000000..ac2d38086276 --- /dev/null +++ b/drivers/isdn/hisax/hscx.h @@ -0,0 +1,46 @@ +/* $Id: hscx.h,v 1.3 1997/07/27 21:38:35 keil Exp $ + + * hscx.h HSCX specific defines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hscx.h,v $ + * Revision 1.3 1997/07/27 21:38:35 keil + * new B-channel interface + * + * Revision 1.2 1997/06/26 11:16:18 keil + * first version + * + * + */ + +/* All Registers original Siemens Spec */ + +#define HSCX_ISTA 0x20 +#define HSCX_CCR1 0x2f +#define HSCX_CCR2 0x2c +#define HSCX_TSAR 0x31 +#define HSCX_TSAX 0x30 +#define HSCX_XCCR 0x32 +#define HSCX_RCCR 0x33 +#define HSCX_MODE 0x22 +#define HSCX_CMDR 0x21 +#define HSCX_EXIR 0x24 +#define HSCX_XAD1 0x24 +#define HSCX_XAD2 0x25 +#define HSCX_RAH2 0x27 +#define HSCX_RSTA 0x27 +#define HSCX_TIMR 0x23 +#define HSCX_STAR 0x21 +#define HSCX_RBCL 0x25 +#define HSCX_XBCH 0x2d +#define HSCX_VSTR 0x2e +#define HSCX_RLCR 0x2e +#define HSCX_MASK 0x20 + +extern int HscxVersion(struct IsdnCardState *cs, char *s); +extern void hscx_sched_event(struct BCState *bcs, int event); +extern void modehscx(struct BCState *bcs, int mode, int bc); +extern void clear_pending_hscx_ints(struct IsdnCardState *cs); +extern void inithscx(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/hscx_irq.c b/drivers/isdn/hisax/hscx_irq.c new file mode 100644 index 000000000000..64e80e7b2798 --- /dev/null +++ b/drivers/isdn/hisax/hscx_irq.c @@ -0,0 +1,320 @@ +/* $Id: hscx_irq.c,v 1.7 1998/02/12 23:07:37 keil Exp $ + + * hscx_irq.c low level b-channel stuff for Siemens HSCX + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * This is an include file for fast inline IRQ stuff + * + * $Log: hscx_irq.c,v $ + * Revision 1.7 1998/02/12 23:07:37 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.6 1997/10/29 19:01:07 keil + * changes for 2.1 + * + * Revision 1.5 1997/10/01 09:21:35 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.4 1997/08/15 17:48:02 keil + * cosmetic + * + * Revision 1.3 1997/07/27 21:38:36 keil + * new B-channel interface + * + * Revision 1.2 1997/06/26 11:16:19 keil + * first version + * + * + */ + + +static inline void +waitforCEC(struct IsdnCardState *cs, int hscx) +{ + int to = 50; + + while ((READHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: waitforCEC timeout\n"); +} + + +static inline void +waitforXFW(struct IsdnCardState *cs, int hscx) +{ + int to = 50; + + while ((!(READHSCX(cs, hscx, HSCX_STAR) & 0x44) == 0x40) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: waitforXFW timeout\n"); +} + +static inline void +WriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + waitforCEC(cs, hscx); + WRITEHSCX(cs, hscx, HSCX_CMDR, data); + restore_flags(flags); +} + + + +static void +hscx_empty_fifo(struct BCState *bcs, int count) +{ + u_char *ptr; + struct IsdnCardState *cs = bcs->cs; + long flags; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hscx_empty_fifo"); + + if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hscx_empty_fifo: incoming packet too large"); + WriteHSCXCMDR(cs, bcs->channel, 0x80); + bcs->hw.hscx.rcvidx = 0; + return; + } + ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx; + bcs->hw.hscx.rcvidx += count; + save_flags(flags); + cli(); + READHSCXFIFO(cs, bcs->channel, ptr, count); + WriteHSCXCMDR(cs, bcs->channel, 0x80); + restore_flags(flags); + if (cs->debug & L1_DEB_HSCX_FIFO) { + char tmp[256]; + char *t = tmp; + + t += sprintf(t, "hscx_empty_fifo %c cnt %d", + bcs->channel ? 'B' : 'A', count); + QuickHex(t, ptr, count); + debugl1(cs, tmp); + } +} + +static void +hscx_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + int more, count; + int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32; + u_char *ptr; + long flags; + + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hscx_fill_fifo"); + + if (!bcs->hw.hscx.tx_skb) + return; + if (bcs->hw.hscx.tx_skb->len <= 0) + return; + + more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0; + if (bcs->hw.hscx.tx_skb->len > fifo_size) { + more = !0; + count = fifo_size; + } else + count = bcs->hw.hscx.tx_skb->len; + + waitforXFW(cs, bcs->channel); + save_flags(flags); + cli(); + ptr = bcs->hw.hscx.tx_skb->data; + skb_pull(bcs->hw.hscx.tx_skb, count); + bcs->tx_cnt -= count; + bcs->hw.hscx.count += count; + WRITEHSCXFIFO(cs, bcs->channel, ptr, count); + WriteHSCXCMDR(cs, bcs->channel, more ? 0x8 : 0xa); + restore_flags(flags); + if (cs->debug & L1_DEB_HSCX_FIFO) { + char tmp[256]; + char *t = tmp; + + t += sprintf(t, "hscx_fill_fifo %c cnt %d", + bcs->channel ? 'B' : 'A', count); + QuickHex(t, ptr, count); + debugl1(cs, tmp); + } +} + +static inline void +hscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx) +{ + u_char r; + struct BCState *bcs = cs->bcs + hscx; + struct sk_buff *skb; + int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32; + int count; + char tmp[32]; + + if (!test_bit(BC_FLG_INIT, &bcs->Flag)) + return; + + if (val & 0x80) { /* RME */ + r = READHSCX(cs, hscx, HSCX_RSTA); + if ((r & 0xf0) != 0xa0) { + if (!(r & 0x80)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX invalid frame"); + if ((r & 0x40) && bcs->mode) + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "HSCX RDO mode=%d", + bcs->mode); + debugl1(cs, tmp); + } + if (!(r & 0x20)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX CRC error"); + WriteHSCXCMDR(cs, hscx, 0x80); + } else { + count = READHSCX(cs, hscx, HSCX_RBCL) & ( + test_bit(HW_IPAC, &cs->HW_Flags)? 0x3f: 0x1f); + if (count == 0) + count = fifo_size; + hscx_empty_fifo(bcs, count); + if ((count = bcs->hw.hscx.rcvidx - 1) > 0) { + if (cs->debug & L1_DEB_HSCX_FIFO) { + sprintf(tmp, "HX Frame %d", count); + debugl1(cs, tmp); + } + if (!(skb = dev_alloc_skb(count))) + printk(KERN_WARNING "HSCX: receive out of memory\n"); + else { + memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count); + skb_queue_tail(&bcs->rqueue, skb); + } + } + } + bcs->hw.hscx.rcvidx = 0; + hscx_sched_event(bcs, B_RCVBUFREADY); + } + if (val & 0x40) { /* RPF */ + hscx_empty_fifo(bcs, fifo_size); + if (bcs->mode == L1_MODE_TRANS) { + /* receive audio data */ + if (!(skb = dev_alloc_skb(fifo_size))) + printk(KERN_WARNING "HiSax: receive out of memory\n"); + else { + memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size); + skb_queue_tail(&bcs->rqueue, skb); + } + bcs->hw.hscx.rcvidx = 0; + hscx_sched_event(bcs, B_RCVBUFREADY); + } + } + if (val & 0x10) { /* XPR */ + if (bcs->hw.hscx.tx_skb) + if (bcs->hw.hscx.tx_skb->len) { + hscx_fill_fifo(bcs); + return; + } else { + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->hw.hscx.tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count); + dev_kfree_skb(bcs->hw.hscx.tx_skb); + bcs->hw.hscx.count = 0; + bcs->hw.hscx.tx_skb = NULL; + } + if ((bcs->hw.hscx.tx_skb = skb_dequeue(&bcs->squeue))) { + bcs->hw.hscx.count = 0; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + hscx_fill_fifo(bcs); + } else { + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + hscx_sched_event(bcs, B_XMTBUFREADY); + } + } +} + +static inline void +hscx_int_main(struct IsdnCardState *cs, u_char val) +{ + + u_char exval; + struct BCState *bcs; + char tmp[32]; + + if (val & 0x01) { + bcs = cs->bcs + 1; + exval = READHSCX(cs, 1, HSCX_EXIR); + if (exval == 0x40) { + if (bcs->mode == 1) + hscx_fill_fifo(bcs); + else { + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame. + */ + if (bcs->hw.hscx.tx_skb) { + skb_push(bcs->hw.hscx.tx_skb, bcs->hw.hscx.count); + bcs->tx_cnt += bcs->hw.hscx.count; + bcs->hw.hscx.count = 0; + } + WriteHSCXCMDR(cs, bcs->channel, 0x01); + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); + debugl1(cs, tmp); + } + } + } else if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "HSCX B EXIR %x", exval); + debugl1(cs, tmp); + } + } + if (val & 0xf8) { + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "HSCX B interrupt %x", val); + debugl1(cs, tmp); + } + hscx_interrupt(cs, val, 1); + } + if (val & 0x02) { + bcs = cs->bcs; + exval = READHSCX(cs, 0, HSCX_EXIR); + if (exval == 0x40) { + if (bcs->mode == L1_MODE_TRANS) + hscx_fill_fifo(bcs); + else { + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame. + */ + if (bcs->hw.hscx.tx_skb) { + skb_push(bcs->hw.hscx.tx_skb, bcs->hw.hscx.count); + bcs->tx_cnt += bcs->hw.hscx.count; + bcs->hw.hscx.count = 0; + } + WriteHSCXCMDR(cs, bcs->channel, 0x01); + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); + debugl1(cs, tmp); + } + } + } else if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "HSCX A EXIR %x", exval); + debugl1(cs, tmp); + } + } + if (val & 0x04) { + exval = READHSCX(cs, 0, HSCX_ISTA); + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "HSCX A interrupt %x", exval); + debugl1(cs, tmp); + } + hscx_interrupt(cs, exval, 0); + } +} diff --git a/drivers/isdn/hisax/ipac.h b/drivers/isdn/hisax/ipac.h new file mode 100644 index 000000000000..6d856ec6cec8 --- /dev/null +++ b/drivers/isdn/hisax/ipac.h @@ -0,0 +1,35 @@ +/* $Id: ipac.h,v 1.2 1997/10/29 18:51:21 keil Exp $ + + * ipac.h IPAC specific defines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: ipac.h,v $ + * Revision 1.2 1997/10/29 18:51:21 keil + * New files + * + * Revision 1.1.2.1 1997/10/17 22:10:48 keil + * new files on 2.0 + * + * + * + */ + + +/* All Registers original Siemens Spec */ + +#define IPAC_CONF 0xC0 +#define IPAC_MASK 0xC1 +#define IPAC_ISTA 0xC1 +#define IPAC_ID 0xC2 +#define IPAC_ACFG 0xC3 +#define IPAC_AOE 0xC4 +#define IPAC_ARX 0xC5 +#define IPAC_PITA1 0xC6 +#define IPAC_PITA2 0xC7 +#define IPAC_POTA1 0xC8 +#define IPAC_POTA2 0xC9 +#define IPAC_PCFG 0xCA +#define IPAC_SCFG 0xCB +#define IPAC_TIMR2 0xCC diff --git a/drivers/isdn/hisax/isac.c b/drivers/isdn/hisax/isac.c new file mode 100644 index 000000000000..9a2624ef4a95 --- /dev/null +++ b/drivers/isdn/hisax/isac.c @@ -0,0 +1,679 @@ +/* $Id: isac.c,v 1.12 1998/02/12 23:07:40 keil Exp $ + + * isac.c ISAC specific routines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: isac.c,v $ + * Revision 1.12 1998/02/12 23:07:40 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.11 1998/02/09 10:54:49 keil + * fixes for leased mode + * + * Revision 1.10 1998/02/02 13:37:37 keil + * new init + * + * Revision 1.9 1997/11/06 17:09:07 keil + * New 2.1 init code + * + * Revision 1.8 1997/10/29 19:00:03 keil + * new layer1,changes for 2.1 + * + * Revision 1.7 1997/10/01 09:21:37 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.6 1997/08/15 17:47:08 keil + * avoid oops because a uninitialised timer + * + * Revision 1.5 1997/08/07 17:48:49 keil + * fix wrong parenthesis + * + * Revision 1.4 1997/07/30 17:11:59 keil + * fixed Timer3 + * + * Revision 1.3 1997/07/27 21:37:40 keil + * T3 implemented; supervisor l1timer; B-channel TEST_LOOP + * + * Revision 1.2 1997/06/26 11:16:15 keil + * first version + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "isdnl1.h" +#include + +#define DBUSY_TIMER_VALUE 80 +#define ARCOFI_USE 1 + +static char *ISACVer[] HISAX_INITDATA = +{"2086/2186 V1.1", "2085 B1", "2085 B2", + "2085 V2.3"}; + +void +ISACVersion(struct IsdnCardState *cs, char *s) +{ + int val; + + val = cs->readisac(cs, ISAC_RBCH); + printk(KERN_INFO "%s ISAC version : %s\n", s, ISACVer[(val >> 5) & 3]); +} + +static void +ph_command(struct IsdnCardState *cs, unsigned int command) +{ + if (cs->debug & L1_DEB_ISAC) { + char tmp[32]; + sprintf(tmp, "ph_command %x", command); + debugl1(cs, tmp); + } + cs->writeisac(cs, ISAC_CIX0, (command << 2) | 3); +} + +static void +manl1_msg(struct IsdnCardState *cs, int msg, void *arg) { + struct PStack *st; + + st = cs->stlist; + while (st) { + st->ma.manl1(st, msg, arg); + st = st->next; + } +} + +static void +isac_new_ph(struct IsdnCardState *cs) +{ + switch (cs->ph_state) { + case (ISAC_IND_RS): + case (ISAC_IND_EI): + ph_command(cs, ISAC_CMD_DUI); + manl1_msg(cs, PH_RESET_IND, NULL); + break; + case (ISAC_IND_DID): + manl1_msg(cs, PH_DEACT_CNF, NULL); + break; + case (ISAC_IND_DR): + manl1_msg(cs, PH_DEACT_IND, NULL); + break; + case (ISAC_IND_PU): + manl1_msg(cs, PH_POWERUP_CNF, NULL); + break; + case (ISAC_IND_RSY): + manl1_msg(cs, PH_RSYNC_IND, NULL); + break; + case (ISAC_IND_ARD): + manl1_msg(cs, PH_INFO2_IND, NULL); + break; + case (ISAC_IND_AI8): + manl1_msg(cs, PH_I4_P8_IND, NULL); + break; + case (ISAC_IND_AI10): + manl1_msg(cs, PH_I4_P10_IND, NULL); + break; + default: + break; + } +} + +static void +isac_bh(struct IsdnCardState *cs) +{ + struct PStack *stptr; + + if (!cs) + return; + + if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy cleared"); + stptr = cs->stlist; + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE_CNF, NULL); + stptr = stptr->next; + } + } + if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) + isac_new_ph(cs); + if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) + DChannel_proc_rcv(cs); + if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) + DChannel_proc_xmt(cs); + if (test_and_clear_bit(D_RX_MON0, &cs->event)) + test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags); + if (test_and_clear_bit(D_RX_MON1, &cs->event)) + test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags); + if (test_and_clear_bit(D_TX_MON0, &cs->event)) + test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags); + if (test_and_clear_bit(D_TX_MON1, &cs->event)) + test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags); +} + +void +isac_empty_fifo(struct IsdnCardState *cs, int count) +{ + u_char *ptr; + long flags; + + if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO)) + debugl1(cs, "isac_empty_fifo"); + + if ((cs->rcvidx + count) >= MAX_DFRAME_LEN) { + if (cs->debug & L1_DEB_WARN) { + char tmp[40]; + sprintf(tmp, "isac_empty_fifo overrun %d", + cs->rcvidx + count); + debugl1(cs, tmp); + } + cs->writeisac(cs, ISAC_CMDR, 0x80); + cs->rcvidx = 0; + return; + } + ptr = cs->rcvbuf + cs->rcvidx; + cs->rcvidx += count; + save_flags(flags); + cli(); + cs->readisacfifo(cs, ptr, count); + cs->writeisac(cs, ISAC_CMDR, 0x80); + restore_flags(flags); + if (cs->debug & L1_DEB_ISAC_FIFO) { + char tmp[128]; + char *t = tmp; + + t += sprintf(t, "isac_empty_fifo cnt %d", count); + QuickHex(t, ptr, count); + debugl1(cs, tmp); + } +} + +static void +isac_fill_fifo(struct IsdnCardState *cs) +{ + int count, more; + u_char *ptr; + long flags; + + if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO)) + debugl1(cs, "isac_fill_fifo"); + + if (!cs->tx_skb) + return; + + count = cs->tx_skb->len; + if (count <= 0) + return; + + more = 0; + if (count > 32) { + more = !0; + count = 32; + } + save_flags(flags); + cli(); + ptr = cs->tx_skb->data; + skb_pull(cs->tx_skb, count); + cs->tx_cnt += count; + cs->writeisacfifo(cs, ptr, count); + cs->writeisac(cs, ISAC_CMDR, more ? 0x8 : 0xa); + restore_flags(flags); + if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { + debugl1(cs, "isac_fill_fifo dbusytimer running"); + del_timer(&cs->dbusytimer); + } + init_timer(&cs->dbusytimer); + cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000); + add_timer(&cs->dbusytimer); + if (cs->debug & L1_DEB_ISAC_FIFO) { + char tmp[128]; + char *t = tmp; + + t += sprintf(t, "isac_fill_fifo cnt %d", count); + QuickHex(t, ptr, count); + debugl1(cs, tmp); + } +} + +void +isac_sched_event(struct IsdnCardState *cs, int event) +{ + test_and_set_bit(event, &cs->event); + queue_task(&cs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +void +isac_interrupt(struct IsdnCardState *cs, u_char val) +{ + u_char exval, v1; + struct sk_buff *skb; + unsigned int count; + long flags; + char tmp[32]; + + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "ISAC interrupt %x", val); + debugl1(cs, tmp); + } + if (val & 0x80) { /* RME */ + exval = cs->readisac(cs, ISAC_RSTA); + if ((exval & 0x70) != 0x20) { + if (exval & 0x40) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC RDO"); + if (!(exval & 0x20)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC CRC error"); + cs->writeisac(cs, ISAC_CMDR, 0x80); + } else { + count = cs->readisac(cs, ISAC_RBCL) & 0x1f; + if (count == 0) + count = 32; + isac_empty_fifo(cs, count); + save_flags(flags); + cli(); + if ((count = cs->rcvidx) > 0) { + cs->rcvidx = 0; + if (!(skb = alloc_skb(count, GFP_ATOMIC))) + printk(KERN_WARNING "HiSax: D receive out of memory\n"); + else { + memcpy(skb_put(skb, count), cs->rcvbuf, count); + skb_queue_tail(&cs->rq, skb); + } + } + restore_flags(flags); + } + cs->rcvidx = 0; + isac_sched_event(cs, D_RCVBUFREADY); + } + if (val & 0x40) { /* RPF */ + isac_empty_fifo(cs, 32); + } + if (val & 0x20) { /* RSC */ + /* never */ + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC RSC interrupt"); + } + if (val & 0x10) { /* XPR */ + if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) + del_timer(&cs->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + isac_sched_event(cs, D_CLEARBUSY); + if (cs->tx_skb) + if (cs->tx_skb->len) { + isac_fill_fifo(cs); + goto afterXPR; + } else { + dev_kfree_skb(cs->tx_skb); + cs->tx_cnt = 0; + cs->tx_skb = NULL; + } + if ((cs->tx_skb = skb_dequeue(&cs->sq))) { + cs->tx_cnt = 0; + isac_fill_fifo(cs); + } else + isac_sched_event(cs, D_XMTBUFREADY); + } + afterXPR: + if (val & 0x04) { /* CISQ */ + cs->ph_state = (cs->readisac(cs, ISAC_CIX0) >> 2) & 0xf; + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "ph_state change %x", cs->ph_state); + debugl1(cs, tmp); + } + isac_sched_event(cs, D_L1STATECHANGE); + } + if (val & 0x02) { /* SIN */ + /* never */ + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC SIN interrupt"); + } + if (val & 0x01) { /* EXI */ + exval = cs->readisac(cs, ISAC_EXIR); + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "ISAC EXIR %02x", exval); + debugl1(cs, tmp); + } + if (exval & 0x04) { + v1 = cs->readisac(cs, ISAC_MOSR); + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "ISAC MOSR %02x", v1); + debugl1(cs, tmp); + } +#if ARCOFI_USE + if (v1 & 0x08) { + if (!cs->mon_rx) + if (!(cs->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC MON RX out of memory!"); + cs->mocr &= 0xf0; + cs->mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + goto afterMONR0; + } else + cs->mon_rxp = 0; + if (cs->mon_rxp >= MAX_MON_FRAME) { + cs->mocr &= 0xf0; + cs->mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->mon_rxp = 0; + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC MON RX overflow!"); + goto afterMONR0; + } + cs->mon_rx[cs->mon_rxp++] = cs->readisac(cs, ISAC_MOR0); + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "ISAC MOR0 %02x", cs->mon_rx[cs->mon_rxp -1]); + debugl1(cs, tmp); + } + if (cs->mon_rxp == 1) { + cs->mocr |= 0x04; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + } + } + afterMONR0: + if (v1 & 0x80) { + if (!cs->mon_rx) + if (!(cs->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC MON RX out of memory!"); + cs->mocr &= 0x0f; + cs->mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + goto afterMONR1; + } else + cs->mon_rxp = 0; + if (cs->mon_rxp >= MAX_MON_FRAME) { + cs->mocr &= 0x0f; + cs->mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->mon_rxp = 0; + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC MON RX overflow!"); + goto afterMONR1; + } + cs->mon_rx[cs->mon_rxp++] = cs->readisac(cs, ISAC_MOR1); + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "ISAC MOR1 %02x", cs->mon_rx[cs->mon_rxp -1]); + debugl1(cs, tmp); + } + if (cs->mon_rxp == 1) { + cs->mocr |= 0x40; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + } + } + afterMONR1: + if (v1 & 0x04) { + cs->mocr &= 0xf0; + cs->mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + isac_sched_event(cs, D_RX_MON0); + } + if (v1 & 0x40) { + cs->mocr &= 0x0f; + cs->mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + isac_sched_event(cs, D_RX_MON1); + } + if (v1 & 0x02) { + if (!cs->mon_tx) { + cs->mocr &= 0xf0; + cs->mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + goto AfterMOX0; + } + if (cs->mon_txp >= cs->mon_txc) { + if (cs->mon_txc) + isac_sched_event(cs, D_TX_MON0); + goto AfterMOX0; + } + cs->writeisac(cs, ISAC_MOX0, + cs->mon_tx[cs->mon_txp++]); + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "ISAC %02x -> MOX0", cs->mon_tx[cs->mon_txp -1]); + debugl1(cs, tmp); + } + } + AfterMOX0: + if (v1 & 0x20) { + if (!cs->mon_tx) { + cs->mocr &= 0x0f; + cs->mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + goto AfterMOX1; + } + if (cs->mon_txp >= cs->mon_txc) { + if (cs->mon_txc) + isac_sched_event(cs, D_TX_MON1); + goto AfterMOX1; + } + cs->writeisac(cs, ISAC_MOX1, + cs->mon_tx[cs->mon_txp++]); + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "ISAC %02x -> MOX1", cs->mon_tx[cs->mon_txp -1]); + debugl1(cs, tmp); + } + } + AfterMOX1: +#endif + } + } +} + +static void +ISAC_l2l1(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + struct sk_buff *skb = arg; + char str[64]; + + switch (pr) { + case (PH_DATA_REQ): + if (cs->tx_skb) { + skb_queue_tail(&cs->sq, skb); +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA Queued", 0); +#endif + } else { + if ((cs->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ + LogFrame(cs, skb->data, skb->len); + sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); + dlogframe(cs, skb->data + 4, skb->len - 4, + str); + } + cs->tx_skb = skb; + cs->tx_cnt = 0; +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA", 0); +#endif + isac_fill_fifo(cs); + } + break; + case (PH_PULL_IND): + if (cs->tx_skb) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, " l2l1 tx_skb exist this shouldn't happen"); + skb_queue_tail(&cs->sq, skb); + break; + } + if ((cs->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ + LogFrame(cs, skb->data, skb->len); + sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); + dlogframe(cs, skb->data + 4, skb->len - 4, + str); + } + cs->tx_skb = skb; + cs->tx_cnt = 0; +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA_PULLED", 0); +#endif + isac_fill_fifo(cs); + break; + case (PH_PULL_REQ): +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + debugl1(cs, "-> PH_REQUEST_PULL"); +#endif + if (!cs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL_CNF, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } +} + +void +isac_l1cmd(struct IsdnCardState *cs, int msg, void *arg) +{ + u_char val; + char tmp[32]; + + switch(msg) { + case PH_RESET_REQ: + if ((cs->ph_state == ISAC_IND_EI) || + (cs->ph_state == ISAC_IND_DR) || + (cs->ph_state == ISAC_IND_RS)) + ph_command(cs, ISAC_CMD_TIM); + else + ph_command(cs, ISAC_CMD_RS); + break; + case PH_ENABLE_REQ: + ph_command(cs, ISAC_CMD_TIM); + break; + case PH_INFO3_REQ: + ph_command(cs, ISAC_CMD_AR8); + break; + case PH_TESTLOOP_REQ: + val = 0; + if (1 & (int) arg) + val |= 0x0c; + if (2 & (int) arg) + val |= 0x3; + if (test_bit(HW_IOM1, &cs->HW_Flags)) { + /* IOM 1 Mode */ + if (!val) { + cs->writeisac(cs, ISAC_SPCR, 0xa); + cs->writeisac(cs, ISAC_ADF1, 0x2); + } else { + cs->writeisac(cs, ISAC_SPCR, val); + cs->writeisac(cs, ISAC_ADF1, 0xa); + } + } else { + /* IOM 2 Mode */ + cs->writeisac(cs, ISAC_SPCR, val); + if (val) + cs->writeisac(cs, ISAC_ADF1, 0x8); + else + cs->writeisac(cs, ISAC_ADF1, 0x0); + } + break; + default: + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "isac_l1cmd unknown %4x", msg); + debugl1(cs, tmp); + } + break; + } +} + +void +setstack_isac(struct PStack *st, struct IsdnCardState *cs) +{ + st->l2.l2l1 = ISAC_l2l1; +} + +static void +dbusy_timer_handler(struct IsdnCardState *cs) +{ + struct PStack *stptr; + + if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy"); + test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); + stptr = cs->stlist; + + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE_IND, NULL); + stptr = stptr->next; + } + } +} + +HISAX_INITFUNC(void +initisac(struct IsdnCardState *cs)) +{ + cs->tqueue.routine = (void *) (void *) isac_bh; + cs->l1cmd = isac_l1cmd; + cs->setstack_d = setstack_isac; + cs->dbusytimer.function = (void *) dbusy_timer_handler; + cs->dbusytimer.data = (long) cs; + init_timer(&cs->dbusytimer); + cs->writeisac(cs, ISAC_MASK, 0xff); + cs->mocr = 0xaa; + if (test_bit(HW_IOM1, &cs->HW_Flags)) { + /* IOM 1 Mode */ + cs->writeisac(cs, ISAC_ADF2, 0x0); + cs->writeisac(cs, ISAC_SPCR, 0xa); + cs->writeisac(cs, ISAC_ADF1, 0x2); + cs->writeisac(cs, ISAC_STCR, 0x70); + cs->writeisac(cs, ISAC_MODE, 0xc9); + } else { + /* IOM 2 Mode */ + cs->writeisac(cs, ISAC_ADF2, 0x80); + cs->writeisac(cs, ISAC_SQXR, 0x2f); + cs->writeisac(cs, ISAC_SPCR, 0x00); + cs->writeisac(cs, ISAC_STCR, 0x70); + cs->writeisac(cs, ISAC_MODE, 0xc9); + cs->writeisac(cs, ISAC_TIMR, 0x00); + cs->writeisac(cs, ISAC_ADF1, 0x00); + } + ph_command(cs, ISAC_CMD_RS); + cs->writeisac(cs, ISAC_MASK, 0x0); +} + +HISAX_INITFUNC(void +clear_pending_isac_ints(struct IsdnCardState *cs)) +{ + int val; + char tmp[64]; + + val = cs->readisac(cs, ISAC_STAR); + sprintf(tmp, "ISAC STAR %x", val); + debugl1(cs, tmp); + val = cs->readisac(cs, ISAC_MODE); + sprintf(tmp, "ISAC MODE %x", val); + debugl1(cs, tmp); + val = cs->readisac(cs, ISAC_ADF2); + sprintf(tmp, "ISAC ADF2 %x", val); + debugl1(cs, tmp); + val = cs->readisac(cs, ISAC_ISTA); + sprintf(tmp, "ISAC ISTA %x", val); + debugl1(cs, tmp); + if (val & 0x01) { + val = cs->readisac(cs, ISAC_EXIR); + sprintf(tmp, "ISAC EXIR %x", val); + debugl1(cs, tmp); + } else if (val & 0x04) { + val = cs->readisac(cs, ISAC_CIR0); + sprintf(tmp, "ISAC CIR0 %x", val); + debugl1(cs, tmp); + cs->ph_state = (val >> 2) & 0xf; + } else { + cs->ph_state = (cs->readisac(cs, ISAC_CIX0) >> 2) & 0xf; + } + isac_sched_event(cs, D_L1STATECHANGE); + cs->writeisac(cs, ISAC_MASK, 0xFF); + cs->writeisac(cs, ISAC_MASK, 0); + cs->writeisac(cs, ISAC_CMDR, 0x41); +} diff --git a/drivers/isdn/hisax/isac.h b/drivers/isdn/hisax/isac.h new file mode 100644 index 000000000000..ac4d556470ff --- /dev/null +++ b/drivers/isdn/hisax/isac.h @@ -0,0 +1,74 @@ +/* $Id: isac.h,v 1.4 1997/10/29 19:09:34 keil Exp $ + + * isac.h ISAC specific defines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: isac.h,v $ + * Revision 1.4 1997/10/29 19:09:34 keil + * new L1 + * + * Revision 1.3 1997/07/27 21:37:41 keil + * T3 implemented; supervisor l1timer; B-channel TEST_LOOP + * + * Revision 1.2 1997/06/26 11:16:16 keil + * first version + * + * + */ + + +/* All Registers original Siemens Spec */ + +#define ISAC_MASK 0x20 +#define ISAC_ISTA 0x20 +#define ISAC_STAR 0x21 +#define ISAC_CMDR 0x21 +#define ISAC_EXIR 0x24 +#define ISAC_RBCH 0x2a +#define ISAC_ADF2 0x39 +#define ISAC_SPCR 0x30 +#define ISAC_ADF1 0x38 +#define ISAC_CIR0 0x31 +#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 ISAC_MOSR 0x3a +#define ISAC_MOCR 0x3a +#define ISAC_MOR0 0x32 +#define ISAC_MOX0 0x32 +#define ISAC_MOR1 0x34 +#define ISAC_MOX1 0x34 + +#define ISAC_CMD_TIM 0x0 +#define ISAC_CMD_RS 0x1 +#define ISAC_CMD_SCZ 0x4 +#define ISAC_CMD_SSZ 0x2 +#define ISAC_CMD_AR8 0x8 +#define ISAC_CMD_AR10 0x9 +#define ISAC_CMD_ARL 0xA +#define ISAC_CMD_DUI 0xF + +#define ISAC_IND_RS 0x1 +#define ISAC_IND_PU 0x7 +#define ISAC_IND_DR 0x0 +#define ISAC_IND_SD 0x2 +#define ISAC_IND_DIS 0x3 +#define ISAC_IND_EI 0x6 +#define ISAC_IND_RSY 0x4 +#define ISAC_IND_ARD 0x8 +#define ISAC_IND_TI 0xA +#define ISAC_IND_ATI 0xB +#define ISAC_IND_AI8 0xC +#define ISAC_IND_AI10 0xD +#define ISAC_IND_DID 0xF + +extern void ISACVersion(struct IsdnCardState *cs, char *s); +extern void initisac(struct IsdnCardState *cs); +extern void isac_interrupt(struct IsdnCardState *cs, u_char val); +extern void clear_pending_isac_ints(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c index 655e6a417880..8c4431b04f1c 100644 --- a/drivers/isdn/hisax/isdnl1.c +++ b/drivers/isdn/hisax/isdnl1.c @@ -1,4 +1,4 @@ -/* $Id: isdnl1.c,v 1.15 1997/05/27 15:17:55 fritz Exp $ +/* $Id: isdnl1.c,v 2.18 1998/02/12 23:07:42 keil Exp $ * isdnl1.c common low level stuff for Siemens Chipsetbased isdn cards * based on the teles driver from Jan den Ouden @@ -11,131 +11,239 @@ * * * $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 2.18 1998/02/12 23:07:42 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 2.17 1998/02/11 17:28:07 keil + * Niccy PnP/PCI support + * + * Revision 2.16 1998/02/09 18:46:08 keil + * Support for Sedlbauer PCMCIA (Marcus Niemann) * - * Revision 1.14 1997/04/07 23:00:08 keil - * GFP_KERNEL ---> GFP_ATOMIC + * Revision 2.15 1998/02/09 10:54:51 keil + * fixes for leased mode * - * Revision 1.13 1997/04/06 22:55:50 keil - * Using SKB's + * Revision 2.14 1998/02/03 23:31:31 keil + * add AMD7930 support * - * Revision 1.12 1997/03/26 13:43:57 keil - * small cosmetics + * Revision 2.13 1998/02/02 13:33:02 keil + * New card support * - * Revision 1.11 1997/03/25 23:11:23 keil - * US NI-1 protocol + * Revision 2.12 1998/01/31 21:41:48 keil + * changes for newer 2.1 kernels * - * Revision 1.10 1997/03/13 14:45:05 keil - * using IRQ proof queue_task + * Revision 2.11 1997/11/12 15:01:23 keil + * COMPAQ_ISA changes * - * Revision 1.9 1997/03/12 21:44:21 keil - * change Interrupt routine from atomic quick to normal + * Revision 2.10 1997/11/08 21:35:48 keil + * new l1 init * - * Revision 1.8 1997/02/09 00:24:31 keil - * new interface handling, one interface per card + * Revision 2.9 1997/11/06 17:09:18 keil + * New 2.1 init code * - * Revision 1.7 1997/01/27 15:56:03 keil - * PCMCIA Teles card and ITK ix1 micro added + * Revision 2.8 1997/10/29 19:00:05 keil + * new layer1,changes for 2.1 * - * Revision 1.6 1997/01/21 22:20:00 keil - * changes for D-channel log; Elsa Quickstep support + * Revision 2.7 1997/10/10 20:56:50 fritz + * New HL interface. * - * Revision 1.5 1997/01/10 12:51:19 keil - * cleanup; set newversion + * Revision 2.6 1997/09/12 10:05:16 keil + * ISDN_CTRL_DEBUG define * - * Revision 1.4 1996/12/08 19:44:53 keil - * L2FRAME_DEBUG and other changes from Pekka Sarnila + * Revision 2.5 1997/09/11 17:24:45 keil + * Add new cards * - * Revision 1.3 1996/11/18 15:34:47 keil - * fix HSCX version code + * Revision 2.4 1997/08/15 17:47:09 keil + * avoid oops because a uninitialised timer * - * Revision 1.2 1996/10/27 22:16:54 keil - * ISAC/HSCX version lookup + * Revision 2.3 1997/08/01 11:16:40 keil + * cosmetics * - * Revision 1.1 1996/10/13 20:04:53 keil - * Initial revision + * Revision 2.2 1997/07/30 17:11:08 keil + * L1deactivated exported * + * Revision 2.1 1997/07/27 21:35:38 keil + * new layer1 interface + * + * Revision 2.0 1997/06/26 11:02:53 keil + * New Layer and card interface + * + * Revision 1.15 1997/05/27 15:17:55 fritz + * Added changes for recent 2.1.x kernels: + * changed return type of isdn_close + * queue_task_* -> queue_task + * clear/set_bit -> test_and_... where apropriate. + * changed type of hard_header_cache parameter. * + * old changes removed KKe * */ -const char *l1_revision = "$Revision: 1.15 $"; +const char *l1_revision = "$Revision: 2.18 $"; #define __NO_VERSION__ #include #include "hisax.h" #include "isdnl1.h" +#include +#if (LINUX_VERSION_CODE < 0x020150) /* 2.1.80 */ +#define kstat_irqs( PAR ) kstat.interrupts( (PAR) ) +#endif + + #if CARD_TELES0 -#include "teles0.h" +extern int setup_teles0(struct IsdnCard *card); #endif #if CARD_TELES3 -#include "teles3.h" +extern int setup_teles3(struct IsdnCard *card); #endif #if CARD_AVM_A1 -#include "avm_a1.h" +extern int setup_avm_a1(struct IsdnCard *card); #endif #if CARD_ELSA -#include "elsa.h" +extern int setup_elsa(struct IsdnCard *card); #endif #if CARD_IX1MICROR2 -#include "ix1_micro.h" +extern int setup_ix1micro(struct IsdnCard *card); #endif -/* #define I4L_IRQ_FLAG SA_INTERRUPT */ -#define I4L_IRQ_FLAG 0 +#if CARD_DIEHLDIVA +extern int setup_diva(struct IsdnCard *card); +#endif -#define HISAX_STATUS_BUFSIZE 4096 +#if CARD_ASUSCOM +extern int setup_asuscom(struct IsdnCard *card); +#endif -#define INCLUDE_INLINE_FUNCS -#include -#include +#if CARD_TELEINT +extern int setup_TeleInt(struct IsdnCard *card); +#endif -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", +#if CARD_SEDLBAUER +extern int setup_sedlbauer(struct IsdnCard *card); +#endif + +#if CARD_SPORTSTER +extern int setup_sportster(struct IsdnCard *card); +#endif + +#if CARD_MIC +extern int setup_mic(struct IsdnCard *card); #endif - "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", "???"}; +#if CARD_NETJET +extern int setup_netjet(struct IsdnCard *card); +#endif + +#if CARD_TELES3C +extern int setup_t163c(struct IsdnCard *card); +#endif + +#if CARD_AMD7930 +extern int setup_amd7930(struct IsdnCard *card); +#endif + +#if CARD_NICCY +extern int setup_niccy(struct IsdnCard *card); +#endif -static char *ISACVer[] = -{"2086/2186 V1.1", "2085 B1", "2085 B2", - "2085 V2.3"}; +#define HISAX_STATUS_BUFSIZE 4096 +#define ISDN_CTRL_DEBUG 1 +#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", "Elsa Quickstep", "Teles PCMCIA", "ITK ix1-micro Rev.2", + "Elsa PCMCIA", "Eicon.Diehl Diva", "ISDNLink", "TeleInt", "Teles 16.3c", + "Sedlbauer Speed Card", "USR Sportster", "ith mic Linux", "Elsa PCI", + "Compaq ISA", "NETjet", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)", + "AMD 7930", "NICCY" +}; extern struct IsdnCard cards[]; extern int nrcards; extern char *HiSax_id; +extern struct IsdnBuffers *tracebuf; + +#define TIMER3_VALUE 7 + +static +struct Fsm l1fsm = +{NULL, 0, 0, NULL, NULL}; + +enum { + ST_L1_F2, + ST_L1_F3, + ST_L1_F4, + ST_L1_F5, + ST_L1_F6, + ST_L1_F7, + ST_L1_F8, +}; + +#define L1_STATE_COUNT (ST_L1_F8+1) + +static char *strL1State[] = +{ + "ST_L1_F2", + "ST_L1_F3", + "ST_L1_F4", + "ST_L1_F5", + "ST_L1_F6", + "ST_L1_F7", + "ST_L1_F8", +}; + +enum { + EV_PH_ACTIVATE, + EV_RESET_IND, + EV_DEACT_CNF, + EV_DEACT_IND, + EV_POWER_UP, + EV_RSYNC_IND, + EV_INFO2_IND, + EV_INFO4_IND, + EV_TIMER_DEACT, + EV_TIMER_ACT, + EV_TIMER3, +}; + +#define L1_EVENT_COUNT (EV_TIMER3 + 1) + +static char *strL1Event[] = +{ + "EV_PH_ACTIVATE", + "EV_RESET_IND", + "EV_DEACT_CNF", + "EV_DEACT_IND", + "EV_POWER_UP", + "EV_RSYNC_IND", + "EV_INFO2_IND", + "EV_INFO4_IND", + "EV_TIMER_DEACT", + "EV_TIMER_ACT", + "EV_TIMER3", +}; /* * Find card with given driverId */ static inline struct IsdnCardState -* -hisax_findcard(int driverid) +*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; + if (cards[i].cs) + if (cards[i].cs->myid == driverid) + return (cards[i].cs); + return (NULL); } int @@ -162,6 +270,7 @@ HiSax_readstatus(u_char * buf, int len, int user, int id, int channel) } } +#if ISDN_CTRL_DEBUG void HiSax_putstatus(struct IsdnCardState *csta, char *buf) { @@ -194,6 +303,23 @@ HiSax_putstatus(struct IsdnCardState *csta, char *buf) csta->iif.statcallb(&ic); } } +#else +#define KDEBUG_DEF +#include "../kdebug.h" + +static int DbgLineNr=0,DbgSequenzNr=1; + +void +HiSax_putstatus(struct IsdnCardState *csta, char *buf) +{ + char tmp[512]; + + if (DbgLineNr==23) + DbgLineNr=0; + sprintf(tmp, "%5d %s",DbgSequenzNr++,buf); + gput_str(tmp,0,DbgLineNr++); +} +#endif int ll_run(struct IsdnCardState *csta) @@ -238,228 +364,119 @@ ll_unload(struct IsdnCardState *csta) } void -debugl1(struct IsdnCardState *sp, char *msg) +debugl1(struct IsdnCardState *cs, 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); + sprintf(tmp, "%s Card %d %s\n", tm, cs->cardnr + 1, msg); + HiSax_putstatus(cs, tmp); } -/* - * ISAC stuff goes here - */ - -char * -ISACVersion(u_char v) +static void +l1m_debug(struct FsmInst *fi, char *s) { - return (ISACVer[(v >> 5) & 3]); + struct PStack *st = fi->userdata; + + debugl1(st->l1.hardware, s); } void -isac_sched_event(struct IsdnCardState *sp, int event) -{ - sp->event |= 1 << event; - queue_task(&sp->tqueue, &tq_immediate); - mark_bh(IMMEDIATE_BH); -} - -int -act_wanted(struct IsdnCardState *sp) +L1activated(struct IsdnCardState *cs) { struct PStack *st; - st = sp->stlist; - while (st) - if (st->l1.act_state) - return (!0); + st = cs->stlist; + while (st) { + if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) + st->l1.l1man(st, PH_ACTIVATE_CNF, NULL); else - st = st->next; - return (0); -} - -void -isac_new_ph(struct IsdnCardState *sp) -{ - int enq; - - enq = act_wanted(sp); - - switch (sp->ph_state) { - case (6): - sp->ph_active = 0; - sp->ph_command(sp, 15); - break; - case (15): - sp->ph_active = 0; - if (enq) - sp->ph_command(sp, 0); - break; - case (0): - sp->ph_active = 0; - if (enq) - sp->ph_command(sp, 0); -#if 0 - else - sp->ph_command(sp, 15); -#endif - break; - case (7): - sp->ph_active = 0; - if (enq) - sp->ph_command(sp, 9); - break; - case (12): - sp->ph_command(sp, 8); - sp->ph_active = 5; - isac_sched_event(sp, ISAC_PHCHANGE); - if (!sp->tx_skb) - sp->tx_skb = skb_dequeue(&sp->sq); - if (sp->tx_skb) { - sp->tx_cnt = 0; - sp->isac_fill_fifo(sp); - } - break; - case (13): - sp->ph_command(sp, 9); - sp->ph_active = 5; - isac_sched_event(sp, ISAC_PHCHANGE); - if (!sp->tx_skb) - sp->tx_skb = skb_dequeue(&sp->sq); - if (sp->tx_skb) { - sp->tx_cnt = 0; - sp->isac_fill_fifo(sp); - } - break; - case (4): - case (8): - sp->ph_active = 0; - break; - default: - sp->ph_active = 0; - break; - } -} - -static void -restart_ph(struct IsdnCardState *sp) -{ - if (!sp->ph_active) { - if ((sp->ph_state == 6) || (sp->ph_state == 0)) { - sp->ph_command(sp, 0); - sp->ph_active = 2; - } else { - sp->ph_command(sp, 1); - sp->ph_active = 1; - } - } else if (sp->ph_active == 2) { - sp->ph_command(sp, 1); - sp->ph_active = 1; + st->l1.l1man(st, PH_ACTIVATE_IND, NULL); + st = st->next; } } - -static void -act_ivated(struct IsdnCardState *sp) +void +L1deactivated(struct IsdnCardState *cs) { struct PStack *st; - st = sp->stlist; + st = cs->stlist; while (st) { - if (st->l1.act_state == 1) { - st->l1.act_state = 2; - st->l1.l1man(st, PH_ACTIVATE, NULL); - } + if (test_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + st->l1.l1l2(st, PH_PAUSE_CNF, NULL); + st->l1.l1man(st, PH_DEACTIVATE_IND, NULL); st = st->next; } + test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags); } -static void -process_new_ph(struct IsdnCardState *sp) -{ - if (sp->ph_active == 5) - act_ivated(sp); -} - -static void -process_xmt(struct IsdnCardState *sp) +void +DChannel_proc_xmt(struct IsdnCardState *cs) { struct PStack *stptr; - if (sp->tx_skb) + if (cs->tx_skb) return; - stptr = sp->stlist; + stptr = cs->stlist; while (stptr != NULL) - if (stptr->l1.requestpull) { - stptr->l1.requestpull = 0; - stptr->l1.l1l2(stptr, PH_PULL_ACK, NULL); + if (test_and_clear_bit(FLG_L1_PULL_REQ, &stptr->l1.Flags)) { + stptr->l1.l1l2(stptr, PH_PULL_CNF, NULL); break; } else stptr = stptr->next; } -static void -process_rcv(struct IsdnCardState *sp) +void +DChannel_proc_rcv(struct IsdnCardState *cs) { struct sk_buff *skb, *nskb; - struct PStack *stptr; - int found, broadc; + struct PStack *stptr = cs->stlist; + int found, tei, sapi; char tmp[64]; - while ((skb = skb_dequeue(&sp->rq))) { + if (stptr) + if (test_bit(FLG_L1_ACTTIMER, &stptr->l1.Flags)) + FsmEvent(&stptr->l1.l1m, EV_TIMER_ACT, NULL); + while ((skb = skb_dequeue(&cs->rq))) { #ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - Logl2Frame(sp, skb, "PH_DATA", 1); + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA", 1); #endif - stptr = sp->stlist; - broadc = (skb->data[1] >> 1) == 127; - - if (broadc) { - if (!(skb->data[0] >> 2)) { /* sapi 0 */ - sp->CallFlags = 3; - if (sp->dlogflag) { - LogFrame(sp, skb->data, skb->len); - dlogframe(sp, skb->data + 3, skb->len - 3, + stptr = cs->stlist; + sapi = skb->data[0] >> 2; + tei = skb->data[1] >> 1; + + if (tei == GROUP_TEI) { + if (sapi == CTRL_SAPI) { /* sapi 0 */ + if (cs->dlogflag) { + LogFrame(cs, skb->data, skb->len); + dlogframe(cs, skb->data + 3, skb->len - 3, "Q.931 frame network->user broadcast"); } - } - while (stptr != NULL) { - if ((skb->data[0] >> 2) == stptr->l2.sap) + while (stptr != NULL) { if ((nskb = skb_clone(skb, GFP_ATOMIC))) - stptr->l1.l1l2(stptr, PH_DATA, nskb); + stptr->l1.l1l2(stptr, PH_DATA_IND, nskb); else printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n"); - stptr = stptr->next; + stptr = stptr->next; + } + } else if (sapi == TEI_SAPI) { + while (stptr != NULL) { + if ((nskb = skb_clone(skb, GFP_ATOMIC))) + stptr->l1.l1tei(stptr, PH_DATA_IND, nskb); + else + printk(KERN_WARNING "HiSax: tei broadcast buffer shortage\n"); + stptr = stptr->next; + } } - SET_SKB_FREE(skb); dev_kfree_skb(skb); - } else { + } else if (sapi == CTRL_SAPI) { found = 0; while (stptr != NULL) - if (((skb->data[0] >> 2) == stptr->l2.sap) && - ((skb->data[1] >> 1) == stptr->l2.tei)) { - stptr->l1.l1l2(stptr, PH_DATA, skb); + if (tei == stptr->l2.tei) { + stptr->l1.l1l2(stptr, PH_DATA_IND, skb); found = !0; break; } else @@ -474,167 +491,70 @@ process_rcv(struct IsdnCardState *sp) 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); + LogFrame(cs, skb->data, skb->len); + dlogframe(cs, skb->data + 4, skb->len - 4, tmp); } - SET_SKB_FREE(skb); dev_kfree_skb(skb); } } - - } - -} - -static void -isac_bh(struct IsdnCardState *sp) -{ - if (!sp) - return; - - if (test_and_clear_bit(ISAC_PHCHANGE, &sp->event)) - process_new_ph(sp); - if (test_and_clear_bit(ISAC_RCVBUFREADY, &sp->event)) - process_rcv(sp); - if (test_and_clear_bit(ISAC_XMTBUFREADY, &sp->event)) - process_xmt(sp); -} - -static void -l2l1(struct PStack *st, int pr, void *arg) -{ - struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware; - struct sk_buff *skb = arg; - char str[64]; - - switch (pr) { - case (PH_DATA): - if (sp->tx_skb) { - skb_queue_tail(&sp->sq, skb); -#ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - Logl2Frame(sp, skb, "PH_DATA Queued", 0); -#endif - } else { - if ((sp->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ - LogFrame(sp, skb->data, skb->len); - sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); - dlogframe(sp, skb->data + st->l2.ihsize, skb->len - st->l2.ihsize, - str); - } - sp->tx_skb = skb; - sp->tx_cnt = 0; -#ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - Logl2Frame(sp, skb, "PH_DATA", 0); -#endif - sp->isac_fill_fifo(sp); - } - break; - case (PH_DATA_PULLED): - if (sp->tx_skb) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, " l2l1 tx_skb exist this shouldn't happen"); - break; - } - if ((sp->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ - LogFrame(sp, skb->data, skb->len); - sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); - dlogframe(sp, skb->data + st->l2.ihsize, skb->len - st->l2.ihsize, - str); - } - sp->tx_skb = skb; - sp->tx_cnt = 0; -#ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - Logl2Frame(sp, skb, "PH_DATA_PULLED", 0); -#endif - sp->isac_fill_fifo(sp); - break; - case (PH_REQUEST_PULL): -#ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - debugl1(sp, "-> PH_REQUEST_PULL"); -#endif - if (!sp->tx_skb) { - st->l1.requestpull = 0; - st->l1.l1l2(st, PH_PULL_ACK, NULL); - } else - st->l1.requestpull = !0; - break; } } - static void -hscx_process_xmt(struct HscxState *hsp) +BChannel_proc_xmt(struct BCState *bcs) { - struct PStack *st = hsp->st; + struct PStack *st = bcs->st; - if (hsp->tx_skb) + if (test_bit(BC_FLG_BUSY, &bcs->Flag)) 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); + if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) + st->l1.l1l2(st, PH_PULL_CNF, NULL); + if (!test_bit(BC_FLG_ACTIV, &bcs->Flag)) + if (!test_bit(BC_FLG_BUSY, &bcs->Flag) && (!skb_queue_len(&bcs->squeue))) + st->ma.manl1(st, PH_DEACTIVATE_CNF, 0); } static void -hscx_process_rcv(struct HscxState *hsp) +BChannel_proc_rcv(struct BCState *bcs) { struct sk_buff *skb; -#ifdef DEBUG_MAGIC - if (hsp->magic != 301270) { - printk(KERN_DEBUG "hscx_process_rcv magic not 301270\n"); - return; - } -#endif - while ((skb = skb_dequeue(&hsp->rqueue))) { - hsp->st->l1.l1l2(hsp->st, PH_DATA, skb); + while ((skb = skb_dequeue(&bcs->rqueue))) { + bcs->st->l1.l1l2(bcs->st, PH_DATA_IND, skb); } } static void -hscx_bh(struct HscxState *hsp) +BChannel_bh(struct BCState *bcs) { - - if (!hsp) + if (!bcs) return; - - if (test_and_clear_bit(HSCX_RCVBUFREADY, &hsp->event)) - hscx_process_rcv(hsp); - if (test_and_clear_bit(HSCX_XMTBUFREADY, &hsp->event)) - hscx_process_xmt(hsp); - + if (test_and_clear_bit(B_RCVBUFREADY, &bcs->event)) + BChannel_proc_rcv(bcs); + if (test_and_clear_bit(B_XMTBUFREADY, &bcs->event)) + BChannel_proc_xmt(bcs); } -/* - * interrupt stuff ends here - */ - void -HiSax_addlist(struct IsdnCardState *sp, +HiSax_addlist(struct IsdnCardState *cs, struct PStack *st) { - st->next = sp->stlist; - sp->stlist = st; + st->next = cs->stlist; + cs->stlist = st; } void -HiSax_rmlist(struct IsdnCardState *sp, +HiSax_rmlist(struct IsdnCardState *cs, struct PStack *st) { struct PStack *p; - if (sp->stlist == st) - sp->stlist = st->next; + FsmDelTimer(&st->l1.timer, 0); + if (cs->stlist == st) + cs->stlist = st->next; else { - p = sp->stlist; + p = cs->stlist; while (p) if (p->next == st) { p->next = st->next; @@ -644,248 +564,117 @@ HiSax_rmlist(struct IsdnCardState *sp, } } -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))) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - } -} - void -setstack_HiSax(struct PStack *st, struct IsdnCardState *sp) +init_bcstate(struct IsdnCardState *cs, + int bc) { - 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", card->sp)) { - printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n", - card->sp->irq); - restore_flags(flags); - return (0); - } - restore_flags(flags); - return (1); -} - -static void -release_irq(int cardnr) -{ - struct IsdnCard *card = cards + cardnr; - - free_irq(card->sp->irq, card->sp); -} - -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); - } - while ((skb = skb_dequeue(&hs->squeue))) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - } - if (hs->tx_skb) { - SET_SKB_FREE(hs->tx_skb); - dev_kfree_skb(hs->tx_skb); - hs->tx_skb = NULL; - } - } - hs->init = 0; + struct BCState *bcs = cs->bcs + bc; + + bcs->cs = cs; + bcs->channel = bc; + bcs->tqueue.next = 0; + bcs->tqueue.sync = 0; + bcs->tqueue.routine = (void *) (void *) BChannel_bh; + bcs->tqueue.data = bcs; + bcs->BC_SetStack = NULL; + bcs->BC_Close = NULL; + bcs->Flag = 0; } static void closecard(int cardnr) { - struct IsdnCardState *csta = cards[cardnr].sp; + struct IsdnCardState *csta = cards[cardnr].cs; struct sk_buff *skb; - - close_hscxstate(csta->hs + 1); - close_hscxstate(csta->hs); + + if (csta->bcs->BC_Close != NULL) { + csta->bcs->BC_Close(csta->bcs + 1); + csta->bcs->BC_Close(csta->bcs); + } if (csta->rcvbuf) { kfree(csta->rcvbuf); csta->rcvbuf = NULL; } while ((skb = skb_dequeue(&csta->rq))) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); } while ((skb = skb_dequeue(&csta->sq))) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); } if (csta->tx_skb) { - SET_SKB_FREE(csta->tx_skb); dev_kfree_skb(csta->tx_skb); 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; + if (csta->mon_rx) { + kfree(csta->mon_rx); + csta->mon_rx = NULL; } + if (csta->mon_tx) { + kfree(csta->mon_tx); + csta->mon_tx = NULL; + } + csta->cardmsg(csta, CARD_RELEASE, NULL); + del_timer(&csta->dbusytimer); ll_unload(csta); } -static int -checkcard(int cardnr, char *id) +HISAX_INITFUNC(static int init_card(struct IsdnCardState *cs)) +{ + int irq_cnt, cnt = 3; + long flags; + + save_flags(flags); + cli(); + irq_cnt = kstat_irqs(cs->irq); + printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], cs->irq, + irq_cnt); + if (cs->cardmsg(cs, CARD_SETIRQ, NULL)) { + printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n", + cs->irq); + return(1); + } + while (cnt) { + cs->cardmsg(cs, CARD_INIT, NULL); + sti(); + current->state = TASK_INTERRUPTIBLE; + /* Timeout 10ms */ + current->timeout = jiffies + (10 * HZ) / 1000; + schedule(); + restore_flags(flags); + printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], + cs->irq, kstat_irqs(cs->irq)); + if (kstat_irqs(cs->irq) == irq_cnt) { + printk(KERN_WARNING + "%s: IRQ(%d) getting no interrupts during init %d\n", + CardType[cs->typ], cs->irq, 4 - cnt); + if (cnt == 1) { + free_irq(cs->irq, cs); + return (2); + } else { + cs->cardmsg(cs, CARD_RESET, NULL); + cnt--; + } + } else { + cs->cardmsg(cs, CARD_TEST, NULL); + return(0); + } + } + restore_flags(flags); + return(3); +} + +HISAX_INITFUNC(static int +checkcard(int cardnr, char *id, int *busy_flag)) { long flags; int ret = 0; struct IsdnCard *card = cards + cardnr; - struct IsdnCardState *sp; + struct IsdnCardState *cs; save_flags(flags); cli(); - if (!(sp = (struct IsdnCardState *) + if (!(cs = (struct IsdnCardState *) kmalloc(sizeof(struct IsdnCardState), GFP_ATOMIC))) { printk(KERN_WARNING "HiSax: No memory for IsdnCardState(card %d)\n", @@ -893,10 +682,16 @@ checkcard(int cardnr, char *id) restore_flags(flags); return (0); } - card->sp = sp; - sp->cardnr = cardnr; - sp->cfg_reg = 0; - sp->protocol = card->protocol; + card->cs = cs; + cs->cardnr = cardnr; + cs->debug = L1_DEB_WARN; + cs->HW_Flags = 0; + cs->busy_flag = busy_flag; +#if TEI_PER_CARD +#else + test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags); +#endif + cs->protocol = card->protocol; if ((card->typ > 0) && (card->typ < 31)) { if (!((1 << card->typ) & SUPORTED_CARDS)) { @@ -913,31 +708,34 @@ checkcard(int cardnr, char *id) restore_flags(flags); return (0); } - if (!(sp->dlogspace = kmalloc(4096, GFP_ATOMIC))) { + if (!(cs->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))) { + if (!(cs->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) { printk(KERN_WARNING "HiSax: No memory for status_buf(card %d)\n", cardnr + 1); - kfree(sp->dlogspace); + kfree(cs->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 = + cs->stlist = NULL; + cs->dlogflag = 0; + cs->mon_tx = NULL; + cs->mon_rx = NULL; + cs->status_read = cs->status_buf; + cs->status_write = cs->status_buf; + cs->status_end = cs->status_buf + HISAX_STATUS_BUFSIZE - 1; + cs->typ = card->typ; + strcpy(cs->iif.id, id); + cs->iif.channels = 2; + cs->iif.maxbufsize = MAX_DATA_SIZE; + cs->iif.hl_hdrlen = MAX_HEADER_LEN; + cs->iif.features = ISDN_FEATURE_L2_X75I | ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L2_TRANS | @@ -953,21 +751,19 @@ checkcard(int cardnr, char *id) #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 + cs->iif.command = HiSax_command; + cs->iif.writecmd = NULL; + cs->iif.writebuf_skb = HiSax_writebuf_skb; + cs->iif.readstat = HiSax_readstatus; + register_isdn(&cs->iif); + cs->myid = cs->iif.channels; + printk(KERN_INFO "HiSax: Card %d Protocol %s Id=%s (%d)\n", cardnr + 1, (card->protocol == ISDN_PTYPE_1TR6) ? "1TR6" : (card->protocol == ISDN_PTYPE_EURO) ? "EDSS1" : (card->protocol == ISDN_PTYPE_LEASED) ? "LEASED" : (card->protocol == ISDN_PTYPE_NI1) ? "NI1" : - "NONE", sp->iif.id, sp->myid); + "NONE", cs->iif.id, cs->myid); switch (card->typ) { #if CARD_TELES0 case ISDN_CTYPE_16_0: @@ -979,6 +775,7 @@ checkcard(int cardnr, char *id) case ISDN_CTYPE_16_3: case ISDN_CTYPE_PNP: case ISDN_CTYPE_TELESPCMCIA: + case ISDN_CTYPE_COMPAQ_ISA: ret = setup_teles3(card); break; #endif @@ -989,7 +786,9 @@ checkcard(int cardnr, char *id) #endif #if CARD_ELSA case ISDN_CTYPE_ELSA: - case ISDN_CTYPE_ELSA_QS1000: + case ISDN_CTYPE_ELSA_PNP: + case ISDN_CTYPE_ELSA_PCMCIA: + case ISDN_CTYPE_ELSA_PCI: ret = setup_elsa(card); break; #endif @@ -998,90 +797,103 @@ checkcard(int cardnr, char *id) 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; +#if CARD_DIEHLDIVA + case ISDN_CTYPE_DIEHLDIVA: + ret = setup_diva(card); + break; #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); +#if CARD_ASUSCOM + case ISDN_CTYPE_ASUSCOM: + ret = setup_asuscom(card); break; #endif -#if CARD_TELES3 - case ISDN_CTYPE_16_3: - case ISDN_CTYPE_PNP: - case ISDN_CTYPE_TELESPCMCIA: - ret = initteles3(sp); +#if CARD_TELEINT + case ISDN_CTYPE_TELEINT: + ret = setup_TeleInt(card); break; #endif -#if CARD_AVM_A1 - case ISDN_CTYPE_A1: - ret = initavm_a1(sp); +#if CARD_SEDLBAUER + case ISDN_CTYPE_SEDLBAUER: + case ISDN_CTYPE_SEDLBAUER_PCMCIA: + ret = setup_sedlbauer(card); break; #endif -#if CARD_ELSA - case ISDN_CTYPE_ELSA: - case ISDN_CTYPE_ELSA_QS1000: - ret = initelsa(sp); +#if CARD_SPORTSTER + case ISDN_CTYPE_SPORTSTER: + ret = setup_sportster(card); break; #endif -#if CARD_IX1MICROR2 - case ISDN_CTYPE_IX1MICROR2: - ret = initix1micro(sp); +#if CARD_MIC + case ISDN_CTYPE_MIC: + ret = setup_mic(card); break; #endif - default: - ret = 0; +#if CARD_NETJET + case ISDN_CTYPE_NETJET: + ret = setup_netjet(card); + break; +#endif +#if CARD_TELES3C + case ISDN_CTYPE_TELES3C: + ret = setup_t163c(card); + break; +#endif +#if CARD_NICCY + case ISDN_CTYPE_NICCY: + ret = setup_niccy(card); + break; +#endif +#if CARD_AMD7930 + case ISDN_CTYPE_AMD7930: + ret = setup_amd7930(card); break; +#endif + default: + printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n", + card->typ); + ll_unload(cs); + restore_flags(flags); + return (0); } if (!ret) { + ll_unload(cs); + restore_flags(flags); + return (0); + } + if (!(cs->rcvbuf = kmalloc(MAX_DFRAME_LEN, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for isac rcvbuf\n"); + return (1); + } + cs->rcvidx = 0; + cs->tx_skb = NULL; + cs->tx_cnt = 0; + cs->event = 0; + cs->tqueue.next = 0; + cs->tqueue.sync = 0; + cs->tqueue.data = cs; + + skb_queue_head_init(&cs->rq); + skb_queue_head_init(&cs->sq); + + init_bcstate(cs, 0); + init_bcstate(cs, 1); + ret = init_card(cs); + if (ret) { closecard(cardnr); + restore_flags(flags); return (0); } - init_tei(sp, sp->protocol); - CallcNewChan(sp); - ll_run(sp); + init_tei(cs, cs->protocol); + CallcNewChan(cs); + ll_run(cs); + cs->l1cmd(cs, PH_RESET_REQ, NULL); + restore_flags(flags); return (1); } -void -HiSax_shiftcards(int idx) +HISAX_INITFUNC(void +HiSax_shiftcards(int idx)) { int i; @@ -1089,8 +901,8 @@ HiSax_shiftcards(int idx) memcpy(&cards[i], &cards[i + 1], sizeof(cards[i])); } -int -HiSax_inithardware(void) +HISAX_INITFUNC(int +HiSax_inithardware(int *busy_flag)) { int foundcards = 0; int i = 0; @@ -1120,15 +932,15 @@ HiSax_inithardware(void) else sprintf(ids, "%s%d", id, i); } - if (checkcard(i, ids)) { + if (checkcard(i, ids, busy_flag)) { foundcards++; i++; } else { printk(KERN_WARNING "HiSax: Card %s not installed !\n", CardType[cards[i].typ]); - if (cards[i].sp) - kfree((void *) cards[i].sp); - cards[i].sp = NULL; + if (cards[i].cs) + kfree((void *) cards[i].cs); + cards[i].cs = NULL; HiSax_shiftcards(i); } } @@ -1144,159 +956,66 @@ HiSax_closehardware(void) 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); + if (cards[i].cs) { + ll_stop(cards[i].cs); + release_tei(cards[i].cs); closecard(i); - kfree((void *) cards[i].sp); - cards[i].sp = NULL; + free_irq(cards[i].cs->irq, cards[i].cs); + kfree((void *) cards[i].cs); + cards[i].cs = NULL; } + Isdnl1Free(); + TeiFree(); 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))) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - } -} - -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; + struct IsdnCardState *cs = cards[cardnr].cs; + struct PStack *stptr; + struct l3_process *pc; + int j, i = 1; printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1); - printk(KERN_DEBUG "HiSax: Type %s\n", CardType[sp->typ]); - printk(KERN_DEBUG "HiSax: debuglevel %x\n", sp->debug); + printk(KERN_DEBUG "HiSax: Type %s\n", CardType[cs->typ]); + printk(KERN_DEBUG "HiSax: debuglevel %x\n", cs->debug); printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n", (ulong) & HiSax_reportcard); + printk(KERN_DEBUG "HiSax: cs 0x%lX\n", (ulong) cs); + printk(KERN_DEBUG "HiSax: cs stl 0x%lX\n", (ulong) & (cs->stlist)); + stptr = cs->stlist; + while (stptr != NULL) { + printk(KERN_DEBUG "HiSax: dst%d 0x%lX\n", i, (ulong) stptr); + printk(KERN_DEBUG "HiSax: dst%d stp 0x%lX\n", i, (ulong) stptr->l1.stlistp); + printk(KERN_DEBUG "HiSax: tei %d sapi %d\n", + stptr->l2.tei, stptr->l2.sap); + printk(KERN_DEBUG "HiSax: man 0x%lX\n", (ulong) stptr->ma.layer); + pc = stptr->l3.proc; + while (pc) { + printk(KERN_DEBUG "HiSax: l3proc %x 0x%lX\n", pc->callref, + (ulong) pc); + printk(KERN_DEBUG "HiSax: state %d st 0x%lX chan 0x%lX\n", + pc->state, (ulong) pc->st, (ulong) pc->chan); + pc = pc->next; + } + stptr = stptr->next; + i++; + } + for (j = 0; j < 2; j++) { + printk(KERN_DEBUG "HiSax: ch%d 0x%lX\n", j, + (ulong) & cs->channel[j]); + stptr = cs->channel[j].b_st; + i = 1; + while (stptr != NULL) { + printk(KERN_DEBUG "HiSax: b_st%d 0x%lX\n", i, (ulong) stptr); + printk(KERN_DEBUG "HiSax: man 0x%lX\n", (ulong) stptr->ma.layer); + stptr = stptr->next; + i++; + } + } } #ifdef L2FRAME_DEBUG /* psa */ @@ -1366,7 +1085,7 @@ l2frames(u_char * ptr) } void -Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir) +Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir) { char tmp[132]; u_char *ptr; @@ -1374,13 +1093,293 @@ Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir) ptr = skb->data; if (ptr[0] & 1 || !(ptr[1] & 1)) - debugl1(sp, "Addres not LAPD"); + debugl1(cs, "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); + debugl1(cs, tmp); } } - #endif + +static void +l1_reset(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L1_F3); +} + +static void +l1_deact_cnf(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + + FsmChangeState(fi, ST_L1_F3); + if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) + cs->l1cmd(cs, PH_ENABLE_REQ, NULL); +} + +static void +l1_deact_req(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L1_F3); + if (!test_bit(FLG_L1_T3RUN, &st->l1.Flags)) { + FsmDelTimer(&st->l1.timer, 1); + FsmAddTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2); + test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags); + } +} + +static void +l1_power_up(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + + if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) { + FsmChangeState(fi, ST_L1_F4); + cs->l1cmd(cs, PH_INFO3_REQ, NULL); + FsmDelTimer(&st->l1.timer, 1); + FsmAddTimer(&st->l1.timer, TIMER3_VALUE * HZ, EV_TIMER3, NULL, 2); + test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags); + } else + FsmChangeState(fi, ST_L1_F3); +} + +static void +l1_go_F5(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L1_F5); +} + +static void +l1_go_F8(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L1_F8); +} + +static void +l1_info2_ind(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + + FsmChangeState(fi, ST_L1_F6); + cs->l1cmd(cs, PH_INFO3_REQ, NULL); +} + +static void +l1_info4_ind(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + + FsmChangeState(fi, ST_L1_F7); + cs->l1cmd(cs, PH_INFO3_REQ, NULL); + if (test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags)) + FsmDelTimer(&st->l1.timer, 4); + if (!test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) { + if (test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags)) + FsmDelTimer(&st->l1.timer, 3); + FsmAddTimer(&st->l1.timer, 110, EV_TIMER_ACT, NULL, 2); + test_and_set_bit(FLG_L1_ACTTIMER, &st->l1.Flags); + } +} + +static void +l1_timer3(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + + test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags); + if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) + L1deactivated(cs); + if (st->l1.l1m.state != ST_L1_F6) + FsmChangeState(fi, ST_L1_F3); +} + +static void +l1_timer_act(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + + test_and_clear_bit(FLG_L1_ACTTIMER, &st->l1.Flags); + test_and_set_bit(FLG_L1_ACTIVATED, &st->l1.Flags); + L1activated(cs); +} + +static void +l1_timer_deact(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + + test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags); + test_and_clear_bit(FLG_L1_ACTIVATED, &st->l1.Flags); + L1deactivated(cs); + cs->l1cmd(cs, PH_DEACT_ACK, NULL); +} + +static void +l1_activate(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + + cs->l1cmd(cs, PH_RESET_REQ, NULL); +} + +static struct FsmNode L1FnList[] HISAX_INITDATA = +{ + {ST_L1_F3, EV_PH_ACTIVATE, l1_activate}, + {ST_L1_F3, EV_RESET_IND, l1_reset}, + {ST_L1_F4, EV_RESET_IND, l1_reset}, + {ST_L1_F5, EV_RESET_IND, l1_reset}, + {ST_L1_F6, EV_RESET_IND, l1_reset}, + {ST_L1_F7, EV_RESET_IND, l1_reset}, + {ST_L1_F8, EV_RESET_IND, l1_reset}, + {ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F6, EV_DEACT_IND, l1_deact_req}, + {ST_L1_F7, EV_DEACT_IND, l1_deact_req}, + {ST_L1_F8, EV_DEACT_IND, l1_deact_req}, + {ST_L1_F3, EV_POWER_UP, l1_power_up}, + {ST_L1_F4, EV_RSYNC_IND, l1_go_F5}, + {ST_L1_F6, EV_RSYNC_IND, l1_go_F8}, + {ST_L1_F7, EV_RSYNC_IND, l1_go_F8}, + {ST_L1_F3, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F4, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F5, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F7, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F8, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F3, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F4, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F5, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F6, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F8, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F3, EV_TIMER3, l1_timer3}, + {ST_L1_F4, EV_TIMER3, l1_timer3}, + {ST_L1_F5, EV_TIMER3, l1_timer3}, + {ST_L1_F6, EV_TIMER3, l1_timer3}, + {ST_L1_F8, EV_TIMER3, l1_timer3}, + {ST_L1_F7, EV_TIMER_ACT, l1_timer_act}, + {ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact}, +}; + +#define L1_FN_COUNT (sizeof(L1FnList)/sizeof(struct FsmNode)) + +HISAX_INITFUNC(void Isdnl1New(void)) +{ + l1fsm.state_count = L1_STATE_COUNT; + l1fsm.event_count = L1_EVENT_COUNT; + l1fsm.strEvent = strL1Event; + l1fsm.strState = strL1State; + FsmNew(&l1fsm, L1FnList, L1_FN_COUNT); +} + +void Isdnl1Free(void) +{ + FsmFree(&l1fsm); +} + +static void +dch_manl1(struct PStack *st, int pr, + void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + char tmp[32]; + + switch (pr) { + case PH_ACTIVATE_REQ: + if (cs->debug) { + sprintf(tmp, "PH_ACTIVATE_REQ %s", + strL1State[st->l1.l1m.state]); + debugl1(cs, tmp); + } + if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) + st->l1.l1man(st, PH_ACTIVATE_CNF, NULL); + else { + test_and_set_bit(FLG_L1_ACTIVATING, &st->l1.Flags); + FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, arg); + } + break; + case PH_DEACTIVATE_REQ: + if (cs->debug) { + sprintf(tmp, "PH_DEACTIVATE_REQ %s", + strL1State[st->l1.l1m.state]); + debugl1(cs, tmp); + } + break; + case PH_TESTLOOP_REQ: + if (1 & (int) arg) + debugl1(cs, "PH_TEST_LOOP B1"); + if (2 & (int) arg) + debugl1(cs, "PH_TEST_LOOP B2"); + if (!(3 & (int) arg)) + debugl1(cs, "PH_TEST_LOOP DISABLED"); + cs->l1cmd(cs, PH_TESTLOOP_REQ, arg); + break; + case PH_RESET_IND: + FsmEvent(&st->l1.l1m, EV_RESET_IND, arg); + break; + case PH_DEACT_CNF: + FsmEvent(&st->l1.l1m, EV_DEACT_CNF, arg); + break; + case PH_DEACT_IND: + FsmEvent(&st->l1.l1m, EV_DEACT_IND, arg); + break; + case PH_POWERUP_CNF: + FsmEvent(&st->l1.l1m, EV_POWER_UP, arg); + break; + case PH_RSYNC_IND: + FsmEvent(&st->l1.l1m, EV_RSYNC_IND, arg); + break; + case PH_INFO2_IND: + FsmEvent(&st->l1.l1m, EV_INFO2_IND, arg); + break; + case PH_I4_P8_IND: + case PH_I4_P10_IND: + FsmEvent(&st->l1.l1m, EV_INFO4_IND, arg); + break; + default: + if (cs->debug) { + sprintf(tmp, "dch_manl1 msg %04X unhandled", pr); + debugl1(cs, tmp); + } + break; + } +} + +void +setstack_HiSax(struct PStack *st, struct IsdnCardState *cs) +{ + st->l1.hardware = cs; + st->protocol = cs->protocol; + st->l1.l1m.fsm = &l1fsm; + st->l1.l1m.state = ST_L1_F3; + st->l1.l1m.debug = cs->debug; + st->l1.l1m.userdata = st; + st->l1.l1m.userint = 0; + st->l1.l1m.printdebug = l1m_debug; + FsmInitTimer(&st->l1.l1m, &st->l1.timer); + setstack_tei(st); + setstack_manager(st); + st->l1.stlistp = &(cs->stlist); + st->ma.manl1 = dch_manl1; + st->l1.Flags = 0; + cs->setstack_d(st, cs); +} diff --git a/drivers/isdn/hisax/isdnl1.h b/drivers/isdn/hisax/isdnl1.h index d01d7012302a..71f081a07ed9 100644 --- a/drivers/isdn/hisax/isdnl1.h +++ b/drivers/isdn/hisax/isdnl1.h @@ -1,18 +1,23 @@ -/* $Id: isdnl1.h,v 1.4 1997/04/06 22:55:52 keil Exp $ - * +/* $Id: isdnl1.h,v 2.5 1998/02/02 13:36:58 keil Exp $ + * $Log: isdnl1.h,v $ - * Revision 1.4 1997/04/06 22:55:52 keil - * Using SKB's + * Revision 2.5 1998/02/02 13:36:58 keil + * more debug + * + * Revision 2.4 1997/11/08 21:35:49 keil + * new l1 init * - * Revision 1.3 1996/12/08 19:41:55 keil - * L2FRAME_DEBUG + * Revision 2.3 1997/10/29 19:07:53 keil + * changes for 2.1 * - * Revision 1.2 1996/10/27 22:26:27 keil - * ISAC/HSCX version functions + * Revision 2.2 1997/07/30 17:11:09 keil + * L1deactivated exported * - * Revision 1.1 1996/10/13 20:03:47 keil - * Initial revision + * Revision 2.1 1997/07/27 21:43:58 keil + * new l1 interface * + * Revision 2.0 1997/06/26 11:02:55 keil + * New Layer and card interface * * */ @@ -29,22 +34,25 @@ #define L1_DEB_HSCX 0x10 #define L1_DEB_HSCX_FIFO 0x20 #define L1_DEB_LAPD 0x40 +#define L1_DEB_IPAC 0x80 +#define L1_DEB_RECEIVE_FRAME 0x100 +#define D_RCVBUFREADY 0 +#define D_XMTBUFREADY 1 +#define D_L1STATECHANGE 2 +#define D_CLEARBUSY 3 +#define D_RX_MON0 4 +#define D_RX_MON1 5 +#define D_TX_MON0 6 +#define D_TX_MON1 7 -#define ISAC_RCVBUFREADY 0 -#define ISAC_XMTBUFREADY 1 -#define ISAC_PHCHANGE 2 - -#define HSCX_RCVBUFREADY 0 -#define HSCX_XMTBUFREADY 1 +#define B_RCVBUFREADY 0 +#define B_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 int get_irq(int cardnr, void *routine); +extern void DChannel_proc_xmt(struct IsdnCardState *cs); +extern void DChannel_proc_rcv(struct IsdnCardState *cs); + #ifdef L2FRAME_DEBUG extern void Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir); diff --git a/drivers/isdn/hisax/isdnl2.c b/drivers/isdn/hisax/isdnl2.c index 9c58eef90f16..eadfa4c8a32a 100644 --- a/drivers/isdn/hisax/isdnl2.c +++ b/drivers/isdn/hisax/isdnl2.c @@ -1,4 +1,4 @@ -/* $Id: isdnl2.c,v 1.10 1997/05/06 09:38:13 keil Exp $ +/* $Id: isdnl2.c,v 2.7 1998/02/12 23:07:47 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,56 +7,51 @@ * 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 2.7 1998/02/12 23:07:47 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() * - * Revision 1.9 1997/04/07 23:02:11 keil - * missing braces + * Revision 2.6 1998/02/02 13:36:15 keil + * bugfix X.75 win calculation * - * Revision 1.8 1997/04/06 22:59:59 keil - * Using SKB's; changing function names; some minor changes + * Revision 2.5 1997/11/06 17:09:22 keil + * New 2.1 init code * - * Revision 1.7 1997/02/09 00:25:44 keil - * new interface handling, one interface per card + * Revision 2.4 1997/10/29 19:02:01 keil + * new LL interface * - * Revision 1.6 1997/01/21 22:23:42 keil - * D-channel log changed + * Revision 2.3 1997/10/01 09:21:39 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. * - * Revision 1.5 1997/01/04 13:47:06 keil - * handling of MDL_REMOVE added (Thanks to Sim Yskes) + * Revision 2.2 1997/07/31 11:49:05 keil + * Eroor handling for no TEI assign * - * Revision 1.4 1996/12/08 19:51:51 keil - * many fixes from Pekka Sarnila + * Revision 2.1 1997/07/27 21:34:38 keil + * cosmetics * - * 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 + * Revision 2.0 1997/06/26 11:07:29 keil + * New q.921 and X.75 Layer2 * * + * Old log removed KKe * */ #define __NO_VERSION__ #include "hisax.h" #include "isdnl2.h" -const char *l2_revision = "$Revision: 1.10 $"; +const char *l2_revision = "$Revision: 2.7 $"; static void l2m_debug(struct FsmInst *fi, char *s); +static struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL}; enum { ST_L2_1, + ST_L2_2, ST_L2_3, ST_L2_4, ST_L2_5, @@ -70,6 +65,7 @@ enum { static char *strL2State[] = { "ST_L2_1", + "ST_L2_2", "ST_L2_3", "ST_L2_4", "ST_L2_5", @@ -81,53 +77,53 @@ static char *strL2State[] = enum { EV_L2_UI, EV_L2_SABMX, - EV_L2_UA, EV_L2_DISC, - EV_L2_I, - EV_L2_RR, - EV_L2_REJ, + EV_L2_DM, + EV_L2_UA, EV_L2_FRMR, + EV_L2_SUPER, + EV_L2_I, EV_L2_DL_DATA, + EV_L2_ACK_PULL, + EV_L2_DL_UNIT_DATA, EV_L2_DL_ESTABLISH, + EV_L2_DL_RELEASE, EV_L2_MDL_ASSIGN, EV_L2_MDL_REMOVE, - EV_L2_DL_UNIT_DATA, - EV_L2_DL_RELEASE, + EV_L2_MDL_ERROR, EV_L2_MDL_NOTEIPROC, + EV_L1_DEACTIVATE, EV_L2_T200, - EV_L2_ACK_PULL, EV_L2_T203, - EV_L2_RNR, }; -#define L2_EVENT_COUNT (EV_L2_RNR+1) +#define L2_EVENT_COUNT (EV_L2_T203+1) static char *strL2Event[] = { "EV_L2_UI", "EV_L2_SABMX", - "EV_L2_UA", "EV_L2_DISC", - "EV_L2_I", - "EV_L2_RR", - "EV_L2_REJ", + "EV_L2_DM", + "EV_L2_UA", "EV_L2_FRMR", + "EV_L2_SUPER", + "EV_L2_I", "EV_L2_DL_DATA", + "EV_L2_ACK_PULL", + "EV_L2_DL_UNIT_DATA", "EV_L2_DL_ESTABLISH", + "EV_L2_DL_RELEASE", "EV_L2_MDL_ASSIGN", "EV_L2_MDL_REMOVE", - "EV_L2_DL_UNIT_DATA", - "EV_L2_DL_RELEASE", + "EV_L2_MDL_ERROR", "EV_L2_MDL_NOTEIPROC", + "EV_L1_DEACTIVATE", "EV_L2_T200", - "EV_L2_ACK_PULL", "EV_L2_T203", - "EV_L2_RNR", }; -int errcount = 0; - -static int l2addrsize(struct Layer2 *tsp); +static int l2addrsize(struct Layer2 *l2); static void InitWin(struct Layer2 *l2) @@ -143,11 +139,9 @@ ReleaseWin(struct Layer2 *l2) { int i, cnt = 0; - for (i = 0; i < MAX_WINDOW; i++) { if (l2->windowar[i]) { cnt++; - SET_SKB_FREE(l2->windowar[i]); dev_kfree_skb(l2->windowar[i]); l2->windowar[i] = NULL; } @@ -156,13 +150,15 @@ ReleaseWin(struct Layer2 *l2) printk(KERN_WARNING "isdl2 freed %d skbuffs in release\n", cnt); } -static int +inline int cansend(struct PStack *st) { int p1; - p1 = (st->l2.va + st->l2.window) % (st->l2.extended ? 128 : 8); - return (st->l2.vs != p1); + p1 = st->l2.vs - st->l2.va; + if (p1 < 0) + p1 += (test_bit(FLG_MOD128, &st->l2.flag) ? 128 : 8); + return ((p1 < st->l2.window) && !test_bit(FLG_PEER_BUSY, &st->l2.flag)); } static void @@ -171,40 +167,54 @@ 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); } } -int -l2headersize(struct Layer2 *tsp, int ui) +static void +discard_ui_queue(struct PStack *st) { - return ((tsp->extended && (!ui) ? 2 : 1) + (tsp->laptype == LAPD ? 2 : 1)); + struct sk_buff *skb; + + while ((skb = skb_dequeue(&st->l2.ui_queue))) { + dev_kfree_skb(skb); + } } -int -l2addrsize(struct Layer2 *tsp) +inline void +clear_exception(struct Layer2 *l2) { - return (tsp->laptype == LAPD ? 2 : 1); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + test_and_clear_bit(FLG_REJEXC, &l2->flag); + test_and_clear_bit(FLG_OWN_BUSY, &l2->flag); + test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); +} + +inline int +l2headersize(struct Layer2 *l2, int ui) +{ + return (((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) + + (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1)); +} + +inline int +l2addrsize(struct Layer2 *l2) +{ + return (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1); } static int -sethdraddr(struct Layer2 *tsp, - u_char * header, int rsp) +sethdraddr(struct Layer2 *l2, u_char * header, int rsp) { u_char *ptr = header; - int crbit; + int crbit = rsp; - if (tsp->laptype == LAPD) { - crbit = rsp; - if (!tsp->orig) - crbit = !crbit; - *ptr++ = (tsp->sap << 2) | (crbit ? 2 : 0); - *ptr++ = (tsp->tei << 1) | 1; + if (test_bit(FLG_LAPD, &l2->flag)) { + *ptr++ = (l2->sap << 2) | (rsp ? 2 : 0); + *ptr++ = (l2->tei << 1) | 1; return (2); } else { - crbit = rsp; - if (tsp->orig) + if (test_bit(FLG_ORIG, &l2->flag)) crbit = !crbit; if (crbit) *ptr++ = 1; @@ -218,79 +228,107 @@ static void enqueue_ui(struct PStack *st, struct sk_buff *skb) { - st->l2.l2l1(st, PH_DATA, skb); + st->l2.l2l1(st, PH_DATA_REQ, skb); } static void enqueue_super(struct PStack *st, struct sk_buff *skb) { - st->l2.l2l1(st, PH_DATA, skb); + st->l2.l2l1(st, PH_DATA_REQ, skb); } -static int -legalnr(struct PStack *st, int nr) +inline int +IsUI(u_char * data, int ext) { - struct Layer2 *l2 = &st->l2; - int lnr, lvs; + return ((data[0] & 0xef) == UI); +} - lvs = (l2->vs >= l2->va) ? l2->vs : (l2->vs + l2->extended ? 128 : 8); - lnr = (nr >= l2->va) ? nr : (nr + l2->extended ? 128 : 8); - return (lnr <= lvs); +inline int +IsUA(u_char * data, int ext) +{ + return ((data[0] & 0xef) == UA); } -static void -setva(struct PStack *st, int nr) +inline int +IsDM(u_char * data, int ext) { - struct Layer2 *l2 = &st->l2; + return ((data[0] & 0xef) == DM); +} - if (l2->va != nr) { - while (l2->va != nr) { - l2->va = (l2->va + 1) % (l2->extended ? 128 : 8); - SET_SKB_FREE(l2->windowar[l2->sow]); - dev_kfree_skb(l2->windowar[l2->sow]); - l2->windowar[l2->sow] = NULL; - l2->sow = (l2->sow + 1) % l2->window; - if (st->l4.l2writewakeup) - st->l4.l2writewakeup(st); - } - } +inline int +IsDISC(u_char * data, int ext) +{ + return ((data[0] & 0xef) == DISC); } -static void -l2s1(struct FsmInst *fi, int event, void *arg) +inline int +IsRR(u_char * data, int ext) { - struct PStack *st = fi->userdata; + if (ext) + return (data[0] == RR); + else + return ((data[0] & 0xf) == 1); +} - st->l2.l2tei(st, MDL_ASSIGN, (void *) st->l2.ces); - FsmChangeState(fi, ST_L2_3); +inline int +IsSABMX(u_char * data, int ext) +{ + u_char d = data[0] & ~0x10; + + return (ext ? d == SABME : d == SABM); } -static void -l2_send_ui(struct FsmInst *fi, int event, void *arg) +inline int +IsREJ(u_char * data, int ext) { - struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; - u_char header[MAX_HEADER_LEN]; - int i; + return (ext ? data[0] == REJ : (data[0] & 0xf) == REJ); +} - i = sethdraddr(&(st->l2), header, CMD); - header[i++] = UI; - memcpy(skb_push(skb, i), header, i); - enqueue_ui(st, skb); +inline int +IsFRMR(u_char * data, int ext) +{ + return ((data[0] & 0xef) == FRMR); } -static void -l2_receive_ui(struct FsmInst *fi, int event, void *arg) +inline int +IsRNR(u_char * data, int ext) { - struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; + return (ext ? data[0] == RNR : (data[0] & 0xf) == RNR); +} - skb_pull(skb, l2headersize(&st->l2, 1)); - st->l2.l2l3(st, DL_UNIT_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 + (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8)); + lnr = (nr >= l2->va) ? nr : (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + return (lnr <= lvs); } -inline void +static void +setva(struct PStack *st, int nr) +{ + struct Layer2 *l2 = &st->l2; + int len; + + while (l2->va != nr) { + l2->va = (l2->va + 1) % (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + len = l2->windowar[l2->sow]->len; + if (PACKET_NOACK == l2->windowar[l2->sow]->pkt_type) + len = -1; + dev_kfree_skb(l2->windowar[l2->sow]); + l2->windowar[l2->sow] = NULL; + l2->sow = (l2->sow + 1) % l2->window; + if (st->lli.l2writewakeup && (len >=0)) + st->lli.l2writewakeup(st, len); + } +} + +static void send_uframe(struct PStack *st, u_char cmd, u_char cr) { struct sk_buff *skb; @@ -307,53 +345,156 @@ send_uframe(struct PStack *st, u_char cmd, u_char cr) enqueue_super(st, skb); } +inline u_char +get_PollFlag(struct PStack * st, struct sk_buff * skb) +{ + return (skb->data[l2addrsize(&(st->l2))] & 0x10); +} + +inline void +FreeSkb(struct sk_buff *skb) +{ + dev_kfree_skb(skb); +} + + +inline u_char +get_PollFlagFree(struct PStack *st, struct sk_buff *skb) +{ + u_char PF; + + PF = get_PollFlag(st, skb); + FreeSkb(skb); + return (PF); +} + static void establishlink(struct FsmInst *fi) { struct PStack *st = fi->userdata; u_char cmd; - FsmChangeState(fi, ST_L2_5); + clear_exception(&st->l2); st->l2.rc = 0; + cmd = (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM) | 0x10; + send_uframe(st, cmd, CMD); + FsmDelTimer(&st->l2.t203, 1); + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 1); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + FsmChangeState(fi, ST_L2_5); +} + +static void +l2_mdl_error(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct PStack *st = fi->userdata; - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 1)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 1"); + switch (event) { + case EV_L2_UA: + if (get_PollFlagFree(st, skb)) + st->ma.layer(st, MDL_ERROR_IND, (void *) 'C'); + else + st->ma.layer(st, MDL_ERROR_IND, (void *) 'D'); + break; + case EV_L2_DM: + if (get_PollFlagFree(st, skb)) + st->ma.layer(st, MDL_ERROR_IND, (void *) 'B'); + else { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'E'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); + } + break; + } +} +static void +l2_dl_establish(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + int state = fi->state; - cmd = (st->l2.extended ? SABME : SABM) | 0x10; - send_uframe(st, cmd, CMD); + FsmChangeState(fi, ST_L2_3); + if (state == ST_L2_1) + st->l2.l2tei(st, MDL_ASSIGN_IND, NULL); } static void -l2_establish(struct FsmInst *fi, int event, void *arg) +l2_send_ui(struct PStack *st) { - establishlink(fi); + struct sk_buff *skb; + u_char header[MAX_HEADER_LEN]; + int i; + + i = sethdraddr(&(st->l2), header, CMD); + header[i++] = UI; + while ((skb = skb_dequeue(&st->l2.ui_queue))) { + memcpy(skb_push(skb, i), header, i); + enqueue_ui(st, skb); + } } static void -l2_send_disconn(struct FsmInst *fi, int event, void *arg) +l2_put_ui(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; - - FsmChangeState(fi, ST_L2_6); + struct sk_buff *skb = arg; - FsmDelTimer(&st->l2.t203_timer, 1); - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 2); - st->l2.t200_running = 0; + skb_queue_tail(&st->l2.ui_queue, skb); + if (fi->state == ST_L2_1) { + FsmChangeState(fi, ST_L2_2); + st->l2.l2tei(st, MDL_ASSIGN_IND, NULL); } - st->l2.rc = 0; - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 2)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 2"); + if (fi->state > ST_L2_3) + l2_send_ui(st); +} +static void +l2_got_ui(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; - if (!((chanp->impair == 2) && (st->l2.laptype == LAPB))) - send_uframe(st, DISC | 0x10, CMD); + skb_pull(skb, l2headersize(&st->l2, 1)); + if (skb->len > st->l2.maxlen) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'O'); + FreeSkb(skb); + } else + st->l2.l2l3(st, DL_UNIT_DATA, skb); +} +static void +l2_establish(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + if (fi->state != ST_L2_4) + discard_i_queue(st); + if (fi->state != ST_L2_5) + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &st->l2.flag); +} + +static void +l2_dl_release(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + if (fi->state == ST_L2_4) { + st->l2.l2man(st, DL_RELEASE, NULL); + return; + } else if (fi->state == ST_L2_5) { + test_and_set_bit(FLG_PEND_REL, &st->l2.flag); + return; + } discard_i_queue(st); + FsmChangeState(fi, ST_L2_6); + st->l2.rc = 0; + send_uframe(st, DISC | 0x10, CMD); + FsmDelTimer(&st->l2.t203, 1); + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 2); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); } static void @@ -361,46 +502,59 @@ l2_got_SABMX(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - int est = 1, state; + int est = 1, state, rsp; u_char PollFlag; state = fi->state; - - skb_pull(skb, l2addrsize(&(st->l2))); - PollFlag = *skb->data & 0x10; - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - - if (ST_L2_4 != state) + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; + if (rsp) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'L'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + if (skb->len != (l2addrsize(&st->l2) + 1)) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + PollFlag = get_PollFlagFree(st, skb); + if (ST_L2_6 == state) { + send_uframe(st, DM | PollFlag, RSP); + return; + } else + send_uframe(st, UA | PollFlag, RSP); + if (ST_L2_5 == state) + return; + if (ST_L2_4 != state) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'F'); if (st->l2.vs != st->l2.va) { discard_i_queue(st); est = 1; } else est = 0; - + } + clear_exception(&st->l2); st->l2.vs = 0; st->l2.va = 0; st->l2.vr = 0; st->l2.sow = 0; - if (ST_L2_7 != state) - FsmChangeState(fi, ST_L2_7); - - send_uframe(st, UA | PollFlag, RSP); - - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 15); - st->l2.t200_running = 0; - } - if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 3)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 3"); + FsmChangeState(fi, ST_L2_7); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 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); + st->l2.l2l1(st, PH_PULL_REQ, NULL); } static void @@ -408,87 +562,152 @@ 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); + u_char PollFlag, cmd = UA; + int state, rel = 1, cst = 1, rsp; - FsmChangeState(fi, ST_L2_4); + state = fi->state; + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; - FsmDelTimer(&st->l2.t203_timer, 3); - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 4); - st->l2.t200_running = 0; + if (rsp) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'L'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + if (skb->len != (l2addrsize(&st->l2) + 1)) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; } - if (!((chanp->impair == 1) && (st->l2.laptype == LAPB))) - send_uframe(st, UA | PollFlag, RSP); - - st->l2.l2man(st, DL_RELEASE, NULL); + PollFlag = get_PollFlagFree(st, skb); + if ((state == ST_L2_4) || (state == ST_L2_5)) { + rel = 0; + cst = 0; + cmd = DM; + } else if (state == ST_L2_6) { + rel = 0; + cst = 0; + } + if (cst) { + FsmChangeState(fi, ST_L2_4); + FsmDelTimer(&st->l2.t203, 3); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + } + send_uframe(st, cmd | PollFlag, RSP); + if (rel) + st->l2.l2man(st, DL_RELEASE, NULL); } + static void -l2_got_st4_disc(struct FsmInst *fi, int event, void *arg) +l2_got_ua(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - struct Channel *chanp = st->l4.userdata; - u_char PollFlag; + u_char PollFlag, est = 1; + int state,rsp; - skb_pull(skb, l2addrsize(&(st->l2))); - PollFlag = *skb->data & 0x10; - SET_SKB_FREE(skb); - dev_kfree_skb(skb); + state = fi->state; + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; - if (!((chanp->impair == 1) && (st->l2.laptype == LAPB))) - send_uframe(st, DM | (PollFlag ? 0x10 : 0x0), RSP); + if (!rsp) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'L'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + if (skb->len != (l2addrsize(&st->l2) + 1)) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8)) + establishlink(fi); + return; + } + PollFlag = get_PollFlag(st, skb); + if (!PollFlag) { + l2_mdl_error(fi, event, arg); + return; + } + FreeSkb(skb); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + if (fi->state == ST_L2_5) { + if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag)) { + discard_i_queue(st); + st->l2.rc = 0; + send_uframe(st, DISC | 0x10, CMD); + FsmChangeState(fi, ST_L2_6); + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 4); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + } else { + if (!test_and_clear_bit(FLG_L3_INIT, &st->l2.flag)) + if (st->l2.vs != st->l2.va) + discard_i_queue(st); + else + est = 0; + st->l2.vs = 0; + st->l2.va = 0; + st->l2.vr = 0; + st->l2.sow = 0; + FsmChangeState(fi, ST_L2_7); + FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 4); + if (est) + st->l2.l2man(st, DL_ESTABLISH, NULL); + } + } else { /* ST_L2_6 */ + st->l2.l2man(st, DL_RELEASE, NULL); + FsmChangeState(fi, ST_L2_4); + } } static void -l2_got_ua_establish(struct FsmInst *fi, int event, void *arg) +l2_got_dm(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - u_char f; + u_char PollFlag; + int state,rsp; - skb_pull(skb, l2addrsize(&(st->l2))); - f = *skb->data & 0x10; - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - 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"); + state = fi->state; + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; - st->l2.l2man(st, DL_ESTABLISH, NULL); + if (!rsp) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'L'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + if (skb->len != (l2addrsize(&st->l2) + 1)) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8)) + establishlink(fi); + return; } -} - -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); - if (f) { - FsmDelTimer(&st->l2.t200_timer, 6); - FsmChangeState(fi, ST_L2_4); + PollFlag = get_PollFlagFree(st, skb); + if (!PollFlag) { + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); + } else { + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + if (fi->state == ST_L2_5 && !test_bit(FLG_L3_INIT, &st->l2.flag)) + discard_i_queue(st); st->l2.l2man(st, DL_RELEASE, NULL); + FsmChangeState(fi, ST_L2_4); } } @@ -502,7 +721,7 @@ enquiry_cr(struct PStack *st, u_char typ, u_char cr, u_char pf) l2 = &st->l2; i = sethdraddr(l2, tmp, cr); - if (l2->extended) { + if (test_bit(FLG_MOD128, &l2->flag)) { tmp[i++] = typ; tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0); } else @@ -516,279 +735,279 @@ enquiry_cr(struct PStack *st, u_char typ, u_char cr, u_char pf) } inline void -enquiry_response(struct PStack *st, u_char typ, u_char final) +enquiry_response(struct PStack *st) { - enquiry_cr(st, typ, RSP, final); + if (test_bit(FLG_OWN_BUSY, &st->l2.flag)) + enquiry_cr(st, RNR, RSP, 1); + else + enquiry_cr(st, RR, RSP, 1); + test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag); } inline void -enquiry_command(struct PStack *st, u_char typ, u_char poll) +transmit_enquiry(struct PStack *st) { - enquiry_cr(st, typ, CMD, poll); + if (test_bit(FLG_OWN_BUSY, &st->l2.flag)) + enquiry_cr(st, RNR, CMD, 1); + else + enquiry_cr(st, RR, CMD, 1); + test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag); + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 12); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); } + static void nrerrorrecovery(struct FsmInst *fi) { - /* should log error here */ + struct PStack *st = fi->userdata; + + st->ma.layer(st, MDL_ERROR_IND, (void *) 'J'); establishlink(fi); } static void -l2_got_st7_RR(struct FsmInst *fi, int event, void *arg) +invoke_retransmission(struct PStack *st, int nr) { - struct 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); + struct Layer2 *l2 = &st->l2; + int p1; + long flags; - if (!((chanp->impair == 4) && (st->l2.laptype == LAPB))) - if ((!rsp) && PollFlag) - enquiry_response(st, RR, PollFlag); - - if (legalnr(st, seq)) { - if (seq == l2->vs) { - setva(st, seq); - FsmDelTimer(&l2->t200_timer, 7); - l2->t200_running = 0; - FsmDelTimer(&l2->t203_timer, 8); - if (FsmAddTimer(&l2->t203_timer, l2->t203, EV_L2_T203, NULL, 5)) - if (l2->l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 5"); - - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); - } else if (l2->va != seq) { - setva(st, seq); - FsmDelTimer(&st->l2.t200_timer, 9); - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 6)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 6"); - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + if (l2->vs != nr) { + save_flags(flags); + cli(); + while (l2->vs != nr) { + l2->vs = l2->vs - 1; + if (l2->vs < 0) + l2->vs += (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + p1 = l2->vs - l2->va; + if (p1 < 0) + p1 += (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + p1 = (p1 + l2->sow) % l2->window; + skb_queue_head(&l2->i_queue, l2->windowar[p1]); + l2->windowar[p1] = 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); + restore_flags(flags); + st->l2.l2l1(st, PH_PULL_REQ, 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) +l2_got_st7_super(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; struct sk_buff *skb = arg; - 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; + int PollFlag, nr, rsp, typ = RR; + struct Layer2 *l2 = &st->l2; - l2->vr = (l2->vr + 1) % (l2->extended ? 128 : 8); - l2->rejexp = 0; + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; - 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)); + skb_pull(skb, l2addrsize(l2)); + if (IsRNR(skb->data, test_bit(FLG_MOD128, &l2->flag))) { + test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + typ = RNR; + } else + test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); + if (IsREJ(skb->data, test_bit(FLG_MOD128, &l2->flag))) + typ = REJ; + if (test_bit(FLG_MOD128, &l2->flag)) { + if (skb->len == 2) { + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; + } else { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } } else { - /* n(s)!=v(r) */ - wasok = 0; - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - if (st->l2.rejexp) { - if (p) - if (!((chanp->impair == 3) && (st->l2.laptype == LAPB))) - enquiry_response(st, RR, p); + if (skb->len == 1) { + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; } else { - st->l2.rejexp = !0; - enquiry_command(st, REJ, 1); + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; } } - 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); + FreeSkb(skb); + if ((!rsp) && PollFlag) + enquiry_response(st); + if (rsp && PollFlag) + st->ma.layer(st, MDL_ERROR_IND, (void *) 'A'); if (legalnr(st, nr)) { - if (nr == st->l2.vs) { + if (typ == REJ) { 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) { + invoke_retransmission(st, nr); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 9); + if (FsmAddTimer(&st->l2.t203, st->l2.T203, + EV_L2_T203, NULL, 6)) + l2m_debug(&st->l2.l2m, "Restart T203 ST7 REJ"); + } else if ((nr == l2->vs) && (typ == RR)) { setva(st, nr); - 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); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 9); + FsmRestartTimer(&st->l2.t203, st->l2.T203, + EV_L2_T203, NULL, 7); + } else if ((l2->va != nr) || (typ == RNR)) { + setva(st, nr); + FsmDelTimer(&st->l2.t203, 9); + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 6); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); } + if (skb_queue_len(&st->l2.i_queue) && (typ == RR)) + st->l2.l2l1(st, PH_PULL_REQ, 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; + 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); + } +} - p1 = l2->vs - l2->va; - if (p1 < 0) - p1 += l2->extended ? 128 : 8; - p1 = (p1 + l2->sow) % l2->window; +static void +l2_feed_iqueue(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; - skb_queue_head(&l2->i_queue, l2->windowar[p1]); - l2->windowar[p1] = NULL; - } - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); - } + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0); + if (!((fi->state == ST_L2_5) && test_bit(FLG_L3_INIT, &st->l2.flag))) + skb_queue_tail(&st->l2.i_queue, skb); + if (fi->state == ST_L2_7) + st->l2.l2l1(st, PH_PULL_REQ, NULL); } static void -l2_got_st7_rej(struct FsmInst *fi, int event, void *arg) +l2_got_iframe(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - int PollFlag, seq, rsp; - struct Layer2 *l2; + struct IsdnCardState *sp = st->l1.hardware; + struct Layer2 *l2 = &(st->l2); + int PollFlag, ns, nr, i, hs, rsp; + char str[64]; - l2 = &st->l2; - if (l2->laptype == LAPD) - rsp = *skb->data & 0x2; - else - rsp = *skb->data == 0x3; - if (l2->orig) + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) rsp = !rsp; - skb_pull(skb, l2addrsize(l2)); - if (l2->extended) { - PollFlag = (skb->data[1] & 0x1) == 0x1; - seq = skb->data[1] >> 1; + if (rsp) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'L'); + FreeSkb(skb); + establishlink(fi); + return; + } + i = l2addrsize(l2); + if (test_bit(FLG_MOD128, &l2->flag)) { + if (skb->len <= (i + 1)) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } else if ((skb->len - i - 1) > l2->maxlen) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'O'); + FreeSkb(skb); + establishlink(fi); + return; + } + PollFlag = ((skb->data[i + 1] & 0x1) == 0x1); + ns = skb->data[i] >> 1; + nr = (skb->data[i + 1] >> 1) & 0x7f; } else { - PollFlag = (skb->data[0] & 0x10); - seq = (skb->data[0] >> 5) & 0x7; + if (skb->len <= i) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } else if ((skb->len - i) > l2->maxlen) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'O'); + FreeSkb(skb); + establishlink(fi); + return; + } + PollFlag = (skb->data[i] & 0x10); + ns = (skb->data[i] >> 1) & 0x7; + nr = (skb->data[i] >> 5) & 0x7; + } + if (test_bit(FLG_OWN_BUSY, &l2->flag)) { + FreeSkb(skb); + enquiry_response(st); + } else if (l2->vr == ns) { + l2->vr = (l2->vr + 1) % (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + test_and_clear_bit(FLG_REJEXC, &l2->flag); + if (test_bit(FLG_LAPD, &l2->flag)) + if (sp->dlogflag) { + hs = l2headersize(l2, 0); + 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 + hs, + skb->len - hs, str); + } + if (PollFlag) + enquiry_response(st); + else + test_and_set_bit(FLG_ACK_PEND, &l2->flag); + skb_pull(skb, l2headersize(l2, 0)); + st->l2.l2l3(st, DL_DATA, skb); + } else { + /* n(s)!=v(r) */ + FreeSkb(skb); + if (test_and_set_bit(FLG_REJEXC, &l2->flag)) { + if (PollFlag) + enquiry_response(st); + } else { + enquiry_cr(st, REJ, RSP, PollFlag); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + } } - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - - if ((!rsp) && PollFlag) - enquiry_response(st, RR, PollFlag); - if (!legalnr(st, seq)) + if (legalnr(st, nr)) { + setva(st, nr); + if (!test_bit(FLG_PEER_BUSY, &st->l2.flag) && (fi->state == ST_L2_7)) { + if (nr == st->l2.vs) { + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 10); + FsmRestartTimer(&st->l2.t203, st->l2.T203, + EV_L2_T203, NULL, 7); + } else if (nr != st->l2.va) { + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, + NULL, 8); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + } + } + } else { + nrerrorrecovery(fi); return; + } + + if (skb_queue_len(&st->l2.i_queue) && (fi->state == ST_L2_7)) + st->l2.l2l1(st, PH_PULL_REQ, NULL); + if (test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag)) + enquiry_cr(st, RR, RSP, 0); +} + +static void +l2_got_tei(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; - setva(st, seq); - invoke_retransmission(st, seq); + st->l2.tei = (int) arg; + + if (fi->state == ST_L2_3) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &st->l2.flag); + } else + FsmChangeState(fi, ST_L2_4); + if (skb_queue_len(&st->l2.ui_queue)) + l2_send_ui(st); } static void @@ -801,21 +1020,21 @@ 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) { + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + } else if (st->l2.rc == st->l2.N200) { FsmChangeState(fi, ST_L2_4); - st->l2.l2tei(st, MDL_VERIFY, (void *) st->l2.tei); + test_and_clear_bit(FLG_T200_RUN, &st->l2.flag); + discard_i_queue(st); + st->ma.layer(st, MDL_ERROR_IND, (void *) 'G'); 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); + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + send_uframe(st, (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM) + | 0x10, CMD); } } @@ -823,30 +1042,65 @@ 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) { + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + } else if (st->l2.rc == st->l2.N200) { FsmChangeState(fi, ST_L2_4); + st->ma.layer(st, MDL_ERROR_IND, (void *) 'H'); st->l2.l2man(st, DL_RELEASE, NULL); } else { st->l2.rc++; + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, + NULL, 9); + send_uframe(st, DISC | 0x10, CMD); + } +} - 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"); +static void +l2_st78_tout_200(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + return; + } + test_and_clear_bit(FLG_T200_RUN, &st->l2.flag); + if (fi->state == ST_L2_7) { + st->l2.rc = 0; + FsmChangeState(fi, ST_L2_8); + } + if (st->l2.rc == st->l2.N200) { + establishlink(fi); + } else { + transmit_enquiry(st); + st->l2.rc++; + } +} - if (!((chanp->impair == 2) && (st->l2.laptype == LAPB))) - send_uframe(st, DISC | 0x10, CMD); +static void +l2_st7_tout_203(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 9); + return; } + FsmChangeState(fi, ST_L2_8); + transmit_enquiry(st); + st->l2.rc = 0; } static void l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct sk_buff *skb; + struct sk_buff *skb, *oskb; struct Layer2 *l2 = &st->l2; u_char header[MAX_HEADER_LEN]; int p1, i; @@ -860,19 +1114,18 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) p1 = l2->vs - l2->va; if (p1 < 0) - p1 += l2->extended ? 128 : 8; + p1 += test_bit(FLG_MOD128, &l2->flag) ? 128 : 8; p1 = (p1 + l2->sow) % l2->window; if (l2->windowar[p1]) { printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n", p1); - SET_SKB_FREE(l2->windowar[p1]); dev_kfree_skb(l2->windowar[p1]); } l2->windowar[p1] = skb_clone(skb, GFP_ATOMIC); i = sethdraddr(&st->l2, header, CMD); - if (l2->extended) { + if (test_bit(FLG_MOD128, &l2->flag)) { header[i++] = l2->vs << 1; header[i++] = l2->vr << 1; l2->vs = (l2->vs + 1) % 128; @@ -880,88 +1133,86 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) header[i++] = (l2->vr << 5) | (l2->vs << 1); l2->vs = (l2->vs + 1) % 8; } - - memcpy(skb_push(skb, i), header, i); - st->l2.l2l1(st, PH_DATA_PULLED, skb); - if (!st->l2.t200_running) { - FsmDelTimer(&st->l2.t203_timer, 13); - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 11)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 9"); - - st->l2.t200_running = !0; + p1 = skb->data - skb->head; + if (p1 >= i) + memcpy(skb_push(skb, i), header, i); + else { + printk(KERN_WARNING + "isdl2 pull_iqueue skb header(%d/%d) too short\n", i, p1); + oskb = skb; + skb = alloc_skb(oskb->len + i, GFP_ATOMIC); + memcpy(skb_put(skb, i), header, i); + memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len); + FreeSkb(oskb); + } + st->l2.l2l1(st, PH_PULL_IND, skb); + test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag); + if (!test_and_set_bit(FLG_T200_RUN, &st->l2.flag)) { + FsmDelTimer(&st->l2.t203, 13); + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 11); } if (skb_queue_len(&l2->i_queue) && cansend(st)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + st->l2.l2l1(st, PH_PULL_REQ, 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) +l2_got_st8_super(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - int PollFlag, seq, rsp; - struct Layer2 *l2; + int PollFlag, nr, rsp, rnr = 0; + struct Layer2 *l2 = &st->l2; - l2 = &st->l2; - if (l2->laptype == LAPD) - rsp = *skb->data & 0x2; - else - rsp = *skb->data == 0x3; - if (l2->orig) + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) rsp = !rsp; - skb_pull(skb, l2addrsize(l2)); - if (l2->extended) { - PollFlag = (skb->data[1] & 0x1) == 0x1; - seq = skb->data[1] >> 1; + + if (IsRNR(skb->data, test_bit(FLG_MOD128, &l2->flag))) { + test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + rnr = 1; + } else + test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); + if (test_bit(FLG_MOD128, &l2->flag)) { + if (skb->len == 2) { + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; + } else { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } } else { - PollFlag = (skb->data[0] & 0x10); - seq = (skb->data[0] >> 5) & 0x7; + if (skb->len == 1) { + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; + } else { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } } - SET_SKB_FREE(skb); - dev_kfree_skb(skb); + FreeSkb(skb); if (rsp && PollFlag) { - if (legalnr(st, seq)) { + if (legalnr(st, nr)) { + setva(st, nr); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 7); + FsmDelTimer(&l2->t203, 8); + if (rnr) { + FsmRestartTimer(&l2->t200, l2->T200, + EV_L2_T200, NULL, 14); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + } else + FsmAddTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 5); + invoke_retransmission(st, nr); FsmChangeState(fi, ST_L2_7); - setva(st, seq); - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 14); - st->l2.t200_running = 0; - } - if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 13)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 11"); - - invoke_retransmission(st, seq); - if (skb_queue_len(&l2->i_queue) && cansend(st)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + st->l2.l2l1(st, PH_PULL_REQ, NULL); else if (fi->userint & LC_FLUSH_WAIT) { fi->userint &= ~LC_FLUSH_WAIT; st->l2.l2man(st, DL_FLUSH, NULL); @@ -969,36 +1220,13 @@ l2_got_st8_rr_rej(struct FsmInst *fi, int event, void *arg) } } else { if (!rsp && PollFlag) - enquiry_response(st, RR, PollFlag); - if (legalnr(st, seq)) { - setva(st, seq); + enquiry_response(st); + if (legalnr(st, nr)) { + setva(st, nr); } } } -static void -l2_st7_tout_203(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - - st->l2.rc = 0; - FsmChangeState(fi, ST_L2_8); - transmit_enquiry(st); -} - -static void -l2_st8_tout_200(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - - if (st->l2.rc == st->l2.n200) { - establishlink(fi); - } else { - st->l2.rc++; - transmit_enquiry(st); - } -} - static void l2_got_FRMR(struct FsmInst *fi, int event, void *arg) { @@ -1006,20 +1234,32 @@ l2_got_FRMR(struct FsmInst *fi, int event, void *arg) struct sk_buff *skb = arg; char tmp[64]; - skb_pull(skb, l2addrsize(&st->l2)); - if (st->l2.l2m.debug) { - if (st->l2.extended) + skb_pull(skb, l2addrsize(&st->l2) + 1); + if (test_bit(FLG_MOD128, &st->l2.flag)) { + if (skb->len < 5) + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + else { 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 + l2m_debug(&st->l2.l2m, tmp); + } + } else { + if (skb->len < 3) + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + else { sprintf(tmp, "FRMR information %2x %2x %2x", skb->data[0], skb->data[1], skb->data[2]); - - l2m_debug(&st->l2.l2m, tmp); + l2m_debug(&st->l2.l2m, tmp); + } } - SET_SKB_FREE(skb); - dev_kfree_skb(skb); + if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */ + (IsUA(skb->data, 0) && (fi->state == ST_L2_7))) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'K'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); + } + FreeSkb(skb); } static void @@ -1027,135 +1267,126 @@ 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 */ + discard_i_queue(st); + discard_ui_queue(st); + st->l2.tei = -1; + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 18); + FsmDelTimer(&st->l2.t203, 19); + if (fi->state != ST_L2_4) + st->l2.l2man(st, DL_RELEASE, NULL); FsmChangeState(fi, ST_L2_1); } -inline int -IsUI(u_char * data, int ext) -{ - return ((data[0] & 0xef) == UI); -} - -inline int -IsUA(u_char * data, int ext) -{ - return ((data[0] & 0xef) == UA); -} - -inline int -IsDISC(u_char * data, int ext) -{ - return ((data[0] & 0xef) == DISC); -} - -inline int -IsRR(u_char * data, int ext) -{ - if (ext) - return (data[0] == RR); - else - return ((data[0] & 0xf) == 1); -} - -inline int -IsI(u_char * data, int ext) -{ - return ((data[0] & 0x1) == 0x0); -} - -inline int -IsSABMX(u_char * data, int ext) +static void +l2_persistant_da(struct FsmInst *fi, int event, void *arg) { - u_char d = data[0] & ~0x10; + struct PStack *st = fi->userdata; - return (ext ? d == SABME : d == SABM); -} - -inline int -IsREJ(u_char * data, int ext) -{ - return (ext ? data[0] == REJ : (data[0] & 0xf) == 0x9); -} - -inline int -IsFRMR(u_char * data, int ext) -{ - return ((data[0] & 0xef) == FRMR); -} - -inline int -IsRNR(u_char * data, int ext) -{ - if (ext) - return (data[0] == RNR); - else - return ((data[0] & 0xf) == 5); + discard_i_queue(st); + discard_ui_queue(st); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 18); + FsmDelTimer(&st->l2.t203, 19); + test_and_clear_bit(FLG_PEND_REL, &st->l2.flag); + clear_exception(&st->l2); + switch (fi->state) { + case ST_L2_3: + st->l2.l2man(st, DL_RELEASE, NULL); + case ST_L2_2: + FsmChangeState(fi, ST_L2_1); + break; + case ST_L2_5: + case ST_L2_6: + case ST_L2_7: + case ST_L2_8: + st->l2.l2man(st, DL_RELEASE, NULL); + FsmChangeState(fi, ST_L2_4); + break; + } } -static struct FsmNode L2FnList[] = +static struct FsmNode L2FnList[] HISAX_INITDATA = { - {ST_L2_1, EV_L2_DL_ESTABLISH, l2s1}, {ST_L2_1, EV_L2_MDL_NOTEIPROC, l2_no_tei}, - {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, - {ST_L2_4, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {ST_L2_1, EV_L2_DL_ESTABLISH, l2_dl_establish}, + {ST_L2_2, EV_L2_DL_ESTABLISH, l2_dl_establish}, {ST_L2_4, EV_L2_DL_ESTABLISH, l2_establish}, - {ST_L2_7, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {ST_L2_5, EV_L2_DL_ESTABLISH, l2_establish}, + {ST_L2_7, EV_L2_DL_ESTABLISH, l2_establish}, + {ST_L2_8, EV_L2_DL_ESTABLISH, l2_establish}, + {ST_L2_4, EV_L2_DL_RELEASE, l2_dl_release}, + {ST_L2_5, EV_L2_DL_RELEASE, l2_dl_release}, + {ST_L2_7, EV_L2_DL_RELEASE, l2_dl_release}, + {ST_L2_8, EV_L2_DL_RELEASE, l2_dl_release}, + {ST_L2_5, EV_L2_DL_DATA, l2_feed_iqueue}, {ST_L2_7, EV_L2_DL_DATA, l2_feed_iqueue}, - {ST_L2_7, EV_L2_DL_RELEASE, l2_send_disconn}, - {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue}, {ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue}, - {ST_L2_8, EV_L2_DL_RELEASE, l2_send_disconn}, - - {ST_L2_1, EV_L2_UI, l2_receive_ui}, - {ST_L2_4, EV_L2_UI, l2_receive_ui}, + {ST_L2_1, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_2, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_3, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_4, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_5, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_6, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_7, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_8, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ERROR, l2_tei_remove}, + {ST_L2_3, EV_L2_MDL_ERROR, l2_tei_remove}, + {ST_L2_4, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_5, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_6, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove}, {ST_L2_4, EV_L2_SABMX, l2_got_SABMX}, - {ST_L2_4, EV_L2_DISC, l2_got_st4_disc}, - {ST_L2_5, EV_L2_UA, l2_got_ua_establish}, - {ST_L2_6, EV_L2_UA, l2_got_ua_disconn}, - {ST_L2_7, EV_L2_UI, l2_receive_ui}, - {ST_L2_7, EV_L2_DISC, l2_got_disconn}, - {ST_L2_7, EV_L2_I, l2_got_st7_data}, - {ST_L2_7, EV_L2_RR, l2_got_st7_RR}, - {ST_L2_7, EV_L2_REJ, l2_got_st7_rej}, + {ST_L2_5, EV_L2_SABMX, l2_got_SABMX}, + {ST_L2_6, EV_L2_SABMX, l2_got_SABMX}, {ST_L2_7, EV_L2_SABMX, l2_got_SABMX}, - {ST_L2_7, EV_L2_FRMR, l2_got_FRMR}, - {ST_L2_8, EV_L2_RR, l2_got_st8_rr_rej}, - {ST_L2_8, EV_L2_REJ, l2_got_st8_rr_rej}, {ST_L2_8, EV_L2_SABMX, l2_got_SABMX}, + {ST_L2_4, EV_L2_DISC, l2_got_disconn}, + {ST_L2_5, EV_L2_DISC, l2_got_disconn}, + {ST_L2_6, EV_L2_DISC, l2_got_disconn}, + {ST_L2_7, EV_L2_DISC, l2_got_disconn}, {ST_L2_8, EV_L2_DISC, l2_got_disconn}, + {ST_L2_4, EV_L2_UA, l2_mdl_error}, + {ST_L2_5, EV_L2_UA, l2_got_ua}, + {ST_L2_6, EV_L2_UA, l2_got_ua}, + {ST_L2_7, EV_L2_UA, l2_mdl_error}, + {ST_L2_8, EV_L2_UA, l2_mdl_error}, + {ST_L2_4, EV_L2_DM, l2_got_dm}, + {ST_L2_5, EV_L2_DM, l2_got_dm}, + {ST_L2_6, EV_L2_DM, l2_got_dm}, + {ST_L2_7, EV_L2_DM, l2_mdl_error}, + {ST_L2_8, EV_L2_DM, l2_mdl_error}, + {ST_L2_1, EV_L2_UI, l2_got_ui}, + {ST_L2_2, EV_L2_UI, l2_got_ui}, + {ST_L2_3, EV_L2_UI, l2_got_ui}, + {ST_L2_4, EV_L2_UI, l2_got_ui}, + {ST_L2_5, EV_L2_UI, l2_got_ui}, + {ST_L2_6, EV_L2_UI, l2_got_ui}, + {ST_L2_7, EV_L2_UI, l2_got_ui}, + {ST_L2_8, EV_L2_UI, l2_got_ui}, + {ST_L2_7, EV_L2_FRMR, l2_got_FRMR}, {ST_L2_8, EV_L2_FRMR, l2_got_FRMR}, - {ST_L2_8, EV_L2_I, l2_got_st8_data}, - + {ST_L2_7, EV_L2_SUPER, l2_got_st7_super}, + {ST_L2_8, EV_L2_SUPER, l2_got_st8_super}, + {ST_L2_7, EV_L2_I, l2_got_iframe}, + {ST_L2_8, EV_L2_I, l2_got_iframe}, {ST_L2_5, EV_L2_T200, l2_st5_tout_200}, {ST_L2_6, EV_L2_T200, l2_st6_tout_200}, - {ST_L2_7, EV_L2_T200, l2_st7_tout_200}, + {ST_L2_7, EV_L2_T200, l2_st78_tout_200}, + {ST_L2_8, EV_L2_T200, l2_st78_tout_200}, {ST_L2_7, EV_L2_T203, l2_st7_tout_203}, - {ST_L2_8, EV_L2_T200, l2_st8_tout_200}, - - {ST_L2_1, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_3, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_4, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_5, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_6, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue}, + {ST_L2_2, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_3, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_4, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_5, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_6, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da}, }; #define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode)) @@ -1165,40 +1396,53 @@ isdnl2_l1l2(struct PStack *st, int pr, void *arg) { struct sk_buff *skb = arg; u_char *datap; - int ret = !0; + int ret = 1, len; switch (pr) { - case (PH_DATA): + case (PH_DATA_IND): datap = skb->data; - datap += l2addrsize(&st->l2); - - if (IsI(datap, st->l2.extended)) + len = l2addrsize(&st->l2); + if (skb->len > len) + datap += len; + else { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + return; + } + if (!(*datap & 1)) /* I-Frame */ ret = FsmEvent(&st->l2.l2m, EV_L2_I, skb); - else if (IsRR(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_RR, skb); - else if (IsUI(datap, st->l2.extended)) + else if ((*datap & 3) == 1) /* S-Frame */ + ret = FsmEvent(&st->l2.l2m, EV_L2_SUPER, skb); + else if (IsUI(datap, test_bit(FLG_MOD128, &st->l2.flag))) ret = FsmEvent(&st->l2.l2m, EV_L2_UI, skb); - else if (IsSABMX(datap, st->l2.extended)) + else if (IsSABMX(datap, test_bit(FLG_MOD128, &st->l2.flag))) ret = FsmEvent(&st->l2.l2m, EV_L2_SABMX, skb); - else if (IsUA(datap, st->l2.extended)) + else if (IsUA(datap, test_bit(FLG_MOD128, &st->l2.flag))) ret = FsmEvent(&st->l2.l2m, EV_L2_UA, skb); - else if (IsDISC(datap, st->l2.extended)) + else if (IsDISC(datap, test_bit(FLG_MOD128, &st->l2.flag))) ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, skb); - else if (IsREJ(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_REJ, skb); - else if (IsFRMR(datap, st->l2.extended)) + else if (IsDM(datap, test_bit(FLG_MOD128, &st->l2.flag))) + ret = FsmEvent(&st->l2.l2m, EV_L2_DM, skb); + else if (IsFRMR(datap, test_bit(FLG_MOD128, &st->l2.flag))) ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, skb); - else if (IsRNR(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_RNR, skb); - + else { + ret = 0; + st->ma.layer(st, MDL_ERROR_IND, (void *) 'L'); + FreeSkb(skb); + } if (ret) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb); + FreeSkb(skb); } break; - case (PH_PULL_ACK): + case (PH_PULL_CNF): FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg); break; + case (PH_PAUSE_IND): + test_and_set_bit(FLG_DCHAN_BUSY, &st->l2.flag); + break; + case (PH_PAUSE_CNF): + test_and_clear_bit(FLG_DCHAN_BUSY, &st->l2.flag); + break; } } @@ -1208,13 +1452,11 @@ isdnl2_l3l2(struct PStack *st, int pr, void *arg) switch (pr) { case (DL_DATA): if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg)) { - SET_SKB_FREE(((struct sk_buff *) arg)); dev_kfree_skb((struct sk_buff *) arg); } break; case (DL_UNIT_DATA): if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg)) { - SET_SKB_FREE(((struct sk_buff *) arg)); dev_kfree_skb((struct sk_buff *) arg); } break; @@ -1237,28 +1479,28 @@ isdnl2_manl2(struct PStack *st, int pr, void *arg) 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): + case (PH_DEACTIVATE_IND): + FsmEvent(&st->l2.l2m, EV_L1_DEACTIVATE, arg); + break; + case (MDL_ASSIGN_REQ): FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg); break; - case (MDL_REMOVE): + case (MDL_REMOVE_REQ): FsmEvent(&st->l2.l2m, EV_L2_MDL_REMOVE, arg); break; + case (MDL_ERROR_REQ): + FsmEvent(&st->l2.l2m, EV_L2_MDL_ERROR, arg); + break; } } void releasestack_isdnl2(struct PStack *st) { - FsmDelTimer(&st->l2.t200_timer, 15); - FsmDelTimer(&st->l2.t203_timer, 16); + FsmDelTimer(&st->l2.t200, 15); + FsmDelTimer(&st->l2.t203, 16); discard_i_queue(st); + discard_ui_queue(st); ReleaseWin(&st->l2); } @@ -1279,13 +1521,10 @@ 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); + skb_queue_head_init(&st->l2.ui_queue); InitWin(&st->l2); - st->l2.rejexp = 0; st->l2.debug = 0; st->l2.l2m.fsm = &l2fsm; @@ -1296,9 +1535,8 @@ setstack_isdnl2(struct PStack *st, char *debug_id) st->l2.l2m.printdebug = l2m_debug; strcpy(st->l2.debug_id, debug_id); - FsmInitTimer(&st->l2.l2m, &st->l2.t200_timer); - FsmInitTimer(&st->l2.l2m, &st->l2.t203_timer); - st->l2.t200_running = 0; + FsmInitTimer(&st->l2.l2m, &st->l2.t200); + FsmInitTimer(&st->l2.l2m, &st->l2.t203); } void @@ -1311,8 +1549,8 @@ releasestack_transl2(struct PStack *st) { } -void -Isdnl2New(void) +HISAX_INITFUNC(void +Isdnl2New(void)) { l2fsm.state_count = L2_STATE_COUNT; l2fsm.event_count = L2_EVENT_COUNT; diff --git a/drivers/isdn/hisax/isdnl3.c b/drivers/isdn/hisax/isdnl3.c index 73b66c73b6b2..026d8eb760f0 100644 --- a/drivers/isdn/hisax/isdnl3.c +++ b/drivers/isdn/hisax/isdnl3.c @@ -1,4 +1,4 @@ -/* $Id: isdnl3.c,v 1.10 1997/04/06 22:54:16 keil Exp $ +/* $Id: isdnl3.c,v 2.5 1998/02/12 23:07:52 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,38 +7,39 @@ * 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 2.5 1998/02/12 23:07:52 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() * - * Revision 1.8 1997/03/21 18:53:44 keil - * Report no protocol error to syslog too + * Revision 2.4 1997/11/06 17:09:25 keil + * New 2.1 init code * - * Revision 1.7 1997/03/17 18:34:38 keil - * fixed oops if no protocol selected during config + * Revision 2.3 1997/10/29 19:07:53 keil + * changes for 2.1 * - * Revision 1.6 1997/02/16 01:04:08 fritz - * Bugfix: Changed timer handling caused hang with 2.1.X + * Revision 2.2 1997/10/01 09:21:41 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. * - * Revision 1.5 1997/02/09 00:26:27 keil - * new interface handling, one interface per card - * leased line changes + * Revision 2.1 1997/08/03 14:36:32 keil + * Implement RESTART procedure * - * Revision 1.4 1997/01/27 23:17:44 keil - * delete timers while unloading + * Revision 2.0 1997/07/27 21:15:42 keil + * New Callref based layer3 * - * Revision 1.3 1997/01/21 22:31:12 keil - * new statemachine; L3 timers + * Revision 1.11 1997/06/26 11:11:44 keil + * SET_SKBFREE now on creation of a SKB * - * Revision 1.2 1996/11/05 19:42:04 keil - * using config.h + * Revision 1.10 1997/04/06 22:54:16 keil + * Using SKB's * - * Revision 1.1 1996/10/13 20:04:54 keil - * Initial revision + * Revision 1.9 1997/03/25 23:11:25 keil + * US NI-1 protocol * + * Revision 1.8 1997/03/21 18:53:44 keil + * Report no protocol error to syslog too * + * Remove old logs /KKe * */ #define __NO_VERSION__ @@ -46,7 +47,72 @@ #include "isdnl3.h" #include -const char *l3_revision = "$Revision: 1.10 $"; +const char *l3_revision = "$Revision: 2.5 $"; + +u_char * +findie(u_char * p, int size, u_char ie, int wanted_set) +{ + int l, codeset, maincodeset; + u_char *pend = p + size; + + /* skip protocol discriminator, callref and message type */ + p++; + l = (*p++) & 0xf; + p += l; + p++; + codeset = 0; + maincodeset = 0; + /* while there are bytes left... */ + while (p < pend) { + if ((*p & 0xf0) == 0x90) { + codeset = *p & 0x07; + if (!(*p & 0x08)) + maincodeset = codeset; + } + if (*p & 0x80) + p++; + else { + if (codeset == wanted_set) { + if (*p == ie) + return (p); + if (*p > ie) + return (NULL); + } + p++; + l = *p++; + p += l; + codeset = maincodeset; + } + } + return (NULL); +} + +int +getcallref(u_char * p) +{ + int l, m = 1, cr = 0; + p++; /* prot discr */ + l = 0xf & *p++; /* callref length */ + if (!l) /* dummy CallRef */ + return(-1); + while (l--) { + cr += m * (*p++); + m *= 8; + } + return (cr); +} + +static int OrigCallRef = 0; + +int +newcallref(void) +{ + if (OrigCallRef == 127) + OrigCallRef = 1; + else + OrigCallRef++; + return (OrigCallRef); +} void l3_debug(struct PStack *st, char *s) @@ -54,34 +120,33 @@ 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); + sprintf(str, "%s l3 %s\n", tm, s); HiSax_putstatus(st->l1.hardware, str); } - - void -newl3state(struct PStack *st, int state) +newl3state(struct l3_process *pc, 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); + if (pc->debug & L3_DEB_STATE) { + sprintf(tmp, "newstate cr %d %d --> %d", pc->callref, + pc->state, state); + l3_debug(pc->st, tmp); } - st->l3.state = state; + pc->state = state; } static void L3ExpireTimer(struct L3Timer *t) { - t->st->l4.l4l3(t->st, t->event, NULL); + t->pc->st->lli.l4l3(t->pc->st, t->event, t->pc); } void -L3InitTimer(struct PStack *st, struct L3Timer *t) +L3InitTimer(struct l3_process *pc, struct L3Timer *t) { - t->st = st; + t->pc = pc; t->tl.function = (void *) L3ExpireTimer; t->tl.data = (long) t; init_timer(&t->tl); @@ -109,9 +174,9 @@ L3AddTimer(struct L3Timer *t, } void -StopAllL3Timer(struct PStack *st) +StopAllL3Timer(struct l3_process *pc) { - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); } struct sk_buff * @@ -132,9 +197,8 @@ no_l3_proto(struct PStack *st, int pr, void *arg) { struct sk_buff *skb = arg; - l3_debug(st, "no protocol"); + HiSax_putstatus(st->l1.hardware, "L3 no D protocol\n"); if (skb) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); } } @@ -151,14 +215,78 @@ extern void setstack_ni1(struct PStack *st); extern void setstack_1tr6(struct PStack *st); #endif +struct l3_process +*getl3proc(struct PStack *st, int cr) +{ + struct l3_process *p = st->l3.proc; + + while (p) + if (p->callref == cr) + return (p); + else + p = p->next; + return (NULL); +} + +struct l3_process +*new_l3_process(struct PStack *st, int cr) +{ + struct l3_process *p, *np; + + if (!(p = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) { + printk(KERN_ERR "HiSax can't get memory for cr %d\n", cr); + return (NULL); + } + if (!st->l3.proc) + st->l3.proc = p; + else { + np = st->l3.proc; + while (np->next) + np = np->next; + np->next = p; + } + p->next = NULL; + p->debug = L3_DEB_WARN; + p->callref = cr; + p->state = 0; + p->chan = NULL; + p->st = st; + p->N303 = st->l3.N303; + L3InitTimer(p, &p->timer); + return (p); +}; + +void +release_l3_process(struct l3_process *p) +{ + struct l3_process *np, *pp = NULL; + + if (!p) + return; + np = p->st->l3.proc; + while (np) { + if (np == p) { + StopAllL3Timer(p); + if (pp) + pp->next = np->next; + else + p->st->l3.proc = np->next; + kfree(p); + return; + } + pp = np; + np = np->next; + } + printk(KERN_ERR "HiSax internal L3 error CR not in list\n"); +}; + 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); + st->l3.proc = NULL; + st->l3.global = NULL; #ifdef CONFIG_HISAX_EURO if (st->protocol == ISDN_PTYPE_EURO) { @@ -176,11 +304,11 @@ setstack_isdnl3(struct PStack *st, struct Channel *chanp) } else #endif if (st->protocol == ISDN_PTYPE_LEASED) { - st->l4.l4l3 = no_l3_proto; + st->lli.l4l3 = no_l3_proto; st->l2.l2l3 = no_l3_proto; - printk(KERN_NOTICE "HiSax: Leased line mode\n"); + printk(KERN_INFO "HiSax: Leased line mode\n"); } else { - st->l4.l4l3 = no_l3_proto; + st->lli.l4l3 = no_l3_proto; st->l2.l2l3 = no_l3_proto; sprintf(tmp, "protocol %s not supported", (st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" : @@ -188,15 +316,18 @@ setstack_isdnl3(struct PStack *st, struct Channel *chanp) (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); + while (st->l3.proc) + release_l3_process(st->l3.proc); + if (st->l3.global) { + StopAllL3Timer(st->l3.global); + kfree(st->l3.global); + st->l3.global = NULL; + } } diff --git a/drivers/isdn/hisax/isdnl3.h b/drivers/isdn/hisax/isdnl3.h index bed989a18613..2ff582b4a4dc 100644 --- a/drivers/isdn/hisax/isdnl3.h +++ b/drivers/isdn/hisax/isdnl3.h @@ -1,6 +1,12 @@ -/* $Id: isdnl3.h,v 1.3 1997/04/06 22:54:17 keil Exp $ - * +/* $Id: isdnl3.h,v 2.0 1997/07/27 21:15:42 keil Exp $ + * $Log: isdnl3.h,v $ + * Revision 2.0 1997/07/27 21:15:42 keil + * New Callref based layer3 + * + * Revision 1.4 1997/06/26 11:20:57 keil + * ? + * * Revision 1.3 1997/04/06 22:54:17 keil * Using SKB's * @@ -24,15 +30,18 @@ #define L3_DEB_CHARGE 0x08 struct stateentry { - int state; - u_char primitive; - void (*rout) (struct PStack *, u_char, void *); + int state; + u_char primitive; + void (*rout) (struct l3_process *, u_char, void *); }; extern void l3_debug(struct PStack *st, char *s); -extern void newl3state(struct PStack *st, int state); -extern void L3InitTimer(struct PStack *st, struct L3Timer *t); +extern void newl3state(struct l3_process *pc, int state); +extern void L3InitTimer(struct l3_process *pc, struct L3Timer *t); extern void L3DelTimer(struct L3Timer *t); -extern int L3AddTimer(struct L3Timer *t, int millisec, int event); -extern void StopAllL3Timer(struct PStack *st); +extern int L3AddTimer(struct L3Timer *t, int millisec, int event); +extern void StopAllL3Timer(struct l3_process *pc); extern struct sk_buff *l3_alloc_skb(int len); +extern struct l3_process *new_l3_process(struct PStack *st, int cr); +extern void release_l3_process(struct l3_process *p); +extern struct l3_process *getl3proc(struct PStack *st, int cr); diff --git a/drivers/isdn/hisax/ix1_micro.c b/drivers/isdn/hisax/ix1_micro.c index f5f5e857a0a0..0c075546b422 100644 --- a/drivers/isdn/hisax/ix1_micro.c +++ b/drivers/isdn/hisax/ix1_micro.c @@ -1,4 +1,4 @@ -/* $Id: ix1_micro.c,v 1.3 1997/04/13 19:54:02 keil Exp $ +/* $Id: ix1_micro.c,v 2.6 1998/02/11 17:28:09 keil Exp $ * ix1_micro.c low level stuff for ITK ix1-micro Rev.2 isdn cards * derived from the original file teles3.c from Karsten Keil @@ -11,6 +11,27 @@ * Beat Doebeli * * $Log: ix1_micro.c,v $ + * Revision 2.6 1998/02/11 17:28:09 keil + * Niccy PnP/PCI support + * + * Revision 2.5 1998/02/02 13:29:42 keil + * fast io + * + * Revision 2.4 1997/11/08 21:35:50 keil + * new l1 init + * + * Revision 2.3 1997/11/06 17:09:35 keil + * New 2.1 init code + * + * Revision 2.2 1997/10/29 18:55:51 keil + * changes for 2.1.60 (irq2dev_map) + * + * Revision 2.1 1997/07/27 21:47:09 keil + * new interface structures + * + * Revision 2.0 1997/06/26 11:02:50 keil + * New Layer and card interface + * * Revision 1.3 1997/04/13 19:54:02 keil * Change in IRQ check delay for SMP * @@ -54,17 +75,16 @@ #define __NO_VERSION__ -#include "siemens.h" #include "hisax.h" -#include "teles3.h" +#include "isac.h" +#include "hscx.h" #include "isdnl1.h" -#include extern const char *CardType[]; -const char *ix1_revision = "$Revision: 1.3 $"; +const char *ix1_revision = "$Revision: 2.6 $"; -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) #define SPECIAL_PORT_OFFSET 3 @@ -73,865 +93,250 @@ const char *ix1_revision = "$Revision: 1.3 $"; #define HSCX_COMMAND_OFFSET 2 #define HSCX_DATA_OFFSET 1 -#define ISAC_FIFOSIZE 16 -#define HSCX_FIFOSIZE 16 - #define TIMEOUT 50 static inline u_char -IsacReadReg(unsigned int adr, u_char off) -{ - byteout(adr + ISAC_COMMAND_OFFSET, off + 0x20); - return bytein(adr + ISAC_DATA_OFFSET); -} - -static inline void -IsacWriteReg(unsigned int adr, u_char off, u_char data) -{ - byteout(adr + ISAC_COMMAND_OFFSET, off + 0x20); - byteout(adr + ISAC_DATA_OFFSET, data); -} - -#define HSCX_OFFSET(WhichHscx,offset) \ -( (WhichHscx) ? (offset+0x60) : (offset+0x20) ) - -static inline u_char -HscxReadReg(unsigned int adr, int WhichHscx, u_char off) +readreg(unsigned int ale, unsigned int adr, u_char off) { - byteout(adr + HSCX_COMMAND_OFFSET, HSCX_OFFSET(WhichHscx, off)); - return bytein(adr + HSCX_DATA_OFFSET); -} - -static inline void -HscxWriteReg(unsigned int adr, int WhichHscx, u_char off, u_char data) -{ - byteout(adr + HSCX_COMMAND_OFFSET, HSCX_OFFSET(WhichHscx, off)); - byteout(adr + HSCX_DATA_OFFSET, data); -} - - -static inline void -IsacReadFifo(unsigned int adr, u_char * data, int size) -{ - byteout(adr + ISAC_COMMAND_OFFSET, 0); - while (size--) - *data++ = bytein(adr + ISAC_DATA_OFFSET); -} - -static void -IsacWriteFifo(unsigned int adr, u_char * data, int size) -{ - byteout(adr + ISAC_COMMAND_OFFSET, 0); - while (size--) { - byteout(adr + ISAC_DATA_OFFSET, *data); - data++; - } -} - -static inline void -HscxReadFifo(unsigned int adr, int WhichHscx, u_char * data, int size) -{ - byteout(adr + HSCX_COMMAND_OFFSET, (WhichHscx) ? 0x40 : 0x00); - while (size--) - *data++ = bytein(adr + HSCX_DATA_OFFSET); -} + register u_char ret; + long flags; -static void -HscxWriteFifo(unsigned int adr, int WhichHscx, u_char * data, int size) -{ - byteout(adr + HSCX_COMMAND_OFFSET, (WhichHscx) ? 0x40 : 0x00); - while (size--) { - byteout(adr + HSCX_DATA_OFFSET, *data); - data++; - } + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); } static inline void -waitforCEC(int adr, int WhichHscx) +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) { - int to = TIMEOUT; + /* fifo read without cli because it's allready done */ - while ((HscxReadReg(adr, WhichHscx, HSCX_STAR) & 0x04) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "ix1-Micro: waitforCEC timeout\n"); + byteout(ale, off); + insb(adr, data, size); } static inline void -waitforXFW(int adr, int WhichHscx) -{ - int to = TIMEOUT; - - while ((!(HscxReadReg(adr, WhichHscx, HSCX_STAR) & 0x44) == 0x40) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "ix1-Micro: waitforXFW timeout\n"); -} - -static inline void -writehscxCMDR(int adr, int WhichHscx, u_char data) +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) { long flags; save_flags(flags); cli(); - waitforCEC(adr, WhichHscx); - HscxWriteReg(adr, WhichHscx, HSCX_CMDR, data); + byteout(ale, off); + byteout(adr, data); restore_flags(flags); } -/* - * fast interrupt here - */ - -static void -hscxreport(struct IsdnCardState *sp, int hscx) -{ - printk(KERN_DEBUG "HSCX %d\n", hscx); - printk(KERN_DEBUG "ISTA %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_ISTA)); - printk(KERN_DEBUG "STAR %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_STAR)); - printk(KERN_DEBUG "EXIR %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_EXIR)); -} - -void -ix1micro_report(struct IsdnCardState *sp) +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) { - printk(KERN_DEBUG "ISAC\n"); - printk(KERN_DEBUG "ISTA %x\n", IsacReadReg(sp->isac, ISAC_ISTA)); - printk(KERN_DEBUG "STAR %x\n", IsacReadReg(sp->isac, ISAC_STAR)); - printk(KERN_DEBUG "EXIR %x\n", IsacReadReg(sp->isac, ISAC_EXIR)); - hscxreport(sp, 0); - hscxreport(sp, 1); + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); } -/* - * HSCX stuff goes here - */ +/* Interface functions */ -static void -hscx_empty_fifo(struct HscxState *hsp, int count) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - HscxReadFifo(sp->hscx[hsp->hscx], hsp->hscx, ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset)); } static void -hscx_fill_fifo(struct HscxState *hsp) +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - struct IsdnCardState *sp = hsp->sp; - int more, count; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_fill_fifo"); - - if (!hsp->tx_skb) - return; - if (hsp->tx_skb->len <= 0) - return; - - more = (hsp->mode == 1) ? 1 : 0; - if (hsp->tx_skb->len > 32) { - more = !0; - count = 32; - } else - count = hsp->tx_skb->len; - - waitforXFW(sp->hscx[hsp->hscx], hsp->hscx); - save_flags(flags); - cli(); - ptr = hsp->tx_skb->data; - skb_pull(hsp->tx_skb, count); - hsp->tx_cnt -= count; - hsp->count += count; - HscxWriteFifo(sp->hscx[hsp->hscx], hsp->hscx, ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_fill_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset, value); } -static inline void -hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx) +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = HscxReadReg(sp->hscx[hsp->hscx], hscx, HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!r & 0x80) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX invalid frame"); - if ((r & 0x40) && hsp->mode) - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX RDO mode=%d", - hsp->mode); - debugl1(sp, tmp); - } - if (!r & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX CRC error"); - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80); - } else { - count = HscxReadReg(sp->hscx[hsp->hscx], hscx, HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - if ((count = hsp->rcvidx - 1) > 0) { - if (sp->debug & L1_DEB_HSCX_FIFO) { - sprintf(tmp, "HX Frame %d", count); - debugl1(sp, tmp); - } - if (!(skb = dev_alloc_skb(count))) - printk(KERN_WARNING "IX1: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "IX1: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - SET_SKB_FREE(hsp->tx_skb); - dev_kfree_skb(hsp->tx_skb); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } + readfifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size); } -/* - * ISAC stuff goes here - */ - static void -isac_empty_fifo(struct IsdnCardState *sp, int count) +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - IsacWriteReg(sp->isac, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; - save_flags(flags); - cli(); - IsacReadFifo(sp->isac, ptr, count); - IsacWriteReg(sp->isac, ISAC_CMDR, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writefifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size); } -static void -isac_fill_fifo(struct IsdnCardState *sp) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - IsacWriteFifo(sp->isac, ptr, count); - IsacWriteReg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_fill_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (readreg(cs->hw.ix1.hscx_ale, + cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0))); } static void -ph_command(struct IsdnCardState *sp, unsigned int command) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - if (sp->debug & L1_DEB_ISAC) { - char tmp[32]; - sprintf(tmp, "ph_command %d", command); - debugl1(sp, tmp); - } - IsacWriteReg(sp->isac, ISAC_CIX0, (command << 2) | 3); + writereg(cs->hw.ix1.hscx_ale, + cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0), value); } +#define READHSCX(cs, nr, reg) readreg(cs->hw.ix1.hscx_ale, \ + cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ix1.hscx_ale, \ + cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0), data) -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; - - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = IsacReadReg(sp->isac, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!exval & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - IsacWriteReg(sp->isac, ISAC_CMDR, 0x80); - } else { - count = IsacReadReg(sp->isac, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - sp->rcvidx = 0; - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "IX1: D receive out of memory\n"); - else { - 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 { - SET_SKB_FREE(sp->tx_skb); - dev_kfree_skb(sp->tx_skb); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (IsacReadReg(sp->isac, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = IsacReadReg(sp->isac, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - } -} +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ix1.hscx_ale, \ + cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt) -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ix1.hscx_ale, \ + cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt) - u_char exval; - struct HscxState *hsp; - char tmp[32]; - - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = HscxReadReg(sp->hscx[1], 1, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = HscxReadReg(sp->hscx[0], 0, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = HscxReadReg(sp->hscx[0], 0, HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs) { - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val, stat = 0; - sp = (struct IsdnCardState *) dev_id; - - if (!sp) { - printk(KERN_WARNING "Teles: Spurious interrupt!\n"); + if (!cs) { + printk(KERN_WARNING "IX1: Spurious interrupt!\n"); return; } - val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA); + val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40); Start_HSCX: if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); stat |= 1; } - val = IsacReadReg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA); Start_ISAC: if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); stat |= 2; } - val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA); + val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40); if (val) { - if (sp->debug & L1_DEB_HSCX) - debugl1(sp, "HSCX IntStat after IntRoutine"); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); goto Start_HSCX; } - val = IsacReadReg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA); if (val) { - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "ISAC IntStat after IntRoutine"); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } if (stat & 1) { - HscxWriteReg(sp->hscx[0], 0, HSCX_MASK, 0xFF); - HscxWriteReg(sp->hscx[1], 1, HSCX_MASK, 0xFF); - HscxWriteReg(sp->hscx[0], 0, HSCX_MASK, 0x0); - HscxWriteReg(sp->hscx[1], 1, HSCX_MASK, 0x0); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0); } if (stat & 2) { - IsacWriteReg(sp->isac, ISAC_MASK, 0xFF); - IsacWriteReg(sp->isac, ISAC_MASK, 0x0); - } -} - - -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); + writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0); } - 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) +release_io_ix1micro(struct IsdnCardState *cs) { - if (card->sp->cfg_reg) - release_region(card->sp->cfg_reg, 4); + if (cs->hw.ix1.cfg_reg) + release_region(cs->hw.ix1.cfg_reg, 4); } static void -clear_pending_ints(struct IsdnCardState *sp) +ix1_reset(struct IsdnCardState *cs) { - int val; - char tmp[64]; + long flags; + int cnt; - 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); + /* reset isac */ + save_flags(flags); + cnt = 3 * (HZ / 10) + 1; + sti(); + while (cnt--) { + byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 1); + HZDELAY(1); /* wait >=10 ms */ } - IsacWriteReg(sp->isac, ISAC_MASK, 0); - IsacWriteReg(sp->isac, ISAC_CMDR, 0x41); + byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 0); + restore_flags(flags); } -int -initix1micro(struct IsdnCardState *sp) +static int +ix1_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - int ret; - int loop = 0; - char tmp[40]; - - sp->counter = kstat_irqs(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_irqs(sp->irq) > sp->counter) - break; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 1; - schedule(); - } - sprintf(tmp, "IRQ %d count %d", sp->irq, - kstat_irqs(sp->irq)); - debugl1(sp, tmp); - if (kstat_irqs(sp->irq) == sp->counter) { - printk(KERN_WARNING - "ix1-Micro: IRQ(%d) getting no interrupts during init\n", - sp->irq); - free_irq(sp->irq, sp); - return (0); - } - } - return (ret); + switch (mt) { + case CARD_RESET: + ix1_reset(cs); + return(0); + case CARD_RELEASE: + release_io_ix1micro(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &ix1micro_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); } -int -setup_ix1micro(struct IsdnCard *card) + +__initfunc(int +setup_ix1micro(struct IsdnCard *card)) { - u_char val, verA, verB; - struct IsdnCardState *sp = card->sp; - long flags; + struct IsdnCardState *cs = card->cs; char tmp[64]; strcpy(tmp, ix1_revision); - printk(KERN_NOTICE "HiSax: ITK IX1 driver Rev. %s\n", HiSax_getrev(tmp)); - if (sp->typ != ISDN_CTYPE_IX1MICROR2) + printk(KERN_INFO "HiSax: ITK IX1 driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_IX1MICROR2) return (0); /* IO-Ports */ - sp->isac = sp->hscx[0] = sp->hscx[1] = sp->cfg_reg = card->para[1]; - sp->irq = card->para[0]; - if (sp->cfg_reg) { - if (check_region((sp->cfg_reg), 4)) { + cs->hw.ix1.isac_ale = card->para[1] + ISAC_COMMAND_OFFSET; + cs->hw.ix1.hscx_ale = card->para[1] + HSCX_COMMAND_OFFSET; + cs->hw.ix1.isac = card->para[1] + ISAC_DATA_OFFSET; + cs->hw.ix1.hscx = card->para[1] + HSCX_DATA_OFFSET; + cs->hw.ix1.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + if (cs->hw.ix1.cfg_reg) { + if (check_region((cs->hw.ix1.cfg_reg), 4)) { printk(KERN_WARNING "HiSax: %s config port %x-%x already in use\n", CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + 4); + cs->hw.ix1.cfg_reg, + cs->hw.ix1.cfg_reg + 4); return (0); } else - request_region(sp->cfg_reg, 4, "ix1micro cfg"); - } - /* 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)) { + request_region(cs->hw.ix1.cfg_reg, 4, "ix1micro cfg"); + } + printk(KERN_INFO + "HiSax: %s config irq:%d io:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.ix1.cfg_reg); + ix1_reset(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &ix1_card_msg; + ISACVersion(cs, "ix1-Micro:"); + if (HscxVersion(cs, "ix1-Micro:")) { printk(KERN_WARNING "ix1-Micro: wrong HSCX versions check IO address\n"); - release_io_ix1micro(card); + release_io_ix1micro(cs); return (0); } - sp->modehscx = &modehscx; - sp->ph_command = &ph_command; - sp->hscx_fill_fifo = &hscx_fill_fifo; - sp->isac_fill_fifo = &isac_fill_fifo; return (1); } diff --git a/drivers/isdn/hisax/ix1_micro.h b/drivers/isdn/hisax/ix1_micro.h deleted file mode 100644 index b1800974bb96..000000000000 --- a/drivers/isdn/hisax/ix1_micro.h +++ /dev/null @@ -1,50 +0,0 @@ -/* $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 index c3ee6a73fa5b..d60a5da66b80 100644 --- a/drivers/isdn/hisax/l3_1tr6.c +++ b/drivers/isdn/hisax/l3_1tr6.c @@ -1,4 +1,4 @@ -/* $Id: l3_1tr6.c,v 1.11 1997/04/06 22:54:18 keil Exp $ +/* $Id: l3_1tr6.c,v 2.4 1998/02/12 23:07:57 keil Exp $ * German 1TR6 D-channel protocol * @@ -6,39 +6,28 @@ * * * $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 2.4 1998/02/12 23:07:57 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() * - * Revision 1.6 1996/12/14 21:07:20 keil - * additional states for CC_REJECT + * Revision 2.3 1997/11/06 17:12:24 keil + * KERN_NOTICE --> KERN_INFO * - * Revision 1.5 1996/12/08 19:55:17 keil - * change CC_REJECT_REQ routine + * Revision 2.2 1997/10/29 19:03:00 keil + * changes for 2.1 * - * Revision 1.4 1996/10/30 10:18:01 keil - * bugfixes in debugging output + * Revision 2.1 1997/08/03 15:28:09 keil + * release L3 empty processes * - * Revision 1.3 1996/10/27 22:15:37 keil - * bugfix reject handling + * Revision 2.0 1997/07/27 21:15:45 keil + * New Callref based layer3 * - * Revision 1.2 1996/10/13 23:08:56 keil - * added missing state for callback reject + * Revision 1.12 1997/06/26 11:11:45 keil + * SET_SKBFREE now on creation of a SKB * - * Revision 1.1 1996/10/13 20:04:55 keil - * Initial revision + * Revision 1.11 1997/04/06 22:54:18 keil + * Using SKB's * + * Old Log removed /KKe * */ @@ -49,16 +38,16 @@ #include extern char *HiSax_getrev(const char *revision); -const char *l3_1tr6_revision = "$Revision: 1.11 $"; +const char *l3_1tr6_revision = "$Revision: 2.4 $"; #define MsgHead(ptr, cref, mty, dis) \ *ptr++ = dis; \ *ptr++ = 0x1; \ - *ptr++ = cref; \ + *ptr++ = cref ^ 0x80; \ *ptr++ = mty static void -l3_1TR6_message(struct PStack *st, u_char mt, u_char pd) +l3_1TR6_message(struct l3_process *pc, u_char mt, u_char pd) { struct sk_buff *skb; u_char *p; @@ -66,12 +55,71 @@ l3_1TR6_message(struct PStack *st, u_char mt, u_char pd) if (!(skb = l3_alloc_skb(4))) return; p = skb_put(skb, 4); - MsgHead(p, st->l3.callref, mt, pd); - st->l3.l3l2(st, DL_DATA, skb); + MsgHead(p, pc->callref, mt, pd); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); +} + +static int +l31tr6_check_messagetype_validity(int mt, int pd) { +/* verify if a message type exists */ + + if (pd == PROTO_DIS_N0) + switch(mt) { + case MT_N0_REG_IND: + case MT_N0_CANC_IND: + case MT_N0_FAC_STA: + case MT_N0_STA_ACK: + case MT_N0_STA_REJ: + case MT_N0_FAC_INF: + case MT_N0_INF_ACK: + case MT_N0_INF_REJ: + case MT_N0_CLOSE: + case MT_N0_CLO_ACK: + return(1); + default: + return(0); + } + else if (pd == PROTO_DIS_N1) + switch(mt) { + case MT_N1_ESC: + case MT_N1_ALERT: + case MT_N1_CALL_SENT: + case MT_N1_CONN: + case MT_N1_CONN_ACK: + case MT_N1_SETUP: + case MT_N1_SETUP_ACK: + case MT_N1_RES: + case MT_N1_RES_ACK: + case MT_N1_RES_REJ: + case MT_N1_SUSP: + case MT_N1_SUSP_ACK: + case MT_N1_SUSP_REJ: + case MT_N1_USER_INFO: + case MT_N1_DET: + case MT_N1_DISC: + case MT_N1_REL: + case MT_N1_REL_ACK: + case MT_N1_CANC_ACK: + case MT_N1_CANC_REJ: + case MT_N1_CON_CON: + case MT_N1_FAC: + case MT_N1_FAC_ACK: + case MT_N1_FAC_CAN: + case MT_N1_FAC_REG: + case MT_N1_FAC_REJ: + case MT_N1_INFO: + case MT_N1_REG_ACK: + case MT_N1_REG_REJ: + case MT_N1_STAT: + return (1); + default: + return(0); + } + return(0); } static void -l3_1tr6_setup_req(struct PStack *st, u_char pr, void *arg) +l3_1tr6_setup_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[128]; @@ -81,16 +129,13 @@ l3_1tr6_setup_req(struct PStack *st, u_char pr, void *arg) u_char channel = 0; int l; - - st->l3.callref = st->pa->callref; - MsgHead(p, st->l3.callref, MT_N1_SETUP, PROTO_DIS_N1); - - teln = st->pa->setup.phone; - st->pa->spv = 0; + MsgHead(p, pc->callref, MT_N1_SETUP, PROTO_DIS_N1); + teln = pc->para.setup.phone; + pc->para.spv = 0; if (!isdigit(*teln)) { switch (0x5f & *teln) { case 'S': - st->pa->spv = 1; + pc->para.spv = 1; break; case 'C': channel = 0x08; @@ -103,8 +148,8 @@ l3_1tr6_setup_req(struct PStack *st, u_char pr, void *arg) channel |= 0x02; break; default: - if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "Wrong MSN Code"); + if (pc->st->l3.debug & L3_DEB_WARN) + l3_debug(pc->st, "Wrong MSN Code"); break; } teln++; @@ -114,22 +159,22 @@ l3_1tr6_setup_req(struct PStack *st, u_char pr, void *arg) *p++ = 1; *p++ = channel; } - if (st->pa->spv) { /* SPV ? */ + if (pc->para.spv) { /* SPV ? */ /* NSF SPV */ *p++ = WE0_netSpecFac; *p++ = 4; /* Laenge */ *p++ = 0; *p++ = FAC_SPV; /* SPV */ - *p++ = st->pa->setup.si1; /* 0 for all Services */ - *p++ = st->pa->setup.si2; /* 0 for all Services */ + *p++ = pc->para.setup.si1; /* 0 for all Services */ + *p++ = pc->para.setup.si2; /* 0 for all Services */ *p++ = WE0_netSpecFac; *p++ = 4; /* Laenge */ *p++ = 0; *p++ = FAC_Activate; /* aktiviere SPV (default) */ - *p++ = st->pa->setup.si1; /* 0 for all Services */ - *p++ = st->pa->setup.si2; /* 0 for all Services */ + *p++ = pc->para.setup.si1; /* 0 for all Services */ + *p++ = pc->para.setup.si2; /* 0 for all Services */ } - eaz = st->pa->setup.eazmsn; + eaz = pc->para.setup.eazmsn; if (*eaz) { *p++ = WE0_origAddr; *p++ = strlen(eaz) + 1; @@ -149,22 +194,21 @@ l3_1tr6_setup_req(struct PStack *st, u_char pr, void *arg) /* Codesatz 6 fuer Service */ *p++ = WE6_serviceInd; *p++ = 2; /* len=2 info,info2 */ - *p++ = st->pa->setup.si1; - *p++ = st->pa->setup.si2; + *p++ = pc->para.setup.si1; + *p++ = pc->para.setup.si2; l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - L3DelTimer(&st->l3.timer); - L3AddTimer(&st->l3.timer, st->l3.t303, CC_T303); - newl3state(st, 1); - st->l3.l3l2(st, DL_DATA, skb); - + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T303, CC_T303); + newl3state(pc, 1); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); } static void -l3_1tr6_setup(struct PStack *st, u_char pr, void *arg) +l3_1tr6_setup(struct l3_process *pc, u_char pr, void *arg) { u_char *p; int bcfound = 0; @@ -172,110 +216,105 @@ l3_1tr6_setup(struct PStack *st, u_char pr, void *arg) 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; + pc->para.bchannel = p[2] & 0x3; bcfound++; - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup without bchannel"); + } else if (pc->st->l3.debug & L3_DEB_WARN) + l3_debug(pc->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"); + pc->para.setup.si1 = p[2]; + pc->para.setup.si2 = p[3]; + } else if (pc->st->l3.debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without service indicator"); p = skb->data; if ((p = findie(p, skb->len, WE0_destAddr, 0))) - iecpy(st->pa->setup.eazmsn, p, 1); + iecpy(pc->para.setup.eazmsn, p, 1); else - st->pa->setup.eazmsn[0] = 0; + pc->para.setup.eazmsn[0] = 0; p = skb->data; if ((p = findie(p, skb->len, WE0_origAddr, 0))) { - iecpy(st->pa->setup.phone, p, 1); + iecpy(pc->para.setup.phone, p, 1); } else - st->pa->setup.phone[0] = 0; + pc->para.setup.phone[0] = 0; p = skb->data; - st->pa->spv = 0; + pc->para.spv = 0; if ((p = findie(p, skb->len, WE0_netSpecFac, 0))) { if ((FAC_SPV == p[3]) || (FAC_Activate == p[3])) - st->pa->spv = 1; + pc->para.spv = 1; } - SET_SKB_FREE(skb); dev_kfree_skb(skb); /* Signal all services, linklevel takes care of Service-Indicator */ if (bcfound) { - if ((st->pa->setup.si1 != 7) && (st->l3.debug & L3_DEB_WARN)) { + if ((pc->para.setup.si1 != 7) && (pc->st->l3.debug & L3_DEB_WARN)) { sprintf(tmp, "non-digital call: %s -> %s", - st->pa->setup.phone, - st->pa->setup.eazmsn); - l3_debug(st, tmp); + pc->para.setup.phone, + pc->para.setup.eazmsn); + l3_debug(pc->st, tmp); } - newl3state(st, 6); - st->l3.l3l4(st, CC_SETUP_IND, NULL); - } + newl3state(pc, 6); + pc->st->l3.l3l4(pc, CC_SETUP_IND, NULL); + } else + release_l3_process(pc); } static void -l3_1tr6_setup_ack(struct PStack *st, u_char pr, void *arg) +l3_1tr6_setup_ack(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); p = skb->data; - newl3state(st, 2); + newl3state(pc, 2); if ((p = findie(p, skb->len, WE0_chanID, 0))) { - st->pa->bchannel = p[2] & 0x3; - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup answer without bchannel"); - SET_SKB_FREE(skb); + pc->para.bchannel = p[2] & 0x3; + } else if (pc->st->l3.debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer without bchannel"); dev_kfree_skb(skb); - L3AddTimer(&st->l3.timer, st->l3.t304, CC_T304); - st->l3.l3l4(st, CC_MORE_INFO, NULL); + L3AddTimer(&pc->timer, T304, CC_T304); + pc->st->l3.l3l4(pc, CC_MORE_INFO, NULL); } static void -l3_1tr6_call_sent(struct PStack *st, u_char pr, void *arg) +l3_1tr6_call_sent(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); p = skb->data; if ((p = findie(p, skb->len, WE0_chanID, 0))) { - st->pa->bchannel = p[2] & 0x3; - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup answer without bchannel"); - SET_SKB_FREE(skb); + pc->para.bchannel = p[2] & 0x3; + } else if (pc->st->l3.debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer without bchannel"); dev_kfree_skb(skb); - L3AddTimer(&st->l3.timer, st->l3.t310, CC_T310); - newl3state(st, 3); - st->l3.l3l4(st, CC_PROCEEDING_IND, NULL); + L3AddTimer(&pc->timer, T310, CC_T310); + newl3state(pc, 3); + pc->st->l3.l3l4(pc, CC_PROCEEDING_IND, NULL); } static void -l3_1tr6_alert(struct PStack *st, u_char pr, void *arg) +l3_1tr6_alert(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb); - L3DelTimer(&st->l3.timer); /* T304 */ - newl3state(st, 4); - st->l3.l3l4(st, CC_ALERTING_IND, NULL); + L3DelTimer(&pc->timer); /* T304 */ + newl3state(pc, 4); + pc->st->l3.l3l4(pc, CC_ALERTING_IND, NULL); } static void -l3_1tr6_info(struct PStack *st, u_char pr, void *arg) +l3_1tr6_info(struct l3_process *pc, u_char pr, void *arg) { u_char *p; int i, tmpcharge = 0; @@ -289,45 +328,42 @@ l3_1tr6_info(struct PStack *st, u_char pr, void *arg) tmpcharge *= 10; tmpcharge += a_charge[i] & 0xf; } - if (tmpcharge > st->pa->chargeinfo) { - st->pa->chargeinfo = tmpcharge; - st->l3.l3l4(st, CC_INFO_CHARGE, NULL); + if (tmpcharge > pc->para.chargeinfo) { + pc->para.chargeinfo = tmpcharge; + pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL); } - if (st->l3.debug & L3_DEB_CHARGE) { - sprintf(tmp, "charging info %d", st->pa->chargeinfo); - l3_debug(st, tmp); + if (pc->st->l3.debug & L3_DEB_CHARGE) { + sprintf(tmp, "charging info %d", pc->para.chargeinfo); + l3_debug(pc->st, tmp); } - } else if (st->l3.debug & L3_DEB_CHARGE) - l3_debug(st, "charging info not found"); - SET_SKB_FREE(skb); + } else if (pc->st->l3.debug & L3_DEB_CHARGE) + l3_debug(pc->st, "charging info not found"); dev_kfree_skb(skb); } static void -l3_1tr6_info_s2(struct PStack *st, u_char pr, void *arg) +l3_1tr6_info_s2(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb); } static void -l3_1tr6_connect(struct PStack *st, u_char pr, void *arg) +l3_1tr6_connect(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); /* T310 */ - newl3state(st, 10); - SET_SKB_FREE(skb); + L3DelTimer(&pc->timer); /* T310 */ + newl3state(pc, 10); dev_kfree_skb(skb); - st->pa->chargeinfo = 0; - st->l3.l3l4(st, CC_SETUP_CNF, NULL); + pc->para.chargeinfo = 0; + pc->st->l3.l3l4(pc, CC_SETUP_CNF, NULL); } static void -l3_1tr6_rel(struct PStack *st, u_char pr, void *arg) +l3_1tr6_rel(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; u_char *p; @@ -335,47 +371,47 @@ l3_1tr6_rel(struct PStack *st, u_char pr, void *arg) p = skb->data; if ((p = findie(p, skb->len, WE0_cause, 0))) { if (p[1] > 0) { - st->pa->cause = p[2]; + pc->para.cause = p[2]; if (p[1] > 1) - st->pa->loc = p[3]; + pc->para.loc = p[3]; else - st->pa->loc = 0; + pc->para.loc = 0; } else { - st->pa->cause = 0; - st->pa->loc = 0; + pc->para.cause = 0; + pc->para.loc = 0; } } else - st->pa->cause = -1; - SET_SKB_FREE(skb); + pc->para.cause = -1; dev_kfree_skb(skb); - StopAllL3Timer(st); - newl3state(st, 0); - l3_1TR6_message(st, MT_N1_REL_ACK, PROTO_DIS_N1); - st->l3.l3l4(st, CC_RELEASE_IND, NULL); + StopAllL3Timer(pc); + newl3state(pc, 0); + l3_1TR6_message(pc, MT_N1_REL_ACK, PROTO_DIS_N1); + pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL); + release_l3_process(pc); } static void -l3_1tr6_rel_ack(struct PStack *st, u_char pr, void *arg) +l3_1tr6_rel_ack(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb); - StopAllL3Timer(st); - newl3state(st, 0); - st->pa->cause = -1; - st->l3.l3l4(st, CC_RELEASE_CNF, NULL); + StopAllL3Timer(pc); + newl3state(pc, 0); + pc->para.cause = -1; + pc->st->l3.l3l4(pc, CC_RELEASE_CNF, NULL); + release_l3_process(pc); } static void -l3_1tr6_disc(struct PStack *st, u_char pr, void *arg) +l3_1tr6_disc(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; u_char *p; int i, tmpcharge = 0; char a_charge[8], tmp[32]; - StopAllL3Timer(st); + StopAllL3Timer(pc); p = skb->data; if ((p = findie(p, skb->len, WE6_chargingInfo, 6))) { iecpy(a_charge, p, 1); @@ -383,104 +419,102 @@ l3_1tr6_disc(struct PStack *st, u_char pr, void *arg) tmpcharge *= 10; tmpcharge += a_charge[i] & 0xf; } - if (tmpcharge > st->pa->chargeinfo) { - st->pa->chargeinfo = tmpcharge; - st->l3.l3l4(st, CC_INFO_CHARGE, NULL); + if (tmpcharge > pc->para.chargeinfo) { + pc->para.chargeinfo = tmpcharge; + pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL); } - if (st->l3.debug & L3_DEB_CHARGE) { - sprintf(tmp, "charging info %d", st->pa->chargeinfo); - l3_debug(st, tmp); + if (pc->st->l3.debug & L3_DEB_CHARGE) { + sprintf(tmp, "charging info %d", pc->para.chargeinfo); + l3_debug(pc->st, tmp); } - } else if (st->l3.debug & L3_DEB_CHARGE) - l3_debug(st, "charging info not found"); + } else if (pc->st->l3.debug & L3_DEB_CHARGE) + l3_debug(pc->st, "charging info not found"); p = skb->data; if ((p = findie(p, skb->len, WE0_cause, 0))) { if (p[1] > 0) { - st->pa->cause = p[2]; + pc->para.cause = p[2]; if (p[1] > 1) - st->pa->loc = p[3]; + pc->para.loc = p[3]; else - st->pa->loc = 0; + pc->para.loc = 0; } else { - st->pa->cause = 0; - st->pa->loc = 0; + pc->para.cause = 0; + pc->para.loc = 0; } } else { - if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "cause not found"); - st->pa->cause = -1; + if (pc->st->l3.debug & L3_DEB_WARN) + l3_debug(pc->st, "cause not found"); + pc->para.cause = -1; } - SET_SKB_FREE(skb); dev_kfree_skb(skb); - newl3state(st, 12); - st->l3.l3l4(st, CC_DISCONNECT_IND, NULL); + newl3state(pc, 12); + pc->st->l3.l3l4(pc, CC_DISCONNECT_IND, NULL); } static void -l3_1tr6_connect_ack(struct PStack *st, u_char pr, void *arg) +l3_1tr6_connect_ack(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb); - newl3state(st, 10); - st->pa->chargeinfo = 0; - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL); + newl3state(pc, 10); + pc->para.chargeinfo = 0; + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc, CC_SETUP_COMPLETE_IND, NULL); } static void -l3_1tr6_alert_req(struct PStack *st, u_char pr, void *arg) +l3_1tr6_alert_req(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 7); - l3_1TR6_message(st, MT_N1_ALERT, PROTO_DIS_N1); + newl3state(pc, 7); + l3_1TR6_message(pc, MT_N1_ALERT, PROTO_DIS_N1); } static void -l3_1tr6_setup_rsp(struct PStack *st, u_char pr, void *arg) +l3_1tr6_setup_rsp(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[24]; u_char *p = tmp; int l; - MsgHead(p, st->l3.callref, MT_N1_CONN, PROTO_DIS_N1); - if (st->pa->spv) { /* SPV ? */ + MsgHead(p, pc->callref, MT_N1_CONN, PROTO_DIS_N1); + if (pc->para.spv) { /* SPV ? */ /* NSF SPV */ *p++ = WE0_netSpecFac; *p++ = 4; /* Laenge */ *p++ = 0; *p++ = FAC_SPV; /* SPV */ - *p++ = st->pa->setup.si1; - *p++ = st->pa->setup.si2; + *p++ = pc->para.setup.si1; + *p++ = pc->para.setup.si2; *p++ = WE0_netSpecFac; *p++ = 4; /* Laenge */ *p++ = 0; *p++ = FAC_Activate; /* aktiviere SPV */ - *p++ = st->pa->setup.si1; - *p++ = st->pa->setup.si2; + *p++ = pc->para.setup.si1; + *p++ = pc->para.setup.si2; } - newl3state(st, 8); + newl3state(pc, 8); l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - st->l3.l3l2(st, DL_DATA, skb); - L3DelTimer(&st->l3.timer); - L3AddTimer(&st->l3.timer, st->l3.t313, CC_T313); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T313, CC_T313); } static void -l3_1tr6_reset(struct PStack *st, u_char pr, void *arg) +l3_1tr6_reset(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 0); + release_l3_process(pc); } static void -l3_1tr6_disconnect_req(struct PStack *st, u_char pr, void *arg) +l3_1tr6_disconnect_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; @@ -489,8 +523,8 @@ l3_1tr6_disconnect_req(struct PStack *st, u_char pr, void *arg) u_char cause = 0x10; u_char clen = 1; - if (st->pa->cause > 0) - cause = st->pa->cause; + if (pc->para.cause > 0) + cause = pc->para.cause; /* Map DSS1 causes */ switch (cause & 0x7f) { case 0x10: @@ -500,57 +534,55 @@ l3_1tr6_disconnect_req(struct PStack *st, u_char pr, void *arg) cause = CAUSE_CallRejected; break; } - StopAllL3Timer(st); - MsgHead(p, st->l3.callref, MT_N1_DISC, PROTO_DIS_N1); + StopAllL3Timer(pc); + MsgHead(p, pc->callref, MT_N1_DISC, PROTO_DIS_N1); *p++ = WE0_cause; *p++ = clen; /* Laenge */ if (clen) *p++ = cause | 0x80; - newl3state(st, 11); + newl3state(pc, 11); l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - st->l3.l3l2(st, DL_DATA, skb); - L3AddTimer(&st->l3.timer, st->l3.t305, CC_T305); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); + L3AddTimer(&pc->timer, T305, CC_T305); } static void -l3_1tr6_release_req(struct PStack *st, u_char pr, void *arg) +l3_1tr6_release_req(struct l3_process *pc, u_char pr, void *arg) { - StopAllL3Timer(st); - newl3state(st, 19); - l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1); + StopAllL3Timer(pc); + newl3state(pc, 19); + l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1); + L3AddTimer(&pc->timer, T308, CC_T308_1); } static void -l3_1tr6_t303(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t303(struct l3_process *pc, u_char pr, void *arg) { - if (st->l3.n_t303 > 0) { - st->l3.n_t303--; - L3DelTimer(&st->l3.timer); - l3_1tr6_setup_req(st, pr, arg); + if (pc->N303 > 0) { + pc->N303--; + L3DelTimer(&pc->timer); + l3_1tr6_setup_req(pc, pr, arg); } else { - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL); - st->l3.n_t303 = 1; - newl3state(st, 0); + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc, CC_NOSETUP_RSP_ERR, NULL); + release_l3_process(pc); } } static void -l3_1tr6_t304(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t304(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3_1tr6_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_SETUP_ERR, NULL); - + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3_1tr6_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL); } static void -l3_1tr6_t305(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t305(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; @@ -559,9 +591,9 @@ l3_1tr6_t305(struct PStack *st, u_char pr, void *arg) u_char cause = 0x90; u_char clen = 1; - L3DelTimer(&st->l3.timer); - if (st->pa->cause > 0) - cause = st->pa->cause; + L3DelTimer(&pc->timer); + if (pc->para.cause > 0) + cause = pc->para.cause; /* Map DSS1 causes */ switch (cause & 0x7f) { case 0x10: @@ -571,53 +603,53 @@ l3_1tr6_t305(struct PStack *st, u_char pr, void *arg) cause = CAUSE_CallRejected; break; } - MsgHead(p, st->l3.callref, MT_N1_REL, PROTO_DIS_N1); + MsgHead(p, pc->callref, MT_N1_REL, PROTO_DIS_N1); *p++ = WE0_cause; *p++ = clen; /* Laenge */ if (clen) *p++ = cause; - newl3state(st, 19); + newl3state(pc, 19); l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - st->l3.l3l2(st, DL_DATA, skb); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); + L3AddTimer(&pc->timer, T308, CC_T308_1); } static void -l3_1tr6_t310(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t310(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3_1tr6_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_SETUP_ERR, NULL); + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3_1tr6_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL); } static void -l3_1tr6_t313(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t313(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3_1tr6_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_CONNECT_ERR, NULL); + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3_1tr6_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc, CC_CONNECT_ERR, NULL); } static void -l3_1tr6_t308_1(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t308_1(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_2); - newl3state(st, 19); + L3DelTimer(&pc->timer); + l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1); + L3AddTimer(&pc->timer, T308, CC_T308_2); + newl3state(pc, 19); } static void -l3_1tr6_t308_2(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t308_2(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_RELEASE_ERR, NULL); - newl3state(st, 0); + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc, CC_RELEASE_ERR, NULL); + release_l3_process(pc); } /* *INDENT-OFF* */ static struct stateentry downstl[] = @@ -688,49 +720,79 @@ static struct stateentry datastln1[] = + static int datastln1_len = sizeof(datastln1) / sizeof(struct stateentry); static void up1tr6(struct PStack *st, int pr, void *arg) { - int i, mt; + int i, mt, cr; + struct l3_process *proc; struct sk_buff *skb = arg; char tmp[80]; + if (skb->len < 4) { + if (st->l3.debug & L3_DEB_PROTERR) { + sprintf(tmp, "up1tr6 len only %d", skb->len); + l3_debug(st, tmp); + } + dev_kfree_skb(skb); + return; + } if ((skb->data[0] & 0xfe) != PROTO_DIS_N0) { if (st->l3.debug & L3_DEB_PROTERR) { - sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d state %d", + sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d", (pr == DL_DATA) ? " " : "(broadcast) ", - skb->data[0], skb->len, st->l3.state); + skb->data[0], skb->len); + l3_debug(st, tmp); + } + dev_kfree_skb(skb); + return; + } + if (skb->data[1] != 1) { + if (st->l3.debug & L3_DEB_PROTERR) { + sprintf(tmp, "up1tr6 CR len not 1"); l3_debug(st, tmp); } - SET_SKB_FREE(skb); dev_kfree_skb(skb); return; } - mt = skb->data[skb->data[1] + 2]; + cr = skb->data[2]; + mt = skb->data[3]; if (skb->data[0] == PROTO_DIS_N0) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); if (st->l3.debug & L3_DEB_STATE) { - sprintf(tmp, "up1tr6%s N0 state %d mt %x unhandled", - (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); + sprintf(tmp, "up1tr6%s N0 mt %x unhandled", + (pr == DL_DATA) ? " " : "(broadcast) ", mt); l3_debug(st, tmp); } } else if (skb->data[0] == PROTO_DIS_N1) { + if (!(proc = getl3proc(st, cr))) { + if ((mt == MT_N1_SETUP) && (cr < 128)) { + if (!(proc = new_l3_process(st, cr))) { + if (st->l3.debug & L3_DEB_PROTERR) { + sprintf(tmp, "up1tr6 no roc mem"); + l3_debug(st, tmp); + } + dev_kfree_skb(skb); + return; + } + } else { + dev_kfree_skb(skb); + return; + } + } for (i = 0; i < datastln1_len; i++) if ((mt == datastln1[i].primitive) && - ((1 << st->l3.state) & datastln1[i].state)) + ((1 << proc->state) & datastln1[i].state)) break; if (i == datastln1_len) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "up1tr6%sstate %d mt %x unhandled", (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); + proc->state, mt); l3_debug(st, tmp); } return; @@ -738,10 +800,10 @@ up1tr6(struct PStack *st, int pr, void *arg) if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "up1tr6%sstate %d mt %x", (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); + proc->state, mt); l3_debug(st, tmp); } - datastln1[i].rout(st, pr, skb); + datastln1[i].rout(proc, pr, skb); } } } @@ -749,26 +811,44 @@ up1tr6(struct PStack *st, int pr, void *arg) static void down1tr6(struct PStack *st, int pr, void *arg) { - int i; + int i, cr; + struct l3_process *proc; + struct Channel *chan; char tmp[80]; + if (CC_SETUP_REQ == pr) { + chan = arg; + cr = newcallref(); + cr |= 0x80; + if (!(proc = new_l3_process(st, cr))) { + return; + } else { + proc->chan = chan; + chan->proc = proc; + proc->para.setup = chan->setup; + proc->callref = cr; + } + } else { + proc = arg; + } + for (i = 0; i < downstl_len; i++) if ((pr == downstl[i].primitive) && - ((1 << st->l3.state) & downstl[i].state)) + ((1 << proc->state) & downstl[i].state)) break; if (i == downstl_len) { if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "down1tr6 state %d prim %d unhandled", - st->l3.state, pr); + proc->state, pr); l3_debug(st, tmp); } } else { if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "down1tr6 state %d prim %d", - st->l3.state, pr); + proc->state, pr); l3_debug(st, tmp); } - downstl[i].rout(st, pr, arg); + downstl[i].rout(proc, pr, arg); } } @@ -777,20 +857,10 @@ setstack_1tr6(struct PStack *st) { char tmp[64]; - st->l4.l4l3 = down1tr6; + st->lli.l4l3 = down1tr6; st->l2.l2l3 = up1tr6; - st->l3.t303 = 4000; - st->l3.t304 = 20000; - st->l3.t305 = 4000; - st->l3.t308 = 4000; - st->l3.t310 = 120000; - st->l3.t313 = 4000; - st->l3.t318 = 4000; - st->l3.t319 = 4000; - st->l3.n_t303 = 0; - - if (st->l3.channr & 1) { - strcpy(tmp, l3_1tr6_revision); - printk(KERN_NOTICE "HiSax: 1TR6 Rev. %s\n", HiSax_getrev(tmp)); - } + st->l3.N303 = 0; + + strcpy(tmp, l3_1tr6_revision); + printk(KERN_INFO "HiSax: 1TR6 Rev. %s\n", HiSax_getrev(tmp)); } diff --git a/drivers/isdn/hisax/l3_1tr6.h b/drivers/isdn/hisax/l3_1tr6.h index 6e2fee72f4b5..90d08793e3dd 100644 --- a/drivers/isdn/hisax/l3_1tr6.h +++ b/drivers/isdn/hisax/l3_1tr6.h @@ -1,13 +1,14 @@ -/* $Id: l3_1tr6.h,v 1.1 1996/10/13 20:03:48 keil Exp $ +/* $Id: l3_1tr6.h,v 2.0 1997/07/27 21:15:47 keil Exp $ * * German 1TR6 D-channel protocol defines * * $Log: l3_1tr6.h,v $ + * Revision 2.0 1997/07/27 21:15:47 keil + * New Callref based layer3 + * * Revision 1.1 1996/10/13 20:03:48 keil * Initial revision * - * - * */ #ifndef l3_1tr6 #define l3_1tr6 @@ -29,7 +30,6 @@ #define MT_N0_CLOSE 0x75 #define MT_N0_CLO_ACK 0x77 - /* * MsgType N1 */ @@ -65,8 +65,6 @@ #define MT_N1_REG_REJ 0x6F #define MT_N1_STAT 0x63 - - /* * W Elemente */ @@ -156,5 +154,13 @@ #define CAUSE_RemoteUserResumed 0x73 #define CAUSE_UserInfoDiscarded 0x7F +#define T303 4000 +#define T304 20000 +#define T305 4000 +#define T308 4000 +#define T310 120000 +#define T313 4000 +#define T318 4000 +#define T319 4000 #endif diff --git a/drivers/isdn/hisax/l3dss1.c b/drivers/isdn/hisax/l3dss1.c index d6089873464f..f8b97fd73135 100644 --- a/drivers/isdn/hisax/l3dss1.c +++ b/drivers/isdn/hisax/l3dss1.c @@ -1,4 +1,4 @@ -/* $Id: l3dss1.c,v 1.15 1997/04/17 11:50:48 keil Exp $ +/* $Id: l3dss1.c,v 2.7 1998/02/12 23:08:01 keil Exp $ * EURO/DSS1 D-channel protocol * @@ -9,71 +9,272 @@ * Fritz Elfert * * $Log: l3dss1.c,v $ - * 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 2.7 1998/02/12 23:08:01 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() * - * Revision 1.8 1997/01/21 22:29:41 keil - * new statemachine; L3 timers + * Revision 2.6 1998/02/03 23:26:35 keil + * V110 extensions from Thomas Pfeiffer * - * Revision 1.7 1996/12/14 21:06:59 keil - * additional states for CC_REJECT + * Revision 2.5 1998/02/02 13:34:28 keil + * Support australian Microlink net and german AOCD * - * Revision 1.6 1996/12/08 22:59:16 keil - * fixed calling party number without octet 3a + * Revision 2.4 1997/11/06 17:12:25 keil + * KERN_NOTICE --> KERN_INFO * - * Revision 1.5 1996/12/08 19:53:31 keil - * fixes from Pekka Sarnila + * Revision 2.3 1997/10/29 19:03:01 keil + * changes for 2.1 * - * Revision 1.4 1996/11/05 19:44:36 keil - * some fixes from Henner Eisen + * Revision 2.2 1997/08/07 17:44:36 keil + * Fix RESTART * - * Revision 1.3 1996/10/30 10:18:01 keil - * bugfixes in debugging output + * Revision 2.1 1997/08/03 14:36:33 keil + * Implement RESTART procedure * - * Revision 1.2 1996/10/27 22:15:16 keil - * bugfix reject handling + * Revision 2.0 1997/07/27 21:15:43 keil + * New Callref based layer3 * - * Revision 1.1 1996/10/13 20:04:55 keil - * Initial revision + * Revision 1.17 1997/06/26 11:11:46 keil + * SET_SKBFREE now on creation of a SKB * + * Revision 1.15 1997/04/17 11:50:48 keil + * pa->loc was undefined, if it was not send by the exchange * + * Old log removed /KKe * */ #define __NO_VERSION__ #include "hisax.h" #include "isdnl3.h" +#include "l3dss1.h" #include extern char *HiSax_getrev(const char *revision); -const char *dss1_revision = "$Revision: 1.15 $"; +const char *dss1_revision = "$Revision: 2.7 $"; + +#define EXT_BEARER_CAPS 1 #define MsgHead(ptr, cref, mty) \ *ptr++ = 0x8; \ *ptr++ = 0x1; \ - *ptr++ = cref; \ + *ptr++ = cref^0x80; \ *ptr++ = mty + +#ifdef HISAX_DE_AOC static void -l3dss1_message(struct PStack *st, u_char mt) +l3dss1_parse_facility(struct l3_process *pc, u_char *p) +{ + int qd_len = 0; + char tmp[32]; + + p++; + qd_len = *p++; + if (qd_len == 0) { + l3_debug(pc->st, "qd_len == 0"); + return; + } + if((*p & 0x1F) != 0x11) { /* Service discriminator, supplementary service */ + l3_debug(pc->st, "supplementary service != 0x11"); + return; + } + while(qd_len > 0 && !(*p & 0x80)) { /* extension ? */ + p++; qd_len--; + } + if(qd_len < 2) { + l3_debug(pc->st, "qd_len < 2"); + return; + } + p++; qd_len--; + if((*p & 0xE0) != 0xA0) { /* class and form */ + l3_debug(pc->st, "class and form != 0xA0"); + return; + } + switch(*p & 0x1F) { /* component tag */ + case 1: /* invoke */ + { + unsigned char nlen, ilen; + int ident; + + p++; qd_len--; + if(qd_len < 1) { + l3_debug(pc->st, "qd_len < 1"); + break; + } + if(*p & 0x80) { /* length format */ + l3_debug(pc->st, "*p & 0x80 length format"); + break; + } + nlen = *p++; qd_len--; + if(qd_len < nlen) { + l3_debug(pc->st, "qd_len < nlen"); + return; + } + qd_len -= nlen; + + if(nlen < 2) { + l3_debug(pc->st, "nlen < 2"); + return; + } + if(*p != 0x02) { /* invoke identifier tag */ + l3_debug(pc->st, "invoke identifier tag !=0x02"); + return; + } + p++; nlen--; + if(*p & 0x80) { /* length format */ + l3_debug(pc->st, "*p & 0x80 length format 2"); + break; + } + ilen = *p++; nlen--; + if(ilen > nlen || ilen == 0) { + l3_debug(pc->st, "ilen > nlen || ilen == 0"); + return; + } + nlen -= ilen; + ident = 0; + while(ilen > 0) { + ident = (ident << 8) | (*p++ & 0xFF); /* invoke identifier */ + ilen--; + } + + if(nlen < 2) { + l3_debug(pc->st, "nlen < 2 22"); + return; + } + if(*p != 0x02) { /* operation value */ + l3_debug(pc->st, "operation value !=0x02"); + return; + } + p++; nlen--; + ilen = *p++; nlen--; + if(ilen > nlen || ilen == 0) { + l3_debug(pc->st, "ilen > nlen || ilen == 0 22"); + return; + } + nlen -= ilen; + ident = 0; + while(ilen > 0) { + ident = (ident << 8) | (*p++ & 0xFF); + ilen--; + } + + #define FOO1(s,a,b) \ + while(nlen > 1) { \ + int ilen = p[1]; \ + if(nlen < ilen+2) { \ + l3_debug(pc->st, "FOO1 nlen < ilen+2"); \ + return; \ + } \ + nlen -= ilen+2; \ + if((*p & 0xFF) == (a)) { \ + int nlen = ilen; \ + p += 2; \ + b; \ + } else { \ + p += ilen+2; \ + } \ + } + + switch(ident) { + default: + break; + case 0x22: /* during */ + FOO1("1A",0x30,FOO1("1C",0xA1,FOO1("1D",0x30,FOO1("1E",0x02,({ + ident = 0; + while(ilen > 0) { + ident = (ident<<8) | *p++; + ilen--; + } + if (ident > pc->para.chargeinfo) { + pc->para.chargeinfo = ident; + pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL); + } + if (pc->st->l3.debug & L3_DEB_CHARGE) { + if (*(p+2) == 0) { + sprintf(tmp, "charging info during %d", pc->para.chargeinfo); + l3_debug(pc->st, tmp); + } + else { + sprintf(tmp, "charging info final %d", pc->para.chargeinfo); + l3_debug(pc->st, tmp); + } + } + }))))) + break; + case 0x24: /* final */ + FOO1("2A",0x30,FOO1("2B",0x30,FOO1("2C",0xA1,FOO1("2D",0x30,FOO1("2E",0x02,({ + ident = 0; + while(ilen > 0) { + ident = (ident<<8) | *p++; + ilen--; + } + if (ident > pc->para.chargeinfo) { + pc->para.chargeinfo = ident; + pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL); + } + if (pc->st->l3.debug & L3_DEB_CHARGE) { + sprintf(tmp, "charging info final %d", pc->para.chargeinfo); + l3_debug(pc->st, tmp); + } + })))))) + break; + } + #undef FOO1 + + } + break; + case 2: /* return result */ + l3_debug(pc->st, "return result break"); + break; + case 3: /* return error */ + l3_debug(pc->st, "return error break"); + break; + default: + l3_debug(pc->st, "default break"); + break; + } +} +#endif + +static int +l3dss1_check_messagetype_validity(int mt) { +/* verify if a message type exists */ + switch(mt) { + case MT_ALERTING: + case MT_CALL_PROCEEDING: + case MT_CONNECT: + case MT_CONNECT_ACKNOWLEDGE: + case MT_PROGRESS: + case MT_SETUP: + case MT_SETUP_ACKNOWLEDGE: + case MT_RESUME: + case MT_RESUME_ACKNOWLEDGE: + case MT_RESUME_REJECT: + case MT_SUSPEND: + case MT_SUSPEND_ACKNOWLEDGE: + case MT_SUSPEND_REJECT: + case MT_USER_INFORMATION: + case MT_DISCONNECT: + case MT_RELEASE: + case MT_RELEASE_COMPLETE: + case MT_RESTART: + case MT_RESTART_ACKNOWLEDGE: + case MT_SEGMENT: + case MT_CONGESTION_CONTROL: + case MT_INFORMATION: + case MT_FACILITY: + case MT_NOTIFY: + case MT_STATUS: + case MT_STATUS_ENQUIRY: + return(1); + default: + return(0); + } + return(0); +} + +static void +l3dss1_message(struct l3_process *pc, u_char mt) { struct sk_buff *skb; u_char *p; @@ -81,63 +282,226 @@ l3dss1_message(struct PStack *st, u_char mt) if (!(skb = l3_alloc_skb(4))) return; p = skb_put(skb, 4); - MsgHead(p, st->l3.callref, mt); - st->l3.l3l2(st, DL_DATA, skb); + MsgHead(p, pc->callref, mt); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); } static void -l3dss1_release_req(struct PStack *st, u_char pr, void *arg) +l3dss1_release_req(struct l3_process *pc, u_char pr, void *arg) { - StopAllL3Timer(st); - newl3state(st, 19); - l3dss1_message(st, MT_RELEASE); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1); + StopAllL3Timer(pc); + newl3state(pc, 19); + l3dss1_message(pc, MT_RELEASE); + L3AddTimer(&pc->timer, T308, CC_T308_1); } static void -l3dss1_release_cmpl(struct PStack *st, u_char pr, void *arg) +l3dss1_release_cmpl(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; int cause = -1; p = skb->data; - st->pa->loc = 0; + pc->para.loc = 0; if ((p = findie(p, skb->len, IE_CAUSE, 0))) { p++; if (*p++ == 2) - st->pa->loc = *p++; + pc->para.loc = *p++; cause = *p & 0x7f; } - SET_SKB_FREE(skb); dev_kfree_skb(skb); - StopAllL3Timer(st); - st->pa->cause = cause; - newl3state(st, 0); - st->l3.l3l4(st, CC_RELEASE_CNF, NULL); + StopAllL3Timer(pc); + pc->para.cause = cause; + newl3state(pc, 0); + pc->st->l3.l3l4(pc, CC_RELEASE_CNF, NULL); + release_l3_process(pc); +} + +#ifdef EXT_BEARER_CAPS + +u_char *EncodeASyncParams(u_char *p, u_char si2) +{ // 7c 06 88 90 21 42 00 bb + + p[0] = p[1] = 0; p[2] = 0x80; + if (si2 & 32) // 7 data bits + p[2] += 16; + else // 8 data bits + p[2] +=24; + + if (si2 & 16) // 2 stop bits + p[2] += 96; + else // 1 stop bit + p[2] = 32; + + if (si2 & 8) // even parity + p[2] += 2; + else // no parity + p[2] += 3; + + switch (si2 & 0x07) + { + case 0: p[0] = 66; // 1200 bit/s + break; + case 1: p[0] = 88; // 1200/75 bit/s + break; + case 2: p[0] = 87; // 75/1200 bit/s + break; + case 3: p[0] = 67; // 2400 bit/s + break; + case 4: p[0] = 69; // 4800 bit/s + break; + case 5: p[0] = 72; // 9600 bit/s + break; + case 6: p[0] = 73; // 14400 bit/s + break; + case 7: p[0] = 75; // 19200 bit/s + break; + } + return p+3; +} + +u_char EncodeSyncParams(u_char si2, u_char ai) +{ + + switch (si2) + { + case 0: return ai + 2; // 1200 bit/s + case 1: return ai + 24; // 1200/75 bit/s + case 2: return ai + 23; // 75/1200 bit/s + case 3: return ai + 3; // 2400 bit/s + case 4: return ai + 5; // 4800 bit/s + case 5: return ai + 8; // 9600 bit/s + case 6: return ai + 9; // 14400 bit/s + case 7: return ai + 11; // 19200 bit/s + case 8: return ai + 14; // 48000 bit/s + case 9: return ai + 15; // 56000 bit/s + case 15: return ai + 40; // negotiate bit/s + default: break; + } + return ai; +} + + +static u_char DecodeASyncParams(u_char si2, u_char *p) +{ u_char info; + + switch (p[5]) + { + case 66: // 1200 bit/s + break; // si2 bleibt gleich + case 88: // 1200/75 bit/s + si2 += 1; + break; + case 87: // 75/1200 bit/s + si2 += 2; + break; + case 67: // 2400 bit/s + si2 += 3; + break; + case 69: // 4800 bit/s + si2 += 4; + break; + case 72: // 9600 bit/s + si2 += 5; + break; + case 73: // 14400 bit/s + si2 += 6; + break; + case 75: // 19200 bit/s + si2 += 7; + break; + } + + info = p[7] & 0x7f; + if ((info & 16) && (!(info & 8))) // 7 data bits + si2 += 32; // else 8 data bits + if ((info & 96) == 96) // 2 stop bits + si2 += 16; // else 1 stop bit + if ((info & 2) && (!(info & 1))) // even parity + si2 += 8; // else no parity + + return si2; +} + + +static u_char DecodeSyncParams(u_char si2, u_char info) +{ + info &= 0x7f; + switch (info) + { + case 40: // bit/s aushandeln --- hat nicht geklappt, ai wird 165 statt 175! + return si2 + 15; + case 15: // 56000 bit/s --- hat nicht geklappt, ai wird 0 statt 169 ! + return si2 + 9; + case 14: // 48000 bit/s + return si2 + 8; + case 11: // 19200 bit/s + return si2 + 7; + case 9: // 14400 bit/s + return si2 + 6; + case 8: // 9600 bit/s + return si2 + 5; + case 5: // 4800 bit/s + return si2 + 4; + case 3: // 2400 bit/s + return si2 + 3; + case 23: // 75/1200 bit/s + return si2 + 2; + case 24: // 1200/75 bit/s + return si2 + 1; + default: // 1200 bit/s + return si2; + } } +static u_char DecodeSI2(struct sk_buff *skb) +{ u_char *p; //, *pend=skb->data + skb->len; + + if ((p = findie(skb->data, skb->len, 0x7c, 0))) + { + switch (p[4] & 0x0f) + { + case 0x01: if (p[1] == 0x04) // sync. Bitratenadaption + return DecodeSyncParams(160, p[5]); // V.110/X.30 + else if (p[1] == 0x06) // async. Bitratenadaption + return DecodeASyncParams(192, p); // V.110/X.30 + break; + case 0x08: // if (p[5] == 0x02) // sync. Bitratenadaption + return DecodeSyncParams(176, p[5]); // V.120 + break; + } + } + return 0; +} + +#endif + + static void -l3dss1_setup_req(struct PStack *st, u_char pr, +l3dss1_setup_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[128]; u_char *p = tmp; u_char channel = 0; - u_char screen = 0; + u_char screen = 0x80; u_char *teln; u_char *msn; + u_char *sub; + u_char *sp; int l; - st->l3.callref = st->pa->callref; - MsgHead(p, st->l3.callref, MT_SETUP); + MsgHead(p, pc->callref, MT_SETUP); /* * Set Bearer Capability, Map info from 1TR6-convention to EDSS1 */ +#ifdef HISAX_EURO_SENDCOMPLETE *p++ = 0xa1; /* complete indicator */ - switch (st->pa->setup.si1) { +#endif + switch (pc->para.setup.si1) { case 1: /* Telephony */ *p++ = 0x4; /* BC-IE-code */ *p++ = 0x3; /* Length */ @@ -157,7 +521,7 @@ l3dss1_setup_req(struct PStack *st, u_char pr, /* * What about info2? Mapping to High-Layer-Compatibility? */ - teln = st->pa->setup.phone; + teln = pc->para.setup.phone; if (*teln) { /* parse number for special things */ if (!isdigit(*teln)) { @@ -179,19 +543,28 @@ l3dss1_setup_req(struct PStack *st, u_char pr, screen = 0x80; break; default: - if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "Wrong MSN Code"); + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "Wrong MSN Code"); break; } teln++; } } if (channel) { - *p++ = 0x18; /* channel indicator */ + *p++ = IE_CHANNEL_ID; *p++ = 1; *p++ = channel; } - msn = st->pa->setup.eazmsn; + msn = pc->para.setup.eazmsn; + sub = NULL; + sp = msn; + while (*sp) { + if ('.' == *sp) { + sub = sp; + *sp = 0; + } else + sp++; + } if (*msn) { *p++ = 0x6c; *p++ = strlen(msn) + (screen ? 2 : 1); @@ -204,241 +577,372 @@ l3dss1_setup_req(struct PStack *st, u_char pr, while (*msn) *p++ = *msn++ & 0x7f; } + if (sub) { + *sub++ = '.'; + *p++ = 0x6d; /* Calling party subaddress */ + *p++ = strlen(sub) + 2; + *p++ = 0x80; /* NSAP coded */ + *p++ = 0x50; /* local IDI format */ + while (*sub) + *p++ = *sub++ & 0x7f; + } + sub = NULL; + sp = teln; + while (*sp) { + if ('.' == *sp) { + sub = sp; + *sp = 0; + } else + sp++; + } *p++ = 0x70; *p++ = strlen(teln) + 1; /* Classify as AnyPref. */ *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */ - while (*teln) *p++ = *teln++ & 0x7f; + if (sub) { + *sub++ = '.'; + *p++ = 0x71; /* Called party subaddress */ + *p++ = strlen(sub) + 2; + *p++ = 0x80; /* NSAP coded */ + *p++ = 0x50; /* local IDI format */ + while (*sub) + *p++ = *sub++ & 0x7f; + } + +#ifdef EXT_BEARER_CAPS + if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) + { // sync. Bitratenadaption, V.110/X.30 + *p++ = 0x7c; *p++ = 0x04; *p++ = 0x88; *p++ = 0x90; *p++ = 0x21; + *p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80); + } + else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) + { // sync. Bitratenadaption, V.120 + *p++ = 0x7c; *p++ = 0x05; *p++ = 0x88; *p++ = 0x90; *p++ = 0x28; + *p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0); + *p++ = 0x82; + } + else if (pc->para.setup.si2 >= 192) + { // async. Bitratenadaption, V.110/X.30 + *p++ = 0x7c; *p++ = 0x06; *p++ = 0x88; *p++ = 0x90; *p++ = 0x21; + p = EncodeASyncParams(p, pc->para.setup.si2 - 192); + } +#endif l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - L3DelTimer(&st->l3.timer); - L3AddTimer(&st->l3.timer, st->l3.t303, CC_T303); - newl3state(st, 1); - st->l3.l3l2(st, DL_DATA, skb); - + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T303, CC_T303); + newl3state(pc, 1); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); } static void -l3dss1_call_proc(struct PStack *st, u_char pr, void *arg) +l3dss1_call_proc(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); p = skb->data; - if ((p = findie(p, skb->len, 0x18, 0))) { - st->pa->bchannel = p[2] & 0x3; - if ((!st->pa->bchannel) && (st->l3.debug & L3_DEB_WARN)) - l3_debug(st, "setup answer without bchannel"); - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup answer without bchannel"); - SET_SKB_FREE(skb); + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + pc->para.bchannel = p[2] & 0x3; + if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN)) + l3_debug(pc->st, "setup answer without bchannel"); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer without bchannel"); dev_kfree_skb(skb); - newl3state(st, 3); - L3AddTimer(&st->l3.timer, st->l3.t310, CC_T310); - st->l3.l3l4(st, CC_PROCEEDING_IND, NULL); + newl3state(pc, 3); + L3AddTimer(&pc->timer, T310, CC_T310); + pc->st->l3.l3l4(pc, CC_PROCEEDING_IND, NULL); } static void -l3dss1_setup_ack(struct PStack *st, u_char pr, void *arg) +l3dss1_setup_ack(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); p = skb->data; - if ((p = findie(p, skb->len, 0x18, 0))) { - st->pa->bchannel = p[2] & 0x3; - if ((!st->pa->bchannel) && (st->l3.debug & L3_DEB_WARN)) - l3_debug(st, "setup answer without bchannel"); - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup answer without bchannel"); - SET_SKB_FREE(skb); + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + pc->para.bchannel = p[2] & 0x3; + if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN)) + l3_debug(pc->st, "setup answer without bchannel"); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer without bchannel"); dev_kfree_skb(skb); - newl3state(st, 2); - L3AddTimer(&st->l3.timer, st->l3.t304, CC_T304); - st->l3.l3l4(st, CC_MORE_INFO, NULL); + newl3state(pc, 2); + L3AddTimer(&pc->timer, T304, CC_T304); + pc->st->l3.l3l4(pc, CC_MORE_INFO, NULL); } static void -l3dss1_disconnect(struct PStack *st, u_char pr, void *arg) +l3dss1_disconnect(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; int cause = -1; - StopAllL3Timer(st); + StopAllL3Timer(pc); p = skb->data; - st->pa->loc = 0; + pc->para.loc = 0; if ((p = findie(p, skb->len, IE_CAUSE, 0))) { p++; if (*p++ == 2) - st->pa->loc = *p++; + pc->para.loc = *p++; cause = *p & 0x7f; } - SET_SKB_FREE(skb); dev_kfree_skb(skb); - newl3state(st, 12); - st->pa->cause = cause; - st->l3.l3l4(st, CC_DISCONNECT_IND, NULL); + newl3state(pc, 12); + pc->para.cause = cause; + pc->st->l3.l3l4(pc, CC_DISCONNECT_IND, NULL); } static void -l3dss1_connect(struct PStack *st, u_char pr, void *arg) +l3dss1_connect(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb); - L3DelTimer(&st->l3.timer); /* T310 */ - newl3state(st, 10); - st->l3.l3l4(st, CC_SETUP_CNF, NULL); + L3DelTimer(&pc->timer); /* T310 */ + newl3state(pc, 10); + pc->para.chargeinfo = 0; + pc->st->l3.l3l4(pc, CC_SETUP_CNF, NULL); } static void -l3dss1_alerting(struct PStack *st, u_char pr, void *arg) +l3dss1_alerting(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb); - L3DelTimer(&st->l3.timer); /* T304 */ - newl3state(st, 4); - st->l3.l3l4(st, CC_ALERTING_IND, NULL); + L3DelTimer(&pc->timer); /* T304 */ + newl3state(pc, 4); + pc->st->l3.l3l4(pc, CC_ALERTING_IND, NULL); } static void -l3dss1_setup(struct PStack *st, u_char pr, void *arg) +l3dss1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; + /* This routine is called if here was no SETUP made (checks in dss1up and in + * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code + * It is called after it is veryfied that Layer2 is up. + * The cause value is allready in pc->para.cause + * MT_STATUS_ENQUIRE in the NULL state is handled too + */ + u_char tmp[16]; + u_char *p=tmp; + int l; + struct sk_buff *skb; + + switch (pc->para.cause) { + case 81: /* 0x51 invalid callreference */ + case 96: /* 0x60 mandory IE missing */ + case 101: /* 0x65 incompatible Callstate */ + MsgHead(p, pc->callref, MT_RELEASE_COMPLETE); + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = pc->para.cause | 0x80; + break; + default: + printk(KERN_ERR "HiSax internal error l3dss1_msg_without_setup\n"); + return; + } + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); + release_l3_process(pc); +} + +static void +l3dss1_setup(struct l3_process *pc, u_char pr, void *arg) +{ + u_char *p, *ptmp[8]; + int i; int bcfound = 0; char tmp[80]; struct sk_buff *skb = arg; + /* ETS 300-104 1.3.4 and 1.3.5 + * we need to detect unknown inform. element from 0 to 7 + */ p = skb->data; - st->pa->callref = getcallref(p); - st->l3.callref = 0x80 + st->pa->callref; + for(i = 0; i < 8; i++) + ptmp[i] = skb->data; + if (findie(ptmp[1], skb->len, 0x01, 0) + || findie(ptmp[2], skb->len, 0x02, 0) + || findie(ptmp[3], skb->len, 0x03, 0) + || findie(ptmp[5], skb->len, 0x05, 0) + || findie(ptmp[6], skb->len, 0x06, 0) + || findie(ptmp[7], skb->len, 0x07, 0)) { + /* if ie is < 8 and not 0 nor 4, send RELEASE_COMPLETE + * cause 0x60 + */ + pc->para.cause = 0x60; + dev_kfree_skb(skb); + if (pc->state == 0) + pc->st->l3.l3l4(pc, CC_ESTABLISH, NULL); + else + l3dss1_msg_without_setup(pc, pr, NULL); + return; + } /* * Channel Identification */ p = skb->data; - if ((p = findie(p, skb->len, 0x18, 0))) { - st->pa->bchannel = p[2] & 0x3; - if (st->pa->bchannel) + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + pc->para.bchannel = p[2] & 0x3; + if (pc->para.bchannel) bcfound++; - else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup without bchannel"); - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup without bchannel"); + else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without bchannel"); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without bchannel"); /* * Bearer Capabilities */ p = skb->data; if ((p = findie(p, skb->len, 0x04, 0))) { - st->pa->setup.si2 = 0; + pc->para.setup.si2 = 0; switch (p[2] & 0x1f) { case 0x00: /* Speech */ case 0x10: /* 3.1 Khz audio */ - st->pa->setup.si1 = 1; + pc->para.setup.si1 = 1; break; case 0x08: /* Unrestricted digital information */ - st->pa->setup.si1 = 7; + pc->para.setup.si1 = 7; +/* JIM, 05.11.97 I wanna set service indicator 2 */ +#ifdef EXT_BEARER_CAPS + pc->para.setup.si2 = DecodeSI2(skb); + printk(KERN_DEBUG "HiSax: SI=%d, AI=%d\n", + pc->para.setup.si1, pc->para.setup.si2); +#endif break; case 0x09: /* Restricted digital information */ - st->pa->setup.si1 = 2; + pc->para.setup.si1 = 2; break; case 0x11: /* Unrestr. digital information with tones/announcements */ - st->pa->setup.si1 = 3; + pc->para.setup.si1 = 3; break; case 0x18: /* Video */ - st->pa->setup.si1 = 4; + pc->para.setup.si1 = 4; break; default: - st->pa->setup.si1 = 0; + pc->para.setup.si1 = 0; } - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup without bearer capabilities"); + } else { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without bearer capabilities"); + /* ETS 300-104 1.3.3 */ + pc->para.cause = 0x60; + dev_kfree_skb(skb); + if (pc->state == 0) + pc->st->l3.l3l4(pc, CC_ESTABLISH, NULL); + else + l3dss1_msg_without_setup(pc, pr, NULL); + return; + } p = skb->data; if ((p = findie(p, skb->len, 0x70, 0))) - iecpy(st->pa->setup.eazmsn, p, 1); + iecpy(pc->para.setup.eazmsn, p, 1); else - st->pa->setup.eazmsn[0] = 0; + pc->para.setup.eazmsn[0] = 0; + p = skb->data; + if ((p = findie(p, skb->len, 0x71, 0))) { + /* Called party subaddress */ + if ((p[1]>=2) && (p[2]==0x80) && (p[3]==0x50)) { + tmp[0]='.'; + iecpy(&tmp[1], p, 2); + strcat(pc->para.setup.eazmsn, tmp); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "wrong called subaddress"); + } p = skb->data; if ((p = findie(p, skb->len, 0x6c, 0))) { - st->pa->setup.plan = p[2]; + pc->para.setup.plan = p[2]; if (p[2] & 0x80) { - iecpy(st->pa->setup.phone, p, 1); - st->pa->setup.screen = 0; + iecpy(pc->para.setup.phone, p, 1); + pc->para.setup.screen = 0; } else { - iecpy(st->pa->setup.phone, p, 2); - st->pa->setup.screen = p[3]; + iecpy(pc->para.setup.phone, p, 2); + pc->para.setup.screen = p[3]; } } else { - st->pa->setup.phone[0] = 0; - st->pa->setup.plan = 0; - st->pa->setup.screen = 0; + pc->para.setup.phone[0] = 0; + pc->para.setup.plan = 0; + pc->para.setup.screen = 0; + } + p = skb->data; + if ((p = findie(p, skb->len, 0x6d, 0))) { + /* Calling party subaddress */ + if ((p[1]>=2) && (p[2]==0x80) && (p[3]==0x50)) { + tmp[0]='.'; + iecpy(&tmp[1], p, 2); + strcat(pc->para.setup.phone, tmp); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "wrong calling subaddress"); } - SET_SKB_FREE(skb); + dev_kfree_skb(skb); if (bcfound) { - if ((st->pa->setup.si1 != 7) && (st->l3.debug & L3_DEB_WARN)) { + if ((pc->para.setup.si1 != 7) && (pc->debug & L3_DEB_WARN)) { sprintf(tmp, "non-digital call: %s -> %s", - st->pa->setup.phone, - st->pa->setup.eazmsn); - l3_debug(st, tmp); + pc->para.setup.phone, pc->para.setup.eazmsn); + l3_debug(pc->st, tmp); } - newl3state(st, 6); - st->l3.l3l4(st, CC_SETUP_IND, NULL); - } + newl3state(pc, 6); + pc->st->l3.l3l4(pc, CC_SETUP_IND, NULL); + } else + release_l3_process(pc); } static void -l3dss1_reset(struct PStack *st, u_char pr, void *arg) +l3dss1_reset(struct l3_process *pc, u_char pr, void *arg) { - StopAllL3Timer(st); - newl3state(st, 0); + release_l3_process(pc); } static void -l3dss1_setup_rsp(struct PStack *st, u_char pr, +l3dss1_setup_rsp(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 8); - l3dss1_message(st, MT_CONNECT); - L3DelTimer(&st->l3.timer); - L3AddTimer(&st->l3.timer, st->l3.t313, CC_T313); + newl3state(pc, 8); + l3dss1_message(pc, MT_CONNECT); + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T313, CC_T313); } static void -l3dss1_connect_ack(struct PStack *st, u_char pr, void *arg) +l3dss1_connect_ack(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb); - newl3state(st, 10); - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL); + newl3state(pc, 10); + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc, CC_SETUP_COMPLETE_IND, NULL); } static void -l3dss1_disconnect_req(struct PStack *st, u_char pr, void *arg) +l3dss1_disconnect_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; @@ -446,12 +950,12 @@ l3dss1_disconnect_req(struct PStack *st, u_char pr, void *arg) int l; u_char cause = 0x10; - if (st->pa->cause > 0) - cause = st->pa->cause; + if (pc->para.cause > 0) + cause = pc->para.cause; - StopAllL3Timer(st); + StopAllL3Timer(pc); - MsgHead(p, st->l3.callref, MT_DISCONNECT); + MsgHead(p, pc->callref, MT_DISCONNECT); *p++ = IE_CAUSE; *p++ = 0x2; @@ -462,13 +966,13 @@ l3dss1_disconnect_req(struct PStack *st, u_char pr, void *arg) if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - newl3state(st, 11); - st->l3.l3l2(st, DL_DATA, skb); - L3AddTimer(&st->l3.timer, st->l3.t305, CC_T305); + newl3state(pc, 11); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); + L3AddTimer(&pc->timer, T305, CC_T305); } static void -l3dss1_reject_req(struct PStack *st, u_char pr, void *arg) +l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; @@ -476,10 +980,10 @@ l3dss1_reject_req(struct PStack *st, u_char pr, void *arg) int l; u_char cause = 0x95; - if (st->pa->cause > 0) - cause = st->pa->cause; + if (pc->para.cause > 0) + cause = pc->para.cause; - MsgHead(p, st->l3.callref, MT_RELEASE_COMPLETE); + MsgHead(p, pc->callref, MT_RELEASE_COMPLETE); *p++ = IE_CAUSE; *p++ = 0x2; @@ -490,13 +994,14 @@ l3dss1_reject_req(struct PStack *st, u_char pr, void *arg) 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); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); + pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL); + newl3state(pc, 0); + release_l3_process(pc); } static void -l3dss1_release(struct PStack *st, u_char pr, void *arg) +l3dss1_release(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; @@ -506,38 +1011,45 @@ l3dss1_release(struct PStack *st, u_char pr, void *arg) if ((p = findie(p, skb->len, IE_CAUSE, 0))) { p++; if (*p++ == 2) - st->pa->loc = *p++; + pc->para.loc = *p++; cause = *p & 0x7f; } - SET_SKB_FREE(skb); + p = skb->data; + if ((p = findie(p, skb->len, IE_FACILITY, 0))) { +#ifdef HISAX_DE_AOC + l3dss1_parse_facility(pc,p); +#else + p = NULL; +#endif + } dev_kfree_skb(skb); - StopAllL3Timer(st); - st->pa->cause = cause; - newl3state(st, 0); - l3dss1_message(st, MT_RELEASE_COMPLETE); - st->l3.l3l4(st, CC_RELEASE_IND, NULL); + StopAllL3Timer(pc); + pc->para.cause = cause; + l3dss1_message(pc, MT_RELEASE_COMPLETE); + pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL); + newl3state(pc, 0); + release_l3_process(pc); } static void -l3dss1_alert_req(struct PStack *st, u_char pr, +l3dss1_alert_req(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 7); - l3dss1_message(st, MT_ALERTING); + newl3state(pc, 7); + l3dss1_message(pc, MT_ALERTING); } static void -l3dss1_status_enq(struct PStack *st, u_char pr, void *arg) +l3dss1_status_enq(struct l3_process *pc, u_char pr, void *arg) { u_char tmp[16]; u_char *p = tmp; int l; struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb); - MsgHead(p, st->l3.callref, MT_STATUS); + MsgHead(p, pc->callref, MT_STATUS); *p++ = IE_CAUSE; *p++ = 0x2; @@ -546,42 +1058,96 @@ l3dss1_status_enq(struct PStack *st, u_char pr, void *arg) *p++ = 0x14; /* CallState */ *p++ = 0x1; - *p++ = st->l3.state & 0x3f; + *p++ = pc->state & 0x3f; + + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); +} + +static void +l3dss1_status_req(struct l3_process *pc, u_char pr, void *arg) +{ + /* ETS 300-104 7.4.1, 8.4.1, 10.3.1, 11.4.1, 12.4.1, 13.4.1, 14.4.1... + if setup has been made and a non expected message type is received, we must send MT_STATUS cause 0x62 */ + u_char tmp[16]; + u_char *p = tmp; + int l; + struct sk_buff *skb = arg; + + dev_kfree_skb(skb); + + MsgHead(p, pc->callref, MT_STATUS); + + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = 0x62 | 0x80; /* status sending */ + + *p++ = 0x14; /* CallState */ + *p++ = 0x1; + *p++ = pc->state & 0x3f; l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - st->l3.l3l2(st, DL_DATA, skb); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); } static void -l3dss1_t303(struct PStack *st, u_char pr, void *arg) +l3dss1_release_ind(struct l3_process *pc, u_char pr, void *arg) { - if (st->l3.n_t303 > 0) { - st->l3.n_t303--; - L3DelTimer(&st->l3.timer); - l3dss1_setup_req(st, pr, arg); + u_char *p; + struct sk_buff *skb = arg; + int callState = 0; + p = skb->data; + + if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) { + p++; + if (1== *p++) + callState = *p; + } + if(callState == 0) { + /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1 + * set down layer 3 without sending any message + */ + pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL); + newl3state(pc, 0); + release_l3_process(pc); } else { - newl3state(st, 0); - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL); - st->l3.n_t303 = 1; + pc->st->l3.l3l4(pc, CC_IGNORE, NULL); } } static void -l3dss1_t304(struct PStack *st, u_char pr, void *arg) +l3dss1_t303(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3dss1_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_SETUP_ERR, NULL); + if (pc->N303 > 0) { + pc->N303--; + L3DelTimer(&pc->timer); + l3dss1_setup_req(pc, pr, arg); + } else { + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc, CC_NOSETUP_RSP_ERR, NULL); + release_l3_process(pc); + } +} + +static void +l3dss1_t304(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3dss1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL); } static void -l3dss1_t305(struct PStack *st, u_char pr, void *arg) +l3dss1_t305(struct l3_process *pc, u_char pr, void *arg) { u_char tmp[16]; u_char *p = tmp; @@ -589,11 +1155,11 @@ l3dss1_t305(struct PStack *st, u_char pr, void *arg) struct sk_buff *skb; u_char cause = 0x90; - L3DelTimer(&st->l3.timer); - if (st->pa->cause > 0) - cause = st->pa->cause; + L3DelTimer(&pc->timer); + if (pc->para.cause > 0) + cause = pc->para.cause | 0x80; - MsgHead(p, st->l3.callref, MT_RELEASE); + MsgHead(p, pc->callref, MT_RELEASE); *p++ = IE_CAUSE; *p++ = 0x2; @@ -604,48 +1170,179 @@ l3dss1_t305(struct PStack *st, u_char pr, void *arg) if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - newl3state(st, 19); - st->l3.l3l2(st, DL_DATA, skb); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1); + newl3state(pc, 19); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); + L3AddTimer(&pc->timer, T308, CC_T308_1); +} + +static void +l3dss1_t310(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3dss1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL); +} + +static void +l3dss1_t313(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3dss1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc, CC_CONNECT_ERR, NULL); +} + +static void +l3dss1_t308_1(struct l3_process *pc, u_char pr, void *arg) +{ + newl3state(pc, 19); + L3DelTimer(&pc->timer); + l3dss1_message(pc, MT_RELEASE); + L3AddTimer(&pc->timer, T308, CC_T308_2); +} + +static void +l3dss1_t308_2(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc, CC_RELEASE_ERR, NULL); + release_l3_process(pc); } static void -l3dss1_t310(struct PStack *st, u_char pr, void *arg) +l3dss1_restart(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3dss1_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_SETUP_ERR, NULL); + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc, CC_DLRL, NULL); + release_l3_process(pc); } static void -l3dss1_t313(struct PStack *st, u_char pr, void *arg) +l3dss1_status(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3dss1_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_CONNECT_ERR, NULL); + u_char *p; + char tmp[64], *t; + int l; + struct sk_buff *skb = arg; + int cause, callState; + + cause = callState = -1; + p = skb->data; + t = tmp; + if ((p = findie(p, skb->len, IE_CAUSE, 0))) { + p++; + l = *p++; + t += sprintf(t,"Status CR %x Cause:", pc->callref); + while (l--) { + cause = *p; + t += sprintf(t," %2x",*p++); + } + } else + sprintf(t,"Status CR %x no Cause", pc->callref); + l3_debug(pc->st, tmp); + p = skb->data; + t = tmp; + t += sprintf(t,"Status state %x ", pc->state); + if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) { + p++; + if (1== *p++) { + callState = *p; + t += sprintf(t,"peer state %x" , *p); + } + else + t += sprintf(t,"peer state len error"); + } else + sprintf(t,"no peer state"); + l3_debug(pc->st, tmp); + if(((cause & 0x7f) == 0x6f) && (callState == 0)) { + /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... + * if received MT_STATUS with cause == 0x6f and call + * state == 0, then we must set down layer 3 + */ + l3dss1_release_ind(pc, pr, arg); + } else + dev_kfree_skb(skb); } static void -l3dss1_t308_1(struct PStack *st, u_char pr, void *arg) +l3dss1_facility(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 19); - L3DelTimer(&st->l3.timer); - l3dss1_message(st, MT_RELEASE); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_2); + u_char *p; + struct sk_buff *skb = arg; + + p = skb->data; + if ((p = findie(p, skb->len, IE_FACILITY, 0))) { +#ifdef HISAX_DE_AOC + l3dss1_parse_facility(pc,p); +#else + p = NULL; +#endif + } } + + static void -l3dss1_t308_2(struct PStack *st, u_char pr, void *arg) +l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 0); - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_RELEASE_ERR, NULL); + u_char tmp[32]; + u_char *p; + u_char ri, chan=0; + int l; + struct sk_buff *skb = arg; + struct l3_process *up; + + newl3state(pc, 2); + L3DelTimer(&pc->timer); + p = skb->data; + if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) { + ri = p[2]; + sprintf(tmp, "Restart %x", ri); + } else { + sprintf(tmp, "Restart without restart IE"); + ri = 0x86; + } + l3_debug(pc->st, tmp); + p = skb->data; + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + chan = p[2] & 3; + sprintf(tmp, "Restart for channel %d", chan); + l3_debug(pc->st, tmp); + } + dev_kfree_skb(skb); + newl3state(pc, 2); + up = pc->st->l3.proc; + while (up) { + if ((ri & 7)==7) + up->st->lli.l4l3(up->st, CC_RESTART, up); + else if (up->para.bchannel == chan) + up->st->lli.l4l3(up->st, CC_RESTART, up); + up = up->next; + } + p = tmp; + MsgHead(p, pc->callref, MT_RESTART_ACKNOWLEDGE); + if (chan) { + *p++ = IE_CHANNEL_ID; + *p++ = 1; + *p++ = chan | 0x80; + } + *p++ = 0x79; /* RESTART Ind */ + *p++ = 1; + *p++ = ri; + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + newl3state(pc, 0); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); } + /* *INDENT-OFF* */ static struct stateentry downstatelist[] = { + {SBIT(0), + CC_ESTABLISH, l3dss1_msg_without_setup}, {SBIT(0), CC_SETUP_REQ, l3dss1_setup_req}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(10), @@ -654,6 +1351,8 @@ static struct stateentry downstatelist[] = CC_RELEASE_REQ, l3dss1_release_req}, {ALL_STATES, CC_DLRL, l3dss1_reset}, + {ALL_STATES, + CC_RESTART, l3dss1_restart}, {SBIT(6), CC_IGNORE, l3dss1_reset}, {SBIT(6), @@ -685,63 +1384,216 @@ static struct stateentry datastatelist[] = { {ALL_STATES, MT_STATUS_ENQUIRY, l3dss1_status_enq}, + {ALL_STATES, + MT_FACILITY, l3dss1_facility}, + {SBIT(19), + MT_STATUS, l3dss1_release_ind}, + {ALL_STATES, + MT_STATUS, l3dss1_status}, {SBIT(0) | SBIT(6), MT_SETUP, l3dss1_setup}, {SBIT(1) | SBIT(2), MT_CALL_PROCEEDING, l3dss1_call_proc}, + {SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_CALL_PROCEEDING, l3dss1_status_req}, {SBIT(1), MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack}, + {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_SETUP_ACKNOWLEDGE, l3dss1_status_req}, {SBIT(1) | SBIT(2) | SBIT(3), MT_ALERTING, l3dss1_alerting}, + {SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_ALERTING, l3dss1_status_req}, {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19), MT_RELEASE_COMPLETE, l3dss1_release_cmpl}, - {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | - SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19), + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | + SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) /*| SBIT(19)*/, MT_RELEASE, l3dss1_release}, + {SBIT(19), MT_RELEASE, l3dss1_release_ind}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10), MT_DISCONNECT, l3dss1_disconnect}, + {SBIT(11), + MT_DISCONNECT, l3dss1_release_req}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4), MT_CONNECT, l3dss1_connect}, + {SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_CONNECT, l3dss1_status_req}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(11) | SBIT(19), + MT_CONNECT_ACKNOWLEDGE, l3dss1_status_req}, {SBIT(8), MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_INVALID, l3dss1_status_req}, }; -/* *INDENT-ON* */ +static int datasllen = sizeof(datastatelist) / sizeof(struct stateentry); -static int datasllen = sizeof(datastatelist) / -sizeof(struct stateentry); +static struct stateentry globalmes_list[] = +{ + {ALL_STATES, + MT_STATUS, l3dss1_status}, + {SBIT(0), + MT_RESTART, l3dss1_global_restart}, +/* {SBIT(1), + MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack}, +*/ +}; +static int globalm_len = sizeof(globalmes_list) / sizeof(struct stateentry); + +#if 0 +static struct stateentry globalcmd_list[] = +{ + {ALL_STATES, + CC_STATUS, l3dss1_status_req}, + {SBIT(0), + CC_RESTART, l3dss1_restart_req}, +}; + +static int globalc_len = sizeof(globalcmd_list) / sizeof(struct stateentry); +#endif +/* *INDENT-ON* */ + +static void +global_handler(struct PStack *st, int mt, struct sk_buff *skb) +{ + int i; + char tmp[64]; + struct l3_process *proc = st->l3.global; + + for (i = 0; i < globalm_len; i++) + if ((mt == globalmes_list[i].primitive) && + ((1 << proc->state) & globalmes_list[i].state)) + break; + if (i == globalm_len) { + dev_kfree_skb(skb); + if (st->l3.debug & L3_DEB_STATE) { + sprintf(tmp, "dss1 global state %d mt %x unhandled", + proc->state, mt); + l3_debug(st, tmp); + } + return; + } else { + if (st->l3.debug & L3_DEB_STATE) { + sprintf(tmp, "dss1 global %d mt %x", + proc->state, mt); + l3_debug(st, tmp); + } + globalmes_list[i].rout(proc, mt, skb); + } +} static void dss1up(struct PStack *st, int pr, void *arg) { - int i, mt; + int i, mt, cr, cause, callState; + char *ptr; struct sk_buff *skb = arg; + struct l3_process *proc; 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 %d state %d", + sprintf(tmp, "dss1up%sunexpected discriminator %x message len %d", (pr == DL_DATA) ? " " : "(broadcast) ", - skb->data[0], skb->len, st->l3.state); + skb->data[0], skb->len); l3_debug(st, tmp); } - SET_SKB_FREE(skb); dev_kfree_skb(skb); return; } + cr = getcallref(skb->data); mt = skb->data[skb->data[1] + 2]; + if (!cr) { /* Global CallRef */ + global_handler(st, mt, skb); + return; + } else if (cr == -1) { /* Dummy Callref */ + dev_kfree_skb(skb); + return; + } else if (!(proc = getl3proc(st, cr))) { + /* No transaction process exist, that means no call with + * this callreference is active + */ + if (mt == MT_SETUP) { + /* Setup creates a new transaction process */ + if (!(proc = new_l3_process(st, cr))) { + /* May be to answer with RELEASE_COMPLETE and + * CAUSE 0x2f "Resource unavailable", but this + * need a new_l3_process too ... arghh + */ + dev_kfree_skb(skb); + return; + } + } else if (mt == MT_STATUS) { + cause = 0; + if((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) { + ptr++; + if (*ptr++ == 2) + ptr++; + cause = *ptr & 0x7f; + } + callState = 0; + if((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) { + ptr++; + if (*ptr++ == 2) + ptr++; + callState = *ptr; + } + if (callState == 0) { + /* ETS 300-104 part 2.4.1 + * if setup has not been made and a message type + * MT_STATUS is received with call state == 0, + * we must send nothing + */ + dev_kfree_skb(skb); + return; + } else { + /* ETS 300-104 part 2.4.2 + * if setup has not been made and a message type + * MT_STATUS is received with call state != 0, + * we must send MT_RELEASE_COMPLETE cause 101 + */ + dev_kfree_skb(skb); + if ((proc = new_l3_process(st, cr))) { + proc->para.cause = 0x65; /* 101 */ + proc->st->l3.l3l4(proc, CC_ESTABLISH, NULL); + } + return; + } + } else if (mt == MT_RELEASE_COMPLETE){ + dev_kfree_skb(skb); + return; + } else { + /* ETS 300-104 part 2 + * if setup has not been made and a message type + * (except MT_SETUP and RELEASE_COMPLETE) is received, + * we must send MT_RELEASE_COMPLETE cause 81 */ + dev_kfree_skb(skb); + if ((proc = new_l3_process(st, cr))) { + proc->para.cause = 0x51; /* 81 */ + proc->st->l3.l3l4(proc, CC_ESTABLISH, NULL); + } + return; + } + } else if (!l3dss1_check_messagetype_validity(mt)) { + /* ETS 300-104 7.4.2, 8.4.2, 10.3.2, 11.4.2, 12.4.2, 13.4.2, + * 14.4.2... + * if setup has been made and invalid message type is received, + * we must send MT_STATUS cause 0x62 + */ + mt = MT_INVALID; /* sorry, not clean, but do the right thing ;-) */ + } + for (i = 0; i < datasllen; i++) if ((mt == datastatelist[i].primitive) && - ((1 << st->l3.state) & datastatelist[i].state)) + ((1 << proc->state) & datastatelist[i].state)) break; if (i == datasllen) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "dss1up%sstate %d mt %x unhandled", (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); + proc->state, mt); l3_debug(st, tmp); } return; @@ -749,36 +1601,55 @@ dss1up(struct PStack *st, int pr, void *arg) if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "dss1up%sstate %d mt %x", (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); + proc->state, mt); l3_debug(st, tmp); } - datastatelist[i].rout(st, pr, skb); + datastatelist[i].rout(proc, pr, skb); } } static void dss1down(struct PStack *st, int pr, void *arg) { - int i; + int i, cr; + struct l3_process *proc; + struct Channel *chan; char tmp[80]; + if (CC_SETUP_REQ == pr) { + chan = arg; + cr = newcallref(); + cr |= 0x80; + if ((proc = new_l3_process(st, cr))) { + proc->chan = chan; + chan->proc = proc; + proc->para.setup = chan->setup; + proc->callref = cr; + } + } else { + proc = arg; + } + if (!proc) { + printk(KERN_ERR "HiSax internal error dss1down without proc\n"); + return; + } for (i = 0; i < downsllen; i++) if ((pr == downstatelist[i].primitive) && - ((1 << st->l3.state) & downstatelist[i].state)) + ((1 << proc->state) & downstatelist[i].state)) break; if (i == downsllen) { if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "dss1down state %d prim %d unhandled", - st->l3.state, pr); + proc->state, pr); l3_debug(st, tmp); } } else { if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "dss1down state %d prim %d", - st->l3.state, pr); + proc->state, pr); l3_debug(st, tmp); } - downstatelist[i].rout(st, pr, arg); + downstatelist[i].rout(proc, pr, arg); } } @@ -787,20 +1658,20 @@ setstack_dss1(struct PStack *st) { char tmp[64]; - st->l4.l4l3 = dss1down; + st->lli.l4l3 = dss1down; st->l2.l2l3 = dss1up; - st->l3.t303 = 4000; - st->l3.t304 = 30000; - st->l3.t305 = 30000; - st->l3.t308 = 4000; - st->l3.t310 = 30000; - st->l3.t313 = 4000; - st->l3.t318 = 4000; - st->l3.t319 = 4000; - st->l3.n_t303 = 1; - - if (st->l3.channr & 1) { - strcpy(tmp, dss1_revision); - printk(KERN_NOTICE "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp)); + st->l3.N303 = 1; + if (!(st->l3.global = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) { + printk(KERN_ERR "HiSax can't get memory for dss1 global CR\n"); + } else { + st->l3.global->state = 0; + st->l3.global->callref = 0; + st->l3.global->next = NULL; + st->l3.global->debug = L3_DEB_WARN; + st->l3.global->st = st; + st->l3.global->N303 = 1; + L3InitTimer(st->l3.global, &st->l3.global->timer); } + strcpy(tmp, dss1_revision); + printk(KERN_INFO "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp)); } diff --git a/drivers/isdn/hisax/l3dss1.h b/drivers/isdn/hisax/l3dss1.h new file mode 100644 index 000000000000..8508c3109820 --- /dev/null +++ b/drivers/isdn/hisax/l3dss1.h @@ -0,0 +1,71 @@ +/* $Id: l3dss1.h,v 1.5 1998/02/02 13:34:30 keil Exp $ + * + * DSS1 (Euro) D-channel protocol defines + * + * $Log: l3dss1.h,v $ + * Revision 1.5 1998/02/02 13:34:30 keil + * Support australian Microlink net and german AOCD + * + * Revision 1.4 1997/10/29 19:07:54 keil + * changes for 2.1 + * + * Revision 1.3 1997/08/07 17:44:37 keil + * Fix RESTART + * + * Revision 1.2 1997/08/03 14:36:34 keil + * Implement RESTART procedure + * + * Revision 1.1 1997/07/27 21:08:38 keil + * new + * + * + * + */ +#define T303 4000 +#define T304 30000 +#define T305 30000 +#define T308 4000 +#define T310 30000 +#define T313 4000 +#define T318 4000 +#define T319 4000 + +/* + * Message-Types + */ + +#define MT_ALERTING 0x01 +#define MT_CALL_PROCEEDING 0x02 +#define MT_CONNECT 0x07 +#define MT_CONNECT_ACKNOWLEDGE 0x0f +#define MT_PROGRESS 0x03 +#define MT_SETUP 0x05 +#define MT_SETUP_ACKNOWLEDGE 0x0d +#define MT_RESUME 0x26 +#define MT_RESUME_ACKNOWLEDGE 0x2e +#define MT_RESUME_REJECT 0x22 +#define MT_SUSPEND 0x25 +#define MT_SUSPEND_ACKNOWLEDGE 0x2d +#define MT_SUSPEND_REJECT 0x21 +#define MT_USER_INFORMATION 0x20 +#define MT_DISCONNECT 0x45 +#define MT_RELEASE 0x4d +#define MT_RELEASE_COMPLETE 0x5a +#define MT_RESTART 0x46 +#define MT_RESTART_ACKNOWLEDGE 0x4e +#define MT_SEGMENT 0x60 +#define MT_CONGESTION_CONTROL 0x79 +#define MT_INFORMATION 0x7b +#define MT_FACILITY 0x62 +#define MT_NOTIFY 0x6e +#define MT_STATUS 0x7d +#define MT_STATUS_ENQUIRY 0x75 + +#define MT_INVALID 0xff + +#define IE_CAUSE 0x08 +#define IE_BEARER 0x04 +#define IE_FACILITY 0x1c +#define IE_CALL_STATE 0x14 +#define IE_CHANNEL_ID 0x18 +#define IE_RESTART_IND 0x79 diff --git a/drivers/isdn/hisax/lmgr.c b/drivers/isdn/hisax/lmgr.c new file mode 100644 index 000000000000..b3e9819b221e --- /dev/null +++ b/drivers/isdn/hisax/lmgr.c @@ -0,0 +1,58 @@ +/* $Id: lmgr.c,v 1.2 1997/10/29 19:09:34 keil Exp $ + + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * Layermanagement module + * + * $Log: lmgr.c,v $ + * Revision 1.2 1997/10/29 19:09:34 keil + * new L1 + * + * Revision 1.1 1997/06/26 11:17:25 keil + * first version + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" + +static void +error_handling_dchan(struct PStack *st, int Error) +{ + switch (Error) { + case 'C': + case 'D': + case 'G': + case 'H': + st->l2.l2tei(st, MDL_ERROR_REQ, NULL); + break; + } +} + +static void +hisax_manager(struct PStack *st, int pr, void *arg) +{ + char tm[32], str[256]; + int Code; + + switch (pr) { + case MDL_ERROR_IND: + Code = (int) arg; + jiftime(tm, jiffies); + sprintf(str, "%s manager: MDL_ERROR %c %s\n", tm, + Code, test_bit(FLG_LAPD, &st->l2.flag) ? + "D-channel" : "B-channel"); + HiSax_putstatus(st->l1.hardware, str); + if (test_bit(FLG_LAPD, &st->l2.flag)) + error_handling_dchan(st, Code); + break; + } +} + +void +setstack_manager(struct PStack *st) +{ + st->ma.layer = hisax_manager; +} diff --git a/drivers/isdn/hisax/mic.c b/drivers/isdn/hisax/mic.c new file mode 100644 index 000000000000..8bf4757c9848 --- /dev/null +++ b/drivers/isdn/hisax/mic.c @@ -0,0 +1,284 @@ +/* $Id: mic.c,v 1.6 1998/02/17 15:39:57 keil Exp $ + + * mic.c low level stuff for mic cards + * + * Copyright (C) 1997 + * + * Author Stephan von Krawczynski + * + * + * $Log: mic.c,v $ + * Revision 1.6 1998/02/17 15:39:57 keil + * fix reset problem + * + * Revision 1.5 1998/02/02 13:29:43 keil + * fast io + * + * Revision 1.4 1997/11/08 21:35:51 keil + * new l1 init + * + * Revision 1.3 1997/11/06 17:09:11 keil + * New 2.1 init code + * + * Revision 1.2 1997/10/29 18:51:17 keil + * New files + * + * Revision 1.1.2.1 1997/10/17 22:10:54 keil + * new files on 2.0 + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *mic_revision = "$Revision: 1.6 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define MIC_ISAC 2 +#define MIC_HSCX 1 +#define MIC_ADR 7 + +/* CARD_ADR (Write) */ +#define MIC_RESET 0x3 /* same as DOS driver */ + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.mic.adr, cs->hw.mic.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.mic.adr, cs->hw.mic.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.mic.adr, + cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.mic.adr, + cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.mic.adr, \ + cs->hw.mic.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.mic.adr, \ + cs->hw.mic.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.mic.adr, \ + cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.mic.adr, \ + cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +mic_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "mic: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 1) { + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0x0); + } + if (stat & 2) { + writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0x0); + } +} + +void +release_io_mic(struct IsdnCardState *cs) +{ + int bytecnt = 8; + + if (cs->hw.mic.cfg_reg) + release_region(cs->hw.mic.cfg_reg, bytecnt); +} + +static int +mic_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + return(0); + case CARD_RELEASE: + release_io_mic(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &mic_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscx(cs); /* /RTSA := ISAC RST */ + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_mic(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, mic_revision); + printk(KERN_INFO "HiSax: mic driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_MIC) + return (0); + + bytecnt = 8; + cs->hw.mic.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + cs->hw.mic.adr = cs->hw.mic.cfg_reg + MIC_ADR; + cs->hw.mic.isac = cs->hw.mic.cfg_reg + MIC_ISAC; + cs->hw.mic.hscx = cs->hw.mic.cfg_reg + MIC_HSCX; + + if (check_region((cs->hw.mic.cfg_reg), bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.mic.cfg_reg, + cs->hw.mic.cfg_reg + bytecnt); + return (0); + } else { + request_region(cs->hw.mic.cfg_reg, bytecnt, "mic isdn"); + } + + printk(KERN_INFO + "mic: defined at 0x%x IRQ %d\n", + cs->hw.mic.cfg_reg, + cs->irq); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &mic_card_msg; + ISACVersion(cs, "mic:"); + if (HscxVersion(cs, "mic:")) { + printk(KERN_WARNING + "mic: wrong HSCX versions check IO address\n"); + release_io_mic(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/netjet.c b/drivers/isdn/hisax/netjet.c new file mode 100644 index 000000000000..0686598e20a0 --- /dev/null +++ b/drivers/isdn/hisax/netjet.c @@ -0,0 +1,1108 @@ +/* $Id: netjet.c,v 1.3 1998/02/12 23:08:05 keil Exp $ + + * netjet.c low level stuff for Traverse Technologie NETJet ISDN cards + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * Thanks to Traverse Technologie Australia for documents and informations + * + * + * $Log: netjet.c,v $ + * Revision 1.3 1998/02/12 23:08:05 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.2 1998/02/02 13:32:06 keil + * New + * + * + * + */ + +#define __NO_VERSION__ +#include +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" +#include +#include +#include +#define fcstab ppp_crc16_table +#include +extern __u16 ppp_crc16_table[256]; /* from ppp code */ + +extern const char *CardType[]; + +const char *NETjet_revision = "$Revision: 1.3 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +/* PCI stuff */ +#define PCI_VENDOR_TRAVERSE_TECH 0xe159 +#define PCI_NETJET_ID 0x0001 + +#define NETJET_CTRL 0x00 +#define NETJET_DMACTRL 0x01 +#define NETJET_AUXCTRL 0x02 +#define NETJET_AUXDATA 0x03 +#define NETJET_IRQMASK0 0x04 +#define NETJET_IRQMASK1 0x05 +#define NETJET_IRQSTAT0 0x06 +#define NETJET_IRQSTAT1 0x07 +#define NETJET_DMA_READ_START 0x08 +#define NETJET_DMA_READ_IRQ 0x0c +#define NETJET_DMA_READ_END 0x10 +#define NETJET_DMA_READ_ADR 0x14 +#define NETJET_DMA_WRITE_START 0x18 +#define NETJET_DMA_WRITE_IRQ 0x1c +#define NETJET_DMA_WRITE_END 0x20 +#define NETJET_DMA_WRITE_ADR 0x24 +#define NETJET_PULSE_CNT 0x28 + +#define NETJET_ISAC_OFF 0xc0 +#define NETJET_ISACIRQ 0x10 + +#define NETJET_DMA_SIZE 512 + +#define HDLC_ZERO_SEARCH 0 +#define HDLC_FLAG_SEARCH 1 +#define HDLC_FLAG_FOUND 2 +#define HDLC_FRAME_FOUND 3 +#define HDLC_NULL 4 +#define HDLC_PART 5 +#define HDLC_FULL 6 + +#define HDLC_FLAG_VALUE 0x7e + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + long flags; + u_char ret; + + save_flags(flags); + cli(); + cs->hw.njet.auxd &= 0xfc; + cs->hw.njet.auxd |= (offset>>4) & 3; + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + ret = bytein(cs->hw.njet.isac + ((offset & 0xf)<<2)); + restore_flags(flags); + return(ret); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + long flags; + + save_flags(flags); + cli(); + cs->hw.njet.auxd &= 0xfc; + cs->hw.njet.auxd |= (offset>>4) & 3; + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + byteout(cs->hw.njet.isac + ((offset & 0xf)<<2), value); + restore_flags(flags); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + cs->hw.njet.auxd &= 0xfc; + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + insb(cs->hw.njet.isac, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + cs->hw.njet.auxd &= 0xfc; + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + outsb(cs->hw.njet.isac, data, size); +} + +void fill_mem(struct BCState *bcs, u_int *pos, u_int cnt, int chan, u_char fill) +{ + u_int mask=0x000000ff, val = 0, *p=pos; + u_int i; + + val |= fill; + if (chan) { + val <<= 8; + mask <<= 8; + } + mask ^= 0xffffffff; + for (i=0; i bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send; + } +} + +void +mode_tiger(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + char tmp[64]; + + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "Tiger mode %d bchan %d/%d", + mode, bc, bcs->channel); + debugl1(cs, tmp); + } + bcs->mode = mode; + bcs->channel = bc; + switch (mode) { + case (L1_MODE_NULL): + fill_mem(bcs, bcs->hw.tiger.send, + NETJET_DMA_SIZE, bc, 0xff); + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "Tiger stat rec %d/%d send %d", + bcs->hw.tiger.r_tot, bcs->hw.tiger.r_err, + bcs->hw.tiger.s_tot); + debugl1(cs, tmp); + } + if ((cs->bcs[0].mode == L1_MODE_NULL) && + (cs->bcs[1].mode == L1_MODE_NULL)) { + cs->hw.njet.dmactrl = 0; + byteout(cs->hw.njet.base + NETJET_DMACTRL, + cs->hw.njet.dmactrl); + byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0); + } + break; + case (L1_MODE_TRANS): + break; + case (L1_MODE_HDLC): + fill_mem(bcs, bcs->hw.tiger.send, + NETJET_DMA_SIZE, bc, 0xff); + bcs->hw.tiger.r_state = HDLC_ZERO_SEARCH; + bcs->hw.tiger.r_tot = 0; + bcs->hw.tiger.r_bitcnt = 0; + bcs->hw.tiger.r_one = 0; + bcs->hw.tiger.r_err = 0; + bcs->hw.tiger.s_tot = 0; + if (! cs->hw.njet.dmactrl) { + fill_mem(bcs, bcs->hw.tiger.send, + NETJET_DMA_SIZE, !bc, 0xff); + cs->hw.njet.dmactrl = 1; + byteout(cs->hw.njet.base + NETJET_DMACTRL, + cs->hw.njet.dmactrl); + byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0x3f); + } + bcs->hw.tiger.sendp = bcs->hw.tiger.send; + bcs->hw.tiger.free = NETJET_DMA_SIZE; + test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag); + break; + } + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "tiger: set %x %x %x %x/%x pulse=%d", + bytein(cs->hw.njet.base + NETJET_DMACTRL), + bytein(cs->hw.njet.base + NETJET_IRQMASK0), + bytein(cs->hw.njet.base + NETJET_IRQSTAT0), + inl(cs->hw.njet.base + NETJET_DMA_READ_ADR), + inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR), + bytein(cs->hw.njet.base + NETJET_PULSE_CNT)); + debugl1(cs, tmp); + } +} + +static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off) +{ + return(5); +} + +static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value) +{ +} + +static void printframe(struct IsdnCardState *cs, u_char *buf, int count, char *s) { + char tmp[128]; + char *t = tmp; + int i=count,j; + u_char *p = buf; + + t += sprintf(t, "tiger %s(%4d)", s, count); + while (i>0) { + if (i>16) + j=16; + else + j=i; + QuickHex(t, p, j); + debugl1(cs, tmp); + p += j; + i -= j; + t = tmp; + t += sprintf(t, "tiger %s ", s); + } +} + +#define MAKE_RAW_BYTE for (j=0; j<8; j++) { \ + bitcnt++;\ + s_val >>= 1;\ + if (val & 1) {\ + s_one++;\ + s_val |= 0x80;\ + } else {\ + s_one = 0;\ + s_val &= 0x7f;\ + }\ + if (bitcnt==8) {\ + bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\ + bitcnt = 0;\ + }\ + if (s_one == 5) {\ + s_val >>= 1;\ + s_val &= 0x7f;\ + bitcnt++;\ + s_one = 0;\ + }\ + if (bitcnt==8) {\ + bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\ + bitcnt = 0;\ + }\ + val >>= 1;\ + } + +static void make_raw_data(struct BCState *bcs) { + register u_int i,s_cnt=0; + register u_char j; + register u_char val; + register u_char s_one = 0; + register u_char s_val = 0; + register u_char bitcnt = 0; + u_int fcs; + char tmp[64]; + + + bcs->hw.tiger.sendbuf[s_cnt++] = HDLC_FLAG_VALUE; + fcs = PPP_INITFCS; + for (i=0; ihw.tiger.tx_skb->len; i++) { + val = bcs->hw.tiger.tx_skb->data[i]; + fcs = PPP_FCS (fcs, val); + MAKE_RAW_BYTE; + } + fcs ^= 0xffff; + val = fcs & 0xff; + MAKE_RAW_BYTE; + val = (fcs>>8) & 0xff; + MAKE_RAW_BYTE; + val = HDLC_FLAG_VALUE; + for (j=0; j<8; j++) { + bitcnt++; + s_val >>= 1; + if (val & 1) + s_val |= 0x80; + else + s_val &= 0x7f; + if (bitcnt==8) { + bcs->hw.tiger.sendbuf[s_cnt++] = s_val; + bitcnt = 0; + } + val >>= 1; + } + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger make_raw: in %d out %d.%d", + bcs->hw.tiger.tx_skb->len, s_cnt, bitcnt); + debugl1(bcs->cs,tmp); + } + if (bitcnt) { + while (8>bitcnt++) { + s_val >>= 1; + s_val |= 0x80; + } + bcs->hw.tiger.sendbuf[s_cnt++] = s_val; + } + bcs->hw.tiger.sendcnt = s_cnt; + bcs->tx_cnt -= bcs->hw.tiger.tx_skb->len; + bcs->hw.tiger.sp = bcs->hw.tiger.sendbuf; +} + +static void got_frame(struct BCState *bcs, int count) { + struct sk_buff *skb; + + if (!(skb = dev_alloc_skb(count))) + printk(KERN_WARNING "TIGER: receive out of memory\n"); + else { + memcpy(skb_put(skb, count), bcs->hw.tiger.rcvbuf, count); + skb_queue_tail(&bcs->rqueue, skb); + } + bcs->event |= 1 << B_RCVBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + if (bcs->cs->debug & L1_DEB_RECEIVE_FRAME) + printframe(bcs->cs, bcs->hw.tiger.rcvbuf, count, "rec"); +} + + + +static void read_raw(struct BCState *bcs, u_int *buf, int cnt){ + int i; + register u_char j; + register u_char val; + u_int *pend = bcs->hw.tiger.rec +NETJET_DMA_SIZE -1; + register u_char state = bcs->hw.tiger.r_state; + register u_char r_one = bcs->hw.tiger.r_one; + register u_char r_val = bcs->hw.tiger.r_val; + register u_int bitcnt = bcs->hw.tiger.r_bitcnt; + u_int *p = buf; + char tmp[64]; + + for (i=0;ichannel ? ((*p>>8) & 0xff) : (*p & 0xff); + p++; + if (p > pend) + p = bcs->hw.tiger.rec; + if (val == 0xff) { + state = HDLC_ZERO_SEARCH; + bcs->hw.tiger.r_tot++; + bitcnt = 0; + r_one = 0; + continue; + } + for (j=0;j<8;j++) { + if (state == HDLC_ZERO_SEARCH) { + if (val & 1) { + r_one++; + } else { + r_one=0; + state= HDLC_FLAG_SEARCH; + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger read_raw: zBit(%d,%d,%d) %x", + bcs->hw.tiger.r_tot,i,j,val); + debugl1(bcs->cs,tmp); + } + } + } else if (state == HDLC_FLAG_SEARCH) { + if (val & 1) { + r_one++; + if (r_one>6) { + state=HDLC_ZERO_SEARCH; + } + } else { + if (r_one==6) { + bitcnt=0; + r_val=0; + state=HDLC_FLAG_FOUND; + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger read_raw: flag(%d,%d,%d) %x", + bcs->hw.tiger.r_tot,i,j,val); + debugl1(bcs->cs,tmp); + } + } + r_one=0; + } + } else if (state == HDLC_FLAG_FOUND) { + if (val & 1) { + r_one++; + if (r_one>6) { + state=HDLC_ZERO_SEARCH; + } else { + r_val >>= 1; + r_val |= 0x80; + bitcnt++; + } + } else { + if (r_one==6) { + bitcnt=0; + r_val=0; + r_one=0; + val >>= 1; + continue; + } else if (r_one!=5) { + r_val >>= 1; + r_val &= 0x7f; + bitcnt++; + } + r_one=0; + } + if ((state != HDLC_ZERO_SEARCH) && + !(bitcnt & 7)) { + state=HDLC_FRAME_FOUND; + bcs->hw.tiger.r_fcs = PPP_INITFCS; + bcs->hw.tiger.rcvbuf[0] = r_val; + bcs->hw.tiger.r_fcs = PPP_FCS (bcs->hw.tiger.r_fcs, r_val); + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger read_raw: byte1(%d,%d,%d) rval %x val %x i %x", + bcs->hw.tiger.r_tot,i,j,r_val,val, + bcs->cs->hw.njet.irqstat0); + debugl1(bcs->cs,tmp); + } + } + } else if (state == HDLC_FRAME_FOUND) { + if (val & 1) { + r_one++; + if (r_one>6) { + state=HDLC_ZERO_SEARCH; + bitcnt=0; + } else { + r_val >>= 1; + r_val |= 0x80; + bitcnt++; + } + } else { + if (r_one==6) { + r_val=0; + r_one=0; + bitcnt++; + if (bitcnt & 7) { + debugl1(bcs->cs, "tiger: frame not byte aligned"); + state=HDLC_FLAG_SEARCH; + bcs->hw.tiger.r_err++; + } else { + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger frame end(%d,%d): fcs(%x) i %x", + i,j,bcs->hw.tiger.r_fcs, bcs->cs->hw.njet.irqstat0); + debugl1(bcs->cs, tmp); + } + if (bcs->hw.tiger.r_fcs == PPP_GOODFCS) { + got_frame(bcs, (bitcnt>>3)-3); + } else + if (bcs->cs->debug) { + debugl1(bcs->cs, "tiger FCS error"); + printframe(bcs->cs, bcs->hw.tiger.rcvbuf, + (bitcnt>>3)-1, "rec"); + bcs->hw.tiger.r_err++; + } + state=HDLC_FLAG_FOUND; + } + bitcnt=0; + } else if (r_one==5) { + val >>= 1; + r_one=0; + continue; + } else { + r_val >>= 1; + r_val &= 0x7f; + bitcnt++; + } + r_one=0; + } + if ((state == HDLC_FRAME_FOUND) && + !(bitcnt & 7)) { + if ((bitcnt>>3)>=HSCX_BUFMAX) { + debugl1(bcs->cs, "tiger: frame to big"); + r_val=0; + state=HDLC_FLAG_SEARCH; + bcs->hw.tiger.r_err++; + } else { + bcs->hw.tiger.rcvbuf[(bitcnt>>3)-1] = r_val; + bcs->hw.tiger.r_fcs = + PPP_FCS (bcs->hw.tiger.r_fcs, r_val); + } + } + } + val >>= 1; + } + bcs->hw.tiger.r_tot++; + } + bcs->hw.tiger.r_state = state; + bcs->hw.tiger.r_one = r_one; + bcs->hw.tiger.r_val = r_val; + bcs->hw.tiger.r_bitcnt = bitcnt; +} + +static void read_tiger(struct IsdnCardState *cs) { + u_int *p; + int cnt = NETJET_DMA_SIZE/2; + + if (cs->hw.njet.irqstat0 & 4) + p = cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1; + else + p = cs->bcs[0].hw.tiger.rec + cnt - 1; + if (cs->bcs[0].mode == L1_MODE_HDLC) + read_raw(cs->bcs, p, cnt); + if (cs->bcs[1].mode == L1_MODE_HDLC) + read_raw(cs->bcs + 1, p, cnt); + cs->hw.njet.irqstat0 &= 0xf3; +} + +static void write_raw(struct BCState *bcs, u_int *buf, int cnt); + +static void fill_dma(struct BCState *bcs) +{ + char tmp[64]; + register u_int *p, *sp; + register int cnt; + + if (!bcs->hw.tiger.tx_skb) + return; + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger fill_dma1: c%d %4x", bcs->channel, + bcs->Flag); + debugl1(bcs->cs,tmp); + } + if (test_and_set_bit(BC_FLG_BUSY, &bcs->Flag)) + return; + make_raw_data(bcs); + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger fill_dma2: c%d %4x", bcs->channel, + bcs->Flag); + debugl1(bcs->cs,tmp); + } + if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) { + write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free); + } else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) { + p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR)); + sp = bcs->hw.tiger.sendp; + if (p == bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send -1; + if (sp == bcs->hw.tiger.s_end) + sp = bcs->hw.tiger.send -1; + cnt = p - sp; + if (cnt <0) { + write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free); + } else { + p++; + cnt++; + if (p > bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send; + p++; + cnt++; + if (p > bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send; + write_raw(bcs, p, bcs->hw.tiger.free - cnt); + } + } else if (test_and_clear_bit(BC_FLG_EMPTY, &bcs->Flag)) { + p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR)); + cnt = bcs->hw.tiger.s_end - p; + if (cnt < 2) { + p = bcs->hw.tiger.send + 1; + cnt = NETJET_DMA_SIZE/2 - 2; + } else { + p++; + p++; + if (cnt <= (NETJET_DMA_SIZE/2)) + cnt += NETJET_DMA_SIZE/2; + cnt--; + cnt--; + } + write_raw(bcs, p, cnt); + } + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger fill_dma3: c%d %4x", bcs->channel, + bcs->Flag); + debugl1(bcs->cs,tmp); + } +} + +static void write_raw(struct BCState *bcs, u_int *buf, int cnt) { + u_int mask, val, *p=buf; + u_int i, s_cnt; + char tmp[64]; + + if (cnt <= 0) + return; + if (test_bit(BC_FLG_BUSY, &bcs->Flag)) { + if (bcs->hw.tiger.sendcnt> cnt) { + s_cnt = cnt; + bcs->hw.tiger.sendcnt -= cnt; + } else { + s_cnt = bcs->hw.tiger.sendcnt; + bcs->hw.tiger.sendcnt = 0; + } + if (bcs->channel) + mask = 0xffff00ff; + else + mask = 0xffffff00; + for (i=0; ichannel ? ((bcs->hw.tiger.sp[i] <<8) & 0xff00) : + (bcs->hw.tiger.sp[i]); + *p &= mask; + *p++ |= val; + if (p>bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send; + } + bcs->hw.tiger.s_tot += s_cnt; + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger write_raw: c%d %x-%x %d/%d %d %x", bcs->channel, + (u_int)buf, (u_int)p, s_cnt, cnt, bcs->hw.tiger.sendcnt, + bcs->cs->hw.njet.irqstat0); + debugl1(bcs->cs,tmp); + } + if (bcs->cs->debug & L1_DEB_HSCX_FIFO) + printframe(bcs->cs, bcs->hw.tiger.sp, s_cnt, "snd"); + bcs->hw.tiger.sp += s_cnt; + bcs->hw.tiger.sendp = p; + if (!bcs->hw.tiger.sendcnt) { + if (!bcs->hw.tiger.tx_skb) { + sprintf(tmp,"tiger write_raw: NULL skb s_cnt %d", s_cnt); + debugl1(bcs->cs, tmp); + } else { + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->hw.tiger.tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.tiger.tx_skb->len); + dev_kfree_skb(bcs->hw.tiger.tx_skb); + bcs->hw.tiger.tx_skb = NULL; + } + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->hw.tiger.free = cnt - s_cnt; + if (bcs->hw.tiger.free > (NETJET_DMA_SIZE/2)) + test_and_set_bit(BC_FLG_HALF, &bcs->Flag); + else { + test_and_clear_bit(BC_FLG_HALF, &bcs->Flag); + test_and_set_bit(BC_FLG_NOFRAME, &bcs->Flag); + } + if ((bcs->hw.tiger.tx_skb = skb_dequeue(&bcs->squeue))) { + fill_dma(bcs); + } else { + mask ^= 0xffffffff; + if (s_cnt < cnt) { + for (i=s_cnt; ibcs->hw.tiger.s_end) + p = bcs->hw.tiger.send; + } + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "tiger write_raw: fill rest %d", + cnt - s_cnt); + debugl1(bcs->cs,tmp); + } + } + bcs->event |= 1 << B_XMTBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + } + } else if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) { + test_and_set_bit(BC_FLG_HALF, &bcs->Flag); + fill_mem(bcs, buf, cnt, bcs->channel, 0xff); + bcs->hw.tiger.free += cnt; + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger write_raw: fill half"); + debugl1(bcs->cs,tmp); + } + } else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) { + test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag); + fill_mem(bcs, buf, cnt, bcs->channel, 0xff); + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger write_raw: fill full"); + debugl1(bcs->cs,tmp); + } + } +} + +static void write_tiger(struct IsdnCardState *cs) { + u_int *p, cnt = NETJET_DMA_SIZE/2; + + if (cs->hw.njet.irqstat0 & 1) + p = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1; + else + p = cs->bcs[0].hw.tiger.send + cnt - 1; + if (cs->bcs[0].mode == L1_MODE_HDLC) + write_raw(cs->bcs, p, cnt); + if (cs->bcs[1].mode == L1_MODE_HDLC) + write_raw(cs->bcs + 1, p, cnt); + cs->hw.njet.irqstat0 &= 0xfc; +} + +static void +tiger_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA_REQ): + save_flags(flags); + cli(); + if (st->l1.bcs->hw.tiger.tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->hw.tiger.tx_skb = skb; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + } + break; + case (PH_PULL_IND): + if (st->l1.bcs->hw.tiger.tx_skb) { + printk(KERN_WARNING "tiger_l2l1: this shouldn't happen\n"); + break; + } + save_flags(flags); + cli(); + st->l1.bcs->hw.tiger.tx_skb = skb; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + break; + case (PH_PULL_REQ): + if (!st->l1.bcs->hw.tiger.tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL_CNF, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } +} + +void +close_tigerstate(struct BCState *bcs) +{ + struct sk_buff *skb; + + mode_tiger(bcs, 0, 0); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + if (bcs->hw.tiger.rcvbuf) { + kfree(bcs->hw.tiger.rcvbuf); + bcs->hw.tiger.rcvbuf = NULL; + } + if (bcs->hw.tiger.sendbuf) { + kfree(bcs->hw.tiger.sendbuf); + bcs->hw.tiger.sendbuf = NULL; + } + while ((skb = skb_dequeue(&bcs->rqueue))) { + dev_kfree_skb(skb); + } + while ((skb = skb_dequeue(&bcs->squeue))) { + dev_kfree_skb(skb); + } + if (bcs->hw.tiger.tx_skb) { + dev_kfree_skb(bcs->hw.tiger.tx_skb); + bcs->hw.tiger.tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +static int +open_tigerstate(struct IsdnCardState *cs, int bc) +{ + struct BCState *bcs = cs->bcs + bc; + + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + if (!(bcs->hw.tiger.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_KERNEL))) { + printk(KERN_WARNING + "HiSax: No memory for tiger.rcvbuf\n"); + return (1); + } + if (!(bcs->hw.tiger.sendbuf = kmalloc(RAW_BUFMAX, GFP_KERNEL))) { + printk(KERN_WARNING + "HiSax: No memory for tiger.sendbuf\n"); + return (1); + } + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->hw.tiger.tx_skb = NULL; + bcs->hw.tiger.sendcnt = 0; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->tx_cnt = 0; + return (0); +} + +static void +tiger_manl1(struct PStack *st, int pr, + void *arg) +{ + switch (pr) { + case (PH_ACTIVATE_REQ): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + mode_tiger(st->l1.bcs, st->l1.mode, st->l1.bc); + st->l1.l1man(st, PH_ACTIVATE_CNF, NULL); + break; + case (PH_DEACTIVATE_REQ): + if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) + mode_tiger(st->l1.bcs, 0, 0); + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + break; + } +} + +int +setstack_tiger(struct PStack *st, struct BCState *bcs) +{ + if (open_tigerstate(st->l1.hardware, bcs->channel)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = tiger_l2l1; + st->ma.manl1 = tiger_manl1; + setstack_manager(st); + bcs->st = st; + return (0); +} + + +__initfunc(void +inittiger(struct IsdnCardState *cs)) +{ + char tmp[128]; + + if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_SIZE * sizeof(unsigned int), + GFP_KERNEL | GFP_DMA))) { + printk(KERN_WARNING + "HiSax: No memory for tiger.send\n"); + return; + } + cs->bcs[0].hw.tiger.s_irq = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE/2 - 1; + cs->bcs[0].hw.tiger.s_end = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1; + cs->bcs[1].hw.tiger.send = cs->bcs[0].hw.tiger.send; + cs->bcs[1].hw.tiger.s_irq = cs->bcs[0].hw.tiger.s_irq; + cs->bcs[1].hw.tiger.s_end = cs->bcs[0].hw.tiger.s_end; + + memset(cs->bcs[0].hw.tiger.send, 0xff, NETJET_DMA_SIZE * sizeof(unsigned int)); + sprintf(tmp, "tiger: send buf %x - %x", (u_int)cs->bcs[0].hw.tiger.send, + (u_int)(cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1)); + debugl1(cs, tmp); + outl(virt_to_bus(cs->bcs[0].hw.tiger.send), + cs->hw.njet.base + NETJET_DMA_READ_START); + outl(virt_to_bus(cs->bcs[0].hw.tiger.s_irq), + cs->hw.njet.base + NETJET_DMA_READ_IRQ); + outl(virt_to_bus(cs->bcs[0].hw.tiger.s_end), + cs->hw.njet.base + NETJET_DMA_READ_END); + if (!(cs->bcs[0].hw.tiger.rec = kmalloc(NETJET_DMA_SIZE * sizeof(unsigned int), + GFP_KERNEL | GFP_DMA))) { + printk(KERN_WARNING + "HiSax: No memory for tiger.rec\n"); + return; + } + sprintf(tmp, "tiger: rec buf %x - %x", (u_int)cs->bcs[0].hw.tiger.rec, + (u_int)(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1)); + debugl1(cs, tmp); + cs->bcs[1].hw.tiger.rec = cs->bcs[0].hw.tiger.rec; + memset(cs->bcs[0].hw.tiger.rec, 0xff, NETJET_DMA_SIZE * sizeof(unsigned int)); + outl(virt_to_bus(cs->bcs[0].hw.tiger.rec), + cs->hw.njet.base + NETJET_DMA_WRITE_START); + outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE/2 - 1), + cs->hw.njet.base + NETJET_DMA_WRITE_IRQ); + outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1), + cs->hw.njet.base + NETJET_DMA_WRITE_END); + sprintf(tmp, "tiger: dmacfg %x/%x pulse=%d", + inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR), + inl(cs->hw.njet.base + NETJET_DMA_READ_ADR), + bytein(cs->hw.njet.base + NETJET_PULSE_CNT)); + debugl1(cs, tmp); + cs->hw.njet.last_is0 = 0; + cs->bcs[0].BC_SetStack = setstack_tiger; + cs->bcs[1].BC_SetStack = setstack_tiger; + cs->bcs[0].BC_Close = close_tigerstate; + cs->bcs[1].BC_Close = close_tigerstate; +} + +void +releasetiger(struct IsdnCardState *cs) +{ + if (cs->bcs[0].hw.tiger.send) { + kfree(cs->bcs[0].hw.tiger.send); + cs->bcs[0].hw.tiger.send = NULL; + } + if (cs->bcs[1].hw.tiger.send) { + cs->bcs[1].hw.tiger.send = NULL; + } + if (cs->bcs[0].hw.tiger.rec) { + kfree(cs->bcs[0].hw.tiger.rec); + cs->bcs[0].hw.tiger.rec = NULL; + } + if (cs->bcs[1].hw.tiger.rec) { + cs->bcs[1].hw.tiger.rec = NULL; + } +} + +static void +netjet_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, sval, stat = 1; + char tmp[128]; + + if (!cs) { + printk(KERN_WARNING "NETjet: Spurious interrupt!\n"); + return; + } + if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) & + NETJET_ISACIRQ)) { + val = ReadISAC(cs, ISAC_ISTA); + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "tiger: i1 %x %x", sval, val); + debugl1(cs, tmp); + } + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + } + if ((cs->hw.njet.irqstat0 = bytein(cs->hw.njet.base + NETJET_IRQSTAT0))) { +/* sprintf(tmp, "tiger: ist0 %x %x %x %x/%x pulse=%d", + sval, + bytein(cs->hw.njet.base + NETJET_DMACTRL), + bytein(cs->hw.njet.base + NETJET_IRQMASK0), + inl(cs->hw.njet.base + NETJET_DMA_READ_ADR), + inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR), + bytein(cs->hw.njet.base + NETJET_PULSE_CNT)); + debugl1(cs, tmp); +*/ + if (cs->hw.njet.last_is0 & cs->hw.njet.irqstat0 & 0xf) { + sprintf(tmp, "tiger: ist0 %x->%x irq lost", + cs->hw.njet.last_is0, cs->hw.njet.irqstat0); + debugl1(cs, tmp); + } + cs->hw.njet.last_is0 = cs->hw.njet.irqstat0; +/* cs->hw.njet.irqmask0 = ((0x0f & cs->hw.njet.irqstat0) ^ 0x0f) | 0x30; +*/ byteout(cs->hw.njet.base + NETJET_IRQSTAT0, cs->hw.njet.irqstat0); +/* byteout(cs->hw.njet.base + NETJET_IRQMASK0, cs->hw.njet.irqmask0); +*/ if (cs->hw.njet.irqstat0 & 0x0c) + read_tiger(cs); + if (cs->hw.njet.irqstat0 & 0x03) + write_tiger(cs); + } +/* if (!testcnt--) { + cs->hw.njet.dmactrl = 0; + byteout(cs->hw.njet.base + NETJET_DMACTRL, + cs->hw.njet.dmactrl); + byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0); + } +*/ if (stat & 2) { + WriteISAC(cs, ISAC_MASK, 0xFF); + WriteISAC(cs, ISAC_MASK, 0x0); + } +} + +static void +reset_netjet(struct IsdnCardState *cs) +{ + long flags; + + save_flags(flags); + sti(); + cs->hw.njet.ctrl_reg = 0xff; /* Reset On */ + byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + cs->hw.njet.ctrl_reg = 0x00; /* Reset Off and status read clear */ + byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + restore_flags(flags); + cs->hw.njet.auxd = 0; + cs->hw.njet.dmactrl = 0; + byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ); + byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ); + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); +} + +void +release_io_netjet(struct IsdnCardState *cs) +{ + byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0); + byteout(cs->hw.njet.base + NETJET_IRQMASK1, 0); + releasetiger(cs); + release_region(cs->hw.njet.base, 256); +} + + +static int +NETjet_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_netjet(cs); + return(0); + case CARD_RELEASE: + release_io_netjet(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &netjet_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inittiger(cs); + clear_pending_isac_ints(cs); + initisac(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + + + +static int pci_index __initdata = 0; + +__initfunc(int +setup_netjet(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + char tmp[64]; +#if CONFIG_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr, found; +#endif + + strcpy(tmp, NETjet_revision); + printk(KERN_INFO "HiSax: Traverse Tech. NETjet driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_NETJET) + return(0); +#if CONFIG_PCI + found = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_TRAVERSE_TECH, + PCI_NETJET_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + found = 1; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + if (found) + break; + } + if (!found) { + printk(KERN_WARNING "NETjet: No PCI card found\n"); + return(0); + } + if (!pci_irq) { + printk(KERN_WARNING "NETjet: No IRQ for PCI card found\n"); + return(0); + } + if (!pci_ioaddr) { + printk(KERN_WARNING "NETjet: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.njet.base = pci_ioaddr; + cs->hw.njet.auxa = pci_ioaddr + NETJET_AUXDATA; + cs->hw.njet.isac = pci_ioaddr | NETJET_ISAC_OFF; + cs->irq = pci_irq; + bytecnt = 256; +#else + printk(KERN_WARNING "NETjet: NO_PCI_BIOS\n"); + printk(KERN_WARNING "NETjet: unable to config NETJET PCI\n"); + return (0); +#endif /* CONFIG_PCI */ + printk(KERN_INFO + "NETjet: PCI card configured at 0x%x IRQ %d\n", + cs->hw.njet.base, cs->irq); + if (check_region(cs->hw.njet.base, bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.njet.base, + cs->hw.njet.base + bytecnt); + return (0); + } else { + request_region(cs->hw.njet.base, bytecnt, "netjet isdn"); + } + reset_netjet(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &dummyrr; + cs->BC_Write_Reg = &dummywr; + cs->BC_Send_Data = &fill_dma; + cs->cardmsg = &NETjet_card_msg; + ISACVersion(cs, "NETjet:"); + return (1); +} diff --git a/drivers/isdn/hisax/niccy.c b/drivers/isdn/hisax/niccy.c new file mode 100644 index 000000000000..1d7d7e5dae8a --- /dev/null +++ b/drivers/isdn/hisax/niccy.c @@ -0,0 +1,354 @@ +/* $Id: niccy.c,v 1.2 1998/02/11 17:31:04 keil Exp $ + + * niccy.c low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and + * compatible (SAGEM cybermodem) + * + * Author Karsten Keil + * + * Thanks to Dr. Neuhaus and SAGEM for informations + * + * $Log: niccy.c,v $ + * Revision 1.2 1998/02/11 17:31:04 keil + * new file + * + * + * + */ + + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" +#include +#include + +extern const char *CardType[]; +const char *niccy_revision = "$Revision: 1.2 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ISAC_PCI_DATA 0 +#define HSCX_PCI_DATA 1 +#define ISAC_PCI_ADDR 2 +#define HSCX_PCI_ADDR 3 +#define ISAC_PNP 0 +#define HSCX_PNP 1 + +/* SUB Types */ +#define NICCY_PNP 1 +#define NICCY_PCI 2 + +/* PCI stuff */ +#define PCI_VENDOR_DR_NEUHAUS 0x1267 +#define PCI_NICCY_ID 0x1016 + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.niccy.hscx_ale, + cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.niccy.hscx_ale, + cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0), value); +} + +#define READHSCX(cs, nr, reg) readreg(cs->hw.niccy.hscx_ale, \ + cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.niccy.hscx_ale, \ + cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.niccy.hscx_ale, \ + cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.niccy.hscx_ale, \ + cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "Niccy: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 1) { + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0); + } + if (stat & 2) { + writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0); + } +} + +void +release_io_niccy(struct IsdnCardState *cs) +{ + if (cs->subtyp == NICCY_PCI) + release_region(cs->hw.niccy.isac, 4); + else { + release_region(cs->hw.niccy.isac, 2); + release_region(cs->hw.niccy.isac_ale, 2); + } +} + +static void +niccy_reset(struct IsdnCardState *cs) +{ + // No reset procedure known +} + +static int +niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + niccy_reset(cs); + return(0); + case CARD_RELEASE: + release_io_niccy(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &niccy_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +static int pci_index __initdata = 0; + +__initfunc(int +setup_niccy(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, niccy_revision); + printk(KERN_INFO "HiSax: Niccy driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_NICCY) + return (0); + + if (card->para[1]) { + cs->hw.niccy.isac = card->para[1] + ISAC_PNP; + cs->hw.niccy.hscx = card->para[1] + HSCX_PNP; + cs->hw.niccy.isac_ale = card->para[2] + ISAC_PNP; + cs->hw.niccy.hscx_ale = card->para[2] + HSCX_PNP; + cs->hw.niccy.cfg_reg = 0; + cs->subtyp = NICCY_PNP; + cs->irq = card->para[0]; + if (check_region((cs->hw.niccy.isac), 2)) { + printk(KERN_WARNING + "HiSax: %s data port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.isac, + cs->hw.niccy.isac + 1); + return (0); + } else + request_region(cs->hw.niccy.isac, 2, "niccy data"); + if (check_region((cs->hw.niccy.isac_ale), 2)) { + printk(KERN_WARNING + "HiSax: %s address port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.isac_ale, + cs->hw.niccy.isac_ale + 1); + release_region(cs->hw.niccy.isac, 2); + return (0); + } else + request_region(cs->hw.niccy.isac_ale, 2, "niccy addr"); + } else { +#if CONFIG_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr; + + cs->subtyp = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_DR_NEUHAUS, + PCI_NICCY_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = NICCY_PCI; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + /* if it won't work try the other PCI addresses + * PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_5 + */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_2, &pci_ioaddr); + if (cs->subtyp) + break; + } + if (!cs->subtyp) { + printk(KERN_WARNING "Niccy: No PCI card found\n"); + return(0); + } + if (!pci_irq) { + printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n"); + return(0); + } + + if (!pci_ioaddr) { + printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.niccy.isac = pci_ioaddr + ISAC_PCI_DATA; + cs->hw.niccy.isac_ale = pci_ioaddr + ISAC_PCI_ADDR; + cs->hw.niccy.hscx = pci_ioaddr + HSCX_PCI_DATA; + cs->hw.niccy.hscx_ale = pci_ioaddr + HSCX_PCI_ADDR; + cs->irq = pci_irq; + if (check_region((cs->hw.niccy.isac), 4)) { + printk(KERN_WARNING + "HiSax: %s data port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.isac, + cs->hw.niccy.isac + 4); + return (0); + } else + request_region(cs->hw.niccy.isac, 4, "niccy"); +#else + printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n"); + printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n"); + return (0); +#endif /* CONFIG_PCI */ + } + printk(KERN_INFO + "HiSax: %s %s config irq:%d data:0x%X ale:0x%X\n", + CardType[cs->typ], (cs->subtyp==1) ? "PnP":"PCI", + cs->irq, cs->hw.niccy.isac, cs->hw.niccy.isac_ale); + niccy_reset(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &niccy_card_msg; + ISACVersion(cs, "Niccy:"); + if (HscxVersion(cs, "Niccy:")) { + printk(KERN_WARNING + "Niccy: wrong HSCX versions check IO address\n"); + release_io_niccy(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/q931.c b/drivers/isdn/hisax/q931.c index 1e1ee47723c0..a0ba3645a5ca 100644 --- a/drivers/isdn/hisax/q931.c +++ b/drivers/isdn/hisax/q931.c @@ -1,4 +1,4 @@ -/* $Id: q931.c,v 1.5 1997/04/06 22:56:43 keil Exp $ +/* $Id: q931.c,v 1.6 1997/07/27 21:09:44 keil Exp $ * q931.c code to decode ITU Q.931 call control messages * @@ -14,6 +14,9 @@ * * * $Log: q931.c,v $ + * Revision 1.6 1997/07/27 21:09:44 keil + * move functions to isdnl3.c + * * Revision 1.5 1997/04/06 22:56:43 keil * Some cosmetic changes * @@ -37,44 +40,6 @@ #include "hisax.h" #include "l3_1tr6.h" -u_char * -findie(u_char * p, int size, u_char ie, int wanted_set) -{ - int l, codeset, maincodeset; - u_char *pend = p + size; - - /* skip protocol discriminator, callref and message type */ - p++; - l = (*p++) & 0xf; - p += l; - p++; - codeset = 0; - maincodeset = 0; - /* while there are bytes left... */ - while (p < pend) { - if ((*p & 0xf0) == 0x90) { - codeset = *p & 0x07; - if (!(*p & 0x08)) - maincodeset = codeset; - } - if (*p & 0x80) - p++; - else { - if (codeset == wanted_set) { - if (*p == ie) - return (p); - if (*p > ie) - return (NULL); - } - p++; - l = *p++; - p += l; - codeset = maincodeset; - } - } - return (NULL); -} - void iecpy(u_char * dest, u_char * iestart, int ieoffset) { @@ -88,14 +53,6 @@ iecpy(u_char * dest, u_char * iestart, int ieoffset) *dest++ = '\0'; } -int -getcallref(u_char * p) -{ - p++; /* prot discr */ - p++; /* callref length */ - return (*p); /* assuming one-byte callref */ -} - /* * According to Table 4-2/Q.931 */ diff --git a/drivers/isdn/hisax/rawhdlc.c b/drivers/isdn/hisax/rawhdlc.c new file mode 100644 index 000000000000..16938bc98342 --- /dev/null +++ b/drivers/isdn/hisax/rawhdlc.c @@ -0,0 +1,539 @@ +/* $Id: rawhdlc.c,v 1.2 1998/02/09 10:53:51 keil Exp $ + + * rawhdlc.c support routines for cards that don't support HDLC + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Brent Baccala + * + * + * Some passive ISDN cards, such as the Traverse NETJet and the AMD 7930, + * don't perform HDLC encapsulation over the B channel. Drivers for + * such cards use support routines in this file to perform B channel HDLC. + * + * Bit-synchronous HDLC encapsulation is a means of encapsulating packets + * over a continuously transmitting serial communications link. + * It looks like this: + * + * 11111111101111110...........0111111011111111111 + * iiiiiiiiiffffffffdddddddddddffffffffiiiiiiiiiii + * + * i = idle f = flag d = data + * + * When idle, the channel sends a continuous string of ones (mark + * idle; illustrated), or a continuous string of flag characters (flag + * idle). The beginning of a data frame is marked by a flag character + * (01111110), then comes the actual data, followed by another flag + * character, after which another frame may be sent immediately (a + * single flag may serve as both the end of one frame and the start of + * the next), or the link may return to idle. Obviously, the flag + * character can not appear anywhere in the data (or a false + * end-of-frame would occur), so the transmitter performs + * "bit-stuffing" - inserting a zero bit after every five one bits, + * irregardless of the original bit after the five ones. Byte + * ordering is irrelevent at this point - the data is treated as a + * string of bits, not bytes. Since no more than 5 ones may now occur + * in a row, the flag sequence, with its 6 ones, is unique. + * + * Upon reception, a zero bit that occur after 5 one bits is simply + * discarded. A series of 6 one bits is end-of-frame, and a series of + * 7 one bits is an abort. Once bit-stuffing has been corrected for, + * an integer number of bytes should now be present. The last two + * of these bytes form the Frame Check Sequence, a CRC that is verified + * and then discarded. Note that bit-stuffing is performed on the FCS + * just as if it were regular data. + * + * + * + * int make_raw_hdlc_data(u_char *src, u_int slen, + * u_char *dst, u_int dsize) + * + * Used for transmission. Copies slen bytes from src to dst, performing + * HDLC encapsulation (flag bytes, bit-stuffing, CRC) in the process. + * dsize is size of destination buffer, and should be at least + * ((6*slen)/5)+5 bytes to ensure adequate space will be available. + * Function returns length (in bytes) of valid destination buffer, or + * 0 upon destination overflow. + * + * void init_hdlc_state(struct hdlc_state *stateptr, int mode) + * + * Initializes hdlc_state structure before first call to read_raw_hdlc_data + * + * mode = 0: Sane mode + * mode = 1/2: + * Insane mode; NETJet use a shared unsigned int memory block ( + * with busmaster DMA), the bit pattern of every word is + * <8 B1> <8 B2> <8 Mon> <2 D> <4 C/I> + * according to Siemens IOM-2 interface, so we have to handle + * the src buffer as unsigned int and have to shift/mask the + * B-channel bytes. + * mode 1 -> B1 mode 2 -> B2 data is used + * + * int read_raw_hdlc_data(struct hdlc_state *saved_state, + * u_char *src, u_int slen, + * u_char *dst, u_int dsize) + * + * Used for reception. Scans source buffer bit-by-bit looking for + * valid HDLC frames, which are copied to destination buffer. HDLC + * state information is stored in a structure, which allows this + * function to process frames spread across several blocks of raw + * HDLC data. Part of the state information is bit offsets into + * the source and destination buffers. + * + * A return value >0 indicates the length of a valid frame, now + * stored in the destination buffer. In this case, the source + * buffer might not be completely processed, so this function should + * be called again with the same source buffer, possibly with a + * different destination buffer. + * + * A return value of zero indicates that the source buffer was + * completely processed without finding a valid end-of-packet; + * however, we might be in the middle of packet reception, so + * the function should be called again with the next block of + * raw HDLC data and the same destination buffer. It is NOT + * permitted to change the destination buffer in this case, + * since data may already have begun to be stored there. + * + * A return value of -1 indicates some kind of error - destination + * buffer overflow, CRC check failed, frame not a multiple of 8 + * bits. Destination buffer probably contains invalid data, which + * should be discarded. Call function again with same source buffer + * and a new (or same) destination buffer. + * + * Suggested calling sequence: + * + * init_hdlc_state(...); + * for (EACH_RAW_DATA_BLOCK) { + * while (len = read_raw_hdlc_data(...)) { + * if (len == -1) DISCARD_FRAME; + * else PROCESS_FRAME; + * } + * } + * + * + * Test the code in this file as follows: + * gcc -DDEBUGME -o rawhdlctest rawhdlc.c + * ./rawhdlctest < rawdata + * + * The file "rawdata" can be easily generated from a HISAX B-channel + * hex dump (CF CF CF 02 ...) using the following perl script: + * + * while(<>) { + * @hexlist = split ' '; + * while ($hexstr = shift(@hexlist)) { + * printf "%c", hex($hexstr); + * } + * } + * + */ + +#ifdef DEBUGME +#include +#endif + +#include +#include +#include "rawhdlc.h" + +/* There's actually an identical copy of this table in the PPP code + * (ppp_crc16_table), but I don't want this code dependant on PPP + */ + +// static +__u16 fcstab[256] = +{ + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +#define HDLC_ZERO_SEARCH 0 +#define HDLC_FLAG_SEARCH 1 +#define HDLC_FLAG_FOUND 2 +#define HDLC_FRAME_FOUND 3 +#define HDLC_NULL 4 +#define HDLC_PART 5 +#define HDLC_FULL 6 + +#define HDLC_FLAG_VALUE 0x7e + + +#define MAKE_RAW_BYTE for (j=0; j<8; j++) { \ + bitcnt++;\ + out_val >>= 1;\ + if (val & 1) {\ + s_one++;\ + out_val |= 0x80;\ + } else {\ + s_one = 0;\ + out_val &= 0x7f;\ + }\ + if (bitcnt==8) {\ + if (d_cnt == dsize) return 0;\ + dst[d_cnt++] = out_val;\ + bitcnt = 0;\ + }\ + if (s_one == 5) {\ + out_val >>= 1;\ + out_val &= 0x7f;\ + bitcnt++;\ + s_one = 0;\ + }\ + if (bitcnt==8) {\ + if (d_cnt == dsize) return 0;\ + dst[d_cnt++] = out_val;\ + bitcnt = 0;\ + }\ + val >>= 1;\ + } + +/* Optimization suggestion: If needed, this function could be + * dramatically sped up using a state machine. Each state would + * correspond to having seen N one bits, and being offset M bits into + * the current output byte. N ranges from 0 to 4, M from 0 to 7, so + * we need 5*8 = 35 states. Each state would have a table with 256 + * entries, one for each input character. Each entry would contain + * three output characters, an output state, an a byte increment + * that's either 1 or 2. All this could fit in four bytes; so we need + * 4 bytes * 256 characters = 1 KB for each state (35 KB total). Zero + * the output buffer before you start. For each character in your + * input, you look it up in the current state's table and get three + * bytes to be or'ed into the output at the current byte offset, and + * an byte increment to move your pointer forward. A simple Perl + * script could generate the tables. Given HDLC semantics, probably + * would be better to set output to all 1s, then use ands instead of ors. + * A smaller state machine could operate on nibbles instead of bytes. + * A state machine for 32-bit architectures could use word offsets + * instead of byte offsets, requiring 5*32 = 160 states; probably + * best to work on nibbles in such a case. + */ + + +int make_raw_hdlc_data(u_char *src, u_int slen, u_char *dst, u_int dsize) +{ + register u_int i,d_cnt=0; + register u_char j; + register u_char val; + register u_char s_one = 0; + register u_char out_val = 0; + register u_char bitcnt = 0; + u_int fcs; + + + dst[d_cnt++] = HDLC_FLAG_VALUE; + fcs = PPP_INITFCS; + for (i=0; i>8) & 0xff; + MAKE_RAW_BYTE; + val = HDLC_FLAG_VALUE; + for (j=0; j<8; j++) { + bitcnt++; + out_val >>= 1; + if (val & 1) + out_val |= 0x80; + else + out_val &= 0x7f; + if (bitcnt==8) { + if (d_cnt == dsize) return 0; + dst[d_cnt++] = out_val; + bitcnt = 0; + } + val >>= 1; + } + if (bitcnt) { + while (8>bitcnt++) { + out_val >>= 1; + out_val |= 0x80; + } + if (d_cnt == dsize) return 0; + dst[d_cnt++] = out_val; + } + + return d_cnt; +} + +void init_hdlc_state(struct hdlc_state *stateptr, int mode) +{ + stateptr->state = HDLC_ZERO_SEARCH; + stateptr->r_one = 0; + stateptr->r_val = 0; + stateptr->o_bitcnt = 0; + stateptr->i_bitcnt = 0; + stateptr->insane_mode = mode; +} + +/* Optimization suggestion: A similar state machine could surely + * be developed for this function as well. + */ + +int read_raw_hdlc_data(struct hdlc_state *saved_state, + u_char *src, u_int slen, u_char *dst, u_int dsize) +{ + int retval=0; + register u_char val; + register u_char state = saved_state->state; + register u_char r_one = saved_state->r_one; + register u_char r_val = saved_state->r_val; + register u_int o_bitcnt = saved_state->o_bitcnt; + register u_int i_bitcnt = saved_state->i_bitcnt; + register u_int fcs = saved_state->fcs; + register u_int *isrc = (u_int *) src; + + /* Use i_bitcnt (bit offset into source buffer) to reload "val" + * in case we're starting up again partway through a source buffer + */ + + if ((i_bitcnt >> 3) < slen) { + if (saved_state->insane_mode==1) { + val = isrc[(i_bitcnt >> 3)] & 0xff; + } else if (saved_state->insane_mode==2) { + val = (isrc[i_bitcnt >> 3] >>8) & 0xff; + } else { + val = src[i_bitcnt >> 3]; + } + val >>= i_bitcnt & 7; + } + + /* One bit per loop. Keep going until we've got something to + * report (retval != 0), or we exhaust the source buffer + */ + + while ((retval == 0) && ((i_bitcnt >> 3) < slen)) { + if ((i_bitcnt & 7) == 0) { + if (saved_state->insane_mode==1) { + val = isrc[(i_bitcnt >> 3)] & 0xff; + } else if (saved_state->insane_mode==2) { + val = (isrc[i_bitcnt >> 3] >>8) & 0xff; + } else { + val = src[i_bitcnt >> 3]; + } +#ifdef DEBUGME + printf("Input byte %d: 0x%2x\n", i_bitcnt>>3, val); +#endif + if (val == 0xff) { + state = HDLC_ZERO_SEARCH; + o_bitcnt = 0; + r_one = 0; + i_bitcnt += 8; + continue; + } + } + +#ifdef DEBUGME + /* printf("Data bit=%d (%d/%d)\n", val&1, i_bitcnt>>3, i_bitcnt&7);*/ +#endif + + if (state == HDLC_ZERO_SEARCH) { + if (val & 1) { + r_one++; + } else { + r_one=0; + state= HDLC_FLAG_SEARCH; + } + } else if (state == HDLC_FLAG_SEARCH) { + if (val & 1) { + r_one++; + if (r_one>6) { + state=HDLC_ZERO_SEARCH; + } + } else { + if (r_one==6) { + o_bitcnt=0; + r_val=0; + state=HDLC_FLAG_FOUND; + } + r_one=0; + } + } else if (state == HDLC_FLAG_FOUND) { + if (val & 1) { + r_one++; + if (r_one>6) { + state=HDLC_ZERO_SEARCH; + } else { + r_val >>= 1; + r_val |= 0x80; + o_bitcnt++; + } + } else { + if (r_one==6) { + o_bitcnt=0; + r_val=0; + r_one=0; + i_bitcnt++; + val >>= 1; + continue; + } else if (r_one!=5) { + r_val >>= 1; + r_val &= 0x7f; + o_bitcnt++; + } + r_one=0; + } + if ((state != HDLC_ZERO_SEARCH) && + !(o_bitcnt & 7)) { +#ifdef DEBUGME + printf("HDLC_FRAME_FOUND at i_bitcnt:%d\n",i_bitcnt); +#endif + state=HDLC_FRAME_FOUND; + fcs = PPP_INITFCS; + dst[0] = r_val; + fcs = PPP_FCS (fcs, r_val); + } + } else if (state == HDLC_FRAME_FOUND) { + if (val & 1) { + r_one++; + if (r_one>6) { + state=HDLC_ZERO_SEARCH; + o_bitcnt=0; + } else { + r_val >>= 1; + r_val |= 0x80; + o_bitcnt++; + } + } else { + if (r_one==6) { + r_val=0; + r_one=0; + o_bitcnt++; + if (o_bitcnt & 7) { + /* Alignment error */ +#ifdef DEBUGME + printf("Alignment error\n"); +#endif + state=HDLC_FLAG_SEARCH; + retval = -1; + } else if (fcs==PPP_GOODFCS) { + /* Valid frame */ + state=HDLC_FLAG_FOUND; + retval = (o_bitcnt>>3)-3; + } else { + /* CRC error */ +#ifdef DEBUGME + printf("CRC error; fcs was 0x%x, should have been 0x%x\n", fcs, PPP_GOODFCS); +#endif + state=HDLC_FLAG_FOUND; + retval = -1; + } + } else if (r_one==5) { + r_one=0; + i_bitcnt++; + val >>= 1; + continue; + } else { + r_val >>= 1; + r_val &= 0x7f; + o_bitcnt++; + } + r_one=0; + } + if ((state == HDLC_FRAME_FOUND) && + !(o_bitcnt & 7)) { + if ((o_bitcnt>>3)>=dsize) { + /* Buffer overflow error */ +#ifdef DEBUGME + printf("Buffer overflow error\n"); +#endif + r_val=0; + state=HDLC_FLAG_SEARCH; + retval = -1; + } else { + dst[(o_bitcnt>>3)-1] = r_val; + fcs = PPP_FCS (fcs, r_val); +#ifdef DEBUGME + printf("Output byte %d: 0x%02x; FCS 0x%04x\n", (o_bitcnt>>3)-1, r_val, fcs); +#endif + } + } + } + i_bitcnt ++; + val >>= 1; + } + + /* We exhausted the source buffer before anything else happened + * (retval==0). Reset i_bitcnt in expectation of a new source + * buffer. Other, we either had an error or a valid frame, so + * reset o_bitcnt in expectation of a new destination buffer. + */ + + if (retval == 0) { + i_bitcnt = 0; + } else { + o_bitcnt = 0; + } + + saved_state->state = state; + saved_state->r_one = r_one; + saved_state->r_val = r_val; + saved_state->fcs = fcs; + saved_state->o_bitcnt = o_bitcnt; + saved_state->i_bitcnt = i_bitcnt; + + return (retval); +} + + + +#ifdef DEBUGME + +char buffer[1024]; +char obuffer[1024]; + +main() +{ + int buflen=0; + int len; + struct hdlc_state hdlc_state; + + while((buffer[buflen] = getc(stdin)) != EOF && buflen<1024) buflen++; + + printf("buflen = %d\n", buflen); + + init_hdlc_state(&hdlc_state, 0); + + while (len = read_raw_hdlc_data(&hdlc_state,buffer,buflen,obuffer,1024)) { + if (len == -1) printf("Error @ byte %d/bit %d\n", + hdlc_state.i_bitcnt>>3, hdlc_state.i_bitcnt & 7); + else { + printf("Frame received: len %d\n", len); + } + } + + printf("Done\n"); +} + +#endif diff --git a/drivers/isdn/hisax/rawhdlc.h b/drivers/isdn/hisax/rawhdlc.h new file mode 100644 index 000000000000..d946fa0b3d78 --- /dev/null +++ b/drivers/isdn/hisax/rawhdlc.h @@ -0,0 +1,26 @@ +/* $Id: rawhdlc.h,v 1.2 1998/02/09 10:53:53 keil Exp $ + + * rawhdlc.h support routines for cards that don't support HDLC + * + * Author Brent Baccala + * + */ + +#ifndef RAWHDLC_H +struct hdlc_state { + char insane_mode; + u_char state; + u_char r_one; + u_char r_val; + u_int o_bitcnt; + u_int i_bitcnt; + u_int fcs; +}; + + +int make_raw_hdlc_data(u_char *src, u_int slen, u_char *dst, u_int dsize); +void init_hdlc_state(struct hdlc_state *stateptr, int mode); +int read_raw_hdlc_data(struct hdlc_state *saved_state, + u_char *src, u_int slen, u_char *dst, u_int dsize); +#define RAWHDLC_H +#endif diff --git a/drivers/isdn/hisax/sedlbauer.c b/drivers/isdn/hisax/sedlbauer.c new file mode 100644 index 000000000000..40dc8e622a04 --- /dev/null +++ b/drivers/isdn/hisax/sedlbauer.c @@ -0,0 +1,356 @@ +/* $Id: sedlbauer.c,v 1.6 1998/02/09 18:46:06 keil Exp $ + + * sedlbauer.c low level stuff for Sedlbauer cards + * includes Support for the Sedlbauer Speed Star + * derived from the original file dynalink.c from Karsten Keil + * + * Copyright (C) 1997,1998 Marcus Niemann (for the modifications to + * the original file dynalink.c) + * + * Author Marcus Niemann (niemann@www-bib.fh-bielefeld.de) + * + * Thanks to Karsten Keil + * Sedlbauer AG for informations + * Edgar Toernig + * + * $Log: sedlbauer.c,v $ + * Revision 1.6 1998/02/09 18:46:06 keil + * Support for Sedlbauer PCMCIA (Marcus Niemann) + * + * Revision 1.5 1998/02/02 13:29:45 keil + * fast io + * + * Revision 1.4 1997/11/08 21:35:52 keil + * new l1 init + * + * Revision 1.3 1997/11/06 17:09:28 keil + * New 2.1 init code + * + * Revision 1.2 1997/10/29 18:55:52 keil + * changes for 2.1.60 (irq2dev_map) + * + * Revision 1.1 1997/09/11 17:32:04 keil + * new + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *Sedlbauer_revision = "$Revision: 1.6 $"; + +const char *Sedlbauer_Types[] = +{"None", "Speed Card", "Speed Win", "Speed Star"}; + +#define SEDL_SPEED_CARD 1 +#define SEDL_SPEED_WIN 2 +#define SEDL_SPEED_STAR 3 + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define SEDL_RESET_ON 0 +#define SEDL_RESET_OFF 1 +#define SEDL_ISAC 2 +#define SEDL_HSCX 3 +#define SEDL_ADR 4 + +#define SEDL_PCMCIA_RESET 0 +#define SEDL_PCMCIA_ISAC 1 +#define SEDL_PCMCIA_HSCX 2 +#define SEDL_PCMCIA_ADR 4 + +#define SEDL_RESET 0x3 /* same as DOS driver */ + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.sedl.adr, + cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.sedl.adr, + cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.sedl.adr, \ + cs->hw.sedl.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.sedl.adr, \ + cs->hw.sedl.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.sedl.adr, \ + cs->hw.sedl.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.sedl.adr, \ + cs->hw.sedl.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +sedlbauer_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n"); + return; + } + + if ((cs->typ == ISDN_CTYPE_SEDLBAUER_PCMCIA) && (*cs->busy_flag == 1)) { + /* The card tends to generate interrupts while being removed + causing us to just crash the kernel. bad. */ + printk(KERN_WARNING "Sedlbauer: card not available!\n"); + return; + } + + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 1) { + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0x0); + } + if (stat & 2) { + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0); + } +} + +void +release_io_sedlbauer(struct IsdnCardState *cs) +{ + int bytecnt = 8; + + if (cs->hw.sedl.cfg_reg) + release_region(cs->hw.sedl.cfg_reg, bytecnt); +} + +static void +reset_sedlbauer(struct IsdnCardState *cs) +{ + long flags; + + if (cs->typ != ISDN_CTYPE_SEDLBAUER_PCMCIA) { + byteout(cs->hw.sedl.reset_on, SEDL_RESET); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + byteout(cs->hw.sedl.reset_off, 0); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + restore_flags(flags); + } +} + +static int +Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_sedlbauer(cs); + return(0); + case CARD_RELEASE: + release_io_sedlbauer(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &sedlbauer_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_sedlbauer(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, Sedlbauer_revision); + printk(KERN_INFO "HiSax: Sedlbauer driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ == ISDN_CTYPE_SEDLBAUER) { + cs->subtyp = SEDL_SPEED_CARD; + } else if (cs->typ == ISDN_CTYPE_SEDLBAUER_PCMCIA) { + cs->subtyp = SEDL_SPEED_STAR; + } else + return (0); + + bytecnt = 8; + cs->hw.sedl.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + if (cs->subtyp == SEDL_SPEED_STAR) { + cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_ADR; + cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_ISAC; + cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_HSCX; + cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_RESET; + cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_RESET; + } else { + cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_ADR; + cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_ISAC; + cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX; + cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_RESET_ON; + cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_RESET_OFF; + } + + /* In case of the sedlbauer pcmcia card, this region is in use, + reserved for us by the card manager. So we do not check it + here, it would fail. */ + if (cs->typ != ISDN_CTYPE_SEDLBAUER_PCMCIA && + check_region((cs->hw.sedl.cfg_reg), bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.sedl.cfg_reg, + cs->hw.sedl.cfg_reg + bytecnt); + return (0); + } else { + request_region(cs->hw.sedl.cfg_reg, bytecnt, "sedlbauer isdn"); + } + + printk(KERN_INFO + "Sedlbauer: defined at 0x%x IRQ %d\n", + cs->hw.sedl.cfg_reg, + cs->irq); + printk(KERN_WARNING + "Sedlbauer %s uses ports 0x%x-0x%x\n", + Sedlbauer_Types[cs->subtyp], + cs->hw.sedl.cfg_reg, + cs->hw.sedl.cfg_reg + bytecnt); + + printk(KERN_INFO "Sedlbauer: resetting card\n"); + reset_sedlbauer(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Sedl_card_msg; + ISACVersion(cs, "Sedlbauer:"); + if (HscxVersion(cs, "Sedlbauer:")) { + printk(KERN_WARNING + "Sedlbauer: wrong HSCX versions check IO address\n"); + release_io_sedlbauer(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/siemens.h b/drivers/isdn/hisax/siemens.h deleted file mode 100644 index f356a30a9474..000000000000 --- a/drivers/isdn/hisax/siemens.h +++ /dev/null @@ -1,71 +0,0 @@ -/* $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/sportster.c b/drivers/isdn/hisax/sportster.c new file mode 100644 index 000000000000..cb867a13fa3d --- /dev/null +++ b/drivers/isdn/hisax/sportster.c @@ -0,0 +1,291 @@ +/* $Id: sportster.c,v 1.5 1998/02/02 13:29:46 keil Exp $ + + * sportster.c low level stuff for USR Sportster internal TA + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation + * + * $Log: sportster.c,v $ + * Revision 1.5 1998/02/02 13:29:46 keil + * fast io + * + * Revision 1.4 1997/11/08 21:35:52 keil + * new l1 init + * + * Revision 1.3 1997/11/06 17:09:29 keil + * New 2.1 init code + * + * Revision 1.2 1997/10/29 18:51:18 keil + * New files + * + * Revision 1.1.2.1 1997/10/17 22:10:58 keil + * new files on 2.0 + * + */ +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; +const char *sportster_revision = "$Revision: 1.5 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define SPORTSTER_ISAC 0xC000 +#define SPORTSTER_HSCXA 0x0000 +#define SPORTSTER_HSCXB 0x4000 +#define SPORTSTER_RES_IRQ 0x8000 +#define SPORTSTER_RESET 0x80 +#define SPORTSTER_INTE 0x40 + +static inline int +calc_off(unsigned int base, unsigned int off) +{ + return(base + ((off & 0xfc)<<8) + ((off & 3)<<1)); +} + +static inline void +read_fifo(unsigned int adr, u_char * data, int size) +{ + insb(adr, data, size); +} + +static void +write_fifo(unsigned int adr, u_char * data, int size) +{ + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (bytein(calc_off(cs->hw.spt.isac, offset))); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + byteout(calc_off(cs->hw.spt.isac, offset), value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + read_fifo(cs->hw.spt.isac, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + write_fifo(cs->hw.spt.isac, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (bytein(calc_off(cs->hw.spt.hscx[hscx], offset))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + byteout(calc_off(cs->hw.spt.hscx[hscx], offset), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) bytein(calc_off(cs->hw.spt.hscx[nr], reg)) +#define WRITEHSCX(cs, nr, reg, data) byteout(calc_off(cs->hw.spt.hscx[nr], reg), data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.spt.hscx[nr], ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.spt.hscx[nr], ptr, cnt) + +#include "hscx_irq.c" + +static void +sportster_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val; + + if (!cs) { + printk(KERN_WARNING "Sportster: Spurious interrupt!\n"); + return; + } + val = READHSCX(cs, 1, HSCX_ISTA); + Start_HSCX: + if (val) + hscx_int_main(cs, val); + val = ReadISAC(cs, ISAC_ISTA); + Start_ISAC: + if (val) + isac_interrupt(cs, val); + val = READHSCX(cs, 1, HSCX_ISTA); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = ReadISAC(cs, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + /* get a new irq impulse if there any pending */ + bytein(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ +1); +} + +void +release_io_sportster(struct IsdnCardState *cs) +{ + int i, adr; + + byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, 0); + for (i=0; i<64; i++) { + adr = cs->hw.spt.cfg_reg + i *1024; + release_region(adr, 8); + } +} + +void +reset_sportster(struct IsdnCardState *cs) +{ + long flags; + + cs->hw.spt.res_irq |= SPORTSTER_RESET; /* Reset On */ + byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq); + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + cs->hw.spt.res_irq &= ~SPORTSTER_RESET; /* Reset Off */ + byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + restore_flags(flags); +} + +static int +Sportster_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_sportster(cs); + return(0); + case CARD_RELEASE: + release_io_sportster(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &sportster_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + cs->hw.spt.res_irq |= SPORTSTER_INTE; /* IRQ On */ + byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +get_io_range(struct IsdnCardState *cs)) +{ + int i, j, adr; + + for (i=0;i<64;i++) { + adr = cs->hw.spt.cfg_reg + i *1024; + if (check_region(adr, 8)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[cs->typ], adr, adr + 8); + break; + } else + request_region(adr, 8, "sportster"); + } + if (i==64) + return(1); + else { + for (j=0; jhw.spt.cfg_reg + j *1024; + release_region(adr, 8); + } + return(0); + } +} + +__initfunc(int +setup_sportster(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, sportster_revision); + printk(KERN_INFO "HiSax: USR Sportster driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_SPORTSTER) + return (0); + + cs->hw.spt.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + if (!get_io_range(cs)) + return (0); + cs->hw.spt.isac = cs->hw.spt.cfg_reg + SPORTSTER_ISAC; + cs->hw.spt.hscx[0] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXA; + cs->hw.spt.hscx[1] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXB; + + switch(cs->irq) { + case 5: cs->hw.spt.res_irq = 1; + break; + case 7: cs->hw.spt.res_irq = 2; + break; + case 10:cs->hw.spt.res_irq = 3; + break; + case 11:cs->hw.spt.res_irq = 4; + break; + case 12:cs->hw.spt.res_irq = 5; + break; + case 14:cs->hw.spt.res_irq = 6; + break; + case 15:cs->hw.spt.res_irq = 7; + break; + default:release_io_sportster(cs); + printk(KERN_WARNING "Sportster: wrong IRQ\n"); + return(0); + } + reset_sportster(cs); + printk(KERN_INFO + "HiSax: %s config irq:%d cfg:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.spt.cfg_reg); + + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Sportster_card_msg; + ISACVersion(cs, "Sportster:"); + if (HscxVersion(cs, "Sportster:")) { + printk(KERN_WARNING + "Sportster: wrong HSCX versions check IO address\n"); + release_io_sportster(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/tei.c b/drivers/isdn/hisax/tei.c index d97595938536..a520787469ae 100644 --- a/drivers/isdn/hisax/tei.c +++ b/drivers/isdn/hisax/tei.c @@ -1,4 +1,4 @@ -/* $Id: tei.c,v 1.8 1997/04/07 22:59:08 keil Exp $ +/* $Id: tei.c,v 2.7 1998/02/12 23:08:11 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,52 +7,112 @@ * Fritz Elfert * * $Log: tei.c,v $ - * Revision 1.8 1997/04/07 22:59:08 keil - * GFP_KERNEL --> GFP_ATOMIC + * Revision 2.7 1998/02/12 23:08:11 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() * - * Revision 1.7 1997/04/06 22:54:03 keil - * Using SKB's + * Revision 2.6 1998/02/02 13:41:42 keil + * fix MDL_ASSIGN for PtP * - * Revision 1.6 1997/02/09 00:25:12 keil - * new interface handling, one interface per card + * Revision 2.5 1997/11/06 17:09:12 keil + * New 2.1 init code * - * Revision 1.5 1997/01/27 15:57:51 keil - * cosmetics + * Revision 2.4 1997/10/29 19:04:46 keil + * changes for 2.1 * - * Revision 1.4 1997/01/21 22:32:44 keil - * Tei verify request + * Revision 2.3 1997/10/01 09:21:43 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. * - * Revision 1.3 1997/01/04 13:45:02 keil - * cleanup,adding remove tei request (thanks to Sim Yskes) + * Revision 2.2 1997/07/31 19:24:39 keil + * fixed a warning * - * Revision 1.2 1996/12/08 19:52:39 keil - * minor debug fix + * Revision 2.1 1997/07/31 11:50:16 keil + * ONE TEI and FIXED TEI handling * - * Revision 1.1 1996/10/13 20:04:57 keil - * Initial revision + * Revision 2.0 1997/07/27 21:13:30 keil + * New TEI managment * + * Revision 1.9 1997/06/26 11:18:02 keil + * New managment * + * Revision 1.8 1997/04/07 22:59:08 keil + * GFP_KERNEL --> GFP_ATOMIC + * + * Revision 1.7 1997/04/06 22:54:03 keil + * Using SKB's + * + * Old log removed/ KKe * */ #define __NO_VERSION__ #include "hisax.h" +#include "isdnl2.h" +#include -extern struct IsdnCard cards[]; -extern int nrcards; +const char *tei_revision = "$Revision: 2.7 $"; -const char *tei_revision = "$Revision: 1.8 $"; +#define ID_REQUEST 1 +#define ID_ASSIGNED 2 +#define ID_DENIED 3 +#define ID_CHK_REQ 4 +#define ID_CHK_RES 5 +#define ID_REMOVE 6 +#define ID_VERIFY 7 -static struct PStack * -findces(struct PStack *st, int ces) +#define TEI_ENTITY_ID 0xf + +static +struct Fsm teifsm = +{NULL, 0, 0, NULL, NULL}; + +void tei_handler(struct PStack *st, u_char pr, struct sk_buff *skb); + +enum { + ST_TEI_NOP, + ST_TEI_IDREQ, + ST_TEI_IDVERIFY, +}; + +#define TEI_STATE_COUNT (ST_TEI_IDVERIFY+1) + +static char *strTeiState[] = { - struct PStack *ptr = *(st->l1.stlistp); + "ST_TEI_NOP", + "ST_TEI_IDREQ", + "ST_TEI_IDVERIFY", +}; - while (ptr) - if (ptr->l2.ces == ces) - return (ptr); - else - ptr = ptr->next; - return (NULL); +enum { + EV_IDREQ, + EV_ASSIGN, + EV_DENIED, + EV_CHKREQ, + EV_REMOVE, + EV_VERIFY, + EV_T202, +}; + +#define TEI_EVENT_COUNT (EV_T202+1) + +static char *strTeiEvent[] = +{ + "EV_IDREQ", + "EV_ASSIGN", + "EV_DENIED", + "EV_CHKREQ", + "EV_REMOVE", + "EV_VERIFY", + "EV_T202", +}; + +unsigned int +random_ri(void) +{ + unsigned int x; + + get_random_bytes(&x, sizeof(x)); + return (x & 0xffff); } static struct PStack * @@ -72,246 +132,357 @@ findtei(struct PStack *st, int tei) } static void -mdl_unit_data_res(struct PStack *st, unsigned int ri, u_char mt, u_char ai) +put_tei_msg(struct PStack *st, u_char m_id, unsigned int ri, u_char tei) { struct sk_buff *skb; u_char *bp; - if (!(skb = alloc_skb(6 + MAX_HEADER_LEN, GFP_ATOMIC))) { + if (!(skb = alloc_skb(8, GFP_ATOMIC))) { printk(KERN_WARNING "HiSax: No skb for TEI manager\n"); return; } - skb_reserve(skb, MAX_HEADER_LEN); + bp = skb_put(skb, 3); + bp[0] = (TEI_SAPI << 2); + bp[1] = (GROUP_TEI << 1) | 0x1; + bp[2] = UI; bp = skb_put(skb, 5); - bp[0] = 0xf; + bp[0] = TEI_ENTITY_ID; bp[1] = ri >> 8; bp[2] = ri & 0xff; - bp[3] = mt; - bp[4] = (ai << 1) | 1; - st->l3.l3l2(st, DL_UNIT_DATA, skb); + bp[3] = m_id; + bp[4] = (tei << 1) | 1; + st->l2.l2l1(st, PH_DATA_REQ, skb); } static void -mdl_unit_data_ind(struct PStack *st, unsigned int ri, u_char mt, u_char ai) +tei_id_request(struct FsmInst *fi, int event, void *arg) { - unsigned int tces; - struct PStack *otsp, *ptr; + struct PStack *st = fi->userdata; 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); - } + if (st->l2.tei != -1) { + sprintf(tmp, "assign request for allready asigned tei %d", + st->l2.tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + return; + } + st->ma.ri = random_ri(); + if (st->ma.debug) { + sprintf(tmp, "assign request ri %d", st->ma.ri); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); } + put_tei_msg(st, ID_REQUEST, st->ma.ri, 127); + FsmChangeState(&st->ma.tei_m, ST_TEI_IDREQ); + FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 1); + st->ma.N202 = 3; } -void -tei_handler(struct PStack *st, - u_char pr, struct sk_buff *skb) +static void +tei_id_assign(struct FsmInst *fi, int event, void *arg) { - u_char *bp; - unsigned int data; - char tmp[32]; + struct PStack *ost, *st = fi->userdata; + struct sk_buff *skb = arg; + struct IsdnCardState *cs; + int ri, tei; + char tmp[64]; - 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); - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - break; - default: - break; + ri = ((unsigned int) skb->data[1] << 8) + skb->data[2]; + tei = skb->data[4] >> 1; + if (st->ma.debug) { + sprintf(tmp, "identity assign ri %d tei %d", ri, tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + if ((ost = findtei(st, tei))) { /* same tei is in use */ + if (ri != ost->ma.ri) { + sprintf(tmp, "possible duplicate assignment tei %d", tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + ost->l2.l2tei(ost, MDL_ERROR_REQ, NULL); + } + } else if (ri == st->ma.ri) { + FsmDelTimer(&st->ma.t202, 1); + FsmChangeState(&st->ma.tei_m, ST_TEI_NOP); + st->ma.manl2(st, MDL_ASSIGN_REQ, (void *) (int) tei); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_ASSIGN_REQ, NULL); } } -unsigned int -randomces(void) +static void +tei_id_denied(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + int ri, tei; + char tmp[64]; + + ri = ((unsigned int) skb->data[1] << 8) + skb->data[2]; + tei = skb->data[4] >> 1; + if (st->ma.debug) { + sprintf(tmp, "identity denied ri %d tei %d", ri, tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } +} + +static void +tei_id_chk_req(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + int tei; + char tmp[64]; + + tei = skb->data[4] >> 1; + if (st->ma.debug) { + sprintf(tmp, "identity check req tei %d", tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) { + FsmDelTimer(&st->ma.t202, 4); + FsmChangeState(&st->ma.tei_m, ST_TEI_NOP); + put_tei_msg(st, ID_CHK_RES, random_ri(), st->l2.tei); + } +} + +static void +tei_id_remove(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + struct IsdnCardState *cs; + int tei; + char tmp[64]; + + tei = skb->data[4] >> 1; + if (st->ma.debug) { + sprintf(tmp, "identity remove tei %d", tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) { + FsmDelTimer(&st->ma.t202, 5); + FsmChangeState(&st->ma.tei_m, ST_TEI_NOP); + st->ma.manl2(st, MDL_REMOVE_REQ, 0); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_REMOVE_REQ, NULL); + } +} + +static void +tei_id_verify(struct FsmInst *fi, int event, void *arg) { - int x = jiffies & 0xffff; + struct PStack *st = fi->userdata; + char tmp[64]; - return (x); + if (st->ma.debug) { + sprintf(tmp, "id verify request for tei %d", st->l2.tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + put_tei_msg(st, ID_VERIFY, 0, st->l2.tei); + FsmChangeState(&st->ma.tei_m, ST_TEI_IDVERIFY); + FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 2); + st->ma.N202 = 2; } static void -tei_man(struct PStack *sp, int i, void *v) +tei_id_req_tout(struct FsmInst *fi, int event, void *arg) { + struct PStack *st = fi->userdata; + char tmp[64]; + struct IsdnCardState *cs; - printk(KERN_DEBUG "tei_man\n"); + if (--st->ma.N202) { + st->ma.ri = random_ri(); + if (st->ma.debug) { + sprintf(tmp, "assign req(%d) ri %d", + 4 - st->ma.N202, st->ma.ri); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + put_tei_msg(st, ID_REQUEST, st->ma.ri, 127); + FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 3); + } else { + sprintf(tmp, "assign req failed"); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + st->ma.manl2(st, MDL_ERROR_IND, 0); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_REMOVE_REQ, NULL); + FsmChangeState(fi, ST_TEI_NOP); + } +} + +static void +tei_id_ver_tout(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + char tmp[64]; + struct IsdnCardState *cs; + + if (--st->ma.N202) { + if (st->ma.debug) { + sprintf(tmp, "id verify req(%d) for tei %d", + 3 - st->ma.N202, st->l2.tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + put_tei_msg(st, ID_VERIFY, 0, st->l2.tei); + FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 4); + } else { + sprintf(tmp, "verify req for tei %d failed", st->l2.tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + st->ma.manl2(st, MDL_REMOVE_REQ, 0); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_REMOVE_REQ, NULL); + FsmChangeState(fi, ST_TEI_NOP); + } +} + +static void +tei_l1l2(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + int mt; + char tmp[64]; + + if (pr == PH_DATA_IND) { + if (skb->len < 3) { + sprintf(tmp, "short mgr frame %d/3", skb->len); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } else if (((skb->data[0] >> 2) != TEI_SAPI) || + ((skb->data[1] >> 1) != GROUP_TEI)) { + sprintf(tmp, "wrong mgr sapi/tei %x/%x", + skb->data[0], skb->data[1]); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } else if ((skb->data[2] & 0xef) != UI) { + sprintf(tmp, "mgr frame is not ui %x", + skb->data[2]); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } else { + skb_pull(skb, 3); + if (skb->len < 5) { + sprintf(tmp, "short mgr frame %d/5", skb->len); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } else if (skb->data[0] != TEI_ENTITY_ID) { + /* wrong management entity identifier, ignore */ + sprintf(tmp, "tei handler wrong entity id %x\n", + skb->data[0]); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } else { + mt = skb->data[3]; + if (mt == ID_ASSIGNED) + FsmEvent(&st->ma.tei_m, EV_ASSIGN, skb); + else if (mt == ID_DENIED) + FsmEvent(&st->ma.tei_m, EV_DENIED, skb); + else if (mt == ID_CHK_REQ) + FsmEvent(&st->ma.tei_m, EV_CHKREQ, skb); + else if (mt == ID_REMOVE) + FsmEvent(&st->ma.tei_m, EV_REMOVE, skb); + else { + sprintf(tmp, "tei handler wrong mt %x\n", + mt); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + } + } + } else { + sprintf(tmp, "tei handler wrong pr %x\n", pr); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + dev_kfree_skb(skb); } static void tei_l2tei(struct PStack *st, int pr, void *arg) { - struct IsdnCardState *sp = st->l1.hardware; + switch (pr) { + case (MDL_ASSIGN_IND): +#ifdef TEI_FIXED + if (st->ma.debug) { + char tmp[64]; + sprintf(tmp, "fixed assign tei %d", TEI_FIXED); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + st->ma.manl2(st, MDL_ASSIGN_REQ, (void *) (int) TEI_FIXED); +#else + FsmEvent(&st->ma.tei_m, EV_IDREQ, arg); +#endif + break; + case (MDL_ERROR_REQ): + FsmEvent(&st->ma.tei_m, EV_VERIFY, arg); + break; + default: + break; + } +} + +static void +tei_debug(struct FsmInst *fi, char *s) +{ + struct PStack *st = fi->userdata; + char tm[32], str[256]; - tei_handler(sp->teistack, pr, arg); + jiftime(tm, jiffies); + sprintf(str, "%s Tei %s\n", tm, s); + HiSax_putstatus(st->l1.hardware, str); } void setstack_tei(struct PStack *st) { st->l2.l2tei = tei_l2tei; + st->ma.T202 = 2000; /* T202 2000 milliseconds */ + st->l1.l1tei = tei_l1l2; + st->ma.debug = 1; + st->ma.tei_m.fsm = &teifsm; + st->ma.tei_m.state = ST_TEI_NOP; + st->ma.tei_m.debug = 1; + st->ma.tei_m.userdata = st; + st->ma.tei_m.userint = 0; + st->ma.tei_m.printdebug = tei_debug; + FsmInitTimer(&st->ma.tei_m, &st->ma.t202); } void init_tei(struct IsdnCardState *sp, int protocol) { - struct PStack *st; - char tmp[128]; - - st = (struct PStack *) kmalloc(sizeof(struct PStack), GFP_ATOMIC); - setstack_HiSax(st, sp); - st->l2.extended = !0; - st->l2.laptype = LAPD; - st->l2.window = 1; - st->l2.orig = !0; - st->protocol = protocol; -/* - * the following is not necessary for tei mng. (broadcast only) - */ - st->l2.t200 = 500; /* 500 milliseconds */ - st->l2.n200 = 4; /* try 4 times */ - st->l2.sap = 63; - st->l2.tei = 127; +} + +void +release_tei(struct IsdnCardState *cs) +{ + struct PStack *st = cs->stlist; - sprintf(tmp, "Card %d tei", sp->cardnr + 1); - setstack_isdnl2(st, tmp); - st->l2.debug = 0; - st->l3.debug = 0; + while (st) { + FsmDelTimer(&st->ma.t202, 1); + st = st->next; + } +} - st->ma.manl2(st, MDL_NOTEIPROC, NULL); +static struct FsmNode TeiFnList[] HISAX_INITDATA = +{ + {ST_TEI_NOP, EV_IDREQ, tei_id_request}, + {ST_TEI_NOP, EV_VERIFY, tei_id_verify}, + {ST_TEI_NOP, EV_REMOVE, tei_id_remove}, + {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req}, + {ST_TEI_IDREQ, EV_T202, tei_id_req_tout}, + {ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign}, + {ST_TEI_IDREQ, EV_DENIED, tei_id_denied}, + {ST_TEI_IDVERIFY, EV_T202, tei_id_ver_tout}, + {ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove}, + {ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req}, +}; - st->l2.l2l3 = (void *) tei_handler; - st->l1.l1man = tei_man; - st->l2.l2man = tei_man; - st->l4.l2writewakeup = NULL; +#define TEI_FN_COUNT (sizeof(TeiFnList)/sizeof(struct FsmNode)) - HiSax_addlist(sp, st); - sp->teistack = st; +HISAX_INITFUNC(void +TeiNew(void)) +{ + teifsm.state_count = TEI_STATE_COUNT; + teifsm.event_count = TEI_EVENT_COUNT; + teifsm.strEvent = strTeiEvent; + teifsm.strState = strTeiState; + FsmNew(&teifsm, TeiFnList, TEI_FN_COUNT); } void -release_tei(struct IsdnCardState *sp) +TeiFree(void) { - struct PStack *st = sp->teistack; - - HiSax_rmlist(sp, st); - kfree((void *) st); + FsmFree(&teifsm); } diff --git a/drivers/isdn/hisax/teleint.c b/drivers/isdn/hisax/teleint.c new file mode 100644 index 000000000000..d86dd79a40f6 --- /dev/null +++ b/drivers/isdn/hisax/teleint.c @@ -0,0 +1,363 @@ +/* $Id: teleint.c,v 1.5 1998/02/02 13:40:47 keil Exp $ + + * teleint.c low level stuff for TeleInt isdn cards + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: teleint.c,v $ + * Revision 1.5 1998/02/02 13:40:47 keil + * fast io + * + * Revision 1.4 1997/11/08 21:35:53 keil + * new l1 init + * + * Revision 1.3 1997/11/06 17:09:30 keil + * New 2.1 init code + * + * Revision 1.2 1997/10/29 18:55:53 keil + * changes for 2.1.60 (irq2dev_map) + * + * Revision 1.1 1997/09/11 17:32:32 keil + * new + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hfc_2bs0.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *TeleInt_revision = "$Revision: 1.5 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + int max_delay = 2000; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = HFC_BUSY & bytein(ale); + while (ret && --max_delay) + ret = HFC_BUSY & bytein(ale); + if (!max_delay) { + printk(KERN_WARNING "TeleInt Busy not inaktive\n"); + restore_flags(flags); + return (0); + } + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + register u_char ret; + int max_delay = 2000; + byteout(ale, off); + + ret = HFC_BUSY & bytein(ale); + while (ret && --max_delay) + ret = HFC_BUSY & bytein(ale); + if (!max_delay) { + printk(KERN_WARNING "TeleInt Busy not inaktive\n"); + return; + } + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + register u_char ret; + int max_delay = 2000; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = HFC_BUSY & bytein(ale); + while (ret && --max_delay) + ret = HFC_BUSY & bytein(ale); + if (!max_delay) { + printk(KERN_WARNING "TeleInt Busy not inaktive\n"); + restore_flags(flags); + return; + } + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + register u_char ret; + int max_delay = 2000; + + /* fifo write without cli because it's allready done */ + byteout(ale, off); + ret = HFC_BUSY & bytein(ale); + while (ret && --max_delay) + ret = HFC_BUSY & bytein(ale); + if (!max_delay) { + printk(KERN_WARNING "TeleInt Busy not inaktive\n"); + return; + } + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + cs->hw.hfc.cip = offset; + return (readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + cs->hw.hfc.cip = offset; + writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + cs->hw.hfc.cip = 0; + readfifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + cs->hw.hfc.cip = 0; + writefifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size); +} + +static u_char +ReadHFC(struct IsdnCardState *cs, int data, u_char reg) +{ + register u_char ret; + + if (data) { + cs->hw.hfc.cip = reg; + byteout(cs->hw.hfc.addr | 1, reg); + ret = bytein(cs->hw.hfc.addr); + if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) { + char tmp[32]; + sprintf(tmp, "hfc RD %02x %02x", reg, ret); + debugl1(cs, tmp); + } + } else + ret = bytein(cs->hw.hfc.addr | 1); + return (ret); +} + +static void +WriteHFC(struct IsdnCardState *cs, int data, u_char reg, u_char value) +{ + byteout(cs->hw.hfc.addr | 1, reg); + cs->hw.hfc.cip = reg; + if (data) + byteout(cs->hw.hfc.addr, value); + if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) { + char tmp[32]; + sprintf(tmp, "hfc W%c %02x %02x", data ? 'D' : 'C', reg, value); + debugl1(cs, tmp); + } +} + +static void +TeleInt_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "TeleInt: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 2) { + writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0xFF); + writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0x0); + } +} + +static void +TeleInt_Timer(struct IsdnCardState *cs) +{ + int stat = 0; + + if (cs->bcs[0].mode) { + stat |= 1; + main_irq_hfc(&cs->bcs[0]); + } + if (cs->bcs[1].mode) { + stat |= 2; + main_irq_hfc(&cs->bcs[1]); + } + cs->hw.hfc.timer.expires = jiffies + 1; + add_timer(&cs->hw.hfc.timer); +} + +void +release_io_TeleInt(struct IsdnCardState *cs) +{ + del_timer(&cs->hw.hfc.timer); + releasehfc(cs); + if (cs->hw.hfc.addr) + release_region(cs->hw.hfc.addr, 2); +} + +static void +reset_TeleInt(struct IsdnCardState *cs) +{ + long flags; + + printk(KERN_INFO "TeleInt: resetting card\n"); + cs->hw.hfc.cirm |= HFC_RESET; + byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 3; + schedule(); + cs->hw.hfc.cirm &= ~HFC_RESET; + byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + restore_flags(flags); +} + +static int +TeleInt_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_TeleInt(cs); + return(0); + case CARD_RELEASE: + release_io_TeleInt(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &TeleInt_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithfc(cs); + clear_pending_isac_ints(cs); + initisac(cs); + cs->hw.hfc.timer.expires = jiffies + 1; + add_timer(&cs->hw.hfc.timer); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_TeleInt(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, TeleInt_revision); + printk(KERN_INFO "HiSax: TeleInt driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_TELEINT) + return (0); + + cs->hw.hfc.addr = card->para[1] & 0x3fe; + cs->irq = card->para[0]; + cs->hw.hfc.cirm = HFC_CIRM; + cs->hw.hfc.isac_spcr = 0x00; + cs->hw.hfc.cip = 0; + cs->hw.hfc.ctmt = HFC_CTMT | HFC_CLTIMER; + cs->bcs[0].hw.hfc.send = NULL; + cs->bcs[1].hw.hfc.send = NULL; + cs->hw.hfc.fifosize = 7 * 1024 + 512; + cs->hw.hfc.timer.function = (void *) TeleInt_Timer; + cs->hw.hfc.timer.data = (long) cs; + init_timer(&cs->hw.hfc.timer); + if (check_region((cs->hw.hfc.addr), 2)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.hfc.addr, + cs->hw.hfc.addr + 2); + return (0); + } else { + request_region(cs->hw.hfc.addr, 2, "TeleInt isdn"); + } + /* HW IO = IO */ + byteout(cs->hw.hfc.addr, cs->hw.hfc.addr & 0xff); + byteout(cs->hw.hfc.addr | 1, ((cs->hw.hfc.addr & 0x300) >> 8) | 0x54); + switch (cs->irq) { + case 3: + cs->hw.hfc.cirm |= HFC_INTA; + break; + case 4: + cs->hw.hfc.cirm |= HFC_INTB; + break; + case 5: + cs->hw.hfc.cirm |= HFC_INTC; + break; + case 7: + cs->hw.hfc.cirm |= HFC_INTD; + break; + case 10: + cs->hw.hfc.cirm |= HFC_INTE; + break; + case 11: + cs->hw.hfc.cirm |= HFC_INTF; + break; + default: + printk(KERN_WARNING "TeleInt: wrong IRQ\n"); + release_io_TeleInt(cs); + return (0); + } + byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); + byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.ctmt); + + printk(KERN_INFO + "TeleInt: defined at 0x%x IRQ %d\n", + cs->hw.hfc.addr, + cs->irq); + + reset_TeleInt(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHFC; + cs->BC_Write_Reg = &WriteHFC; + cs->cardmsg = &TeleInt_card_msg; + ISACVersion(cs, "TeleInt:"); + return (1); +} diff --git a/drivers/isdn/hisax/teles0.c b/drivers/isdn/hisax/teles0.c index 6aaba70d3918..7cd173154b3a 100644 --- a/drivers/isdn/hisax/teles0.c +++ b/drivers/isdn/hisax/teles0.c @@ -1,4 +1,4 @@ -/* $Id: teles0.c,v 1.8 1997/04/13 19:54:04 keil Exp $ +/* $Id: teles0.c,v 2.6 1998/02/03 23:27:47 keil Exp $ * teles0.c low level stuff for Teles Memory IO isdn cards * based on the teles driver from Jan den Ouden @@ -10,71 +10,73 @@ * Beat Doebeli * * $Log: teles0.c,v $ - * Revision 1.8 1997/04/13 19:54:04 keil - * Change in IRQ check delay for SMP + * Revision 2.6 1998/02/03 23:27:47 keil + * IRQ 9 * - * Revision 1.7 1997/04/06 22:54:04 keil - * Using SKB's + * Revision 2.5 1998/02/02 13:29:47 keil + * fast io * - * Revision 1.6 1997/01/27 15:52:18 keil - * SMP proof,cosmetics + * Revision 2.4 1997/11/08 21:35:54 keil + * new l1 init * - * Revision 1.5 1997/01/21 22:25:59 keil - * cleanups + * Revision 2.3 1997/11/06 17:09:31 keil + * New 2.1 init code * - * Revision 1.4 1996/11/05 19:41:27 keil - * more changes for 2.1 + * Revision 2.2 1997/10/29 18:55:57 keil + * changes for 2.1.60 (irq2dev_map) * - * Revision 1.3 1996/10/30 10:22:58 keil - * Changes for 2.1 kernels + * Revision 2.1 1997/07/27 21:47:10 keil + * new interface structures * - * Revision 1.2 1996/10/27 22:08:34 keil - * cosmetic changes + * Revision 2.0 1997/06/26 11:02:43 keil + * New Layer and card interface * - * Revision 1.1 1996/10/13 20:04:58 keil - * Initial revision + * Revision 1.8 1997/04/13 19:54:04 keil + * Change in IRQ check delay for SMP * + * Revision 1.7 1997/04/06 22:54:04 keil + * Using SKB's * + * removed old log info /KKe * */ #define __NO_VERSION__ -#include "siemens.h" #include "hisax.h" -#include "teles0.h" #include "isdnl1.h" -#include +#include "isac.h" +#include "hscx.h" extern const char *CardType[]; -const char *teles0_revision = "$Revision: 1.8 $"; +const char *teles0_revision = "$Revision: 2.6 $"; -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) static inline u_char readisac(unsigned int adr, u_char off) { - return readb(adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off); + return readb(adr + ((off & 1) ? 0x2ff : 0x100) + off); } static inline void writeisac(unsigned int adr, u_char off, u_char data) { - writeb(data, adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off); + writeb(data, adr + ((off & 1) ? 0x2ff : 0x100) + off); } static inline u_char readhscx(unsigned int adr, int hscx, u_char off) { - return readb(adr + (hscx ? 0x1e0 : 0x1a0) + + return readb(adr + (hscx ? 0x1c0 : 0x180) + ((off & 1) ? 0x1ff : 0) + off); } static inline void writehscx(unsigned int adr, int hscx, u_char off, u_char data) { - writeb(data, adr + (hscx ? 0x1e0 : 0x1a0) + + writeb(data, adr + (hscx ? 0x1c0 : 0x180) + ((off & 1) ? 0x1ff : 0) + off); } @@ -87,7 +89,7 @@ read_fifo_isac(unsigned int adr, u_char * data, int size) data[i] = readb(ad); } -static void +static inline void write_fifo_isac(unsigned int adr, u_char * data, int size) { register int i; @@ -113,745 +115,204 @@ write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) 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"); -} +/* Interface functions */ -static inline void -waitforXFW(int adr, int hscx) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - int to = 50; - - while ((!(readhscx(adr, hscx, HSCX_STAR) & 0x44) == 0x40) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Teles0: waitforXFW timeout\n"); + return (readisac(cs->hw.teles0.membase, offset)); } -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) +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - 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); + writeisac(cs->hw.teles0.membase, offset, value); } -/* - * HSCX stuff goes here - */ - static void -hscx_empty_fifo(struct HscxState *hsp, int count) +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->membase, hsp->hscx, 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo_hscx(sp->membase, hsp->hscx, ptr, count); - writehscxCMDR(sp->membase, hsp->hscx, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + read_fifo_isac(cs->hw.teles0.membase, data, size); } static void -hscx_fill_fifo(struct HscxState *hsp) +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - struct IsdnCardState *sp = hsp->sp; - int more, count; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_fill_fifo"); - - if (!hsp->tx_skb) - return; - if (hsp->tx_skb->len <= 0) - return; - - more = (hsp->mode == 1) ? 1 : 0; - if (hsp->tx_skb->len > 32) { - more = !0; - count = 32; - } else - count = hsp->tx_skb->len; - - waitforXFW(sp->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); - } + write_fifo_isac(cs->hw.teles0.membase, data, size); } -static inline void -hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = 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 "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 { - SET_SKB_FREE(hsp->tx_skb); - dev_kfree_skb(hsp->tx_skb); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } + return (readhscx(cs->hw.teles0.membase, hscx, offset)); } -/* - * ISAC stuff goes here - */ - static void -isac_empty_fifo(struct IsdnCardState *sp, int count) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - 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); - } + writehscx(cs->hw.teles0.membase, hscx, offset, value); } -static void -isac_fill_fifo(struct IsdnCardState *sp) -{ - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - write_fifo_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 "AVM: D receive out of memory\n"); - else { - 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 { - SET_SKB_FREE(sp->tx_skb); - dev_kfree_skb(sp->tx_skb); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (readisac(sp->membase, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = readisac(sp->membase, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - } -} +/* + * fast interrupt HSCX stuff goes here + */ -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ +#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg) +#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt) - u_char exval; - struct HscxState *hsp; - char tmp[32]; - - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = readhscx(sp->membase, 1, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->membase, hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = readhscx(sp->membase, 0, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->membase, hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = readhscx(sp->membase, 0, HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void -telesS0_interrupt(int intno, void *dev_id, struct pt_regs *regs) +teles0_interrupt(int intno, void *dev_id, struct pt_regs *regs) { - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val, stat = 0; + int count = 0; - sp = (struct IsdnCardState *) dev_id; - - if (!sp) { + if (!cs) { printk(KERN_WARNING "Teles0: Spurious interrupt!\n"); return; } - val = readhscx(sp->membase, 1, HSCX_ISTA); + val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); Start_HSCX: if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); stat |= 1; } - val = readisac(sp->membase, ISAC_ISTA); + val = readisac(cs->hw.teles0.membase, ISAC_ISTA); Start_ISAC: if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); stat |= 2; } - val = readhscx(sp->membase, 1, HSCX_ISTA); - if (val) { - if (sp->debug & L1_DEB_HSCX) - debugl1(sp, "HSCX IntStat after IntRoutine"); + count++; + val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); + if (val && count < 20) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); goto Start_HSCX; } - val = readisac(sp->membase, ISAC_ISTA); - if (val) { - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "ISAC IntStat after IntRoutine"); + val = readisac(cs->hw.teles0.membase, ISAC_ISTA); + if (val && count < 20) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } if (stat & 1) { - writehscx(sp->membase, 0, HSCX_MASK, 0xFF); - writehscx(sp->membase, 1, HSCX_MASK, 0xFF); - writehscx(sp->membase, 0, HSCX_MASK, 0x0); - writehscx(sp->membase, 1, HSCX_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF); + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0); } if (stat & 2) { - writeisac(sp->membase, ISAC_MASK, 0xFF); - writeisac(sp->membase, ISAC_MASK, 0x0); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0); } } - -static void -initisac(struct IsdnCardState *sp) -{ - 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) +release_io_teles0(struct IsdnCardState *cs) { - if (card->sp->cfg_reg) - release_region(card->sp->cfg_reg, 8); + if (cs->hw.teles0.cfg_reg) + release_region(cs->hw.teles0.cfg_reg, 8); } -static void -clear_pending_ints(struct IsdnCardState *sp) +static int +reset_teles0(struct IsdnCardState *cs) { - int val; - char tmp[64]; + u_char cfval; + long flags; - 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); + save_flags(flags); + sti(); + if (cs->hw.teles0.cfg_reg) { + switch (cs->irq) { + case 2: + case 9: + cfval = 0x00; + break; + case 3: + cfval = 0x02; + break; + case 4: + cfval = 0x04; + break; + case 5: + cfval = 0x06; + break; + case 10: + cfval = 0x08; + break; + case 11: + cfval = 0x0A; + break; + case 12: + cfval = 0x0C; + break; + case 15: + cfval = 0x0E; + break; + default: + return(1); + } + cfval |= ((cs->hw.teles0.membase >> 9) & 0xF0); + byteout(cs->hw.teles0.cfg_reg + 4, cfval); + HZDELAY(HZ / 10 + 1); + byteout(cs->hw.teles0.cfg_reg + 4, cfval | 1); + HZDELAY(HZ / 10 + 1); } - writeisac(sp->membase, ISAC_MASK, 0); - writeisac(sp->membase, ISAC_CMDR, 0x41); + writeb(0, cs->hw.teles0.membase + 0x80); + HZDELAY(HZ / 5 + 1); + writeb(1, cs->hw.teles0.membase + 0x80); + HZDELAY(HZ / 5 + 1); + restore_flags(flags); + return(0); } -int -initteles0(struct IsdnCardState *sp) +static int +Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - int ret; - int loop = 0; - char tmp[40]; - - sp->counter = kstat_irqs(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_irqs(sp->irq) > sp->counter) - break; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 1; - schedule(); - } - sprintf(tmp, "IRQ %d count %d", sp->irq, - kstat_irqs(sp->irq)); - debugl1(sp, tmp); - if (kstat_irqs(sp->irq) == sp->counter) { - printk(KERN_WARNING - "Teles0: IRQ(%d) getting no interrupts during init\n", - sp->irq); - free_irq(sp->irq, sp); - return (0); - } - } - return (ret); + switch (mt) { + case CARD_RESET: + reset_teles0(cs); + return(0); + case CARD_RELEASE: + release_io_teles0(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &teles0_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); } -int -setup_teles0(struct IsdnCard *card) +__initfunc(int +setup_teles0(struct IsdnCard *card)) { - u_char cfval, val, verA, verB; - struct IsdnCardState *sp = card->sp; - long flags; + u_char val; + struct IsdnCardState *cs = card->cs; char tmp[64]; strcpy(tmp, teles0_revision); - printk(KERN_NOTICE "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp)); - if ((sp->typ != ISDN_CTYPE_16_0) && (sp->typ != ISDN_CTYPE_8_0)) + printk(KERN_INFO "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp)); + if ((cs->typ != ISDN_CTYPE_16_0) && (cs->typ != ISDN_CTYPE_8_0)) return (0); - if (sp->typ == ISDN_CTYPE_16_0) - sp->cfg_reg = card->para[2]; + if (cs->typ == ISDN_CTYPE_16_0) + cs->hw.teles0.cfg_reg = card->para[2]; else /* 8.0 */ - sp->cfg_reg = 0; + cs->hw.teles0.cfg_reg = 0; if (card->para[1] < 0x10000) { card->para[1] <<= 4; @@ -859,110 +320,69 @@ setup_teles0(struct IsdnCard *card) "Teles0: membase configured DOSish, assuming 0x%lx\n", (unsigned long) card->para[1]); } - sp->membase = card->para[1]; - sp->irq = card->para[0]; - if (sp->cfg_reg) { - if (check_region((sp->cfg_reg), 8)) { + cs->hw.teles0.membase = card->para[1]; + cs->irq = card->para[0]; + if (cs->hw.teles0.cfg_reg) { + if (check_region((cs->hw.teles0.cfg_reg), 8)) { printk(KERN_WARNING "HiSax: %s config port %x-%x already in use\n", CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + 8); + cs->hw.teles0.cfg_reg, + cs->hw.teles0.cfg_reg + 8); return (0); } else { - request_region(sp->cfg_reg, 8, "teles cfg"); + request_region(cs->hw.teles0.cfg_reg, 8, "teles cfg"); } } - switch (sp->irq) { - case 2: - cfval = 0x00; - break; - case 3: - cfval = 0x02; - break; - case 4: - cfval = 0x04; - break; - case 5: - cfval = 0x06; - break; - case 10: - cfval = 0x08; - break; - case 11: - cfval = 0x0A; - break; - case 12: - cfval = 0x0C; - break; - case 15: - cfval = 0x0E; - break; - default: - cfval = 0x00; - break; - } - cfval |= ((card->para[1] >> 9) & 0xF0); - if (sp->cfg_reg) { - if ((val = bytein(sp->cfg_reg + 0)) != 0x51) { + if (cs->hw.teles0.cfg_reg) { + if ((val = bytein(cs->hw.teles0.cfg_reg + 0)) != 0x51) { printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n", - sp->cfg_reg + 0, val); - release_region(sp->cfg_reg, 8); + cs->hw.teles0.cfg_reg + 0, val); + release_region(cs->hw.teles0.cfg_reg, 8); return (0); } - if ((val = bytein(sp->cfg_reg + 1)) != 0x93) { + if ((val = bytein(cs->hw.teles0.cfg_reg + 1)) != 0x93) { printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n", - sp->cfg_reg + 1, val); - release_region(sp->cfg_reg, 8); + cs->hw.teles0.cfg_reg + 1, val); + release_region(cs->hw.teles0.cfg_reg, 8); return (0); } - val = bytein(sp->cfg_reg + 2); /* 0x1e=without AB - * 0x1f=with AB - * 0x1c 16.3 ??? - */ + val = bytein(cs->hw.teles0.cfg_reg + 2); /* 0x1e=without AB + * 0x1f=with AB + * 0x1c 16.3 ??? + */ if (val != 0x1e && val != 0x1f) { printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n", - sp->cfg_reg + 2, val); - release_region(sp->cfg_reg, 8); + cs->hw.teles0.cfg_reg + 2, val); + release_region(cs->hw.teles0.cfg_reg, 8); return (0); } - save_flags(flags); - byteout(sp->cfg_reg + 4, cfval); - sti(); - HZDELAY(HZ / 10 + 1); - byteout(sp->cfg_reg + 4, cfval | 1); - HZDELAY(HZ / 10 + 1); - restore_flags(flags); } - printk(KERN_NOTICE - "HiSax: %s config irq:%d mem:%x cfg:%x\n", - CardType[sp->typ], sp->irq, - sp->membase, sp->cfg_reg); - verA = readhscx(sp->membase, 0, HSCX_VSTR) & 0xf; - verB = readhscx(sp->membase, 1, HSCX_VSTR) & 0xf; - printk(KERN_INFO "Teles0: HSCX version A: %s B: %s\n", - HscxVersion(verA), HscxVersion(verB)); - val = readisac(sp->membase, ISAC_RBCH); - printk(KERN_INFO "Teles0: ISAC %s\n", - ISACVersion(val)); - - if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) { + /* 16.0 and 8.0 designed for IOM1 */ + test_and_set_bit(HW_IOM1, &cs->HW_Flags); + printk(KERN_INFO + "HiSax: %s config irq:%d mem:0x%X cfg:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.teles0.membase, cs->hw.teles0.cfg_reg); + if (reset_teles0(cs)) { + printk(KERN_WARNING "Teles0: wrong IRQ\n"); + release_io_teles0(cs); + return (0); + } + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Teles_card_msg; + ISACVersion(cs, "Teles0:"); + if (HscxVersion(cs, "Teles0:")) { printk(KERN_WARNING "Teles0: wrong HSCX versions check IO/MEM addresses\n"); - release_io_teles0(card); + release_io_teles0(cs); return (0); } - save_flags(flags); - writeb(0, sp->membase + 0x80); - sti(); - HZDELAY(HZ / 5 + 1); - writeb(1, sp->membase + 0x80); - HZDELAY(HZ / 5 + 1); - restore_flags(flags); - - sp->modehscx = &modehscx; - sp->ph_command = &ph_command; - sp->hscx_fill_fifo = &hscx_fill_fifo; - sp->isac_fill_fifo = &isac_fill_fifo; return (1); } diff --git a/drivers/isdn/hisax/teles0.h b/drivers/isdn/hisax/teles0.h deleted file mode 100644 index 3baaa5cbd0fd..000000000000 --- a/drivers/isdn/hisax/teles0.h +++ /dev/null @@ -1,21 +0,0 @@ -/* $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 index 4b92e5ab7b7f..261d054fb8a1 100644 --- a/drivers/isdn/hisax/teles3.c +++ b/drivers/isdn/hisax/teles3.c @@ -1,4 +1,4 @@ -/* $Id: teles3.c,v 1.11 1997/04/13 19:54:05 keil Exp $ +/* $Id: teles3.c,v 2.7 1998/02/02 13:29:48 keil Exp $ * teles3.c low level stuff for Teles 16.3 & PNP isdn cards * @@ -11,6 +11,30 @@ * Beat Doebeli * * $Log: teles3.c,v $ + * Revision 2.7 1998/02/02 13:29:48 keil + * fast io + * + * Revision 2.6 1997/11/13 16:22:44 keil + * COMPAQ_ISA reset + * + * Revision 2.5 1997/11/12 15:01:25 keil + * COMPAQ_ISA changes + * + * Revision 2.4 1997/11/08 21:35:56 keil + * new l1 init + * + * Revision 2.3 1997/11/06 17:09:33 keil + * New 2.1 init code + * + * Revision 2.2 1997/10/29 18:55:59 keil + * changes for 2.1.60 (irq2dev_map) + * + * Revision 2.1 1997/07/27 21:47:12 keil + * new interface structures + * + * Revision 2.0 1997/06/26 11:02:46 keil + * New Layer and card interface + * * Revision 1.11 1997/04/13 19:54:05 keil * Change in IRQ check delay for SMP * @@ -35,36 +59,20 @@ * Revision 1.6 1997/01/27 15:52:55 keil * SMP proof,cosmetics, PCMCIA added * - * Revision 1.5 1997/01/21 22:28:32 keil - * cleanups - * - * Revision 1.4 1996/12/14 21:05:41 keil - * Reset for 16.3 PnP - * - * Revision 1.3 1996/11/05 19:56:54 keil - * debug output fixed - * - * Revision 1.2 1996/10/27 22:09:15 keil - * cosmetic changes - * - * Revision 1.1 1996/10/13 20:04:59 keil - * Initial revision - * - * + * removed old log info /KKe * */ #define __NO_VERSION__ -#include "siemens.h" #include "hisax.h" -#include "teles3.h" +#include "isac.h" +#include "hscx.h" #include "isdnl1.h" -#include extern const char *CardType[]; -const char *teles3_revision = "$Revision: 1.11 $"; +const char *teles3_revision = "$Revision: 2.7 $"; -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) static inline u_char readreg(unsigned int adr, u_char off) @@ -82,953 +90,410 @@ writereg(unsigned int adr, u_char off, u_char data) static inline void read_fifo(unsigned int adr, u_char * data, int size) { - insb(adr + 0x1e, data, size); + insb(adr, data, size); } static void write_fifo(unsigned int adr, u_char * data, int size) { - outsb(adr + 0x1e, data, size); + outsb(adr, data, size); } -static inline void -waitforCEC(int adr) -{ - int to = 50; +/* Interface functions */ - 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) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - int to = 50; - - while ((!(readreg(adr, HSCX_STAR) & 0x44) == 0x40) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Teles3: waitforXFW timeout\n"); + return (readreg(cs->hw.teles3.isac, offset)); } -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) +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo(sp->hscx[hsp->hscx], ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writereg(cs->hw.teles3.isac, offset, value); } static void -hscx_fill_fifo(struct HscxState *hsp) -{ - struct IsdnCardState *sp = hsp->sp; - int more, count; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_fill_fifo"); - - if (!hsp->tx_skb) - return; - if (hsp->tx_skb->len <= 0) - return; - - more = (hsp->mode == 1) ? 1 : 0; - if (hsp->tx_skb->len > 32) { - more = !0; - count = 32; - } else - count = hsp->tx_skb->len; - - waitforXFW(sp->hscx[hsp->hscx]); - save_flags(flags); - cli(); - ptr = hsp->tx_skb->data; - skb_pull(hsp->tx_skb, count); - hsp->tx_cnt -= count; - hsp->count += count; - write_fifo(sp->hscx[hsp->hscx], ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_fill_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } -} - -static inline void -hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx) +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = readreg(sp->hscx[hsp->hscx], HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!r & 0x80) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX invalid frame"); - if ((r & 0x40) && hsp->mode) - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX RDO mode=%d", - hsp->mode); - debugl1(sp, tmp); - } - if (!r & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX CRC error"); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - } else { - count = readreg(sp->hscx[hsp->hscx], HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - if ((count = hsp->rcvidx - 1) > 0) { - if (!(skb = dev_alloc_skb(count))) - printk(KERN_WARNING "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 { - SET_SKB_FREE(hsp->tx_skb); - dev_kfree_skb(hsp->tx_skb); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } + read_fifo(cs->hw.teles3.isacfifo, data, size); } -/* - * ISAC stuff goes here - */ - static void -isac_empty_fifo(struct IsdnCardState *sp, int count) +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo(sp->isac, ptr, count); - writereg(sp->isac, ISAC_CMDR, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + write_fifo(cs->hw.teles3.isacfifo, data, size); } -static void -isac_fill_fifo(struct IsdnCardState *sp) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - write_fifo(sp->isac, ptr, count); - writereg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_fill_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (readreg(cs->hw.teles3.hscx[hscx], offset)); } static void -ph_command(struct IsdnCardState *sp, unsigned int command) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - if (sp->debug & L1_DEB_ISAC) { - char tmp[32]; - sprintf(tmp, "ph_command %d", command); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_CIX0, (command << 2) | 3); + writereg(cs->hw.teles3.hscx[hscx], offset, value); } +/* + * fast interrupt HSCX stuff goes here + */ -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; - - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = readreg(sp->isac, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!exval & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - writereg(sp->isac, ISAC_CMDR, 0x80); - } else { - count = readreg(sp->isac, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "AVM: D receive out of memory\n"); - else { - 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 { - SET_SKB_FREE(sp->tx_skb); - dev_kfree_skb(sp->tx_skb); - 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]; +#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.hscx[nr], reg) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.hscx[nr], reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt) - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = readreg(sp->hscx[1], HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = readreg(sp->hscx[0], HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = readreg(sp->hscx[0], HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs) { #define MAXCOUNT 20 - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val, stat = 0; int count = 0; - sp = (struct IsdnCardState *) dev_id; - - if (!sp) { + if (!cs) { printk(KERN_WARNING "Teles: Spurious interrupt!\n"); return; } - val = readreg(sp->hscx[1], HSCX_ISTA); + val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA); Start_HSCX: if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); stat |= 1; } - val = readreg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.teles3.isac, ISAC_ISTA); Start_ISAC: if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); stat |= 2; } count++; - val = readreg(sp->hscx[1], HSCX_ISTA); + val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA); if (val && count < MAXCOUNT) { - if (sp->debug & L1_DEB_HSCX) - debugl1(sp, "HSCX IntStat after IntRoutine"); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); goto Start_HSCX; } - val = readreg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.teles3.isac, ISAC_ISTA); if (val && count < MAXCOUNT) { - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "ISAC IntStat after IntRoutine"); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } if (count >= MAXCOUNT) printk(KERN_WARNING "Teles3: more than %d loops in teles3_interrupt\n", count); if (stat & 1) { - writereg(sp->hscx[0], HSCX_MASK, 0xFF); - writereg(sp->hscx[1], HSCX_MASK, 0xFF); - writereg(sp->hscx[0], HSCX_MASK, 0x0); - writereg(sp->hscx[1], HSCX_MASK, 0x0); + writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0x0); + writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0x0); } if (stat & 2) { - writereg(sp->isac, ISAC_MASK, 0xFF); - writereg(sp->isac, ISAC_MASK, 0x0); + writereg(cs->hw.teles3.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.teles3.isac, ISAC_MASK, 0x0); } } - -static void -initisac(struct IsdnCardState *sp) -{ - unsigned int adr = sp->isac; - - /* 16.3 IOM 2 Mode */ - writereg(adr, ISAC_MASK, 0xff); - writereg(adr, ISAC_ADF2, 0x80); - writereg(adr, ISAC_SQXR, 0x2f); - writereg(adr, ISAC_SPCR, 0x0); - writereg(adr, ISAC_ADF1, 0x2); - writereg(adr, ISAC_STCR, 0x70); - writereg(adr, ISAC_MODE, 0xc9); - writereg(adr, ISAC_TIMR, 0x0); - writereg(adr, ISAC_ADF1, 0x0); - writereg(adr, ISAC_CMDR, 0x41); - writereg(adr, ISAC_CIX0, (1 << 2) | 3); - writereg(adr, ISAC_MASK, 0xff); - writereg(adr, ISAC_MASK, 0x0); -} - -static void -modehscx(struct HscxState *hs, int mode, int ichan) -{ - struct IsdnCardState *sp = hs->sp; - int hscx = hs->hscx; - - if (sp->debug & L1_DEB_HSCX) { - char tmp[40]; - sprintf(tmp, "hscx %c mode %d ichan %d", - 'A' + hscx, mode, ichan); - debugl1(sp, tmp); - } - hs->mode = mode; - writereg(sp->hscx[hscx], HSCX_CCR1, 0x85); - writereg(sp->hscx[hscx], HSCX_XAD1, 0xFF); - writereg(sp->hscx[hscx], HSCX_XAD2, 0xFF); - writereg(sp->hscx[hscx], HSCX_RAH2, 0xFF); - writereg(sp->hscx[hscx], HSCX_XBCH, 0x0); - writereg(sp->hscx[hscx], HSCX_RLCR, 0x0); - - switch (mode) { - case (0): - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0xff); - writereg(sp->hscx[hscx], HSCX_TSAR, 0xff); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - writereg(sp->hscx[hscx], HSCX_MODE, 0x84); - break; - case (1): - if (ichan == 0) { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } else { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x3); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x3); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } - writereg(sp->hscx[hscx], HSCX_MODE, 0xe4); - writereg(sp->hscx[hscx], HSCX_CMDR, 0x41); - break; - case (2): - if (ichan == 0) { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } else { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x3); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x3); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } - writereg(sp->hscx[hscx], HSCX_MODE, 0x8c); - writereg(sp->hscx[hscx], HSCX_CMDR, 0x41); - break; - } - writereg(sp->hscx[hscx], HSCX_ISTA, 0x00); -} - inline static void -release_ioregs(struct IsdnCard *card, int mask) +release_ioregs(struct IsdnCardState *cs, int mask) { if (mask & 1) - release_region(card->sp->isac, 32); + release_region(cs->hw.teles3.isac + 32, 32); if (mask & 2) - release_region(card->sp->hscx[0], 32); + release_region(cs->hw.teles3.hscx[0] + 32, 32); if (mask & 4) - release_region(card->sp->hscx[1], 32); + release_region(cs->hw.teles3.hscx[1] + 32, 32); } void -release_io_teles3(struct IsdnCard *card) +release_io_teles3(struct IsdnCardState *cs) { - if (card->sp->typ == ISDN_CTYPE_TELESPCMCIA) - release_region(card->sp->hscx[0], 97); + if (cs->typ == ISDN_CTYPE_TELESPCMCIA) + release_region(cs->hw.teles3.cfg_reg, 97); else { - if (card->sp->cfg_reg) - release_region(card->sp->cfg_reg, 8); - release_ioregs(card, 0x7); + if (cs->hw.teles3.cfg_reg) + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + release_region(cs->hw.teles3.cfg_reg, 1); + } else { + release_region(cs->hw.teles3.cfg_reg, 8); + } + release_ioregs(cs, 0x7); } } -static void -clear_pending_ints(struct IsdnCardState *sp) +static int +reset_teles3(struct IsdnCardState *cs) { - int val; - char tmp[64]; - - val = readreg(sp->hscx[1], HSCX_ISTA); - sprintf(tmp, "HSCX B ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readreg(sp->hscx[1], HSCX_EXIR); - sprintf(tmp, "HSCX B EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x02) { - val = readreg(sp->hscx[0], HSCX_EXIR); - sprintf(tmp, "HSCX A EXIR %x", val); - debugl1(sp, tmp); - } - val = readreg(sp->hscx[0], HSCX_ISTA); - sprintf(tmp, "HSCX A ISTA %x", val); - debugl1(sp, tmp); - val = readreg(sp->hscx[1], HSCX_STAR); - sprintf(tmp, "HSCX B STAR %x", val); - debugl1(sp, tmp); - val = readreg(sp->hscx[0], HSCX_STAR); - sprintf(tmp, "HSCX A STAR %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_STAR); - sprintf(tmp, "ISAC STAR %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_MODE); - sprintf(tmp, "ISAC MODE %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_ADF2); - sprintf(tmp, "ISAC ADF2 %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_ISTA); - sprintf(tmp, "ISAC ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readreg(sp->isac, ISAC_EXIR); - sprintf(tmp, "ISAC EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x04) { - val = readreg(sp->isac, ISAC_CIR0); - sprintf(tmp, "ISAC CIR0 %x", val); - debugl1(sp, tmp); + long flags; + u_char irqcfg; + + if (cs->typ != ISDN_CTYPE_TELESPCMCIA) { + if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) { + switch (cs->irq) { + case 2: + case 9: + irqcfg = 0x00; + break; + case 3: + irqcfg = 0x02; + break; + case 4: + irqcfg = 0x04; + break; + case 5: + irqcfg = 0x06; + break; + case 10: + irqcfg = 0x08; + break; + case 11: + irqcfg = 0x0A; + break; + case 12: + irqcfg = 0x0C; + break; + case 15: + irqcfg = 0x0E; + break; + default: + return(1); + } + save_flags(flags); + byteout(cs->hw.teles3.cfg_reg + 4, irqcfg); + sti(); + HZDELAY(HZ / 10 + 1); + byteout(cs->hw.teles3.cfg_reg + 4, irqcfg | 1); + HZDELAY(HZ / 10 + 1); + restore_flags(flags); + } else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + save_flags(flags); + byteout(cs->hw.teles3.cfg_reg, 0xff); + HZDELAY(2); + byteout(cs->hw.teles3.cfg_reg, 0x00); + HZDELAY(2); + restore_flags(flags); + } else { + /* Reset off for 16.3 PnP , thanks to Georg Acher */ + save_flags(flags); + byteout(cs->hw.teles3.isac + 0x3c, 0); + HZDELAY(2); + byteout(cs->hw.teles3.isac + 0x3c, 1); + HZDELAY(2); + restore_flags(flags); + } } - writereg(sp->isac, ISAC_MASK, 0); - writereg(sp->isac, ISAC_CMDR, 0x41); + return(0); } -int -initteles3(struct IsdnCardState *sp) +static int +Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - int ret; - int loop = 0; - char tmp[40]; - - sp->counter = kstat_irqs(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_irqs(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_irqs(sp->irq), loop); - debugl1(sp, tmp); - if (kstat_irqs(sp->irq) <= sp->counter) { - printk(KERN_WARNING - "Teles3: IRQ(%d) getting no interrupts during init\n", - sp->irq); - free_irq(sp->irq, sp); - return (0); - } - } - return (ret); + switch (mt) { + case CARD_RESET: + reset_teles3(cs); + return(0); + case CARD_RELEASE: + release_io_teles3(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &teles3_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); } -int -setup_teles3(struct IsdnCard *card) +__initfunc(int +setup_teles3(struct IsdnCard *card)) { - u_char cfval = 0, val, verA, verB; - struct IsdnCardState *sp = card->sp; - long flags; + u_char val; + struct IsdnCardState *cs = card->cs; char tmp[64]; strcpy(tmp, teles3_revision); - printk(KERN_NOTICE "HiSax: Teles IO driver Rev. %s\n", HiSax_getrev(tmp)); - if ((sp->typ != ISDN_CTYPE_16_3) && (sp->typ != ISDN_CTYPE_PNP) - && (sp->typ != ISDN_CTYPE_TELESPCMCIA)) + printk(KERN_INFO "HiSax: Teles IO driver Rev. %s\n", HiSax_getrev(tmp)); + if ((cs->typ != ISDN_CTYPE_16_3) && (cs->typ != ISDN_CTYPE_PNP) + && (cs->typ != ISDN_CTYPE_TELESPCMCIA) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) return (0); - if (sp->typ == ISDN_CTYPE_16_3) { - sp->cfg_reg = card->para[1]; - switch (sp->cfg_reg) { + if (cs->typ == ISDN_CTYPE_16_3) { + cs->hw.teles3.cfg_reg = card->para[1]; + switch (cs->hw.teles3.cfg_reg) { case 0x180: case 0x280: case 0x380: - sp->cfg_reg |= 0xc00; + cs->hw.teles3.cfg_reg |= 0xc00; break; } - sp->isac = sp->cfg_reg - 0x400; - sp->hscx[0] = sp->cfg_reg - 0xc00; - sp->hscx[1] = sp->cfg_reg - 0x800; - } else if (sp->typ == ISDN_CTYPE_TELESPCMCIA) { - sp->cfg_reg = 0; - sp->hscx[0] = card->para[1]; - sp->hscx[1] = card->para[1] + 0x20; - sp->isac = card->para[1] + 0x40; - } else { /* PNP */ - sp->cfg_reg = 0; - sp->isac = card->para[1]; - sp->hscx[0] = card->para[2]; - sp->hscx[1] = card->para[2] + 0x20; - } - sp->irq = card->para[0]; - if (sp->typ == ISDN_CTYPE_TELESPCMCIA) { - if (check_region((sp->hscx[0]), 97)) { + cs->hw.teles3.isac = cs->hw.teles3.cfg_reg - 0x420; + cs->hw.teles3.hscx[0] = cs->hw.teles3.cfg_reg - 0xc20; + cs->hw.teles3.hscx[1] = cs->hw.teles3.cfg_reg - 0x820; + } else if (cs->typ == ISDN_CTYPE_TELESPCMCIA) { + cs->hw.teles3.cfg_reg = card->para[1]; + cs->hw.teles3.hscx[0] = card->para[1] - 0x20; + cs->hw.teles3.hscx[1] = card->para[1]; + cs->hw.teles3.isac = card->para[1] + 0x20; + } else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + cs->hw.teles3.cfg_reg = card->para[3]; + cs->hw.teles3.isac = card->para[2] - 32; + cs->hw.teles3.hscx[0] = card->para[1] - 32; + cs->hw.teles3.hscx[1] = card->para[1]; + } else { /* PNP */ + cs->hw.teles3.cfg_reg = 0; + cs->hw.teles3.isac = card->para[1] - 32; + cs->hw.teles3.hscx[0] = card->para[2] - 32; + cs->hw.teles3.hscx[1] = card->para[2]; + } + cs->irq = card->para[0]; + cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e; + cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e; + cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e; + if (cs->typ == ISDN_CTYPE_TELESPCMCIA) { + if (check_region((cs->hw.teles3.cfg_reg), 97)) { printk(KERN_WARNING "HiSax: %s ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[0], - sp->hscx[0] + 96); + CardType[cs->typ], + cs->hw.teles3.cfg_reg, + cs->hw.teles3.cfg_reg + 96); return (0); } else - request_region(sp->hscx[0], 97, "HiSax Teles PCMCIA"); + request_region(cs->hw.teles3.hscx[0], 97, "HiSax Teles PCMCIA"); } else { - if (sp->cfg_reg) { - if (check_region((sp->cfg_reg), 8)) { - printk(KERN_WARNING - "HiSax: %s config port %x-%x already in use\n", - CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + 8); - return (0); + if (cs->hw.teles3.cfg_reg) { + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + if (check_region((cs->hw.teles3.cfg_reg), 1)) { + printk(KERN_WARNING + "HiSax: %s config port %x already in use\n", + CardType[card->typ], + cs->hw.teles3.cfg_reg); + return (0); + } else + request_region(cs->hw.teles3.cfg_reg, 1, "teles3 cfg"); } else { - request_region(sp->cfg_reg, 8, "teles3 cfg"); + if (check_region((cs->hw.teles3.cfg_reg), 8)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.teles3.cfg_reg, + cs->hw.teles3.cfg_reg + 8); + return (0); + } else + request_region(cs->hw.teles3.cfg_reg, 8, "teles3 cfg"); } } - if (check_region((sp->isac), 32)) { + if (check_region((cs->hw.teles3.isac + 32), 32)) { printk(KERN_WARNING "HiSax: %s isac ports %x-%x already in use\n", - CardType[sp->typ], - sp->isac, - sp->isac + 32); - if (sp->cfg_reg) { - release_region(sp->cfg_reg, 8); - } + CardType[cs->typ], + cs->hw.teles3.isac + 32, + cs->hw.teles3.isac + 64); + if (cs->hw.teles3.cfg_reg) + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + release_region(cs->hw.teles3.cfg_reg, 1); + } else { + release_region(cs->hw.teles3.cfg_reg, 8); + } return (0); - } else { - request_region(sp->isac, 32, "HiSax isac"); - } - if (check_region((sp->hscx[0]), 32)) { + } else + request_region(cs->hw.teles3.isac + 32, 32, "HiSax isac"); + if (check_region((cs->hw.teles3.hscx[0] + 32), 32)) { printk(KERN_WARNING "HiSax: %s hscx A ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[0], - sp->hscx[0] + 32); - if (sp->cfg_reg) { - release_region(sp->cfg_reg, 8); - } - release_ioregs(card, 1); + CardType[cs->typ], + cs->hw.teles3.hscx[0] + 32, + cs->hw.teles3.hscx[0] + 64); + if (cs->hw.teles3.cfg_reg) + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + release_region(cs->hw.teles3.cfg_reg, 1); + } else { + release_region(cs->hw.teles3.cfg_reg, 8); + } + release_ioregs(cs, 1); return (0); - } else { - request_region(sp->hscx[0], 32, "HiSax hscx A"); - } - if (check_region((sp->hscx[1]), 32)) { + } else + request_region(cs->hw.teles3.hscx[0] + 32, 32, "HiSax hscx A"); + if (check_region((cs->hw.teles3.hscx[1] + 32), 32)) { printk(KERN_WARNING "HiSax: %s hscx B ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[1], - sp->hscx[1] + 32); - if (sp->cfg_reg) { - release_region(sp->cfg_reg, 8); - } - release_ioregs(card, 3); + CardType[cs->typ], + cs->hw.teles3.hscx[1] + 32, + cs->hw.teles3.hscx[1] + 64); + if (cs->hw.teles3.cfg_reg) + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + release_region(cs->hw.teles3.cfg_reg, 1); + } else { + release_region(cs->hw.teles3.cfg_reg, 8); + } + release_ioregs(cs, 3); return (0); - } else { - request_region(sp->hscx[1], 32, "HiSax hscx B"); - } - switch (sp->irq) { - case 2: - cfval = 0x00; - break; - case 3: - cfval = 0x02; - break; - case 4: - cfval = 0x04; - break; - case 5: - cfval = 0x06; - break; - case 10: - cfval = 0x08; - break; - case 11: - cfval = 0x0A; - break; - case 12: - cfval = 0x0C; - break; - case 15: - cfval = 0x0E; - break; - default: - cfval = 0x00; - break; - } + } else + request_region(cs->hw.teles3.hscx[1] + 32, 32, "HiSax hscx B"); } - if (sp->cfg_reg) { - if ((val = bytein(sp->cfg_reg + 0)) != 0x51) { + if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) { + if ((val = bytein(cs->hw.teles3.cfg_reg + 0)) != 0x51) { printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n", - sp->cfg_reg + 0, val); - release_io_teles3(card); + cs->hw.teles3.cfg_reg + 0, val); + release_io_teles3(cs); return (0); } - if ((val = bytein(sp->cfg_reg + 1)) != 0x93) { + if ((val = bytein(cs->hw.teles3.cfg_reg + 1)) != 0x93) { printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n", - sp->cfg_reg + 1, val); - release_io_teles3(card); + cs->hw.teles3.cfg_reg + 1, val); + release_io_teles3(cs); return (0); } - val = bytein(sp->cfg_reg + 2); /* 0x1e=without AB - * 0x1f=with AB - * 0x1c 16.3 ??? - * 0x46 16.3 with AB + Video (Teles-Vision) - */ - if (val != 0x46 && val != 0x1c && val != 0x1e && val != 0x1f) { + val = bytein(cs->hw.teles3.cfg_reg + 2);/* 0x1e=without AB + * 0x1f=with AB + * 0x1c 16.3 ??? + * 0x39 16.3 1.1 + * 0x46 16.3 with AB + Video (Teles-Vision) + */ + if (val != 0x46 && val != 0x39 && val != 0x1c && val != 0x1e && val != 0x1f) { printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n", - sp->cfg_reg + 2, val); - release_io_teles3(card); + cs->hw.teles3.cfg_reg + 2, val); + release_io_teles3(cs); return (0); } - save_flags(flags); - byteout(sp->cfg_reg + 4, cfval); - sti(); - HZDELAY(HZ / 10 + 1); - byteout(sp->cfg_reg + 4, cfval | 1); - HZDELAY(HZ / 10 + 1); - restore_flags(flags); - } else { - /* Reset off for 16.3 PnP , thanks to Georg Acher */ - save_flags(flags); - byteout(sp->isac + 0x1c, 1); - HZDELAY(2); - restore_flags(flags); } - printk(KERN_NOTICE - "HiSax: %s config irq:%d isac:%x cfg:%x\n", - CardType[sp->typ], sp->irq, - sp->isac, sp->cfg_reg); - printk(KERN_NOTICE - "HiSax: hscx A:%x hscx B:%x\n", - sp->hscx[0], sp->hscx[1]); - verA = readreg(sp->hscx[0], HSCX_VSTR) & 0xf; - verB = readreg(sp->hscx[1], HSCX_VSTR) & 0xf; - printk(KERN_INFO "Teles3: HSCX version A: %s B: %s\n", - HscxVersion(verA), HscxVersion(verB)); - val = readreg(sp->isac, ISAC_RBCH); - printk(KERN_INFO "Teles3: ISAC %s\n", - ISACVersion(val)); - if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) { + printk(KERN_INFO + "HiSax: %s config irq:%d isac:0x%X cfg:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.teles3.isac + 32, cs->hw.teles3.cfg_reg); + printk(KERN_INFO + "HiSax: hscx A:0x%X hscx B:0x%X\n", + cs->hw.teles3.hscx[0] + 32, cs->hw.teles3.hscx[1] + 32); + + if (reset_teles3(cs)) { + printk(KERN_WARNING "Teles3: wrong IRQ\n"); + release_io_teles3(cs); + return (0); + } + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Teles_card_msg; + ISACVersion(cs, "Teles3:"); + if (HscxVersion(cs, "Teles3:")) { printk(KERN_WARNING "Teles3: wrong HSCX versions check IO address\n"); - release_io_teles3(card); + release_io_teles3(cs); return (0); } - sp->modehscx = &modehscx; - sp->ph_command = &ph_command; - sp->hscx_fill_fifo = &hscx_fill_fifo; - sp->isac_fill_fifo = &isac_fill_fifo; return (1); } diff --git a/drivers/isdn/hisax/teles3.h b/drivers/isdn/hisax/teles3.h deleted file mode 100644 index 8f8dc808126c..000000000000 --- a/drivers/isdn/hisax/teles3.h +++ /dev/null @@ -1,21 +0,0 @@ -/* $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/hisax/teles3c.c b/drivers/isdn/hisax/teles3c.c new file mode 100644 index 000000000000..848c46be9ac8 --- /dev/null +++ b/drivers/isdn/hisax/teles3c.c @@ -0,0 +1,199 @@ +/* $Id: teles3c.c,v 1.2 1998/02/02 13:27:07 keil Exp $ + + * teles3c.c low level stuff for teles 16.3c + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: teles3c.c,v $ + * Revision 1.2 1998/02/02 13:27:07 keil + * New + * + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_2bds0.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *teles163c_revision = "$Revision: 1.2 $"; + +static void +t163c_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat; + char tmp[32]; + + if (!cs) { + printk(KERN_WARNING "teles3c: Spurious interrupt!\n"); + return; + } + if ((HFCD_ANYINT | HFCD_BUSY_NBUSY) & + (stat = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_STAT))) { + val = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_INT_S1); + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "teles3c: stat(%02x) s1(%02x)", stat, val); + debugl1(cs, tmp); + } + hfc2bds0_interrupt(cs, val); + } else { + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "teles3c: irq_no_irq stat(%02x)", stat); + debugl1(cs, tmp); + } + } +} + +static void +t163c_Timer(struct IsdnCardState *cs) +{ + cs->hw.hfcD.timer.expires = jiffies + 75; + /* WD RESET */ +/* WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt | 0x80); + add_timer(&cs->hw.hfcD.timer); +*/ +} + +void +release_io_t163c(struct IsdnCardState *cs) +{ + release2bds0(cs); + del_timer(&cs->hw.hfcD.timer); + if (cs->hw.hfcD.addr) + release_region(cs->hw.hfcD.addr, 2); +} + +static void +reset_t163c(struct IsdnCardState *cs) +{ + long flags; + + printk(KERN_INFO "teles3c: resetting card\n"); + cs->hw.hfcD.cirm = HFCD_RESET | HFCD_MEM8K; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 3; + schedule(); + cs->hw.hfcD.cirm = HFCD_MEM8K; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + cs->hw.hfcD.cirm |= HFCD_INTB; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* INT B */ + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CLKDEL, 0x0e); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_TEST, HFCD_AUTO_AWAKE); /* S/T Auto awake */ + cs->hw.hfcD.ctmt = HFCD_TIM25 | HFCD_AUTO_TIMER; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); + cs->hw.hfcD.int_m2 = HFCD_IRQ_ENABLE; + cs->hw.hfcD.int_m1 = HFCD_INTS_B1TRANS | HFCD_INTS_B2TRANS | + HFCD_INTS_DTRANS | HFCD_INTS_B1REC | HFCD_INTS_B2REC | + HFCD_INTS_DREC | HFCD_INTS_L1STATE; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M1, cs->hw.hfcD.int_m1); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M2, cs->hw.hfcD.int_m2); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, HFCD_LOAD_STATE | 2); /* HFC ST 2 */ + udelay(10); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, 2); /* HFC ST 2 */ + cs->hw.hfcD.mst_m = 0; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, HFCD_MASTER); /* HFC Master */ + cs->hw.hfcD.sctrl = 0; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl); + restore_flags(flags); +} + +static int +t163c_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + long flags; + char tmp[32]; + + if (cs->debug & L1_DEB_ISAC) { + + sprintf(tmp, "teles3c: card_msg %x", mt); + debugl1(cs, tmp); + } + switch (mt) { + case CARD_RESET: + reset_t163c(cs); + return(0); + case CARD_RELEASE: + release_io_t163c(cs); + return(0); + case CARD_SETIRQ: + cs->hw.hfcD.timer.expires = jiffies + 75; + add_timer(&cs->hw.hfcD.timer); + return(request_irq(cs->irq, &t163c_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + init2bds0(cs); + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (80*HZ)/1000; + schedule(); + cs->hw.hfcD.ctmt |= HFCD_TIM800; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m); + restore_flags(flags); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_t163c(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, teles163c_revision); + printk(KERN_INFO "HiSax: Teles 16.3c driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_TELES3C) + return (0); + cs->debug = 0xff; + cs->hw.hfcD.addr = card->para[1] & 0xfffe; + cs->irq = card->para[0]; + cs->hw.hfcD.cip = 0; + cs->hw.hfcD.int_s1 = 0; + cs->hw.hfcD.send = NULL; + cs->bcs[0].hw.hfc.send = NULL; + cs->bcs[1].hw.hfc.send = NULL; + cs->hw.hfcD.bfifosize = 1024 + 512; + cs->hw.hfcD.dfifosize = 512; + cs->ph_state = 0; + cs->hw.hfcD.fifo = 255; + if (check_region((cs->hw.hfcD.addr), 2)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.hfcD.addr, + cs->hw.hfcD.addr + 2); + return (0); + } else { + request_region(cs->hw.hfcD.addr, 2, "teles3c isdn"); + } + /* Teles 16.3c IO ADR is 0x200 | YY0U (YY Bit 15/14 address) */ + outb(0x00, cs->hw.hfcD.addr); + outb(0x56, cs->hw.hfcD.addr | 1); + printk(KERN_INFO + "teles3c: defined at 0x%x IRQ %d HZ %d\n", + cs->hw.hfcD.addr, + cs->irq, HZ); + + set_cs_func(cs); + cs->hw.hfcD.timer.function = (void *) t163c_Timer; + cs->hw.hfcD.timer.data = (long) cs; + init_timer(&cs->hw.hfcD.timer); + reset_t163c(cs); + cs->cardmsg = &t163c_card_msg; + return (1); +} diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c index 0d839512d00e..2cc0e17f401c 100644 --- a/drivers/isdn/icn/icn.c +++ b/drivers/isdn/icn/icn.c @@ -1,4 +1,4 @@ -/* $Id: icn.c,v 1.44 1997/03/30 16:51:26 calle Exp $ +/* $Id: icn.c,v 1.49 1998/02/13 11:14:15 keil Exp $ * ISDN low-level module for the ICN active ISDN-Card. * @@ -19,6 +19,25 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: icn.c,v $ + * Revision 1.49 1998/02/13 11:14:15 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.48 1997/10/10 15:56:14 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * + * Revision 1.47 1997/10/01 09:21:51 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.46 1997/08/21 22:38:33 fritz + * Fixed a typo. + * + * 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. @@ -190,7 +209,7 @@ #undef MAP_DEBUG static char -*revision = "$Revision: 1.44 $"; +*revision = "$Revision: 1.49 $"; static int icn_addcard(int, char *, char *); @@ -205,10 +224,20 @@ icn_free_queue(icn_card * card, int channel) { struct sk_buff_head *queue = &card->spqueue[channel]; struct sk_buff *skb; + unsigned long flags; while ((skb = skb_dequeue(queue))) dev_kfree_skb(skb); + save_flags(flags); + cli(); + card->xlen[channel] = 0; card->sndcount[channel] = 0; + if (card->xskb[channel]) { + card->xskb[channel] = NULL; + restore_flags(flags); + dev_kfree_skb(card->xskb[channel]); + } else + restore_flags(flags); } /* Put a value into a shift-register, highest bit first. @@ -440,12 +469,14 @@ icn_pollbchan_send(int channel, icn_card * card) struct sk_buff *skb; isdn_ctrl cmd; - if (!(card->sndcount[channel] || + if (!(card->sndcount[channel] || card->xskb[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]))) { + while (sbfree && + (card->sndcount[channel] || + skb_queue_len(&card->spqueue[channel]) || + card->xskb[channel])) { save_flags(flags); cli(); if (card->xmit_lock[channel]) { @@ -454,7 +485,19 @@ icn_pollbchan_send(int channel, icn_card * card) } card->xmit_lock[channel]++; restore_flags(flags); - skb = skb_dequeue(&card->spqueue[channel]); + skb = card->xskb[channel]; + if (!skb) { + skb = skb_dequeue(&card->spqueue[channel]); + if (skb) { + /* Pop ACK-flag off skb. + * Store length to xlen. + */ + if (*(skb_pull(skb,1))) + card->xlen[channel] = skb->len; + else + card->xlen[channel] = 0; + } + } if (!skb) break; if (skb->len > ICN_FRAGSIZE) { @@ -471,13 +514,22 @@ icn_pollbchan_send(int channel, icn_card * card) sbnext; /* switch to next buffer */ icn_maprelease_channel(card, mch & 2); if (!skb->len) { - dev_kfree_skb(skb); - cmd.command = ISDN_STAT_BSENT; - cmd.driver = card->myid; - cmd.arg = channel; - card->interface.statcallb(&cmd); - } else - skb_queue_head(&card->spqueue[channel], skb); + save_flags(flags); + cli(); + if (card->xskb[channel]) { + card->xskb[channel] = NULL; + restore_flags(flags); + dev_kfree_skb(skb); + } else + restore_flags(flags); + if (card->xlen[channel]) { + cmd.command = ISDN_STAT_BSENT; + cmd.driver = card->myid; + cmd.arg = channel; + cmd.parm.length = card->xlen[channel]; + card->interface.statcallb(&cmd); + } + } card->xmit_lock[channel] = 0; if (!icn_trymaplock_channel(card, mch)) break; @@ -557,12 +609,11 @@ static icn_stat icn_stat_table[] = * This routine is called periodically via timer. */ -static int +static void 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; @@ -575,7 +626,7 @@ icn_parse_status(u_char * status, int channel, icn_card * card) s++; } if (action == -1) - return 0; + return; cmd.driver = card->myid; cmd.arg = channel; switch (action) { @@ -591,7 +642,6 @@ icn_parse_status(u_char * status, int channel, icn_card * card) cli(); card->rcvidx[channel] = 0; restore_flags(flags); - dflag |= (channel + 1); break; case 3: { @@ -645,7 +695,6 @@ icn_parse_status(u_char * status, int channel, icn_card * card) 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); @@ -675,7 +724,7 @@ icn_parse_status(u_char * status, int channel, icn_card * card) break; } card->interface.statcallb(&cmd); - return dflag; + return; } static void @@ -701,7 +750,6 @@ 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; @@ -722,7 +770,7 @@ icn_polldchan(unsigned long data) card->imsg[1] <= '2' && card->imsg[2] == ';') { ch = (card->imsg[1] - '0') - 1; p = &card->imsg[3]; - dflag |= icn_parse_status(p, ch, card); + icn_parse_status(p, ch, card); } else { p = card->imsg; if (!strncmp(p, "DRV1.", 5)) { @@ -771,10 +819,6 @@ icn_polldchan(unsigned long data) 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 */ @@ -807,7 +851,7 @@ icn_polldchan(unsigned long data) */ static int -icn_sendbuf(int channel, struct sk_buff *skb, icn_card * card) +icn_sendbuf(int channel, int ack, struct sk_buff *skb, icn_card * card) { int len = skb->len; unsigned long flags; @@ -827,6 +871,10 @@ icn_sendbuf(int channel, struct sk_buff *skb, icn_card * card) cli(); nskb = skb_clone(skb, GFP_ATOMIC); if (nskb) { + /* Push ACK flag as one + * byte in front of data. + */ + *(skb_push(nskb, 1)) = ack?1:0; skb_queue_tail(&card->spqueue[channel], nskb); dev_kfree_skb(skb); } else @@ -1382,7 +1430,8 @@ icn_command(isdn_ctrl * c, icn_card * card) } current->timeout = jiffies + ICN_BOOT_TIMEOUT1; schedule(); - sprintf(cbuf, "00;FV2ON\n01;EAZ1\n02;EAZ2\n"); + sprintf(cbuf, "00;FV2ON\n01;EAZ%c\n02;EAZ%c\n", + (a & 1)?'1':'C', (a & 2)?'2':'C'); i = icn_writecmd(cbuf, strlen(cbuf), 0, card); printk(KERN_INFO "icn: (%s) Leased-line mode enabled\n", @@ -1638,14 +1687,14 @@ if_readstatus(u_char * buf, int len, int user, int id, int channel) } static int -if_sendbuf(int id, int channel, struct sk_buff *skb) +if_sendbuf(int id, int channel, int ack, 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)); + return (icn_sendbuf(channel, ack, skb, card)); } printk(KERN_ERR "icn: if_sendbuf called with invalid driverId!\n"); @@ -1669,6 +1718,7 @@ icn_initcard(int port, char *id) } memset((char *) card, 0, sizeof(icn_card)); card->port = port; + card->interface.hl_hdrlen = 1; card->interface.channels = ICN_BCH; card->interface.maxbufsize = 4000; card->interface.command = if_command; @@ -1781,11 +1831,7 @@ icn_init(void) 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); diff --git a/drivers/isdn/icn/icn.h b/drivers/isdn/icn/icn.h index 991d57e4ddee..3bd2819cedb8 100644 --- a/drivers/isdn/icn/icn.h +++ b/drivers/isdn/icn/icn.h @@ -1,4 +1,4 @@ -/* $Id: icn.h,v 1.26 1997/02/14 12:23:16 fritz Exp $ +/* $Id: icn.h,v 1.28 1997/10/10 15:56:18 fritz Exp $ * ISDN lowlevel-module for the ICN active ISDN-Card. * @@ -19,6 +19,16 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: icn.h,v $ + * Revision 1.28 1997/10/10 15:56:18 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * + * Revision 1.27 1997/10/01 09:21:56 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * * Revision 1.26 1997/02/14 12:23:16 fritz * Added support for new insmod parameter handling. * @@ -265,6 +275,9 @@ typedef struct icn_card { 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 */ + int xlen[ICN_BCH]; /* Byte-counters/Flags for sent-ACK */ + struct sk_buff *xskb[ICN_BCH]; + /* Current transmitted skb */ struct sk_buff_head spqueue[ICN_BCH]; /* Sendqueue */ char regname[35]; /* Name used for request_region */ @@ -303,7 +316,6 @@ 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"); @@ -314,7 +326,6 @@ 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__ */ diff --git a/drivers/isdn/isdn_audio.c b/drivers/isdn/isdn_audio.c index 84da942941c5..d097366ed5d9 100644 --- a/drivers/isdn/isdn_audio.c +++ b/drivers/isdn/isdn_audio.c @@ -1,4 +1,4 @@ -/* $Id: isdn_audio.c,v 1.8 1997/03/02 14:29:16 fritz Exp $ +/* $Id: isdn_audio.c,v 1.10 1998/02/20 17:09:40 fritz Exp $ * Linux ISDN subsystem, audio conversion and compression (linklevel). * @@ -20,6 +20,14 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_audio.c,v $ + * Revision 1.10 1998/02/20 17:09:40 fritz + * Changes for recent kernels. + * + * Revision 1.9 1997/10/01 09:20:25 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * * Revision 1.8 1997/03/02 14:29:16 fritz * More ttyI related cleanup. * @@ -53,7 +61,7 @@ #include "isdn_audio.h" #include "isdn_common.h" -char *isdn_audio_revision = "$Revision: 1.8 $"; +char *isdn_audio_revision = "$Revision: 1.10 $"; /* * Misc. lookup-tables. @@ -531,7 +539,6 @@ isdn_audio_goertzel(int *sample, modem_info * info) 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; diff --git a/drivers/isdn/isdn_cards.c b/drivers/isdn/isdn_cards.c index c7f4e8b08c89..30a4a77033ce 100644 --- a/drivers/isdn/isdn_cards.c +++ b/drivers/isdn/isdn_cards.c @@ -1,4 +1,4 @@ -/* $Id: isdn_cards.c,v 1.6 1997/04/23 18:56:03 fritz Exp $ +/* $Id: isdn_cards.c,v 1.7 1998/02/20 17:24:28 fritz Exp $ * Linux ISDN subsystem, initialization for non-modularized drivers. * @@ -19,6 +19,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_cards.c,v $ + * Revision 1.7 1998/02/20 17:24:28 fritz + * Added ACT2000 init. + * * Revision 1.6 1997/04/23 18:56:03 fritz * Old Teles driver removed, Changed doc and scripts accordingly. * @@ -82,4 +85,7 @@ isdn_cards_init(void) capi_init(); capidrv_init(); #endif +#if CONFIG_ISDN_DRV_ACT2000 + act2000_init(); +#endif } diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c index c97c7781c114..3f4953f06a72 100644 --- a/drivers/isdn/isdn_common.c +++ b/drivers/isdn/isdn_common.c @@ -1,4 +1,4 @@ -/* $Id: isdn_common.c,v 1.44 1997/05/27 15:17:23 fritz Exp $ +/* $Id: isdn_common.c,v 1.55 1998/02/23 23:35:32 fritz Exp $ * Linux ISDN subsystem, common used functions (linklevel). * @@ -21,6 +21,55 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.c,v $ + * Revision 1.55 1998/02/23 23:35:32 fritz + * Eliminated some compiler warnings. + * + * Revision 1.54 1998/02/22 19:44:19 fritz + * Bugfixes and improvements regarding V.110, V.110 now running. + * + * Revision 1.53 1998/02/20 17:18:05 fritz + * Changes for recent kernels. + * Added common stub for sending commands to lowlevel. + * Added V.110. + * + * Revision 1.52 1998/01/31 22:05:57 keil + * Lots of changes for X.25 support: + * Added generic support for connection-controlling encapsulation protocols + * Added support of BHUP status message + * Added support for additional p_encap X25IFACE + * Added support for kernels >= 2.1.72 + * + * Revision 1.51 1998/01/31 19:17:29 calle + * merged changes from and for 2.1.82 + * + * Revision 1.50 1997/12/12 06:12:11 calle + * moved EXPORT_SYMBOL(register_isdn) from isdn_syms.c to isdn_common.c + * + * Revision 1.49 1997/11/06 17:16:52 keil + * Sync to 2.1.62 changes + * + * Revision 1.48 1997/11/02 23:55:50 keil + * Andi Kleen's changes for 2.1.60 + * without it the isdninfo and isdnctrl devices won't work + * + * Revision 1.47 1997/10/09 21:28:46 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * New L1 error status (not yet in use). + * Cleaned up obsolete structures. + * Implemented Cisco-SLARP. + * Changed local net-interface data to be dynamically allocated. + * Removed old 2.0 compatibility stuff. + * + * Revision 1.46 1997/10/01 09:20:27 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.45 1997/08/21 23:11:41 fritz + * Added changes for kernels >= 2.1.45 + * * Revision 1.44 1997/05/27 15:17:23 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -197,12 +246,9 @@ */ #include -#define __NO_VERSION__ #include #include -#if (LINUX_VERSION_CODE >= 0x020117) #include -#endif #include #include "isdn_common.h" #include "isdn_tty.h" @@ -211,6 +257,7 @@ #ifdef CONFIG_ISDN_AUDIO #include "isdn_audio.h" #endif +#include "isdn_v110.h" #include "isdn_cards.h" /* Debugflags */ @@ -218,7 +265,7 @@ isdn_dev *dev = (isdn_dev *) 0; -static char *isdn_revision = "$Revision: 1.44 $"; +static char *isdn_revision = "$Revision: 1.55 $"; extern char *isdn_net_revision; extern char *isdn_tty_revision; @@ -232,6 +279,7 @@ extern char *isdn_audio_revision; #else static char *isdn_audio_revision = ": none $"; #endif +extern char *isdn_v110_revision; static int isdn_writebuf_stub(int, int, const u_char *, int, int); @@ -260,13 +308,6 @@ isdn_dumppkt(char *s, u_char * p, int len, int dumplen) } #endif -static __inline void -isdn_trash_skb(struct sk_buff *skb) -{ - SET_SKB_FREE(skb); - kfree_skb(skb); -} - static void isdn_free_queue(struct sk_buff_head *queue) { @@ -277,7 +318,7 @@ isdn_free_queue(struct sk_buff_head *queue) cli(); if (skb_queue_len(queue)) while ((skb = skb_dequeue(queue))) - isdn_trash_skb(skb); + dev_kfree_skb(skb); restore_flags(flags); } @@ -294,6 +335,7 @@ 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 int isdn_timer_cnt4 = 0; static void isdn_timer_funct(ulong dummy) @@ -323,6 +365,11 @@ isdn_timer_funct(ulong dummy) if (tf & ISDN_TIMER_MODEMRING) isdn_tty_modem_ring(); } + if (++isdn_timer_cnt4 > ISDN_TIMER_KEEPINT) { + isdn_timer_cnt4 = 0; + if (tf & ISDN_TIMER_KEEPALIVE) + isdn_net_slarp_out(); + } #if (defined CONFIG_ISDN_PPP) && (defined CONFIG_ISDN_MPP) if (tf & ISDN_TIMER_IPPP) isdn_ppp_timer_timeout(); @@ -374,22 +421,71 @@ isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb) int i; if ((i = isdn_dc2minor(di, channel)) == -1) { - isdn_trash_skb(skb); + dev_kfree_skb(skb); return; } /* Update statistics */ dev->ibytes[i] += skb->len; + /* First, try to deliver data to network-device */ if (isdn_net_rcv_skb(i, skb)) return; + + /* V.110 handling + * makes sense for async streams only, so it is + * called after possible net-device delivery. + */ + if (dev->v110[i]) { + atomic_inc(&dev->v110use[i]); + skb = isdn_v110_decode(dev->v110[i], skb); + atomic_dec(&dev->v110use[i]); + if (!skb) + return; + } + /* No network-device found, deliver to tty or raw-channel */ - SET_SKB_FREE(skb); if (skb->len) { if (isdn_tty_rcv_skb(i, di, channel, skb)) return; wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]); } else - isdn_trash_skb(skb); + dev_kfree_skb(skb); +} + +/* + * Intercept command from Linklevel to Lowlevel. + * If layer 2 protocol is V.110 and this is not supported by current + * lowlevel-driver, use driver's transparent mode and handle V.110 in + * linklevel instead. + */ +int +isdn_command(isdn_ctrl *cmd) +{ + if (cmd->command == ISDN_CMD_SETL2) { + int idx = isdn_dc2minor(cmd->driver, cmd->arg & 255); + unsigned long l2prot = (cmd->arg >> 8) & 255; + unsigned long features = (dev->drv[cmd->driver]->interface->features + >> ISDN_FEATURE_L2_SHIFT) & + ISDN_FEATURE_L2_MASK; + unsigned long l2_feature = (1 << l2prot); + + switch (l2prot) { + case ISDN_PROTO_L2_V11096: + case ISDN_PROTO_L2_V11019: + case ISDN_PROTO_L2_V11038: + /* If V.110 requested, but not supported by + * HL-driver, set emulator-flag and change + * Layer-2 to transparent + */ + if (!(features & l2_feature)) { + dev->v110emu[idx] = l2prot; + cmd->arg = (cmd->arg & 255) | + (ISDN_PROTO_L2_TRANS << 8); + } else + dev->v110emu[idx] = 0; + } + } + return dev->drv[cmd->driver]->interface->command(cmd); } void @@ -403,7 +499,7 @@ isdn_all_eaz(int di, int ch) cmd.arg = ch; cmd.command = ISDN_CMD_SETEAZ; cmd.parm.num[0] = '\0'; - (void) dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); } static int @@ -424,7 +520,9 @@ isdn_status_callback(isdn_ctrl * c) return -1; if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; - if (isdn_net_stat_callback(i, c->command)) + if (isdn_net_stat_callback(i, c)) + return 0; + if (isdn_v110_stat_callback(i, c)) return 0; if (isdn_tty_stat_callback(i, c)) return 0; @@ -456,14 +554,14 @@ isdn_status_callback(isdn_ctrl * c) cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_HANGUP; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); return 0; } /* Try to find a network-interface which will accept incoming call */ cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_LOCK; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); r = isdn_net_find_icall(di, c->arg, i, c->parm.setup); switch (r) { case 0: @@ -476,7 +574,7 @@ isdn_status_callback(isdn_ctrl * c) cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_HANGUP; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); retval = 2; } break; @@ -486,7 +584,7 @@ isdn_status_callback(isdn_ctrl * c) cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_ACCEPTD; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); retval = 1; break; case 2: /* For calling back, first reject incoming call ... */ @@ -496,7 +594,7 @@ isdn_status_callback(isdn_ctrl * c) cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_HANGUP; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); if (r == 3) break; /* Fall through */ @@ -509,7 +607,7 @@ isdn_status_callback(isdn_ctrl * c) cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_UNLOCK; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); } return retval; break; @@ -522,7 +620,8 @@ isdn_status_callback(isdn_ctrl * c) if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; if (strcmp(c->parm.num, "0")) - isdn_net_stat_callback(i, c->command); + isdn_net_stat_callback(i, c); + isdn_tty_stat_callback(i, c); break; case ISDN_STAT_CAUSE: #ifdef ISDN_DEBUG_STATCALLB @@ -541,14 +640,15 @@ isdn_status_callback(isdn_ctrl * c) if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; /* Find any net-device, waiting for D-channel setup */ - if (isdn_net_stat_callback(i, c->command)) + if (isdn_net_stat_callback(i, c)) break; + isdn_v110_stat_callback(i, c); /* Find any ttyI, waiting for D-channel setup */ if (isdn_tty_stat_callback(i, c)) { cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_ACCEPTB; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); break; } break; @@ -563,8 +663,9 @@ isdn_status_callback(isdn_ctrl * c) dev->drv[di]->flags &= ~(1 << (c->arg)); isdn_info_update(); /* Signal hangup to network-devices */ - if (isdn_net_stat_callback(i, c->command)) + if (isdn_net_stat_callback(i, c)) break; + isdn_v110_stat_callback(i, c); if (isdn_tty_stat_callback(i, c)) break; break; @@ -579,8 +680,9 @@ isdn_status_callback(isdn_ctrl * c) return 0; dev->drv[di]->flags |= (1 << (c->arg)); isdn_info_update(); - if (isdn_net_stat_callback(i, c->command)) + if (isdn_net_stat_callback(i, c)) break; + isdn_v110_stat_callback(i, c); if (isdn_tty_stat_callback(i, c)) break; break; @@ -594,6 +696,12 @@ isdn_status_callback(isdn_ctrl * c) return 0; dev->drv[di]->flags &= ~(1 << (c->arg)); isdn_info_update(); +#ifdef CONFIG_ISDN_X25 + /* Signal hangup to network-devices */ + if (isdn_net_stat_callback(i, c)) + break; +#endif + isdn_v110_stat_callback(i, c); if (isdn_tty_stat_callback(i, c)) break; break; @@ -605,7 +713,7 @@ isdn_status_callback(isdn_ctrl * c) #endif if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; - if (isdn_net_stat_callback(i, c->command)) + if (isdn_net_stat_callback(i, c)) break; if (isdn_tty_stat_callback(i, c)) break; @@ -636,6 +744,8 @@ isdn_status_callback(isdn_ctrl * c) isdn_info_update(); restore_flags(flags); return 0; + case ISDN_STAT_L1ERR: + break; default: return -1; } @@ -752,7 +862,7 @@ isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, int user ISDN_AUDIO_SKB_LOCK(skb) = 0; #endif skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]); - isdn_trash_skb(skb); + dev_kfree_skb(skb); } else { /* Not yet emptied this buff, so it * must stay in the queue, for further calls @@ -848,8 +958,8 @@ isdn_info_update(void) wake_up_interruptible(&(dev->info_waitq)); } -static RWTYPE -isdn_read(struct file *file, char *buf, RWARG count, loff_t *off) +static ssize_t +isdn_read(struct file *file, char *buf, size_t count, loff_t * off) { uint minor = MINOR(file->f_dentry->d_inode->i_rdev); int len = 0; @@ -857,6 +967,9 @@ isdn_read(struct file *file, char *buf, RWARG count, loff_t *off) int drvidx; int chidx; + if (off != &file->f_pos) + return -ESPIPE; + if (minor == ISDN_MINOR_STATUS) { char *p; if (!file->private_data) { @@ -922,18 +1035,22 @@ isdn_read(struct file *file, char *buf, RWARG count, loff_t *off) return -ENODEV; } -static LSTYPE isdn_lseek(struct file *file, LSARG offset, int orig) +static loff_t +isdn_lseek(struct file *file, loff_t offset, int orig) { return -ESPIPE; } -static RWTYPE -isdn_write(struct file *file, const char *buf, RWARG count, loff_t * off) +static ssize_t +isdn_write(struct file *file, const char *buf, size_t count, loff_t * off) { uint minor = MINOR(file->f_dentry->d_inode->i_rdev); int drvidx; int chidx; + if (off != &file->f_pos) + return -ESPIPE; + if (minor == ISDN_MINOR_STATUS) return -EPERM; if (!dev->drivers) @@ -972,41 +1089,6 @@ isdn_write(struct file *file, const char *buf, RWARG count, loff_t * off) return -ENODEV; } -#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); - - if (minor == ISDN_MINOR_STATUS) { - if (file->private_data) - return 1; - else { - if (st) - select_wait(&(dev->info_waitq), st); - return 0; - } - } - if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) { - 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; - } -#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) { @@ -1041,7 +1123,6 @@ isdn_poll(struct file *file, poll_table * wait) printk(KERN_ERR "isdn_common: isdn_poll 2 -> what the hell\n"); return POLLERR; } -#endif static int isdn_set_allcfg(char *src) @@ -1055,7 +1136,7 @@ isdn_set_allcfg(char *src) if ((ret = isdn_net_rmall())) return ret; if ((ret = copy_from_user((char *) &i, src, sizeof(int)))) - return ret; + return ret; save_flags(flags); cli(); src += sizeof(int); @@ -1082,7 +1163,7 @@ isdn_set_allcfg(char *src) restore_flags(flags); return ret; } - GET_USER(phone.phone[phone_len], src++); + get_user(phone.phone[phone_len], src++); if ((phone.phone[phone_len] == ' ') || (phone.phone[phone_len] == '\0')) { if (phone_len) { @@ -1122,28 +1203,30 @@ isdn_get_allcfg(char *dest) cli(); p = dev->netdev; while (p) { + isdn_net_local *lp = p->local; + if ((ret = verify_area(VERIFY_WRITE, (void *) dest, sizeof(cfg) + 200))) { restore_flags(flags); return ret; } - strcpy(cfg.eaz, p->local.msn); - cfg.exclusive = p->local.exclusive; - if (p->local.pre_device >= 0) { - sprintf(cfg.drvid, "%s,%d", dev->drvid[p->local.pre_device], - p->local.pre_channel); + strcpy(cfg.eaz, lp->msn); + cfg.exclusive = lp->exclusive; + if (lp->pre_device >= 0) { + sprintf(cfg.drvid, "%s,%d", dev->drvid[lp->pre_device], + lp->pre_channel); } else cfg.drvid[0] = '\0'; - cfg.onhtime = p->local.onhtime; - cfg.charge = p->local.charge; - cfg.l2_proto = p->local.l2_proto; - cfg.l3_proto = p->local.l3_proto; - cfg.p_encap = p->local.p_encap; - cfg.secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0; - cfg.callback = (p->local.flags & ISDN_NET_CALLBACK) ? 1 : 0; - cfg.chargehup = (p->local.hupflags & ISDN_CHARGEHUP) ? 1 : 0; - cfg.ihup = (p->local.hupflags & ISDN_INHUP) ? 1 : 0; - cfg.chargeint = p->local.chargeint; - if (copy_to_user(dest, p->local.name, 10)) { + cfg.onhtime = lp->onhtime; + cfg.charge = lp->charge; + cfg.l2_proto = lp->l2_proto; + cfg.l3_proto = lp->l3_proto; + cfg.p_encap = lp->p_encap; + cfg.secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0; + cfg.callback = (lp->flags & ISDN_NET_CALLBACK) ? 1 : 0; + cfg.chargehup = (lp->hupflags & ISDN_CHARGEHUP) ? 1 : 0; + cfg.ihup = (lp->hupflags & ISDN_INHUP) ? 1 : 0; + cfg.chargeint = lp->chargeint; + if (copy_to_user(dest, lp->name, 10)) { restore_flags(flags); return -EFAULT; } @@ -1153,14 +1236,14 @@ isdn_get_allcfg(char *dest) return -EFAULT; } dest += sizeof(cfg); - strcpy(phone.name, p->local.name); + strcpy(phone.name, lp->name); phone.outgoing = 0; if ((ret = isdn_net_getphones(&phone, dest)) < 0) { restore_flags(flags); return ret; } else dest += ret; - strcpy(phone.name, p->local.name); + strcpy(phone.name, lp->name); phone.outgoing = 1; if ((ret = isdn_net_getphones(&phone, dest)) < 0) { restore_flags(flags); @@ -1343,9 +1426,9 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) /* 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); + if ((ret = copy_from_user(name, (char *) arg, sizeof(name)))) + return ret; + return isdn_net_force_hangup(name); break; #endif /* CONFIG_NETDEVICES */ case IIOCSETVER: @@ -1366,7 +1449,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) int i; char *p; if ((ret = copy_from_user((char *) &iocts, (char *) arg, - sizeof(isdn_ioctl_struct)))) + sizeof(isdn_ioctl_struct)))) return ret; if (strlen(iocts.drvid)) { if ((p = strchr(iocts.drvid, ','))) @@ -1440,7 +1523,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) for (i = 0; i < ISDN_MAX_CHANNELS; i++) { if ((ret = copy_from_user(dev->mdm.info[i].emu.profile, p, - ISDN_MODEM_ANZREG))) + ISDN_MODEM_ANZREG))) return ret; p += ISDN_MODEM_ANZREG; if ((ret = copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN))) @@ -1482,7 +1565,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) while (1) { if ((ret = verify_area(VERIFY_READ, p, 1))) return ret; - GET_USER(bname[j], p++); + get_user(bname[j], p++); switch (bname[j]) { case '\0': loop = 0; @@ -1554,7 +1637,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) c.command = ISDN_CMD_IOCTL; c.arg = cmd; memcpy(c.parm.num, (char *) &iocts.arg, sizeof(ulong)); - ret = dev->drv[drvidx]->interface->command(&c); + ret = isdn_command(&c); memcpy((char *) &iocts.arg, c.parm.num, sizeof(ulong)); if ((copy_to_user((char *) arg, &iocts, sizeof(isdn_ioctl_struct)))) return -EFAULT; @@ -1616,7 +1699,7 @@ isdn_open(struct inode *ino, struct file *filep) return -ENODEV; c.command = ISDN_CMD_LOCK; c.driver = drvidx; - (void) dev->drv[drvidx]->interface->command(&c); + isdn_command(&c); MOD_INC_USE_COUNT; return 0; } @@ -1627,7 +1710,7 @@ isdn_open(struct inode *ino, struct file *filep) c.command = ISDN_CMD_LOCK; c.driver = drvidx; MOD_INC_USE_COUNT; - (void) dev->drv[drvidx]->interface->command(&c); + isdn_command(&c); return 0; } #ifdef CONFIG_ISDN_PPP @@ -1641,7 +1724,7 @@ isdn_open(struct inode *ino, struct file *filep) return -ENODEV; } -static CLOSETYPE +static int isdn_close(struct inode *ino, struct file *filep) { uint minor = MINOR(ino->i_rdev); @@ -1659,39 +1742,39 @@ isdn_close(struct inode *ino, struct file *filep) else dev->infochain = (infostruct *) (p->next); kfree(p); - return CLOSEVAL; + return 0; } q = p; p = (infostruct *) (p->next); } printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n"); - return CLOSEVAL; + return 0; } if (minor < ISDN_MINOR_CTRL) { drvidx = isdn_minor2drv(minor); if (drvidx < 0) - return CLOSEVAL; + return 0; c.command = ISDN_CMD_UNLOCK; c.driver = drvidx; - (void) dev->drv[drvidx]->interface->command(&c); - return CLOSEVAL; + isdn_command(&c); + return 0; } if (minor <= ISDN_MINOR_CTRLMAX) { drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); if (drvidx < 0) - return CLOSEVAL; + return 0; if (dev->profd == current) dev->profd = NULL; c.command = ISDN_CMD_UNLOCK; c.driver = drvidx; - (void) dev->drv[drvidx]->interface->command(&c); - return CLOSEVAL; + isdn_command(&c); + return 0; } #ifdef CONFIG_ISDN_PPP if (minor <= ISDN_MINOR_PPPMAX) isdn_ppp_release(minor - ISDN_MINOR_PPP, filep); #endif - return CLOSEVAL; + return 0; } static struct file_operations isdn_fops = @@ -1700,11 +1783,7 @@ static struct file_operations isdn_fops = isdn_read, isdn_write, 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, @@ -1731,6 +1810,8 @@ isdn_map_eaz2msn(char *msn, int di) * Find an unused ISDN-channel, whose feature-flags match the * given L2- and L3-protocols. */ +#define L2V (~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038)) + int isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev ,int pre_chan) @@ -1738,11 +1819,18 @@ isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev int i; ulong flags; ulong features; + ulong vfeatures; isdn_ctrl cmd; save_flags(flags); cli(); - features = (1 << l2_proto) | (0x100 << l3_proto); + features = ((1 << l2_proto) | (0x10000 << l3_proto)); + vfeatures = (((1 << l2_proto) | (0x10000 << l3_proto)) & + ~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038)); + /* If Layer-2 protocol is V.110, accept drivers with + * transparent feature even if these don't support V.110 + * because we can emulate this in linklevel. + */ for (i = 0; i < ISDN_MAX_CHANNELS; i++) if (USG_NONE(dev->usage[i]) && (dev->drvmap[i] != -1)) { @@ -1751,7 +1839,9 @@ isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev ((pre_dev != d) || (pre_chan != dev->chanmap[i]))) continue; if ((dev->drv[d]->running)) { - if ((dev->drv[d]->interface->features & features) == features) { + if (((dev->drv[d]->interface->features & features) == features) || + (((dev->drv[d]->interface->features & vfeatures) == vfeatures) && + (dev->drv[d]->interface->features & ISDN_FEATURE_L2_TRANS))) { if ((pre_dev < 0) || (pre_chan < 0)) { dev->usage[i] &= ISDN_USAGE_EXCLUSIVE; dev->usage[i] |= usage; @@ -1759,7 +1849,7 @@ isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev cmd.driver = d; cmd.arg = 0; cmd.command = ISDN_CMD_LOCK; - (void) dev->drv[d]->interface->command(&cmd); + isdn_command(&cmd); restore_flags(flags); return i; } else { @@ -1770,7 +1860,7 @@ isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev cmd.driver = d; cmd.arg = 0; cmd.command = ISDN_CMD_LOCK; - (void) dev->drv[d]->interface->command(&cmd); + isdn_command(&cmd); restore_flags(flags); return i; } @@ -1808,7 +1898,7 @@ isdn_free_channel(int di, int ch, int usage) cmd.arg = ch; cmd.command = ISDN_CMD_UNLOCK; restore_flags(flags); - (void) dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); return; } restore_flags(flags); @@ -1836,31 +1926,6 @@ isdn_unexclusive_channel(int di, int ch) restore_flags(flags); } -/* - * receive callback handler for drivers not supporting sk_buff's. - * Parameters: - * - * di = Driver-Index. - * channel = Number of B-Channel (0...) - * buf = pointer to packet-data - * len = Length of packet-data - * - */ -static void -isdn_receive_callback(int drvidx, int chan, u_char * buf, int len) -{ - 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"); -} - /* * writebuf replacement for SKB_ABLE drivers */ @@ -1869,60 +1934,69 @@ isdn_writebuf_stub(int drvidx, int chan, const u_char * buf, int len, int user) { int ret; + int hl = dev->drv[drvidx]->interface->hl_hdrlen; + struct sk_buff *skb = alloc_skb(hl + len, GFP_ATOMIC); - 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); + if (!skb) + return 0; + skb_reserve(skb, hl); + 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); - } + ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, 1, skb); + if (ret <= 0) + dev_kfree_skb(skb); if (ret > 0) dev->obytes[isdn_dc2minor(drvidx, chan)] += ret; return ret; } /* - * writebuf_skb replacement for NON SKB_ABLE drivers - * If lowlevel-device does not support supports skbufs, use - * standard send-routine, else sind directly. - * * Return: length of data on success, -ERRcode on failure. */ - int -isdn_writebuf_skb_stub(int drvidx, int chan, struct sk_buff *skb) +isdn_writebuf_skb_stub(int drvidx, int chan, int ack, struct sk_buff *skb) { int ret; - int len = skb->len; /* skb pointer no longer valid after free */ - - if (dev->drv[drvidx]->interface->writebuf_skb) - ret = dev->drv[drvidx]->interface-> - writebuf_skb(drvidx, chan, skb); - else { - if ((ret = dev->drv[drvidx]->interface-> - writebuf(drvidx, chan, skb->data, skb->len, 0)) == len) + struct sk_buff *nskb = NULL; + int v110_ret = skb->len; + int idx = isdn_dc2minor(drvidx, chan); + + if (dev->v110[idx]) { + atomic_inc(&dev->v110use[idx]); + nskb = isdn_v110_encode(dev->v110[idx], skb); + atomic_dec(&dev->v110use[idx]); + if (!nskb) + return 0; + v110_ret = *((int *)nskb->data); + skb_pull(nskb, sizeof(int)); + if (!nskb->len) { + dev_kfree_skb(nskb); dev_kfree_skb(skb); - } - if (ret > 0) - dev->obytes[isdn_dc2minor(drvidx, chan)] += len; + return v110_ret; + } + /* V.110 must always be acknowledged */ + ack = 1; + ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, nskb); + } else + ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb); + if (ret > 0) { + dev->obytes[idx] += ret; + if (dev->v110[idx]) { + atomic_inc(&dev->v110use[idx]); + dev->v110[idx]->skbuser++; + atomic_dec(&dev->v110use[idx]); + dev_kfree_skb(skb); + /* For V.110 return unencoded data length */ + ret = v110_ret; + if (ret == skb->len) + dev_kfree_skb(skb); + } + } else + if (dev->v110[idx]) + dev_kfree_skb(nskb); return ret; } @@ -1930,6 +2004,8 @@ isdn_writebuf_skb_stub(int drvidx, int chan, struct sk_buff *skb) * Low-level-driver registration */ +EXPORT_SYMBOL(register_isdn); + int register_isdn(isdn_if * i) { @@ -1951,7 +2027,7 @@ register_isdn(isdn_if * i) ISDN_MAX_CHANNELS); return 0; } - if ((!i->writebuf_skb) && (!i->writebuf)) { + if (!i->writebuf_skb) { printk(KERN_WARNING "register_isdn: No write routine given.\n"); return 0; } @@ -2019,7 +2095,6 @@ register_isdn(isdn_if * i) i->channels = drvidx; i->rcvcallb_skb = isdn_receive_skb_callback; - i->rcvcallb = isdn_receive_callback; i->statcallb = isdn_status_callback; if (!strlen(i->id)) sprintf(i->id, "line%d", drvidx); @@ -2078,11 +2153,7 @@ int isdn_init(void) { int i; - char irev[50]; - char trev[50]; - char nrev[50]; - char prev[50]; - char arev[50]; + char tmprev[50]; sti(); if (!(dev = (isdn_dev *) kmalloc(sizeof(isdn_dev), GFP_KERNEL))) { @@ -2126,18 +2197,18 @@ isdn_init(void) } #endif /* CONFIG_ISDN_PPP */ - 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); - 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)); + strcpy(tmprev, isdn_revision); + printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(tmprev)); + strcpy(tmprev, isdn_tty_revision); + printk("%s/", isdn_getrev(tmprev)); + strcpy(tmprev, isdn_net_revision); + printk("%s/", isdn_getrev(tmprev)); + strcpy(tmprev, isdn_ppp_revision); + printk("%s/", isdn_getrev(tmprev)); + strcpy(tmprev, isdn_audio_revision); + printk("%s/", isdn_getrev(tmprev)); + strcpy(tmprev, isdn_v110_revision); + printk("%s", isdn_getrev(tmprev)); #ifdef MODULE printk(" loaded\n"); diff --git a/drivers/isdn/isdn_common.h b/drivers/isdn/isdn_common.h index d0df7fe7965a..b9d5df7153e9 100644 --- a/drivers/isdn/isdn_common.h +++ b/drivers/isdn/isdn_common.h @@ -1,4 +1,4 @@ -/* $Id: isdn_common.h,v 1.6 1997/02/28 02:32:44 fritz Exp $ +/* $Id: isdn_common.h,v 1.9 1998/02/20 17:19:01 fritz Exp $ * header for Linux ISDN subsystem, common used functions and debugging-switches (linklevel). * @@ -21,6 +21,24 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.h,v $ + * Revision 1.9 1998/02/20 17:19:01 fritz + * Added common stub for sending commands to lowlevel. + * + * Revision 1.8 1997/10/09 21:28:49 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * New L1 error status (not yet in use). + * Cleaned up obsolete structures. + * Implemented Cisco-SLARP. + * Changed local net-interface data to be dynamically allocated. + * Removed old 2.0 compatibility stuff. + * + * Revision 1.7 1997/10/01 09:20:30 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * * Revision 1.6 1997/02/28 02:32:44 fritz * Cleanup: Moved some tty related stuff from isdn_common.c * to isdn_tty.c @@ -61,6 +79,7 @@ extern void isdn_MOD_INC_USE_COUNT(void); extern void isdn_MOD_DEC_USE_COUNT(void); extern void isdn_free_channel(int di, int ch, int usage); extern void isdn_all_eaz(int di, int ch); +extern int isdn_command(isdn_ctrl *); extern int isdn_dc2minor(int di, int ch); extern void isdn_info_update(void); extern char *isdn_map_eaz2msn(char *msn, int di); @@ -69,13 +88,8 @@ 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 isdn_writebuf_skb_stub(int, 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); #endif diff --git a/drivers/isdn/isdn_concap.c b/drivers/isdn/isdn_concap.c new file mode 100644 index 000000000000..0d9abb22635b --- /dev/null +++ b/drivers/isdn/isdn_concap.c @@ -0,0 +1,108 @@ +/* $Id: isdn_concap.c,v 1.2 1998/01/31 22:49:21 keil Exp $ + + * Stuff to support the concap_proto by isdn4linux. isdn4linux - specific + * stuff goes here. Stuff that depends only on the concap protocol goes to + * another -- protocol specific -- source file. + * + * $Log: isdn_concap.c,v $ + * Revision 1.2 1998/01/31 22:49:21 keil + * correct comments + * + * Revision 1.1 1998/01/31 22:27:57 keil + * New files from Henner Eisen for X.25 support + * + */ + + +#include +#include "isdn_x25iface.h" +#include "isdn_net.h" +#include +#include "isdn_concap.h" + +/* The declaration of this (or a plublic variant thereof) should really go + in linux/isdn.h. But we really need it here (and isdn_ppp, like us, also + refers to that private function currently owned by isdn_net.c) */ +extern int isdn_net_force_dial_lp(isdn_net_local *); + + +/* The following set of device service operations are for encapsulation + protocols that require for reliable datalink sematics. That means: + + - before any data is to be submitted the connection must explicitly + be set up. + - after the successful set up of the connection is signalled the + connection is considered to be reliably up. + + Auto-dialing ist not compatible with this requirements. Thus, auto-dialing + is completely bypassed. + + It might be possible to implement a (non standardized) datalink protocol + that provides a reliable data link service while using some auto dialing + mechanism. Such a protocol would need an auxiliary channel (i.e. user-user- + signaling on the D-channel) while the B-channel is down. + */ + + +int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb) +{ + int tmp; + struct device *ndev = concap -> net_dev; + isdn_net_local *lp = (isdn_net_local *) ndev->priv; + + IX25DEBUG( "isdn_concap_dl_data_req: %s \n", concap->net_dev->name); + lp->huptimer = 0; + tmp=isdn_net_send_skb(ndev, lp, skb); + IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, tmp); + return tmp; +} + + +int isdn_concap_dl_connect_req(struct concap_proto *concap) +{ + struct device *ndev = concap -> net_dev; + isdn_net_local *lp = (isdn_net_local *) ndev->priv; + int ret; + IX25DEBUG( "isdn_concap_dl_connect_req: %s \n", ndev -> name); + + /* dial ... */ + ret = isdn_net_force_dial_lp( lp ); + if ( ret ) IX25DEBUG("dialing failed\n"); + return 0; +} + +int isdn_concap_dl_disconn_req(struct concap_proto *concap) +{ + IX25DEBUG( "isdn_concap_dl_disconn_req: %s \n", concap -> net_dev -> name); + + isdn_net_hangup( concap -> net_dev ); + return 0; +} + +struct concap_device_ops isdn_concap_reliable_dl_dops = { + &isdn_concap_dl_data_req, + &isdn_concap_dl_connect_req, + &isdn_concap_dl_disconn_req +}; + +struct concap_device_ops isdn_concap_demand_dial_dops = { + NULL, /* set this first entry to something like &isdn_net_start_xmit, + but the entry part of the current isdn_net_start_xmit must be + separated first. */ + /* no connection control for demand dial semantics */ + NULL, + NULL, +}; + +/* The following should better go into a dedicated source file such that + this sourcefile does not need to include any protocol specific header + files. For now: + */ +struct concap_proto * isdn_concap_new( int encap ) +{ + switch ( encap ) { + case ISDN_NET_ENCAP_X25IFACE: + return isdn_x25iface_proto_new(); + } + return NULL; +} diff --git a/drivers/isdn/isdn_concap.h b/drivers/isdn/isdn_concap.h new file mode 100644 index 000000000000..722f8ee33324 --- /dev/null +++ b/drivers/isdn/isdn_concap.h @@ -0,0 +1,7 @@ +/* $Id: isdn_concap.h,v 1.2 1998/01/31 22:49:21 keil Exp $ + */ +extern struct concap_device_ops isdn_concap_reliable_dl_dops; +extern struct concap_device_ops isdn_concap_demand_dial_dops; +extern struct concap_proto * isdn_concap_new( int ); + + diff --git a/drivers/isdn/isdn_net.c b/drivers/isdn/isdn_net.c index c44fcc74a754..46b460370803 100644 --- a/drivers/isdn/isdn_net.c +++ b/drivers/isdn/isdn_net.c @@ -1,4 +1,4 @@ -/* $Id: isdn_net.c,v 1.44 1997/05/27 15:17:26 fritz Exp $ +/* $Id: isdn_net.c,v 1.55 1998/02/23 19:38:22 fritz Exp $ * Linux ISDN subsystem, network interfaces and related functions (linklevel). * @@ -21,6 +21,54 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_net.c,v $ + * Revision 1.55 1998/02/23 19:38:22 fritz + * Corrected check for modified feature-flags. + * + * Revision 1.54 1998/02/20 17:15:07 fritz + * Changes for recent kernels. + * Ugly workaround for adjusting Ethernet frames with recent kernels. + * replaced direct calls to lowlevel-driver command by common hook. + * + * Revision 1.53 1998/01/31 22:05:54 keil + * Lots of changes for X.25 support: + * Added generic support for connection-controlling encapsulation protocols + * Added support of BHUP status message + * Added support for additional p_encap X25IFACE + * Added support for kernels >= 2.1.72 + * + * Revision 1.52 1998/01/31 19:29:51 calle + * Merged changes from and for 2.1.82, not tested only compiled ... + * + * Revision 1.51 1997/10/09 21:28:50 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * New L1 error status (not yet in use). + * Cleaned up obsolete structures. + * Implemented Cisco-SLARP. + * Changed local net-interface data to be dynamically allocated. + * Removed old 2.0 compatibility stuff. + * + * Revision 1.50 1997/10/01 09:20:32 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.49 1997/08/21 14:38:13 fritz + * Bugfix: Did not compile without SyncPPP. + * + * Revision 1.48 1997/06/22 11:57:15 fritz + * Added ability to adjust slave triggerlevel. + * + * Revision 1.47 1997/06/21 10:52:05 fritz + * Removed wrong SET_SKB_FREE in isdn_net_send_skb() + * + * 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 @@ -196,16 +244,18 @@ #include #include #include -#if (LINUX_VERSION_CODE >= 0x020117) -#include +#ifndef DEV_NUMBUFFS +#include #endif +#include #include "isdn_common.h" #include "isdn_net.h" #ifdef CONFIG_ISDN_PPP #include "isdn_ppp.h" #endif -#ifndef DEV_NUMBUFFS -#include +#ifdef CONFIG_ISDN_X25 +#include +#include "isdn_concap.h" #endif /* Prototypes */ @@ -218,7 +268,7 @@ static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *); static void dev_purge_queues(struct device *dev); /* move this to net/core/dev.c */ #endif -char *isdn_net_revision = "$Revision: 1.44 $"; +char *isdn_net_revision = "$Revision: 1.55 $"; /* * Code for raw-networking over ISDN @@ -229,22 +279,28 @@ 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 - ); + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0); } static void isdn_net_reset(struct device *dev) { +#ifdef CONFIG_ISDN_X25 + struct concap_device_ops * dops = + ( (isdn_net_local *) dev->priv ) -> dops; + struct concap_proto * cprot = + ( (isdn_net_local *) dev->priv ) -> netdev -> cprot; +#endif ulong flags; save_flags(flags); cli(); /* Avoid glitch on writes to CMD regs */ dev->interrupt = 0; dev->tbusy = 0; +#ifdef CONFIG_ISDN_X25 + if( cprot && cprot -> pops && dops ) + cprot -> pops -> restart ( cprot, dev, dops ); +#endif restore_flags(flags); } @@ -254,14 +310,22 @@ isdn_net_open(struct device *dev) { int i; struct device *p; + struct in_device *in_dev; isdn_net_reset(dev); dev->start = 1; - /* Fill in the MAC-level header. */ + /* Fill in the MAC-level header (not needed, but for compatibility... */ for (i = 0; i < ETH_ALEN - sizeof(u32); i++) dev->dev_addr[i] = 0xfc; - memset(&(dev->dev_addr[i]), 0, sizeof(u32)); - + if ((in_dev = dev->ip_ptr) != NULL) { + /* + * Any address will do - we take the first + */ + struct in_ifaddr *ifa = in_dev->ifa_list; + if (ifa != NULL) + memcpy(dev->dev_addr+2, &ifa->ifa_local, 4); + } + /* If this interface has slaves, start them also */ if ((p = (((isdn_net_local *) dev->priv)->slave))) { @@ -356,7 +420,7 @@ isdn_net_autohup() anymore = 0; while (p) { - isdn_net_local *l = (isdn_net_local *) & (p->local); + isdn_net_local *l = p->local; if ((jiffies - last_jiffies) == 0) l->cps = l->transcount; else @@ -405,18 +469,24 @@ isdn_net_autohup() * Return: 1 = Event handled, 0 = not for us or unknown Event. */ int -isdn_net_stat_callback(int idx, int cmd) +isdn_net_stat_callback(int idx, isdn_ctrl *c) { isdn_net_dev *p = dev->st_netdev[idx]; - + int cmd = c->command; + if (p) { - isdn_net_local *lp = &(p->local); + isdn_net_local *lp = p->local; +#ifdef CONFIG_ISDN_X25 + struct concap_proto *cprot = lp -> netdev -> cprot; + struct concap_proto_ops *pops = cprot ? cprot -> pops : 0; +#endif 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++; + lp->stats.tx_bytes += c->parm.length; if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && lp->sav_skb) { struct device *mdev; if (lp->master) @@ -449,6 +519,16 @@ isdn_net_stat_callback(int idx, int cmd) break; case ISDN_STAT_DHUP: /* Either D-Channel-hangup or error during dialout */ +#ifdef CONFIG_ISDN_X25 + /* If we are not connencted then dialing had + failed. If there are generic encap protocol + receiver routines signal the closure of + the link*/ + + if( !(lp->flags & ISDN_NET_CONNECTED) + && pops && pops -> disconn_ind ) + pops -> disconn_ind(cprot); +#endif /* CONFIG_ISDN_X25 */ if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) { lp->flags &= ~ISDN_NET_CONNECTED; if (lp->first_skb) { @@ -475,6 +555,18 @@ isdn_net_stat_callback(int idx, int cmd) return 1; } break; +#ifdef CONFIG_ISDN_X25 + case ISDN_STAT_BHUP: + /* B-Channel-hangup */ + /* try if there are generic encap protocol + receiver routines and signal the closure of + the link */ + if( pops && pops -> disconn_ind ){ + pops -> disconn_ind(cprot); + return 1; + } + break; +#endif /* CONFIG_ISDN_X25 */ case ISDN_STAT_BCONN: /* B-Channel is up */ switch (lp->dialstate) { @@ -492,6 +584,8 @@ isdn_net_stat_callback(int idx, int cmd) dev->rx_netdev[idx] = p; lp->dialstate = 0; isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 1); + if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK) + isdn_timer_ctrl(ISDN_TIMER_KEEPALIVE, 1); printk(KERN_INFO "isdn_net: %s connected\n", lp->name); /* If first Chargeinfo comes before B-Channel connect, * we correct the timestamp here. @@ -504,7 +598,15 @@ isdn_net_stat_callback(int idx, int cmd) if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) isdn_ppp_wakeup_daemon(lp); #endif +#ifdef CONFIG_ISDN_X25 + /* try if there are generic concap receiver routines */ + if( pops ) + if( pops->connect_ind) + pops->connect_ind(cprot); + +#endif /* CONFIG_ISDN_X25 */ if (lp->first_skb) { + if (!(isdn_net_xmit(&p->dev, lp, lp->first_skb))) lp->first_skb = NULL; } @@ -573,11 +675,13 @@ isdn_net_dial(void) isdn_ctrl cmd; while (p) { + isdn_net_local *lp = p->local; + #ifdef ISDN_DEBUG_NET_DIAL - if (p->local.dialstate) - printk(KERN_DEBUG "%s: dialstate=%d\n", p->local.name, p->local.dialstate); + if (lp->dialstate) + printk(KERN_DEBUG "%s: dialstate=%d\n", lp->name, lp->dialstate); #endif - switch (p->local.dialstate) { + switch (lp->dialstate) { case 0: /* Nothing to do for this interface */ break; @@ -587,132 +691,133 @@ isdn_net_dial(void) */ save_flags(flags); cli(); - p->local.dial = p->local.phone[1]; + lp->dial = lp->phone[1]; restore_flags(flags); - if (!p->local.dial) { + if (!lp->dial) { printk(KERN_WARNING "%s: phone number deleted?\n", - p->local.name); + lp->name); isdn_net_hangup(&p->dev); break; } anymore = 1; - p->local.dialstate++; + lp->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.driver = lp->isdn_device; + cmd.arg = lp->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)); + isdn_command(&cmd); + sprintf(cmd.parm.num, "%s", isdn_map_eaz2msn(lp->msn, cmd.driver)); cmd.command = ISDN_CMD_SETEAZ; - dev->drv[p->local.isdn_device]->interface->command(&cmd); - p->local.dialretry = 0; + isdn_command(&cmd); + lp->dialretry = 0; anymore = 1; - p->local.dialstate++; + lp->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. */ - cmd.driver = p->local.isdn_device; + cmd.driver = lp->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.arg = lp->isdn_channel + (lp->l2_proto << 8); + isdn_command(&cmd); + cmd.driver = lp->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; + cmd.arg = lp->isdn_channel + (lp->l3_proto << 8); + isdn_command(&cmd); + cmd.driver = lp->isdn_device; + cmd.arg = lp->isdn_channel; save_flags(flags); cli(); - if (!p->local.dial) { + if (!lp->dial) { restore_flags(flags); printk(KERN_WARNING "%s: phone number deleted?\n", - p->local.name); + lp->name); isdn_net_hangup(&p->dev); break; } - if (!strcmp(p->local.dial->num, "LEASED")) { + if (!strcmp(lp->dial->num, "LEASED")) { restore_flags(flags); - p->local.dialstate = 4; - printk(KERN_INFO "%s: Open leased line ...\n", p->local.name); + lp->dialstate = 4; + printk(KERN_INFO "%s: Open leased line ...\n", lp->name); } else { - sprintf(cmd.parm.setup.phone, "%s", p->local.dial->num); + sprintf(cmd.parm.setup.phone, "%s", lp->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++; + if (!(lp->dial = (isdn_net_phone *) lp->dial->next)) { + lp->dial = lp->phone[1]; + lp->dialretry++; } restore_flags(flags); + cmd.driver = lp->isdn_device; 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); + isdn_map_eaz2msn(lp->msn, cmd.driver)); + i = isdn_dc2minor(lp->isdn_device, lp->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; + printk(KERN_INFO "%s: dialing %d %s...\n", lp->name, + lp->dialretry - 1, cmd.parm.setup.phone); + lp->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", lp->isdn_device, + lp->isdn_channel); #endif - dev->drv[p->local.isdn_device]->interface->command(&cmd); + isdn_command(&cmd); } - p->local.huptimer = 0; - p->local.outgoing = 1; - if (p->local.chargeint) { - p->local.hupflags |= ISDN_HAVECHARGE; - p->local.hupflags &= ~ISDN_WAITCHARGE; + lp->huptimer = 0; + lp->outgoing = 1; + if (lp->chargeint) { + lp->hupflags |= ISDN_HAVECHARGE; + lp->hupflags &= ~ISDN_WAITCHARGE; } else { - p->local.hupflags |= ISDN_WAITCHARGE; - p->local.hupflags &= ~ISDN_HAVECHARGE; + lp->hupflags |= ISDN_WAITCHARGE; + lp->hupflags &= ~ISDN_HAVECHARGE; } anymore = 1; - p->local.dialstate = - (p->local.cbdelay && - (p->local.flags & ISDN_NET_CBOUT)) ? 12 : 4; + lp->dialstate = + (lp->cbdelay && + (lp->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; + if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10) + if (lp->dialretry < lp->dialmax) { + lp->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.driver = lp->isdn_device; + cmd.arg = lp->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); + lp->dtimer = 0; + lp->dialstate++; + isdn_command(&cmd); 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", lp->dtimer); #endif - if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10) - p->local.dialstate = 3; + if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10) + lp->dialstate = 3; anymore = 1; break; case 7: @@ -720,69 +825,69 @@ isdn_net_dial(void) * 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", lp->dtimer); #endif - cmd.driver = p->local.isdn_device; + cmd.driver = lp->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.arg = lp->isdn_channel + (lp->l2_proto << 8); + isdn_command(&cmd); + cmd.driver = lp->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) + cmd.arg = lp->isdn_channel + (lp->l3_proto << 8); + isdn_command(&cmd); + if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT15) isdn_net_hangup(&p->dev); else { anymore = 1; - p->local.dialstate++; + lp->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.driver = lp->isdn_device; + cmd.arg = lp->isdn_channel; cmd.command = ISDN_CMD_ACCEPTB; - dev->drv[p->local.isdn_device]->interface->command(&cmd); + isdn_command(&cmd); anymore = 1; - p->local.dtimer = 0; - p->local.dialstate++; + lp->dtimer = 0; + lp->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", lp->dtimer); #endif - if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10) + if (lp->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; + if (lp->dtimer++ > lp->cbdelay) + lp->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; + if (lp->dtimer++ > lp->cbdelay) { + printk(KERN_INFO "%s: hangup waiting for callback ...\n", lp->name); + lp->dtimer = 0; + lp->dialstate = 4; + cmd.driver = lp->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); + cmd.arg = lp->isdn_channel; + isdn_command(&cmd); + isdn_all_eaz(lp->isdn_device, lp->isdn_channel); } anymore = 1; break; default: printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n", - p->local.dialstate, p->local.name); + lp->dialstate, lp->name); } p = (isdn_net_dev *) p->next; } @@ -797,6 +902,10 @@ isdn_net_hangup(struct device *d) { isdn_net_local *lp = (isdn_net_local *) d->priv; isdn_ctrl cmd; +#ifdef CONFIG_ISDN_X25 + struct concap_proto *cprot = lp -> netdev -> cprot; + struct concap_proto_ops *pops = cprot ? cprot -> pops : 0; +#endif if (lp->flags & ISDN_NET_CONNECTED) { lp->flags &= ~ISDN_NET_CONNECTED; @@ -804,10 +913,18 @@ isdn_net_hangup(struct device *d) #ifdef CONFIG_ISDN_PPP isdn_ppp_free(lp); #endif +#ifdef CONFIG_ISDN_X25 + /* try if there are generic encap protocol + receiver routines and signal the closure of + the link */ + if( pops && pops -> disconn_ind ) + pops -> disconn_ind(cprot); +#endif /* CONFIG_ISDN_X25 */ + cmd.driver = lp->isdn_device; cmd.command = ISDN_CMD_HANGUP; cmd.arg = lp->isdn_channel; - (void) dev->drv[cmd.driver]->interface->command(&cmd); + isdn_command(&cmd); printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, lp->charge); isdn_all_eaz(lp->isdn_device, lp->isdn_channel); } @@ -820,28 +937,43 @@ typedef struct { } ip_ports; static void -isdn_net_log_packet(u_char * buf, isdn_net_local * lp) +isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp) { - u_char *p = buf; - unsigned short proto = ETH_P_IP; + u_char *p = skb->nh.raw; /* hopefully, this was set correctly */ + unsigned short proto = ntohs(skb->protocol); 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; + /* This check stolen from 2.1.72 dev_queue_xmit_nit() */ + if (skb->nh.raw < skb->data || skb->nh.raw >= skb->tail) { + /* fall back to old isdn_net_log_packet method() */ + char * buf = skb->data; + + printk(KERN_DEBUG "isdn_net: protocol %04x is buggy, dev %s\n", skb->protocol, lp->name); + p = buf; + proto = ETH_P_IP; + 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; +#ifdef CONFIG_ISDN_PPP + case ISDN_NET_ENCAP_SYNCPPP: + proto = ntohs(skb->protocol); + p = &buf[IPPP_MAX_HEADER]; + break; +#endif + } } data_ofs = ((p[0] & 15) * 4); switch (proto) { @@ -891,7 +1023,7 @@ isdn_net_log_packet(u_char * buf, isdn_net_local * lp) /* * Generic routine to send out an skbuf. - * If lowlevel-device does not support supports skbufs, use + * If lowlevel-device does not support support skbufs, use * standard send-routine, else send directly. * * Return: 0 on success, !0 on failure. @@ -904,14 +1036,13 @@ isdn_net_send_skb(struct device *ndev, isdn_net_local * lp, int ret; int len = skb->len; /* save len */ - ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb); + ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb); if (ret == len) { lp->transcount += len; clear_bit(0, (void *) &(ndev->tbusy)); return 0; } if (ret < 0) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); lp->stats.tx_errors++; clear_bit(0, (void *) &(ndev->tbusy)); @@ -945,7 +1076,7 @@ isdn_net_xmit(struct device *ndev, isdn_net_local * lp, struct sk_buff *skb) #endif /* Reset hangup-timeout */ lp->huptimer = 0; - if (lp->cps > 7000) { + if (lp->cps > lp->triggercps) { /* Device overloaded */ /* @@ -988,35 +1119,64 @@ isdn_net_xmit(struct device *ndev, isdn_net_local * lp, struct sk_buff *skb) return ret; } +static void +isdn_net_adjust_hdr(struct sk_buff *skb, struct device *dev) +{ + isdn_net_local *lp = (isdn_net_local *) dev->priv; + if (!skb) + return; + if (lp->p_encap == ISDN_NET_ENCAP_ETHER) { + ulong pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN; + if (pullsize) + skb_pull(skb, pullsize); + } +} + /* * Try sending a packet. * If this interface isn't connected to a ISDN-Channel, find a free channel, * and start dialing. */ -int +static int isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev) { isdn_net_local *lp = (isdn_net_local *) ndev->priv; +#ifdef CONFIG_ISDN_X25 + struct concap_proto * cprot = lp -> netdev -> cprot; +#endif if (ndev->tbusy) { if (jiffies - ndev->trans_start < (2 * HZ)) return 1; if (!lp->dialstate) lp->stats.tx_errors++; - ndev->tbusy = 0; ndev->trans_start = jiffies; } - if (skb == NULL) { - return 0; - } - /* Avoid timer-based retransmission conflicts. */ - if (test_and_set_bit(0, (void *) &ndev->tbusy) != 0) - printk(KERN_WARNING - "%s: Transmitter access conflict.\n", - ndev->name); - else { - u_char *buf = skb->data; + ndev->tbusy = 1; /* left instead of obsolete test_and_set_bit() */ +#ifdef CONFIG_ISDN_X25 +/* At this point hard_start_xmit() passes control to the encapsulation + protocol (if present). + For X.25 auto-dialing is completly bypassed because: + - It does not conform with the semantics of a reliable datalink + service as needed by X.25 PLP. + - I don't want that the interface starts dialing when the network layer + sends a message which requests to disconnect the lapb link (or if it + sends any other message not resulting in data transmission). + Instead, dialing will be initiated by the encapsulation protocol entity + when a dl_establish request is received from the upper layer. +*/ + if( cprot ) { + return cprot -> pops -> encap_and_xmit ( cprot , skb); + } else +#endif + /* auto-dialing xmit function */ + { +#ifdef ISDN_DEBUG_NET_DUMP + u_char *buf; +#endif + isdn_net_adjust_hdr(skb, ndev); #ifdef ISDN_DEBUG_NET_DUMP + buf = skb->data; isdn_dumppkt("S:", buf, skb->len, 40); #endif if (!(lp->flags & ISDN_NET_CONNECTED)) { @@ -1033,24 +1193,15 @@ isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev) lp->pre_device, lp->pre_channel)) < 0) { restore_flags(flags); -#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); ndev->tbusy = 0; return 0; -#endif } /* Log packet, which triggered dialing */ if (dev->net_verbose) - isdn_net_log_packet(buf, lp); + isdn_net_log_skb(skb, lp); lp->dialstate = 1; lp->flags |= ISDN_NET_CONNECTED; /* Connect interface with channel */ @@ -1114,12 +1265,26 @@ static int isdn_net_close(struct device *dev) { struct device *p; +#ifdef CONFIG_ISDN_X25 + struct concap_proto * cprot = + ( (isdn_net_local *) dev->priv ) -> netdev -> cprot; + /* printk(KERN_DEBUG "isdn_net_close %s\n" , dev-> name ); */ +#endif +#ifdef CONFIG_ISDN_X25 + if( cprot && cprot -> pops ) cprot -> pops -> close( cprot ); +#endif dev->tbusy = 1; dev->start = 0; if ((p = (((isdn_net_local *) dev->priv)->slave))) { /* If this interface has slaves, stop them also */ while (p) { +#ifdef CONFIG_ISDN_X25 + cprot = ( (isdn_net_local *) p->priv ) + -> netdev -> cprot; + if( cprot && cprot -> pops ) + cprot -> pops -> close( cprot ); +#endif isdn_net_hangup(p); p->tbusy = 1; p->start = 0; @@ -1156,6 +1321,7 @@ isdn_net_type_trans(struct sk_buff *skb, struct device *dev) struct ethhdr *eth; unsigned char *rawp; + skb->mac.raw = skb->data; skb_pull(skb, ETH_HLEN); eth = skb->mac.ethernet; @@ -1170,7 +1336,7 @@ isdn_net_type_trans(struct sk_buff *skb, struct device *dev) * so don't forget to remove it. */ - else if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { + else if (dev->flags & (IFF_PROMISC /*| IFF_ALLMULTI*/)) { if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN)) skb->pkt_type = PACKET_OTHERHOST; } @@ -1193,6 +1359,97 @@ isdn_net_type_trans(struct sk_buff *skb, struct device *dev) return htons(ETH_P_802_2); } +static void +isdn_net_slarp_send(isdn_net_local *lp, int is_reply) +{ + unsigned short hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; + struct sk_buff *skb = dev_alloc_skb(hl + sizeof(cisco_hdr) + sizeof(cisco_slarp)); + unsigned long t = (jiffies / HZ * 1000000); + int len; + cisco_hdr *ch; + cisco_slarp *s; + + if (!skb) { + printk(KERN_WARNING + "%s: Could not allocate SLARP reply\n", lp->name); + return; + } + skb_reserve(skb, hl); + ch = (cisco_hdr *)skb_put(skb, sizeof(cisco_hdr)); + ch->addr = CISCO_ADDR_UNICAST; + ch->ctrl = 0; + ch->type = htons(CISCO_TYPE_SLARP); + s = (cisco_slarp *)skb_put(skb, sizeof(cisco_slarp)); + if (is_reply) { + s->code = htonl(CISCO_SLARP_REPLY); + memset(&s->slarp.reply.ifaddr, 0, sizeof(__u32)); + memset(&s->slarp.reply.netmask, 0, sizeof(__u32)); + } else { + lp->cisco_myseq++; + s->code = htonl(CISCO_SLARP_KEEPALIVE); + s->slarp.keepalive.my_seq = htonl(lp->cisco_myseq); + s->slarp.keepalive.your_seq = htonl(lp->cisco_yourseq); + } + s->rel = 0xffff; + s->t1 = t >> 16; + s->t0 = t & 0xffff; + len = skb->len; + if (isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 0, skb) != len) + dev_kfree_skb(skb); +} + +static void +isdn_net_slarp_in(isdn_net_local *lp, struct sk_buff *skb) +{ + cisco_slarp *s = (cisco_slarp *)skb->data; + + switch (ntohl(s->code)) { + case CISCO_SLARP_REQUEST: + isdn_net_slarp_send(lp, 1); + break; + case CISCO_SLARP_REPLY: + /* Ignore replies */ + break; + case CISCO_SLARP_KEEPALIVE: + lp->cisco_yourseq = s->slarp.keepalive.my_seq; + if (ntohl(s->slarp.keepalive.my_seq == lp->cisco_myseq)) { + if (lp->cisco_loop++ == 2) { + printk(KERN_WARNING "%s: Keepalive Loop\n", + lp->name); + lp->cisco_myseq ^= jiffies; + } + } else + lp->cisco_loop = 0; + break; + } + kfree_skb(skb); +} + +/* + * Called every 10 sec. via timer-interrupt if + * any network-interface has Cisco-Keepalive-Encapsulation + * and is online. + * Send Keepalive-Packet and re-schedule. + */ +void +isdn_net_slarp_out(void) +{ + isdn_net_dev *p = dev->netdev; + int anymore = 0; + + while (p) { + isdn_net_local *l = p->local; + if ((l->p_encap == ISDN_NET_ENCAP_CISCOHDLCK) && + (l->flags & ISDN_NET_CONNECTED) && + (!l->dialstate) ) { + anymore = 1; + isdn_net_slarp_send(l, 0); + } + p = (isdn_net_dev *) p->next; + } + isdn_timer_ctrl(ISDN_TIMER_KEEPALIVE, anymore); +} + /* * Got a packet from ISDN-Channel. */ @@ -1200,23 +1457,19 @@ static void 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' */ +#ifdef CONFIG_ISDN_PPP int proto = PPP_PROTOCOL(skb->data); #endif +#ifdef CONFIG_ISDN_X25 + struct concap_proto *cprot = lp -> netdev -> cprot; +#endif + cisco_hdr *ch; 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)) -#endif - lp->huptimer = 0; + lp->stats.rx_packets++; + lp->stats.rx_bytes += skb->len; if (lp->master) { /* Bundling: If device is a slave-device, deliver to master, also * handle master's statistics and hangup-timeout @@ -1224,16 +1477,9 @@ isdn_net_receive(struct device *ndev, struct sk_buff *skb) ndev = lp->master; 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)) -#endif - lp->huptimer = 0; + lp->stats.rx_bytes += skb->len; } + skb->dev = ndev; skb->pkt_type = PACKET_HOST; skb->mac.raw = skb->data; @@ -1243,22 +1489,61 @@ isdn_net_receive(struct device *ndev, struct sk_buff *skb) switch (lp->p_encap) { case ISDN_NET_ENCAP_ETHER: /* Ethernet over ISDN */ + olp->huptimer = 0; + lp->huptimer = 0; skb->protocol = isdn_net_type_trans(skb, ndev); break; case ISDN_NET_ENCAP_UIHDLC: /* HDLC with UI-frame (for ispa with -h1 option) */ + olp->huptimer = 0; + lp->huptimer = 0; skb_pull(skb, 2); /* Fall through */ case ISDN_NET_ENCAP_RAWIP: /* RAW-IP without MAC-Header */ + olp->huptimer = 0; + lp->huptimer = 0; skb->protocol = htons(ETH_P_IP); break; + case ISDN_NET_ENCAP_CISCOHDLCK: + ch = (cisco_hdr *)skb->data; + if ((ch->addr != CISCO_ADDR_UNICAST) && + (ch->addr != CISCO_ADDR_BROADCAST) ) { + printk(KERN_WARNING "%s: Unknown Cisco addr 0x%02x\n", + lp->name, ch->addr); + kfree_skb(skb); + return; + } + if (ch->ctrl != 0) { + printk(KERN_WARNING "%s: Unknown Cisco ctrl 0x%02x\n", + lp->name, ch->ctrl); + kfree_skb(skb); + return; + } + switch (ntohs(ch->type)) { + case CISCO_TYPE_INET: + skb_pull(skb, 4); + skb->protocol = htons(ETH_P_IP); + break; + case CISCO_TYPE_SLARP: + skb_pull(skb, 4); + isdn_net_slarp_in(olp, skb); + return; + default: + printk(KERN_WARNING "%s: Unknown Cisco type 0x%04x\n", + lp->name, ch->type); + kfree_skb(skb); + return; + } + 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 */ + olp->huptimer = 0; + lp->huptimer = 0; skb->protocol = *(unsigned short *) &(skb->data[0]); skb_pull(skb, 2); if (*(unsigned short *) skb->data == 0xFFFF) @@ -1266,10 +1551,26 @@ isdn_net_receive(struct device *ndev, struct sk_buff *skb) break; #ifdef CONFIG_ISDN_PPP case ISDN_NET_ENCAP_SYNCPPP: + /* + * If encapsulation is syncppp, don't reset + * huptimer on LCP packets. + */ + if (proto != PPP_LCP) { + olp->huptimer = 0; + lp->huptimer = 0; + } isdn_ppp_receive(lp->netdev, olp, skb); return; #endif default: +#ifdef CONFIG_ISDN_X25 + /* try if there are generic sync_device receiver routines */ + if(cprot) if(cprot -> pops) + if( cprot -> pops -> data_ind){ + cprot -> pops -> data_ind(cprot,skb); + return; + }; +#endif /* CONFIG_ISDN_X25 */ printk(KERN_WARNING "%s: unknown encapsulation, dropping\n", lp->name); kfree_skb(skb); @@ -1290,7 +1591,7 @@ isdn_net_rcv_skb(int idx, struct sk_buff *skb) isdn_net_dev *p = dev->rx_netdev[idx]; if (p) { - isdn_net_local *lp = &p->local; + isdn_net_local *lp = p->local; if ((lp->flags & ISDN_NET_CONNECTED) && (!lp->dialstate)) { isdn_net_receive(&p->dev, skb); @@ -1329,15 +1630,15 @@ my_eth_header(struct sk_buff *skb, struct device *dev, unsigned short type, * Anyway, the loopback-device should never use this function... */ - if (dev->flags & IFF_LOOPBACK) { + if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) { memset(eth->h_dest, 0, dev->addr_len); - return (dev->hard_header_len); + return ETH_HLEN /*(dev->hard_header_len)*/; } if (daddr) { memcpy(eth->h_dest, daddr, dev->addr_len); - return dev->hard_header_len; + return ETH_HLEN /*dev->hard_header_len*/; } - return -dev->hard_header_len; + return -ETH_HLEN /*dev->hard_header_len*/; } /* @@ -1356,6 +1657,13 @@ isdn_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, 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; @@ -1377,43 +1685,21 @@ isdn_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, *((ushort *) & skb->data[2]) = htons(type); len = 4; break; +#ifdef CONFIG_ISDN_X25 + default: + /* try if there are generic concap protocol routines */ + if( lp-> netdev -> cprot ){ + printk(KERN_WARNING "isdn_net_header called with concap_proto!\n"); + len = 0; + break; + } + break; +#endif /* CONFIG_ISDN_X25 */ } 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) -{ - 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 - } - return ret; -} -#else static int isdn_net_rebuild_header(struct sk_buff *skb) { @@ -1439,12 +1725,12 @@ isdn_net_rebuild_header(struct sk_buff *skb) * Try to get ARP to resolve the header. */ #ifdef CONFIG_INET - ret = arp_find(eth->h_dest, skb) ? 1 : 0; + ret = arp_find(eth->h_dest, skb); #endif } return ret; } -#endif + /* * Interface-setup. (called just after registering a new interface) */ @@ -1465,21 +1751,13 @@ isdn_net_init(struct device *ndev) return -ENODEV; } 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; -#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|IFF_POINTOPOINT; @@ -1599,13 +1877,13 @@ isdn_net_swapbind(int drvidx) #endif p = dev->netdev; while (p) { - if (p->local.pre_device == drvidx) - switch (p->local.pre_channel) { + if (p->local->pre_device == drvidx) + switch (p->local->pre_channel) { case 0: - p->local.pre_channel = 1; + p->local->pre_channel = 1; break; case 1: - p->local.pre_channel = 0; + p->local->pre_channel = 0; break; } p = (isdn_net_dev *) p->next; @@ -1676,6 +1954,7 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz); /* Accept only calls with Si1 = 7 (Data-Transmission) */ if (si1 != 7) { + restore_flags(flags); if (dev->net_verbose > 1) printk(KERN_INFO "isdn_net: Service-Indicator not 7, ignored\n"); return 0; @@ -1689,6 +1968,8 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) #endif swapped = 0; while (p) { + isdn_net_local *lp = p->local; + /* If last check has triggered as binding-swap, revert it */ switch (swapped) { case 2: @@ -1699,25 +1980,25 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) break; } swapped = 0; - if (!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) + if (!strcmp(isdn_map_eaz2msn(lp->msn, di), eaz)) ematch = 1; #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n", - p->local.name, p->local.msn, p->local.flags, p->local.dialstate); + lp->name, lp->msn, lp->flags, lp->dialstate); #endif - if ((!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) && /* EAZ is matching */ - (((!(p->local.flags & ISDN_NET_CONNECTED)) && /* but not connected */ + if ((!strcmp(isdn_map_eaz2msn(lp->msn, di), eaz)) && /* EAZ is matching */ + (((!(lp->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 */ + ((((lp->dialstate == 4) || (lp->dialstate == 12)) && /* if dialing */ + (!(lp->flags & ISDN_NET_CALLBACK))) /* but no callback */ ))) { #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n", - p->local.pre_device, p->local.pre_channel); + lp->pre_device, lp->pre_channel); #endif if (dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) { - if ((p->local.pre_channel != ch) || - (p->local.pre_device != di)) { + if ((lp->pre_channel != ch) || + (lp->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 @@ -1741,8 +2022,8 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) #endif /* Yes, swap bindings only, if the original * binding is bound to channel 1 of this driver */ - if ((p->local.pre_device == di) && - (p->local.pre_channel == 1)) { + if ((lp->pre_device == di) && + (lp->pre_channel == 1)) { isdn_net_swapbind(di); swapped = 1; } else { @@ -1764,8 +2045,8 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) printk(KERN_DEBUG "n_fi: final check\n"); #endif if ((dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) && - ((p->local.pre_channel != ch) || - (p->local.pre_device != di))) { + ((lp->pre_channel != ch) || + (lp->pre_device != di))) { #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: final check failed\n"); #endif @@ -1786,16 +2067,15 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: match2\n"); #endif - n = p->local.phone[0]; - if (p->local.flags & ISDN_NET_SECURE) { + n = lp->phone[0]; + if (lp->flags & ISDN_NET_SECURE) { while (n) { if (isdn_net_wildmat(nr, n->num)) break; n = (isdn_net_phone *) n->next; } } - if (n || (!(p->local.flags & ISDN_NET_SECURE))) { - isdn_net_local *lp = &(p->local); + if (n || (!(lp->flags & ISDN_NET_SECURE))) { #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: match3\n"); #endif @@ -1804,7 +2084,7 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) */ if (!p->dev.start) { restore_flags(flags); - printk(KERN_INFO "%s: incoming call, if down -> rejected\n", + printk(KERN_INFO "%s: incoming call, interface down -> rejected\n", lp->name); return 3; } @@ -1872,12 +2152,12 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) eaz); /* if this interface is dialing, it does it probably on a different device, so free this device */ - if ((p->local.dialstate == 4) || (p->local.dialstate == 12)) { + if ((lp->dialstate == 4) || (lp->dialstate == 12)) { #ifdef CONFIG_ISDN_PPP 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_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET); } dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE; @@ -1885,16 +2165,16 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) strcpy(dev->num[idx], nr); isdn_info_update(); dev->st_netdev[idx] = lp->netdev; - p->local.isdn_device = di; - p->local.isdn_channel = ch; - p->local.ppp_slot = -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 |= ISDN_WAITCHARGE; - p->local.hupflags &= ~ISDN_HAVECHARGE; + lp->isdn_device = di; + lp->isdn_channel = ch; + lp->ppp_slot = -1; + lp->flags |= ISDN_NET_CONNECTED; + lp->dialstate = 7; + lp->dtimer = 0; + lp->outgoing = 0; + lp->huptimer = 0; + lp->hupflags |= ISDN_WAITCHARGE; + lp->hupflags &= ~ISDN_HAVECHARGE; #ifdef CONFIG_ISDN_PPP if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) if (isdn_ppp_bind(lp) < 0) { @@ -1926,7 +2206,7 @@ isdn_net_findif(char *name) isdn_net_dev *p = dev->netdev; while (p) { - if (!strcmp(p->local.name, name)) + if (!strcmp(p->local->name, name)) return p; p = (isdn_net_dev *) p->next; } @@ -1989,7 +2269,7 @@ isdn_net_force_dial(char *name) if (!p) return -ENODEV; - return (isdn_net_force_dial_lp(&p->local)); + return (isdn_net_force_dial_lp(p->local)); } /* @@ -2010,20 +2290,25 @@ isdn_net_new(char *name, struct device *master) return NULL; } memset(netdev, 0, sizeof(isdn_net_dev)); + if (!(netdev->local = (isdn_net_local *) kmalloc(sizeof(isdn_net_local), GFP_KERNEL))) { + printk(KERN_WARNING "isdn_net: Could not allocate device locals\n"); + return NULL; + } + memset(netdev->local, 0, sizeof(isdn_net_local)); if (name == NULL) - strcpy(netdev->local.name, " "); + strcpy(netdev->local->name, " "); else - strcpy(netdev->local.name, name); - netdev->dev.name = netdev->local.name; - netdev->dev.priv = &netdev->local; + strcpy(netdev->local->name, name); + 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; + netdev->local->p_encap = ISDN_NET_ENCAP_RAWIP; if (master) { /* Device shall be a slave */ struct device *p = (((isdn_net_local *) master->priv)->slave); struct device *q = master; - netdev->local.master = master; + netdev->local->master = master; /* Put device at end of slave-chain */ while (p) { q = p; @@ -2037,41 +2322,43 @@ isdn_net_new(char *name, struct device *master) /* Device shall be a master */ if (register_netdev(&netdev->dev) != 0) { printk(KERN_WARNING "isdn_net: Could not register net-device\n"); + kfree(netdev->local); kfree(netdev); return NULL; } } - netdev->local.magic = ISDN_NET_MAGIC; + netdev->local->magic = ISDN_NET_MAGIC; #ifdef CONFIG_ISDN_PPP netdev->mp_last = NULL; /* mpqueue is empty */ netdev->ib.next_num = 0; netdev->ib.last = NULL; #endif - netdev->queue = &netdev->local; - netdev->local.last = &netdev->local; - netdev->local.netdev = netdev; - netdev->local.next = &netdev->local; - - netdev->local.isdn_device = -1; - netdev->local.isdn_channel = -1; - netdev->local.pre_device = -1; - netdev->local.pre_channel = -1; - 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.l2_proto = ISDN_PROTO_L2_X75I; - netdev->local.l3_proto = ISDN_PROTO_L3_TRANS; - netdev->local.slavedelay = 10 * HZ; - netdev->local.srobin = &netdev->dev; - netdev->local.hupflags = ISDN_INHUP; /* Do hangup even on incoming calls */ - netdev->local.onhtime = 10; /* Default hangup-time for saving costs + netdev->queue = netdev->local; + netdev->local->last = netdev->local; + netdev->local->netdev = netdev; + netdev->local->next = netdev->local; + + netdev->local->isdn_device = -1; + netdev->local->isdn_channel = -1; + netdev->local->pre_device = -1; + netdev->local->pre_channel = -1; + 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->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 = ISDN_INHUP; /* Do hangup even on incoming calls */ + netdev->local->onhtime = 10; /* Default hangup-time for saving costs 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->dialmax = 1; + 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; @@ -2095,7 +2382,7 @@ isdn_net_newslave(char *parm) if (!(n = isdn_net_findif(parm))) return NULL; /* Master must be a real interface, not a slave */ - if (n->local.master) + if (n->local->master) return NULL; /* Master must not be started yet */ if (n->dev.start) @@ -2120,10 +2407,15 @@ isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) int drvidx; int chidx; char drvid[25]; - +#ifdef CONFIG_ISDN_X25 + ulong flags; +#endif if (p) { + isdn_net_local *lp = p->local; + /* See if any registered driver supports the features we want */ - features = (1 << cfg->l2_proto) | (256 << cfg->l3_proto); + features = ((1 << cfg->l2_proto) << ISDN_FEATURE_L2_SHIFT) | + ((1 << cfg->l3_proto) << ISDN_FEATURE_L3_SHIFT); for (i = 0; i < ISDN_MAX_DRIVERS; i++) if (dev->drv[i]) if ((dev->drv[i]->interface->features & features) == features) @@ -2132,22 +2424,68 @@ 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 (lp->p_encap != cfg->p_encap){ +#ifdef CONFIG_ISDN_X25 + struct concap_proto * cprot = p -> cprot; +#endif if (p->dev.start) { printk(KERN_WARNING "%s: cannot change encap when if is up\n", - p->local.name); + lp->name); return -EBUSY; } - if (cfg->p_encap == ISDN_NET_ENCAP_SYNCPPP) { +#ifdef CONFIG_ISDN_X25 + /* delete old encapsulation protocol if present ... */ + save_flags(flags); + cli(); /* avoid races with incoming events trying to + call cprot->pops methods */ + if( cprot && cprot -> pops ) + cprot -> pops -> proto_del ( cprot ); + p -> cprot = NULL; + lp -> dops = NULL; + restore_flags(flags); + /* ... , prepare for configuration of new one ... */ + switch ( cfg -> p_encap ){ + case ISDN_NET_ENCAP_X25IFACE: + lp -> dops = &isdn_concap_reliable_dl_dops; + } + /* ... and allocate new one ... */ + p -> cprot = isdn_concap_new( cfg -> p_encap ); + /* p -> cprot == NULL now if p_encap is not supported + by means of the concap_proto mechanism */ + /* the protocol is not configured yet; this will + happen later when isdn_net_reset() is called */ +#endif + } + switch ( cfg->p_encap ) { + case ISDN_NET_ENCAP_SYNCPPP: #ifndef CONFIG_ISDN_PPP printk(KERN_WARNING "%s: SyncPPP support not configured\n", - p->local.name); + lp->name); return -EINVAL; #else p->dev.type = ARPHRD_PPP; /* change ARP type */ p->dev.addr_len = 0; #endif + break; + case ISDN_NET_ENCAP_X25IFACE: +#ifndef CONFIG_ISDN_X25 + printk(KERN_WARNING "%s: isdn-x25 support not configured\n", + p->local->name); + return -EINVAL; +#else + p->dev.type = ARPHRD_X25; /* change ARP type */ + p->dev.addr_len = 0; +#endif + break; + default: + if( cfg->p_encap >= 0 && + cfg->p_encap <= ISDN_NET_ENCAP_MAX_ENCAP ) + break; + printk(KERN_WARNING + "%s: encapsulation protocol %d not supported\n", + p->local->name, cfg->p_encap); + return -EINVAL; } if (strlen(cfg->drvid)) { /* A bind has been requested ... */ @@ -2175,20 +2513,20 @@ isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) return -ENODEV; } else { /* Parameters are valid, so get them */ - drvidx = p->local.pre_device; - chidx = p->local.pre_channel; + drvidx = lp->pre_device; + chidx = lp->pre_channel; } if (cfg->exclusive > 0) { int flags; /* 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, + if ((i = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto, + lp->l3_proto, drvidx, chidx)) < 0) { /* Grab failed, because desired channel is in use */ - p->local.exclusive = -1; + lp->exclusive = -1; restore_flags(flags); return -EBUSY; } @@ -2196,93 +2534,82 @@ isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) dev->usage[i] = ISDN_USAGE_EXCLUSIVE; isdn_info_update(); restore_flags(flags); - p->local.exclusive = i; + lp->exclusive = i; } else { /* Non-exclusive binding or unbind. */ - 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); + lp->exclusive = -1; + if ((lp->pre_device != -1) && (cfg->exclusive == -1)) { + isdn_unexclusive_channel(lp->pre_device, lp->pre_channel); + isdn_free_channel(lp->pre_device, lp->pre_channel, ISDN_USAGE_NET); drvidx = -1; chidx = -1; } } - strcpy(p->local.msn, cfg->eaz); - 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; + strcpy(lp->msn, cfg->eaz); + lp->pre_device = drvidx; + lp->pre_channel = chidx; + lp->onhtime = cfg->onhtime; + lp->charge = cfg->charge; + lp->l2_proto = cfg->l2_proto; + lp->l3_proto = cfg->l3_proto; + lp->cbdelay = cfg->cbdelay; + lp->dialmax = cfg->dialmax; + lp->triggercps = cfg->triggercps; + lp->slavedelay = cfg->slavedelay * HZ; + lp->pppbind = cfg->pppbind; if (cfg->secure) - p->local.flags |= ISDN_NET_SECURE; + lp->flags |= ISDN_NET_SECURE; else - p->local.flags &= ~ISDN_NET_SECURE; + lp->flags &= ~ISDN_NET_SECURE; if (cfg->cbhup) - p->local.flags |= ISDN_NET_CBHUP; + lp->flags |= ISDN_NET_CBHUP; else - p->local.flags &= ~ISDN_NET_CBHUP; + lp->flags &= ~ISDN_NET_CBHUP; switch (cfg->callback) { case 0: - p->local.flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT); + lp->flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT); break; case 1: - p->local.flags |= ISDN_NET_CALLBACK; - p->local.flags &= ~ISDN_NET_CBOUT; + lp->flags |= ISDN_NET_CALLBACK; + lp->flags &= ~ISDN_NET_CBOUT; break; case 2: - p->local.flags |= ISDN_NET_CBOUT; - p->local.flags &= ~ISDN_NET_CALLBACK; + lp->flags |= ISDN_NET_CBOUT; + lp->flags &= ~ISDN_NET_CALLBACK; break; } if (cfg->chargehup) - p->local.hupflags |= ISDN_CHARGEHUP; + lp->hupflags |= ISDN_CHARGEHUP; else - p->local.hupflags &= ~ISDN_CHARGEHUP; + lp->hupflags &= ~ISDN_CHARGEHUP; if (cfg->ihup) - p->local.hupflags |= ISDN_INHUP; + lp->hupflags |= ISDN_INHUP; else - p->local.hupflags &= ~ISDN_INHUP; + lp->hupflags &= ~ISDN_INHUP; if (cfg->chargeint > 10) { - p->local.hupflags |= ISDN_CHARGEHUP | ISDN_HAVECHARGE | ISDN_MANCHARGE; - p->local.chargeint = cfg->chargeint * HZ; + lp->hupflags |= ISDN_CHARGEHUP | ISDN_HAVECHARGE | ISDN_MANCHARGE; + lp->chargeint = cfg->chargeint * HZ; } - if (cfg->p_encap != p->local.p_encap) { + if (cfg->p_encap != lp->p_encap) { 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|IFF_POINTOPOINT; } 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.hard_header_cache = lp->org_hhc; + p->dev.header_cache_update = lp->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|IFF_POINTOPOINT; } } } - p->local.p_encap = cfg->p_encap; + lp->p_encap = cfg->p_encap; return 0; } return -ENODEV; @@ -2297,39 +2624,42 @@ isdn_net_getcfg(isdn_net_ioctl_cfg * cfg) isdn_net_dev *p = isdn_net_findif(cfg->name); if (p) { - strcpy(cfg->eaz, p->local.msn); - cfg->exclusive = p->local.exclusive; - if (p->local.pre_device >= 0) { - sprintf(cfg->drvid, "%s,%d", dev->drvid[p->local.pre_device], - p->local.pre_channel); + isdn_net_local *lp = p->local; + + strcpy(cfg->eaz, lp->msn); + cfg->exclusive = lp->exclusive; + if (lp->pre_device >= 0) { + sprintf(cfg->drvid, "%s,%d", dev->drvid[lp->pre_device], + lp->pre_channel); } else cfg->drvid[0] = '\0'; - cfg->onhtime = p->local.onhtime; - cfg->charge = p->local.charge; - cfg->l2_proto = p->local.l2_proto; - cfg->l3_proto = p->local.l3_proto; - cfg->p_encap = p->local.p_encap; - cfg->secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0; + cfg->onhtime = lp->onhtime; + cfg->charge = lp->charge; + cfg->l2_proto = lp->l2_proto; + cfg->l3_proto = lp->l3_proto; + cfg->p_encap = lp->p_encap; + cfg->secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0; cfg->callback = 0; - if (p->local.flags & ISDN_NET_CALLBACK) + if (lp->flags & ISDN_NET_CALLBACK) cfg->callback = 1; - if (p->local.flags & ISDN_NET_CBOUT) + if (lp->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->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); + cfg->cbhup = (lp->flags & ISDN_NET_CBHUP) ? 1 : 0; + cfg->chargehup = (lp->hupflags & 4) ? 1 : 0; + cfg->ihup = (lp->hupflags & 8) ? 1 : 0; + cfg->cbdelay = lp->cbdelay; + cfg->dialmax = lp->dialmax; + cfg->triggercps = lp->triggercps; + cfg->slavedelay = lp->slavedelay / HZ; + cfg->chargeint = (lp->hupflags & ISDN_CHARGEHUP) ? + (lp->chargeint / HZ) : 0; + cfg->pppbind = lp->pppbind; + if (lp->slave) + strcpy(cfg->slave, ((isdn_net_local *) lp->slave->priv)->name); else cfg->slave[0] = '\0'; - if (p->local.master) - strcpy(cfg->master, ((isdn_net_local *) p->local.master->priv)->name); + if (lp->master) + strcpy(cfg->master, ((isdn_net_local *) lp->master->priv)->name); else cfg->master[0] = '\0'; return 0; @@ -2352,8 +2682,8 @@ isdn_net_addphone(isdn_net_ioctl_phone * phone) if (!(n = (isdn_net_phone *) kmalloc(sizeof(isdn_net_phone), GFP_KERNEL))) return -ENOMEM; strcpy(n->num, phone->phone); - n->next = p->local.phone[phone->outgoing & 1]; - p->local.phone[phone->outgoing & 1] = n; + n->next = p->local->phone[phone->outgoing & 1]; + p->local->phone[phone->outgoing & 1] = n; return 0; } return -ENODEV; @@ -2378,7 +2708,7 @@ 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++; @@ -2413,16 +2743,16 @@ isdn_net_delphone(isdn_net_ioctl_phone * phone) if (p) { save_flags(flags); cli(); - n = p->local.phone[inout]; + 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 (p->local->dial == n) + p->local->dial = n->next; if (m) m->next = n->next; else - p->local.phone[inout] = n->next; + p->local->phone[inout] = n->next; kfree(n); return 0; } @@ -2449,15 +2779,15 @@ isdn_net_rmallphone(isdn_net_dev * p) save_flags(flags); cli(); for (i = 0; i < 2; i++) { - n = p->local.phone[i]; + n = p->local->phone[i]; while (n) { m = n->next; kfree(n); n = m; } - p->local.phone[i] = NULL; + p->local->phone[i] = NULL; } - p->local.dial = NULL; + p->local->dial = NULL; restore_flags(flags); return 0; } @@ -2472,9 +2802,9 @@ isdn_net_force_hangup(char *name) struct device *q; if (p) { - if (p->local.isdn_device < 0) + if (p->local->isdn_device < 0) return 1; - q = p->local.slave; + q = p->local->slave; /* If this interface has slaves, do a hangup for them also. */ while (q) { isdn_net_hangup(q); @@ -2496,7 +2826,7 @@ isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q) save_flags(flags); cli(); - if (p->local.master) { + if (p->local->master) { /* If it's a slave, it may be removed even if it is busy. However * it has to be hung up first. */ @@ -2507,30 +2837,37 @@ isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q) restore_flags(flags); return -EBUSY; } +#ifdef CONFIG_ISDN_X25 + if( p -> cprot && p -> cprot -> pops ) + p -> cprot -> pops -> proto_del ( p -> cprot ); +#endif /* Free all phone-entries */ isdn_net_rmallphone(p); /* If interface is bound exclusive, free channel-usage */ - if (p->local.exclusive != -1) - isdn_unexclusive_channel(p->local.pre_device, p->local.pre_channel); - if (p->local.master) { + if (p->local->exclusive != -1) + isdn_unexclusive_channel(p->local->pre_device, p->local->pre_channel); + if (p->local->master) { /* It's a slave-device, so update master's slave-pointer if necessary */ - if (((isdn_net_local *) (p->local.master->priv))->slave == &p->dev) - ((isdn_net_local *) (p->local.master->priv))->slave = p->local.slave; - } else + if (((isdn_net_local *) (p->local->master->priv))->slave == &p->dev) + ((isdn_net_local *) (p->local->master->priv))->slave = p->local->slave; + } else { /* Unregister only if it's a master-device */ + p->dev.hard_header_cache = p->local->org_hhc; + p->dev.header_cache_update = p->local->org_hcu; unregister_netdev(&p->dev); + } /* Unlink device from chain */ if (q) q->next = p->next; else dev->netdev = p->next; - if (p->local.slave) { + if (p->local->slave) { /* If this interface has a slave, remove it also */ - char *slavename = ((isdn_net_local *) (p->local.slave->priv))->name; + char *slavename = ((isdn_net_local *) (p->local->slave->priv))->name; isdn_net_dev *n = dev->netdev; q = NULL; while (n) { - if (!strcmp(n->local.name, slavename)) { + if (!strcmp(n->local->name, slavename)) { isdn_net_realrm(n, q); break; } @@ -2543,6 +2880,7 @@ isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q) isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0); restore_flags(flags); + kfree(p->local); kfree(p); return 0; @@ -2561,7 +2899,7 @@ isdn_net_rm(char *name) p = dev->netdev; q = NULL; while (p) { - if (!strcmp(p->local.name, name)) + if (!strcmp(p->local->name, name)) return (isdn_net_realrm(p, q)); q = p; p = (isdn_net_dev *) p->next; @@ -2585,7 +2923,7 @@ isdn_net_rmall(void) save_flags(flags); cli(); while (dev->netdev) { - if (!dev->netdev->local.master) { + if (!dev->netdev->local->master) { /* Remove master-devices only, slaves get removed with their master */ if ((ret = isdn_net_realrm(dev->netdev, NULL))) { restore_flags(flags); diff --git a/drivers/isdn/isdn_net.h b/drivers/isdn/isdn_net.h index 56df21081e9c..19a084dd2189 100644 --- a/drivers/isdn/isdn_net.h +++ b/drivers/isdn/isdn_net.h @@ -1,4 +1,4 @@ -/* $Id: isdn_net.h,v 1.5 1997/02/10 20:12:47 fritz Exp $ +/* $Id: isdn_net.h,v 1.6 1997/10/09 21:28:54 fritz Exp $ * header for Linux ISDN subsystem, network related functions (linklevel). * @@ -21,6 +21,16 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_net.h,v $ + * Revision 1.6 1997/10/09 21:28:54 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * New L1 error status (not yet in use). + * Cleaned up obsolete structures. + * Implemented Cisco-SLARP. + * Changed local net-interface data to be dynamically allocated. + * Removed old 2.0 compatibility stuff. + * * Revision 1.5 1997/02/10 20:12:47 fritz * Changed interface for reporting incoming calls. * @@ -45,11 +55,46 @@ #define ISDN_INHUP 8 /* Even if incoming, close after huptimeout */ #define ISDN_MANCHARGE 16 /* Charge Interval manually set */ +/* + * Definitions for Cisco-HDLC header. + */ + +typedef struct cisco_hdr { + __u8 addr; /* unicast/broadcast */ + __u8 ctrl; /* Always 0 */ + __u16 type; /* IP-typefield */ +} cisco_hdr; + +typedef struct cisco_slarp { + __u32 code; /* SLREQ/SLREPLY/KEEPALIVE */ + union { + struct { + __u32 ifaddr; /* My interface address */ + __u32 netmask; /* My interface netmask */ + } reply; + struct { + __u32 my_seq; /* Packet sequence number */ + __u32 your_seq; + } keepalive; + } slarp; + __u16 rel; /* Always 0xffff */ + __u16 t1; /* Uptime in usec >> 16 */ + __u16 t0; /* Uptime in usec & 0xffff */ +} cisco_slarp; + +#define CISCO_ADDR_UNICAST 0x0f +#define CISCO_ADDR_BROADCAST 0x8f +#define CISCO_TYPE_INET 0x0800 +#define CISCO_TYPE_SLARP 0x8035 +#define CISCO_SLARP_REPLY 0 +#define CISCO_SLARP_REQUEST 1 +#define CISCO_SLARP_KEEPALIVE 2 + 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_stat_callback(int, isdn_ctrl *); extern int isdn_net_setcfg(isdn_net_ioctl_cfg *); extern int isdn_net_getcfg(isdn_net_ioctl_cfg *); extern int isdn_net_addphone(isdn_net_ioctl_phone *); @@ -65,3 +110,4 @@ 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 *); +extern void isdn_net_slarp_out(void); diff --git a/drivers/isdn/isdn_ppp.c b/drivers/isdn/isdn_ppp.c index 0f29e340afbe..a4a45d9eac49 100644 --- a/drivers/isdn/isdn_ppp.c +++ b/drivers/isdn/isdn_ppp.c @@ -1,4 +1,4 @@ -/* $Id: isdn_ppp.c,v 1.27 1997/03/30 16:51:17 calle Exp $ +/* $Id: isdn_ppp.c,v 1.33 1998/02/20 17:11:54 fritz Exp $ * * Linux ISDN subsystem, functions for synchronous PPP (linklevel). * @@ -19,6 +19,37 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_ppp.c,v $ + * Revision 1.33 1998/02/20 17:11:54 fritz + * Changes for recent kernels. + * + * Revision 1.32 1998/01/31 19:29:55 calle + * Merged changes from and for 2.1.82, not tested only compiled ... + * + * Revision 1.31 1997/10/09 21:29:01 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * New L1 error status (not yet in use). + * Cleaned up obsolete structures. + * Implemented Cisco-SLARP. + * Changed local net-interface data to be dynamically allocated. + * Removed old 2.0 compatibility stuff. + * + * Revision 1.30 1997/10/01 09:20:38 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.29 1997/08/21 23:11:44 fritz + * Added changes for kernels >= 2.1.45 + * + * 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. @@ -128,9 +159,7 @@ #include #include #include -#if (LINUX_VERSION_CODE >= 0x020117) #include -#endif #include "isdn_common.h" #include "isdn_ppp.h" #include "isdn_net.h" @@ -148,6 +177,12 @@ static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, 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); @@ -160,7 +195,7 @@ static int isdn_ppp_fill_mpqueue(isdn_net_dev *, struct sk_buff **skb, static void isdn_ppp_free_mpqueue(isdn_net_dev *); #endif -char *isdn_ppp_revision = "$Revision: 1.27 $"; +char *isdn_ppp_revision = "$Revision: 1.33 $"; static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS]; static struct isdn_ppp_compressor *ipc_head = NULL; @@ -267,7 +302,7 @@ isdn_ppp_bind(isdn_net_local * lp) char exclusive[ISDN_MAX_CHANNELS]; /* exclusive flags */ memset(exclusive, 0, ISDN_MAX_CHANNELS); while (net_dev) { /* step through net devices to find exclusive minors */ - isdn_net_local *lp = &net_dev->local; + isdn_net_local *lp = net_dev->local; if (lp->pppbind >= 0) exclusive[lp->pppbind] = 1; net_dev = net_dev->next; @@ -382,7 +417,12 @@ isdn_ppp_open(int min, struct file *file) 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 = NULL; is->mp_seqno = 0; /* MP sequence number */ is->pppcfg = 0; /* ppp configuration */ @@ -644,50 +684,6 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg) return 0; } -#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; - 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->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); - 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; - } - return 1; -} -#else unsigned int isdn_ppp_poll(struct file *file, poll_table * wait) { @@ -700,7 +696,8 @@ isdn_ppp_poll(struct file *file, poll_table * wait) is = file->private_data; if (is->debug & 0x2) - printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", MINOR(file->f_dentry->d_inode->i_rdev)); + printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", + MINOR(file->f_dentry->d_inode->i_rdev)); poll_wait(file, &is->wq, wait); @@ -725,8 +722,6 @@ isdn_ppp_poll(struct file *file, poll_table * wait) restore_flags(flags); return mask; } -#endif - /* * fill up isdn_ppp_read() queue .. @@ -798,31 +793,35 @@ isdn_ppp_read(int min, struct file *file, char *buf, int count) struct ippp_buf_queue *b; int r; unsigned long flags; + unsigned char *save_buf; is = file->private_data; if (!(is->state & IPPP_OPEN)) return 0; + if ((r = verify_area(VERIFY_WRITE, (void *) buf, count))) + return r; + save_flags(flags); 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; - if ((r = copy_to_user(buf, b->buf, count))) { - restore_flags(flags); - return r; - } - kfree(b->buf); b->buf = NULL; is->first = b; + restore_flags(flags); + copy_to_user(buf, save_buf, count); + kfree(save_buf); + return count; } @@ -872,14 +871,13 @@ isdn_ppp_write(int min, struct file *file, const char *buf, int count) printk(KERN_WARNING "isdn_ppp_write: out of memory!\n"); return count; } - SET_SKB_FREE(skb); if (copy_from_user(skb_put(skb, count), buf, count)) return -EFAULT; if (is->debug & 0x40) { printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len); isdn_ppp_frame_log("xmit", skb->data, skb->len, 32); } - if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb)) != count) { + if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb)) != count) { if (lp->sav_skb) { dev_kfree_skb(lp->sav_skb); printk(KERN_INFO "isdn_ppp_write: freeing sav_skb (%d,%d)!\n", cnt, count); @@ -934,46 +932,66 @@ isdn_ppp_cleanup(void) 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) +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: %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); else if (is->pppcfg & SC_REJ_COMP_AC) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); 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; + 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, @@ -987,6 +1005,10 @@ isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *sk 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 */ @@ -995,9 +1017,14 @@ isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *sk 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) @@ -1006,11 +1033,14 @@ isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *sk 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; { @@ -1064,7 +1094,6 @@ isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *sk if (!q) { net_dev->ib.modify = 0; printk(KERN_WARNING "ippp/MPPP: Bad! Can't alloc sq node!\n"); - SET_SKB_FREE(skb); dev_kfree_skb(skb); return; /* discard */ } @@ -1093,7 +1122,8 @@ isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *sk * 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); net_dev->ib.modify = 0; @@ -1102,7 +1132,7 @@ isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *sk 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); } /* @@ -1115,19 +1145,21 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff 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(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) @@ -1140,10 +1172,9 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff 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) { + 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++; - SET_SKB_FREE(skb); + net_dev->local->stats.rx_dropped++; dev_kfree_skb(skb); return; } @@ -1164,11 +1195,9 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff 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++; + net_dev->local->stats.rx_dropped++; dev_kfree_skb(skb_old); return; } @@ -1176,11 +1205,10 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff 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, + pkt_len = slhc_uncompress(ippp_table[net_dev->local->ppp_slot]->slcomp, skb->data, skb_old->len); dev_kfree_skb(skb_old); if (pkt_len < 0) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); lp->stats.rx_dropped++; return; @@ -1191,26 +1219,45 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff #else 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); return; #endif 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); 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; return; } +/* + * isdn_ppp_skb_push .. + * checks whether we have enough space at the beginning of the SKB + * and allocs a new SKB if necessary + */ +static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len) +{ + struct sk_buff *skb = *skb_p; + + if(skb_headroom(skb) < len) { + printk(KERN_ERR "isdn_ppp_skb_push:under %d %d\n",skb_headroom(skb),len); + dev_kfree_skb(skb); + return NULL; + } + return skb_push(skb,len); +} + + /* * send ppp frame .. we expect a PIDCOMPressable proto -- * (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP) @@ -1223,12 +1270,10 @@ int isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) { struct device *mdev = ((isdn_net_local *) (dev->priv))->master; /* get master (for redundancy) */ - isdn_net_local *lp, - *mlp; + isdn_net_local *lp,*mlp; isdn_net_dev *nd; unsigned int proto = PPP_IP; /* 0x21 */ - struct ippp_struct *ipt, - *ipts; + struct ippp_struct *ipt,*ipts; if (mdev) mlp = (isdn_net_local *) (mdev->priv); @@ -1250,6 +1295,7 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) printk(KERN_INFO "%s: IP frame delayed.\n", dev->name); return 1; } + switch (ntohs(skb->protocol)) { case ETH_P_IP: proto = PPP_IP; @@ -1297,11 +1343,18 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) * after this line .. requeueing in the device queue is no longer allowed!!! */ + /* 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); @@ -1310,14 +1363,13 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) int pktlen; new_skb->dev = skb->dev; - 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)); - if (buf != skb->data) { /* copied to new buffer ??? (btw: WHY must slhc copy it?? *sigh*) */ + if (buf != skb->data) { if (new_skb->data != buf) printk(KERN_ERR "isdn_ppp: FATAL error after slhc_compress!!\n"); dev_kfree_skb(skb); @@ -1339,6 +1391,11 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) } #endif + /* + * 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); @@ -1349,13 +1406,17 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) ipts->mp_seqno++; nd->queue = nd->queue->next; if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) { - unsigned char *data = skb_push(skb, 3); + unsigned char *data = isdn_ppp_skb_push(&skb, 3); + if(!data) + return 0; mp_seqno &= 0xfff; - data[0] = MP_BEGIN_FRAG | MP_END_FRAG | (mp_seqno >> 8); /* (B)egin & (E)ndbit .. */ + 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 { - unsigned char *data = skb_push(skb, 5); + 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; @@ -1365,17 +1426,29 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) proto = PPP_MP; /* MP Protocol, 0x003d */ } #endif + + /* + * 'link' compression + */ + skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1); + if( (ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff) ) { - unsigned char *data = skb_push(skb,1); + unsigned char *data = isdn_ppp_skb_push(&skb,1); + if(!data) + return 0; data[0] = proto & 0xff; } else { - unsigned char *data = skb_push(skb,2); + 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 = skb_push(skb,2); + unsigned char *data = isdn_ppp_skb_push(&skb,2); + if(!data) + return 0; data[0] = 0xff; /* All Stations */ data[1] = 0x03; /* Unnumbered information */ } @@ -1406,10 +1479,8 @@ isdn_ppp_free_sqqueue(isdn_net_dev * p) p->ib.sq = NULL; while (q) { struct sqqueue *qn = q->next; - if (q->skb) { - SET_SKB_FREE(q->skb); + if (q->skb) dev_kfree_skb(q->skb); - } kfree(q); q = qn; } @@ -1424,7 +1495,6 @@ isdn_ppp_free_mpqueue(isdn_net_dev * p) while (q) { struct mpqueue *ql = q->next; - SET_SKB_FREE(q->skb); dev_kfree_skb(q->skb); kfree(q); q = ql; @@ -1599,7 +1669,6 @@ isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff **skb, int BEbyte, long if (!(*skb)) { while (q) { struct mpqueue *ql = q->next; - SET_SKB_FREE(q->skb); dev_kfree_skb(q->skb); kfree(q); q = ql; @@ -1612,7 +1681,6 @@ isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff **skb, int BEbyte, long struct mpqueue *ql = q->next; memcpy((*skb)->data + cnt, q->skb->data, q->skb->len); cnt += q->skb->len; - SET_SKB_FREE(q->skb); dev_kfree_skb(q->skb); kfree(q); q = ql; @@ -1632,13 +1700,15 @@ isdn_ppp_cleanup_sqqueue(isdn_net_dev * net_dev, isdn_net_local * lp, long min_s struct sqqueue *q; 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); + 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); @@ -1663,32 +1733,30 @@ isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min_sqno) struct mpqueue *ql, *q = dev->mp_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; - SET_SKB_FREE(q->skb); - dev_kfree_skb(q->skb); - kfree(q); + 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; + printk(KERN_DEBUG "ippp, freeing packet with sqno: %ld\n",q->sqno); + dev_kfree_skb(q->skb); + 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) - slhc_toss(ippp_table[dev->local.ppp_slot]->slcomp); + slhc_toss(ippp_table[dev->local->ppp_slot]->slcomp); #endif } @@ -1708,7 +1776,7 @@ isdn_ppp_timer_timeout(void) *qn; while (net_dev) { - isdn_net_local *lp = &net_dev->local; + isdn_net_local *lp = net_dev->local; if (net_dev->ib.modify || lp->master) { /* interface locked or slave? */ net_dev = net_dev->next; continue; @@ -1728,7 +1796,8 @@ 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; @@ -1804,7 +1873,7 @@ 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 = 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); @@ -1853,7 +1922,7 @@ isdn_ppp_dial_slave(char *name) if (!(ndev = isdn_net_findif(name))) return 1; - lp = &ndev->local; + lp = ndev->local; if (!(lp->flags & ISDN_NET_CONNECTED)) return 5; @@ -1884,7 +1953,7 @@ isdn_ppp_hangup_slave(char *name) if (!(ndev = isdn_net_findif(name))) return 1; - lp = &ndev->local; + lp = ndev->local; if (!(lp->flags & ISDN_NET_CONNECTED)) return 5; @@ -1905,6 +1974,128 @@ 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"); + dev_kfree_skb(skb); + return NULL; +#else + if(!master) { + /* + * single link compression + */ + if(!is->link_compressor) { + printk(KERN_ERR "ippp: no (link) compressor defined!\n"); + dev_kfree_skb(skb); + 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"); + dev_kfree_skb(skb); + 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) { + dev_kfree_skb(skb_out); + return skb_in; + } + + dev_kfree_skb(skb_in); + *proto = new_proto; + return skb_out; +#endif + +} + +/* + * we received a CCP frame .. + * not a clean solution, but we SHOULD handle a few cased in the kernel + */ +static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, + struct sk_buff *skb) +{ +#if 0 + printk(KERN_DEBUG "isdn_ppp_receive_cpp: %02x %02x %02x %02x %02x %02x %02x %02x\n", + skb->data[0],skb->data[1],skb->data[2],skb->data[3], + skb->data[4],skb->data[5],skb->data[6],skb->data[7] ); +#endif +} int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc) { @@ -1937,6 +2128,7 @@ static int isdn_ppp_set_compressor(struct ippp_struct *is,int num) if(ipc->num == num) { return 0; is->compressor = ipc; + is->link_compressor = ipc; } ipc = ipc->next; } diff --git a/drivers/isdn/isdn_ppp.h b/drivers/isdn/isdn_ppp.h index 11184b8c0cfd..0e05af08b535 100644 --- a/drivers/isdn/isdn_ppp.h +++ b/drivers/isdn/isdn_ppp.h @@ -1,4 +1,4 @@ -/* $Id: isdn_ppp.h,v 1.9 1997/02/11 18:32:59 fritz Exp $ +/* $Id: isdn_ppp.h,v 1.12 1998/01/31 22:07:48 keil Exp $ * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel). * @@ -19,6 +19,21 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_ppp.h,v $ + * Revision 1.12 1998/01/31 22:07:48 keil + * changes for newer kernels + * + * Revision 1.11 1997/10/01 09:20:44 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * 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(). * @@ -63,11 +78,7 @@ 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); -#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 unsigned int isdn_ppp_poll(struct file *, struct poll_table_struct *); 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 *); @@ -78,3 +89,7 @@ extern void isdn_ppp_wakeup_daemon(isdn_net_local *); #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 deleted file mode 100644 index 20cf9c6f6da7..000000000000 --- a/drivers/isdn/isdn_syms.c +++ /dev/null @@ -1,54 +0,0 @@ -/* $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 - -#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 0929dbc26967..fd976ff50ca7 100644 --- a/drivers/isdn/isdn_tty.c +++ b/drivers/isdn/isdn_tty.c @@ -1,4 +1,4 @@ -/* $Id: isdn_tty.c,v 1.41 1997/05/27 15:17:31 fritz Exp $ +/* $Id: isdn_tty.c,v 1.47 1998/02/22 19:44:14 fritz Exp $ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel). * @@ -20,6 +20,35 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_tty.c,v $ + * Revision 1.47 1998/02/22 19:44:14 fritz + * Bugfixes and improvements regarding V.110, V.110 now running. + * + * Revision 1.46 1998/02/20 17:23:08 fritz + * Changes for recent kernels. + * Merged in contributions by Thomas Pfeiffer (V.110 T.70+ Extended FAX stuff) + * Added symbolic constants for Modem-Registers. + * + * Revision 1.45 1998/01/31 22:07:49 keil + * changes for newer kernels + * + * Revision 1.44 1998/01/31 19:30:02 calle + * Merged changes from and for 2.1.82, not tested only compiled ... + * + * Revision 1.43 1997/10/09 21:29:04 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * New L1 error status (not yet in use). + * Cleaned up obsolete structures. + * Implemented Cisco-SLARP. + * Changed local net-interface data to be dynamically allocated. + * Removed old 2.0 compatibility stuff. + * + * Revision 1.42 1997/10/01 09:20:49 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * * Revision 1.41 1997/05/27 15:17:31 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -199,6 +228,8 @@ #define VBUFX (VBUF/16) #endif +#define FIX_FILE_TRANSFER + /* Prototypes */ static int isdn_tty_edit_at(const char *, int, modem_info *, int); @@ -223,12 +254,63 @@ static int bit2si[8] = static int si2bit[8] = {4, 1, 4, 4, 4, 4, 4, 4}; -char *isdn_tty_revision = "$Revision: 1.41 $"; +char *isdn_tty_revision = "$Revision: 1.47 $"; #define DLE 0x10 #define ETX 0x03 #define DC4 0x14 +/* + * Definition of some special Registers of AT-Emulator + */ +#define REG_RINGATA 0 +#define REG_RINGCNT 1 +#define REG_ESC 2 +#define REG_CR 3 +#define REG_LF 4 +#define REG_BS 5 + +#define REG_RESP 12 +#define BIT_RESP 1 +#define REG_RESPNUM 12 +#define BIT_RESPNUM 2 +#define REG_ECHO 12 +#define BIT_ECHO 4 +#define REG_DCD 12 +#define BIT_DCD 8 +#define REG_CTS 12 +#define BIT_CTS 16 +#define REG_DTRR 12 +#define BIT_DTRR 32 +#define REG_DSR 12 +#define BIT_DSR 64 +#define REG_CPPP 12 +#define BIT_CPPP 128 + +#define REG_DELXMT 13 +#define BIT_DELXMT 1 +#define REG_T70 13 +#define BIT_T70 2 +#define BIT_T70_EXT 32 +#define REG_DTRHUP 13 +#define BIT_DTRHUP 4 +#define REG_RESPXT 13 +#define BIT_RESPXT 8 +#define REG_CIDONCE 13 +#define BIT_CIDONCE 16 +#define REG_RUNG 13 +#define BIT_RUNG 64 + +#define REG_L2PROT 14 +#define REG_L3PROT 15 +#define REG_PSIZE 16 +#define REG_WSIZE 17 +#define REG_SI1 18 +#define REG_SI2 19 +#define REG_SI1I 20 +#define REG_PLAN 21 +#define REG_SCREEN 22 + /* 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. @@ -272,10 +354,9 @@ isdn_tty_try_read(modem_info * info, struct sk_buff *skb) #ifdef CONFIG_ISDN_AUDIO } #endif - if (info->emu.mdmreg[12] & 128) + if (info->emu.mdmreg[REG_CPPP] & BIT_CPPP) tty->flip.flag_buf_ptr[len - 1] = 0xff; queue_task(&tty->flip.tqueue, &tq_timer); - SET_SKB_FREE(skb); kfree_skb(skb); return 1; } @@ -319,7 +400,7 @@ isdn_tty_readmodem(void) tty->flip.char_buf_ptr, tty->flip.flag_buf_ptr, c, 0); /* CISCO AsyncPPP Hack */ - if (!(info->emu.mdmreg[12] & 128)) + if (!(info->emu.mdmreg[REG_CPPP] & BIT_CPPP)) memset(tty->flip.flag_buf_ptr, 0, r); tty->flip.count += r; tty->flip.flag_buf_ptr += r; @@ -371,19 +452,26 @@ isdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb) #endif ) { /* If Modem not listening, drop data */ - SET_SKB_FREE(skb); kfree_skb(skb); 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); + if (info->emu.mdmreg[REG_T70] & BIT_T70) { + if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT) { + /* T.70 decoding: throw away the T.70 header (2 or 4 bytes) */ + if (skb->data[0] == 3) /* pure data packet -> 4 byte headers */ + skb_pull(skb, 4); + else + if (skb->data[0] == 1) /* keepalive packet -> 2 byte hdr */ + skb_pull(skb, 2); + } else + /* 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); return 1; } @@ -454,16 +542,12 @@ isdn_tty_cleanup_xmit(modem_info * info) save_flags(flags); cli(); if (skb_queue_len(&info->xmit_queue)) - while ((skb = skb_dequeue(&info->xmit_queue))) { - SET_SKB_FREE(skb); + while ((skb = skb_dequeue(&info->xmit_queue))) kfree_skb(skb); - } #ifdef CONFIG_ISDN_AUDIO if (skb_queue_len(&info->dtmf_queue)) - while ((skb = skb_dequeue(&info->dtmf_queue))) { - SET_SKB_FREE(skb); + while ((skb = skb_dequeue(&info->dtmf_queue))) kfree_skb(skb); - } #endif restore_flags(flags); } @@ -479,7 +563,7 @@ isdn_tty_tint(modem_info * info) return; len = skb->len; if ((slen = isdn_writebuf_skb_stub(info->isdn_driver, - info->isdn_channel, skb)) == len) { + info->isdn_channel, 1, skb)) == len) { struct tty_struct *tty = info->tty; info->send_outstanding++; info->msr |= UART_MSR_CTS; @@ -492,7 +576,6 @@ isdn_tty_tint(modem_info * info) } if (slen < 0) { /* Error: no channel, already shutdown, or wrong parameter */ - SET_SKB_FREE(skb); dev_kfree_skb(skb); return; } @@ -590,7 +673,7 @@ isdn_tty_end_vrx(const char *buf, int c, int from_user) while (c--) { if (from_user) - GET_USER(ch, buf); + get_user(ch, buf); else ch = *buf; if ((ch != 0x11) && (ch != 0x13)) @@ -640,7 +723,7 @@ isdn_tty_senddown(modem_info * info) restore_flags(flags); return; } - if ((info->emu.mdmreg[12] & 0x10) != 0) + if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0) info->msr &= ~UART_MSR_CTS; info->lsr &= ~UART_LSR_TEMT; if (info->isdn_driver < 0) { @@ -710,10 +793,13 @@ isdn_tty_senddown(modem_info * info) } } #endif /* CONFIG_ISDN_AUDIO */ - SET_SKB_FREE(skb); - if (info->emu.mdmreg[13] & 2) + if (info->emu.mdmreg[REG_T70] & BIT_T70) { /* Add T.70 simplified header */ - memcpy(skb_push(skb, 4), "\1\0\1\0", 4); + if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT) + memcpy(skb_push(skb, 2), "\1\0", 2); + else + memcpy(skb_push(skb, 4), "\1\0\1\0", 4); + } skb_queue_tail(&info->xmit_queue, skb); } @@ -761,27 +847,27 @@ isdn_tty_dial(char *n, modem_info * info, atemu * m) { int usg = ISDN_USAGE_MODEM; int si = 7; - int l2 = m->mdmreg[14]; + int l2 = m->mdmreg[REG_L2PROT]; isdn_ctrl cmd; ulong flags; int i; int j; for (j = 7; j >= 0; j--) - if (m->mdmreg[18] & (1 << j)) { + if (m->mdmreg[REG_SI1] & (1 << j)) { si = bit2si[j]; break; } #ifdef CONFIG_ISDN_AUDIO if (si == 1) { - l2 = 4; + l2 = ISDN_PROTO_L2_TRANS; usg = ISDN_USAGE_VOICE; } #endif - m->mdmreg[20] = si2bit[si]; + m->mdmreg[REG_SI1I] = si2bit[si]; save_flags(flags); cli(); - i = isdn_get_free_channel(usg, l2, m->mdmreg[15], -1, -1); + i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1); if (i < 0) { restore_flags(flags); isdn_tty_modem_result(6, info); @@ -798,32 +884,32 @@ isdn_tty_dial(char *n, modem_info * info, atemu * m) cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; cmd.command = ISDN_CMD_CLREAZ; - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver)); cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETEAZ; - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL2; info->last_l2 = l2; cmd.arg = info->isdn_channel + (l2 << 8); - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL3; - cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8); - dev->drv[info->isdn_driver]->interface->command(&cmd); + cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); + isdn_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.parm.setup.si2 = m->mdmreg[REG_SI2]; cmd.command = ISDN_CMD_DIAL; info->dialing = 1; strcpy(dev->num[i], n); isdn_info_update(); - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); } } @@ -865,6 +951,9 @@ isdn_tty_modem_hup(modem_info * info, int local) info->adpcmr = NULL; } #endif + if ((info->msr & UART_MSR_RI) && + (info->emu.mdmreg[REG_RUNG] & BIT_RUNG)) + isdn_tty_modem_result(12, info); info->msr &= ~(UART_MSR_DCD | UART_MSR_RI); info->lsr |= UART_LSR_TEMT; if (info->isdn_driver >= 0) { @@ -872,11 +961,11 @@ isdn_tty_modem_hup(modem_info * info, int 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_command(&cmd); } isdn_all_eaz(info->isdn_driver, info->isdn_channel); - info->emu.mdmreg[1] = 0; - usage = (info->emu.mdmreg[20] == 1) ? + info->emu.mdmreg[REG_RINGCNT] = 0; + usage = (info->emu.mdmreg[REG_SI1I] == 1) ? ISDN_USAGE_VOICE : ISDN_USAGE_MODEM; isdn_free_channel(info->isdn_driver, info->isdn_channel, usage); @@ -937,7 +1026,7 @@ isdn_tty_change_speed(modem_info * info) isdn_tty_modem_ncarrier(info); } else { info->mcr &= ~UART_MCR_DTR; - if (info->emu.mdmreg[13] & 4) { + if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { #ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "Mhup in changespeed\n"); #endif @@ -1020,7 +1109,7 @@ isdn_tty_shutdown(modem_info * info) 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) { + if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { isdn_tty_modem_reset_regs(info, 0); #ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n"); @@ -1047,8 +1136,8 @@ isdn_tty_shutdown(modem_info * info) static int isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count) { - int c, - total = 0; + int c; + int total = 0; ulong flags; modem_info *info = (modem_info *) tty->driver_data; @@ -1074,7 +1163,7 @@ isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int co #ifdef CONFIG_ISDN_AUDIO if (!info->vonline) #endif - isdn_tty_check_esc(buf, m->mdmreg[2], c, + isdn_tty_check_esc(buf, m->mdmreg[REG_ESC], c, &(m->pluscount), &(m->lastplus), from_user); @@ -1116,7 +1205,7 @@ isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int co } else #endif info->xmit_count += c; - if (m->mdmreg[13] & 1) { + if (m->mdmreg[REG_DELXMT] & BIT_DELXMT) { isdn_tty_senddown(info); isdn_tty_tint(info); } @@ -1304,7 +1393,7 @@ isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value) uint arg; int pre_dtr; - GET_USER(arg, (uint *) value); + get_user(arg, (uint *) value); switch (cmd) { case TIOCMBIS: #ifdef ISDN_DEBUG_MODEM_IOCTL @@ -1327,7 +1416,7 @@ isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value) } if (arg & TIOCM_DTR) { info->mcr &= ~UART_MCR_DTR; - if (info->emu.mdmreg[13] & 4) { + if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { isdn_tty_modem_reset_regs(info, 0); #ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "Mhup in TIOCMBIC\n"); @@ -1348,7 +1437,7 @@ isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value) | ((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) { + if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { isdn_tty_modem_reset_regs(info, 0); #ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "Mhup in TIOCMSET\n"); @@ -1414,7 +1503,7 @@ isdn_tty_ioctl(struct tty_struct *tty, struct file *file, error = verify_area(VERIFY_READ, (void *) arg, sizeof(long)); if (error) return error; - GET_USER(arg, (ulong *) arg); + get_user(arg, (ulong *) arg); tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); @@ -1443,7 +1532,6 @@ isdn_tty_ioctl(struct tty_struct *tty, struct file *file, 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); @@ -1823,10 +1911,10 @@ static void isdn_tty_modem_reset_regs(modem_info * info, int force) { atemu *m = &info->emu; - if ((m->mdmreg[12] & 32) || force) { + if ((m->mdmreg[REG_DTRR] & BIT_DTRR) || 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[REG_PSIZE] * 16; } #ifdef CONFIG_ISDN_AUDIO isdn_tty_modem_reset_vpar(m); @@ -1881,6 +1969,7 @@ isdn_tty_modem_init(void) m->tty_modem.stop = NULL; m->tty_modem.start = NULL; m->tty_modem.hangup = isdn_tty_hangup; + m->tty_modem.driver_name = "isdn_tty"; /* * The callout device is just like normal device except for * major number and the subtype code. @@ -1905,7 +1994,7 @@ isdn_tty_modem_init(void) sprintf(info->last_num, "none"); info->last_dir = 0; info->last_lhup = 1; - info->last_l2 = 0; + info->last_l2 = -1; info->last_si = 0; isdn_tty_reset_profile(&info->emu); isdn_tty_modem_reset_regs(info, 1); @@ -1927,7 +2016,7 @@ isdn_tty_modem_init(void) #ifdef CONFIG_ISDN_AUDIO skb_queue_head_init(&info->dtmf_queue); #endif - if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_SIZE + 5, GFP_KERNEL))) { + if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, GFP_KERNEL))) { printk(KERN_ERR "Could not allocate modem xmit-buffer\n"); return -3; } @@ -1977,12 +2066,12 @@ isdn_tty_find_icall(int di, int ch, setup_parm setup) #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]); + info->emu.mdmreg[REG_SI1], info->emu.mdmreg[REG_SI2]); #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[REG_SI1] & si2bit[si1]) && /* SI1 is matching */ + (info->emu.mdmreg[REG_SI2] == si2)) { /* SI2 is matching */ idx = isdn_dc2minor(di, ch); #ifdef ISDN_DEBUG_MODEM_ICALL printk(KERN_DEBUG "m_fi: match1\n"); @@ -1990,7 +2079,10 @@ isdn_tty_find_icall(int di, int ch, setup_parm setup) info->flags, info->isdn_driver, info->isdn_channel, dev->usage[idx]); #endif - if ((info->flags & ISDN_ASYNC_NORMAL_ACTIVE) && + if ( +#ifndef FIX_FILE_TRANSFER + (info->flags & ISDN_ASYNC_NORMAL_ACTIVE) && +#endif (info->isdn_driver == -1) && (info->isdn_channel == -1) && (USG_NONE(dev->usage[idx]))) { @@ -2001,9 +2093,9 @@ isdn_tty_find_icall(int di, int ch, setup_parm setup) dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE; dev->usage[idx] |= (si1 == 1) ? ISDN_USAGE_VOICE : ISDN_USAGE_MODEM; strcpy(dev->num[idx], nr); - info->emu.mdmreg[20] = si2bit[si1]; - info->emu.mdmreg[21] = setup.plan; - info->emu.mdmreg[22] = setup.screen; + info->emu.mdmreg[REG_SI1I] = si2bit[si1]; + info->emu.mdmreg[REG_PLAN] = setup.plan; + info->emu.mdmreg[REG_SCREEN] = setup.screen; isdn_info_update(); restore_flags(flags); printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr, @@ -2029,12 +2121,20 @@ isdn_tty_stat_callback(int i, isdn_ctrl * c) { int mi; modem_info *info; + char *e; if (i < 0) return 0; if ((mi = dev->m_idx[i]) >= 0) { info = &dev->mdm.info[mi]; switch (c->command) { + case ISDN_STAT_CINF: + printk(KERN_DEBUG "CHARGEINFO on ttyI%d: %ld %s\n", info->line, c->arg, c->parm.num); + info->emu.charge = (unsigned) simple_strtoul(c->parm.num, &e, 10); + if (e == c->parm.num) + info->emu.charge = 0; + + break; case ISDN_STAT_BSENT: #ifdef ISDN_TTY_STAT_DEBUG printk(KERN_DEBUG "tty_STAT_BSENT ttyI%d\n", info->line); @@ -2093,6 +2193,7 @@ isdn_tty_stat_callback(int i, isdn_ctrl * c) */ if (TTY_IS_ACTIVE(info)) { info->msr |= UART_MSR_DCD; + info->emu.charge = 0; if (info->dialing) { info->dialing = 0; info->last_dir = 1; @@ -2184,13 +2285,13 @@ isdn_tty_at_cout(char *msg, modem_info * info) for (p = msg; *p; p++) { switch (*p) { case '\r': - c = m->mdmreg[3]; + c = m->mdmreg[REG_CR]; break; case '\n': - c = m->mdmreg[4]; + c = m->mdmreg[REG_LF]; break; case '\b': - c = m->mdmreg[5]; + c = m->mdmreg[REG_BS]; break; default: c = *p; @@ -2293,14 +2394,14 @@ isdn_tty_modem_result(int code, modem_info * info) static char *msg[] = {"OK", "CONNECT", "RING", "NO CARRIER", "ERROR", "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER", - "RINGING", "NO MSN/EAZ", "VCON"}; + "RINGING", "NO MSN/EAZ", "VCON", "RUNG"}; ulong flags; char s[10]; switch (code) { case 2: - m->mdmreg[1]++; /* RING */ - if (m->mdmreg[1] == m->mdmreg[0]) + m->mdmreg[REG_RINGCNT]++; /* RING */ + if (m->mdmreg[REG_RINGCNT] == m->mdmreg[REG_RINGATA]) /* Automatically accept incoming call */ isdn_tty_cmd_ATA(info); break; @@ -2313,7 +2414,7 @@ isdn_tty_modem_result(int code, modem_info * info) #endif save_flags(flags); cli(); - m->mdmreg[1] = 0; + m->mdmreg[REG_RINGCNT] = 0; del_timer(&info->nc_timer); info->ncarrier = 0; if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) { @@ -2356,15 +2457,19 @@ isdn_tty_modem_result(int code, modem_info * info) info->online = 1; break; } - if (m->mdmreg[12] & 1) { + if (m->mdmreg[REG_RESP] & BIT_RESP) { /* Show results */ isdn_tty_at_cout("\r\n", info); - if (m->mdmreg[12] & 2) { + if (m->mdmreg[REG_RESPNUM] & BIT_RESPNUM) { /* Show numeric results */ sprintf(s, "%d", code); isdn_tty_at_cout(s, info); } else { - if ((code == 2) && (!(m->mdmreg[13] & 16))) { + if ((code == 2) && + (m->mdmreg[REG_RUNG] & BIT_RUNG) && + (m->mdmreg[REG_RINGCNT] > 1)) + return; + if ((code == 2) && (!(m->mdmreg[REG_CIDONCE] & BIT_CIDONCE))) { isdn_tty_at_cout("CALLER NUMBER: ", info); isdn_tty_at_cout(dev->num[info->drv_index], info); isdn_tty_at_cout("\r\n", info); @@ -2373,7 +2478,8 @@ isdn_tty_modem_result(int code, modem_info * info) switch (code) { case 2: /* Print CID only once, _after_ 1.st RING */ - if ((m->mdmreg[13] & 16) && (m->mdmreg[1] == 1)) { + if ((m->mdmreg[REG_CIDONCE] & BIT_CIDONCE) && + (m->mdmreg[REG_RINGCNT] == 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); @@ -2383,18 +2489,39 @@ isdn_tty_modem_result(int code, modem_info * info) case 6: case 7: case 8: - m->mdmreg[1] = 0; + m->mdmreg[REG_RINGCNT] = 0; /* Append Cause-Message if enabled */ - if (m->mdmreg[13] & 8) { + if (m->mdmreg[REG_RESPXT] & BIT_RESPXT) { 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) + switch (m->mdmreg[REG_L2PROT]) { + case ISDN_PROTO_L2_X75I: + case ISDN_PROTO_L2_X75UI: + case ISDN_PROTO_L2_X75BUI: + isdn_tty_at_cout("/X.75", info); + break; + case ISDN_PROTO_L2_HDLC: + isdn_tty_at_cout("/HDLC", info); + break; + case ISDN_PROTO_L2_V11096: + isdn_tty_at_cout("/V110/9600", info); + break; + case ISDN_PROTO_L2_V11019: + isdn_tty_at_cout("/V110/19200", info); + break; + case ISDN_PROTO_L2_V11038: + isdn_tty_at_cout("/V110/38400", info); + break; + } + if (m->mdmreg[REG_T70] & BIT_T70) { isdn_tty_at_cout("/T.70", info); + if (m->mdmreg[REG_T70] & BIT_T70_EXT) + isdn_tty_at_cout("+", info); + } break; } } @@ -2479,16 +2606,25 @@ isdn_tty_report(modem_info * info) isdn_tty_at_cout(" Layer-2 Protocol: ", info); switch (info->last_l2) { case ISDN_PROTO_L2_X75I: - isdn_tty_at_cout("x75i", info); + isdn_tty_at_cout("X.75i", info); break; case ISDN_PROTO_L2_X75UI: - isdn_tty_at_cout("x75ui", info); + isdn_tty_at_cout("X.75ui", info); break; case ISDN_PROTO_L2_X75BUI: - isdn_tty_at_cout("x75bui", info); + isdn_tty_at_cout("X.75bui", info); break; case ISDN_PROTO_L2_HDLC: - isdn_tty_at_cout("hdlc", info); + isdn_tty_at_cout("HDLC", info); + break; + case ISDN_PROTO_L2_V11096: + isdn_tty_at_cout("V.110 9600 Baud", info); + break; + case ISDN_PROTO_L2_V11019: + isdn_tty_at_cout("V.110 19200 Baud", info); + break; + case ISDN_PROTO_L2_V11038: + isdn_tty_at_cout("V.110 38400 Baud", info); break; case ISDN_PROTO_L2_TRANS: isdn_tty_at_cout("transparent", info); @@ -2497,7 +2633,12 @@ isdn_tty_report(modem_info * info) isdn_tty_at_cout("unknown", info); break; } - isdn_tty_at_cout((m->mdmreg[13] & 2) ? "/t.70\r\n" : "\r\n", info); + if (m->mdmreg[REG_T70] & BIT_T70) { + isdn_tty_at_cout("/T.70", info); + if (m->mdmreg[REG_T70] & BIT_T70_EXT) + isdn_tty_at_cout("+", info); + } + isdn_tty_at_cout("\r\n", info); isdn_tty_at_cout(" Service: ", info); switch (info->last_si) { case 1: @@ -2535,30 +2676,36 @@ isdn_tty_cmd_ATand(char **p, modem_info * info) /* &B - Set Buffersize */ p[0]++; i = isdn_getnum(p); - if ((i < 0) || (i > ISDN_SERIAL_XMIT_SIZE)) + if ((i < 0) || (i > ISDN_SERIAL_XMIT_MAX)) PARSE_ERROR1; #ifdef CONFIG_ISDN_AUDIO - if ((m->mdmreg[18] & 1) && (i > VBUF)) + if ((m->mdmreg[REG_SI1] & 1) && (i > VBUF)) PARSE_ERROR1; #endif - m->mdmreg[16] = i / 16; - info->xmit_size = m->mdmreg[16] * 16; + m->mdmreg[REG_PSIZE] = i / 16; + info->xmit_size = m->mdmreg[REG_PSIZE] * 16; + switch (m->mdmreg[REG_L2PROT]) { + case ISDN_PROTO_L2_V11096: + case ISDN_PROTO_L2_V11019: + case ISDN_PROTO_L2_V11038: + info->xmit_size /= 10; + } break; case 'D': /* &D - Set DCD-Low-behavior */ p[0]++; switch (isdn_getnum(p)) { case 0: - m->mdmreg[13] &= ~4; - m->mdmreg[12] &= ~32; + m->mdmreg[REG_DTRHUP] &= ~BIT_DTRHUP; + m->mdmreg[REG_DTRR] &= ~BIT_DTRR; break; case 2: - m->mdmreg[13] |= 4; - m->mdmreg[12] &= ~32; + m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP; + m->mdmreg[REG_DTRR] &= ~BIT_DTRR; break; case 3: - m->mdmreg[13] |= 4; - m->mdmreg[12] |= 32; + m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP; + m->mdmreg[REG_DTRR] |= BIT_DTRR; break; default: PARSE_ERROR1 @@ -2575,12 +2722,46 @@ isdn_tty_cmd_ATand(char **p, modem_info * info) isdn_tty_reset_profile(m); isdn_tty_modem_reset_regs(info, 1); break; + case 'R': + /* &R - Set V.110 bitrate adaption */ + p[0]++; + i = isdn_getnum(p); + switch (i) { + case 0: + /* Switch off V.110, back to X.75 */ + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; + m->mdmreg[REG_SI2] = 0; + info->xmit_size = m->mdmreg[REG_PSIZE] * 16; + break; + case 9600: + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11096; + m->mdmreg[REG_SI2] = 197; + info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10; + break; + case 19200: + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11019; + m->mdmreg[REG_SI2] = 199; + info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10; + break; + case 38400: + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11038; + m->mdmreg[REG_SI2] = 198; /* no existing standard for this */ + info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10; + break; + default: + PARSE_ERROR1; + } + /* Switch off T.70 */ + m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT); + /* Set Service 7 */ + m->mdmreg[REG_SI1] |= 4; + break; case 'S': /* &S - Set Windowsize */ p[0]++; i = isdn_getnum(p); if ((i > 0) && (i < 9)) - m->mdmreg[17] = i; + m->mdmreg[REG_WSIZE] = i; else PARSE_ERROR1; break; @@ -2610,19 +2791,27 @@ isdn_tty_cmd_ATand(char **p, modem_info * info) } break; case 'X': - /* &X - Switch to BTX-Mode */ + /* &X - Switch to BTX-Mode and T.70 */ p[0]++; switch (isdn_getnum(p)) { case 0: - m->mdmreg[13] &= ~2; - info->xmit_size = m->mdmreg[16] * 16; + m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT); + info->xmit_size = m->mdmreg[REG_PSIZE] * 16; break; case 1: - m->mdmreg[13] |= 2; - m->mdmreg[14] = 0; + m->mdmreg[REG_T70] |= BIT_T70; + m->mdmreg[REG_T70] &= ~BIT_T70_EXT; + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; + info->xmit_size = 112; + m->mdmreg[REG_SI1] = 4; + m->mdmreg[REG_SI2] = 0; + break; + case 2: + m->mdmreg[REG_T70] |= (BIT_T70 | BIT_T70_EXT); + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; info->xmit_size = 112; - m->mdmreg[18] = 4; - m->mdmreg[19] = 0; + m->mdmreg[REG_SI1] = 4; + m->mdmreg[REG_SI2] = 0; break; default: PARSE_ERROR1; @@ -2639,22 +2828,28 @@ 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) + case REG_L2PROT: + if (mval > ISDN_PROTO_L2_MAX) return 1; break; - case 16: - if ((mval * 16) > ISDN_SERIAL_XMIT_SIZE) + case REG_PSIZE: + if ((mval * 16) > ISDN_SERIAL_XMIT_MAX) return 1; #ifdef CONFIG_ISDN_AUDIO - if ((m->mdmreg[18] & 1) && (mval > VBUFX)) + if ((m->mdmreg[REG_SI1] & 1) && (mval > VBUFX)) return 1; #endif info->xmit_size = mval * 16; + switch (m->mdmreg[REG_L2PROT]) { + case ISDN_PROTO_L2_V11096: + case ISDN_PROTO_L2_V11019: + case ISDN_PROTO_L2_V11038: + info->xmit_size /= 10; + } break; - case 20: - case 21: - case 22: + case REG_SI1I: + case REG_PLAN: + case REG_SCREEN: /* readonly registers */ return 1; } @@ -2741,31 +2936,31 @@ isdn_tty_cmd_ATA(modem_info * info) /* Accept incoming call */ info->last_dir = 0; strcpy(info->last_num, dev->num[info->drv_index]); - m->mdmreg[1] = 0; + m->mdmreg[REG_RINGCNT] = 0; info->msr &= ~UART_MSR_RI; - l2 = m->mdmreg[14]; + l2 = m->mdmreg[REG_L2PROT]; #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; + if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) { + if (m->mdmreg[REG_SI1I] == 1) + l2 = ISDN_PROTO_L2_TRANS; else - l2 = 0; + l2 = ISDN_PROTO_L2_X75I; } #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); + isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL3; - cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8); - dev->drv[info->isdn_driver]->interface->command(&cmd); + cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); + isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; cmd.command = ISDN_CMD_ACCEPTD; - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); } else isdn_tty_modem_result(8, info); } @@ -2787,7 +2982,7 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) case '?': p[0]++; sprintf(rs, "\r\n%d", - (m->mdmreg[18] & 1) ? 8 : 0); + (m->mdmreg[REG_SI1] & 1) ? 8 : 0); isdn_tty_at_cout(rs, info); break; case '=': @@ -2795,18 +2990,22 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) switch (*p[0]) { case '0': p[0]++; - m->mdmreg[18] = 4; + m->mdmreg[REG_SI1] = 4; info->xmit_size = - m->mdmreg[16] * 16; + m->mdmreg[REG_PSIZE] * 16; + break; + case '2': + printk(KERN_DEBUG "isdn_tty: FCLASS=2\n"); + p[0]++; break; case '8': p[0]++; - m->mdmreg[18] = 5; + m->mdmreg[REG_SI1] = 5; info->xmit_size = VBUF; break; case '?': p[0]++; - isdn_tty_at_cout("\r\n0,8", + isdn_tty_at_cout("\r\n0,2,8", info); break; default: @@ -2824,7 +3023,7 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) case '?': p[0]++; sprintf(rs, "\r\n%d", - m->mdmreg[0]); + m->mdmreg[REG_RINGATA]); isdn_tty_at_cout(rs, info); break; case '=': @@ -2832,13 +3031,100 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) par = isdn_getnum(p); if ((par < 0) || (par > 255)) PARSE_ERROR1; - m->mdmreg[0] = par; + m->mdmreg[REG_RINGATA] = par; break; default: PARSE_ERROR1; } return 0; } + if (!strncmp(p[0], "TBC=", 4)) { /* UNKLAR !! */ + p[0] += 4; + printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]); + switch (*p[0]) { + case '0': + p[0]++; + break; + default: + PARSE_ERROR1; + } + return 0; + } + if (!strncmp(p[0], "BOR=", 4)) { /* UNKLAR !! */ + p[0] += 4; + printk(KERN_DEBUG "isdn_tty: Fax FBOR=%c\n", *p[0]); + switch (*p[0]) { + case '0': + p[0]++; + break; + default: + PARSE_ERROR1; + } + return 0; + } + if (!strncmp(p[0], "DCC=", 4)) { /* SETUP irgendwie !! */ + int i, val[]={0,0,0,0,0,0,0,0}; + + p[0] += 4; + if (*p[0] == '?') { + isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0,1),(0),(0),(0-7)",info); + p[0]++; + } else { + for (i=0; (*p[0]>='0') && (*p[0]<='9'); i++) { + val[i] = *p[0] - 48; + p[0]++; + if (*p[0] == ',') + p[0]++; + } + printk(KERN_DEBUG "isdn_tty: Fax Setup values=%d,%d,%d,%d,%d,%d,%d,%d\n", + val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]); + } + return 0; + } + if (!strncmp(p[0], "LID=", 4)) { /* set sender ID !! */ + char senderID[80]; + int i; + + p[0] += 4; + if (*p[0] =='"') + p[0]++; + for(i=0; (*p[0]) && (*p[0] != '"'); i++) + senderID[i] = *p[0]++; + senderID[i] = 0; + if (*p[0] =='"') + p[0]++; + printk(KERN_DEBUG "isdn_tty: Fax sender=>%s<\n", senderID); + return 0; + } + if (!strncmp(p[0], "MFR?", 4)) { + p[0] += 4; + printk(KERN_DEBUG "isdn_tty: FMFR?\n"); + isdn_tty_at_cout("\r\nISDNfax", info); + return 0; + } + if (!strncmp(p[0], "MDL?", 4)) { + p[0] += 4; + printk(KERN_DEBUG "isdn_tty: FMDL?\n"); + isdn_tty_at_cout("\r\nAVM-B1", info); + return 0; + } + if (!strncmp(p[0], "AP=?", 4)) { + p[0] += 4; + printk(KERN_DEBUG "isdn_tty: FAP=?\n"); + return 0; + } + if (!strncmp(p[0], "PHCTO=", 6)) { + /* beim trace mit dem zyxel folgt der wert 30 ;*/ + p[0] += 6; + printk(KERN_DEBUG "isdn_tty: FPHCTO=%s\n", p[0]); + return 0; + } + if (!strncmp(p[0], "CR=", 3)) { + p[0] += 3; + printk(KERN_DEBUG "isdn_tty: FCR=%s\n", p[0]); + return 0; + } + printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]); PARSE_ERROR1; } @@ -3110,10 +3396,10 @@ isdn_tty_parse_at(modem_info * info) p++; switch (isdn_getnum(&p)) { case 0: - m->mdmreg[12] &= ~4; + m->mdmreg[REG_ECHO] &= ~BIT_ECHO; break; case 1: - m->mdmreg[12] |= 4; + m->mdmreg[REG_ECHO] |= BIT_ECHO; break; default: PARSE_ERROR; @@ -3149,6 +3435,11 @@ isdn_tty_parse_at(modem_info * info) p++; isdn_tty_report(info); break; + case '3': + p++; + sprintf(ds, "\r\n%d", info->emu.charge); + isdn_tty_at_cout(ds, info); + break; default: } break; @@ -3166,10 +3457,10 @@ isdn_tty_parse_at(modem_info * info) p++; switch (isdn_getnum(&p)) { case 0: - m->mdmreg[12] |= 1; + m->mdmreg[REG_RESP] |= BIT_RESP; break; case 1: - m->mdmreg[12] &= ~1; + m->mdmreg[REG_RESP] &= ~BIT_RESP; break; default: PARSE_ERROR; @@ -3186,10 +3477,10 @@ isdn_tty_parse_at(modem_info * info) p++; switch (isdn_getnum(&p)) { case 0: - m->mdmreg[12] |= 2; + m->mdmreg[REG_RESP] |= BIT_RESPNUM; break; case 1: - m->mdmreg[12] &= ~2; + m->mdmreg[REG_RESP] &= ~BIT_RESPNUM; break; default: PARSE_ERROR; @@ -3210,7 +3501,7 @@ isdn_tty_parse_at(modem_info * info) return; break; case 'V': - if (!(m->mdmreg[18] & 1)) + if (!(m->mdmreg[REG_SI1] & 1)) PARSE_ERROR; p++; if (isdn_tty_cmd_PLUSV(&p, info)) @@ -3261,14 +3552,14 @@ isdn_tty_edit_at(const char *p, int count, modem_info * info, int user) for (cnt = count; cnt > 0; p++, cnt--) { if (user) - GET_USER(c, p); + get_user(c, p); else c = *p; total++; - if (c == m->mdmreg[3] || c == m->mdmreg[4]) { + if (c == m->mdmreg[REG_CR] || c == m->mdmreg[REG_LF]) { /* Separator (CR oder LF) */ m->mdmcmd[m->mdmcmdl] = 0; - if (m->mdmreg[12] & 4) { + if (m->mdmreg[REG_ECHO] & BIT_ECHO) { eb[0] = c; eb[1] = 0; isdn_tty_at_cout(eb, info); @@ -3278,18 +3569,18 @@ isdn_tty_edit_at(const char *p, int count, modem_info * info, int user) m->mdmcmdl = 0; continue; } - if (c == m->mdmreg[5] && m->mdmreg[5] < 128) { + if (c == m->mdmreg[REG_BS] && m->mdmreg[REG_BS] < 128) { /* Backspace-Funktion */ if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) { if (m->mdmcmdl) m->mdmcmdl--; - if (m->mdmreg[12] & 4) + if (m->mdmreg[REG_ECHO] & BIT_ECHO) isdn_tty_at_cout("\b", info); } continue; } if (cmdchar(c)) { - if (m->mdmreg[12] & 4) { + if (m->mdmreg[REG_ECHO] & BIT_ECHO) { eb[0] = c; eb[1] = 0; isdn_tty_at_cout(eb, info); diff --git a/drivers/isdn/isdn_v110.c b/drivers/isdn/isdn_v110.c new file mode 100644 index 000000000000..ae62378b8757 --- /dev/null +++ b/drivers/isdn/isdn_v110.c @@ -0,0 +1,645 @@ +/* $Id: isdn_v110.c,v 1.2 1998/02/22 19:44:25 fritz Exp $ + + * Linux ISDN subsystem, V.110 related functions (linklevel). + * + * Copyright by Thomas Pfeiffer (pfeiffer@pds.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: isdn_v110.c,v $ + * Revision 1.2 1998/02/22 19:44:25 fritz + * Bugfixes and improvements regarding V.110, V.110 now running. + * + * Revision 1.1 1998/02/20 17:32:09 fritz + * First checkin (not yet completely functionable). + * + */ +#include +#include +#include +#include + +#include +#include "isdn_v110.h" + +#undef ISDN_V110_DEBUG + +char *isdn_v110_revision = "$Revision: 1.2 $"; + +#define V110_38400 255 +#define V110_19200 15 +#define V110_9600 3 + +/* Die folgenden Daten sind fertig kodierte Matrizen, jeweils + als online und offline matrix für 9600, 19200 und 38400 + */ +static unsigned char V110_OnMatrix_9600[] = +{0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, + 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, + 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, + 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd}; + +static unsigned char V110_OffMatrix_9600[] = +{0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +static unsigned char V110_OnMatrix_19200[] = +{0xf0, 0xf0, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, + 0xfd, 0xff, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7}; + +static unsigned char V110_OffMatrix_19200[] = +{0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +static unsigned char V110_OnMatrix_38400[] = +{0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0xfd, 0x7f, 0x7f, 0x7f, 0x7f}; + +static unsigned char V110_OffMatrix_38400[] = +{0x00, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff}; + + +/* FlipBits dreht die Reihenfolge von jeweils keylen bits in einem byte um. + Aus der Bitreihenfolge 76543210 werden bei keylen=4 die bits 45670123, + bei keylen=2 die bits 67452301. Dies ist notwendig, weil die reihenfolge + auf der isdn-leitung falsch herum ist. + */ + +static __inline unsigned char +FlipBits(unsigned char c, int keylen) +{ + unsigned char b = c; + unsigned char bit = 128; + int i; + int j; + int hunks = (8 / keylen); + + c = 0; + for (i = 0; i < hunks; i++) { + for (j = 0; j < keylen; j++) { + if (b & (bit >> j)) + c |= bit >> (keylen - j - 1); + } + bit >>= keylen; + } + return c; +} + + +/* isdn_v110_open allocates and initializes private V.110 data + * structures and returns a pointer to these. + */ +static isdn_v110_stream * +isdn_v110_open(unsigned char key, int hdrlen, int maxsize) +{ + int i; + isdn_v110_stream *v; + + if ((v = kmalloc(sizeof(isdn_v110_stream), GFP_KERNEL)) == NULL) + return NULL; + memset(v, 0, sizeof(isdn_v110_stream)); + v->key = key; + v->nbits = 0; + for (i = 0; key & (1 << i); i++) + v->nbits++; + + v->nbytes = 8 / v->nbits; + v->decodelen = 0; + + switch (key) { + case V110_38400: + v->OnlineFrame = V110_OnMatrix_38400; + v->OfflineFrame = V110_OffMatrix_38400; + break; + case V110_19200: + v->OnlineFrame = V110_OnMatrix_19200; + v->OfflineFrame = V110_OffMatrix_19200; + break; + default: + v->OnlineFrame = V110_OnMatrix_9600; + v->OfflineFrame = V110_OffMatrix_9600; + break; + } + v->framelen = v->nbytes * 10; + v->SyncInit = 5; + v->introducer = 0; + v->dbit = 1; + v->b = 0; + v->skbres = hdrlen; + v->maxsize = maxsize - hdrlen; + if ((v->encodebuf = kmalloc(maxsize, GFP_KERNEL)) == NULL) { + kfree(v); + return NULL; + } + return v; +} + +/* isdn_v110_close frees private V.110 data structures */ +static void +isdn_v110_close(isdn_v110_stream * v) +{ + if (v == NULL) + return; +#ifdef ISDN_V110_DEBUG + printk(KERN_DEBUG "v110 close\n"); +#if 0 + printk(KERN_DEBUG "isdn_v110_close: nbytes=%d\n", v->nbytes); + printk(KERN_DEBUG "isdn_v110_close: nbits=%d\n", v->nbits); + printk(KERN_DEBUG "isdn_v110_close: key=%d\n", v->key); + printk(KERN_DEBUG "isdn_v110_close: SyncInit=%d\n", v->SyncInit); + printk(KERN_DEBUG "isdn_v110:close: decodelen=%d\n", v->decodelen); + printk(KERN_DEBUG "isdn_v110_close: framelen=%d\n", v->framelen); +#endif +#endif + kfree(v->encodebuf); + kfree(v); +} + + +/* ValidHeaderBytes prüft, wieviele bytes in v->decodebuf gültig sind */ + +static int +ValidHeaderBytes(isdn_v110_stream * v) +{ + int i; + for (i = 0; (i < v->decodelen) && (i < v->nbytes); i++) + if ((v->decodebuf[i] & v->key) != 0) + break; + return i; +} + +/* SyncHeader schiebt den decodebuf pointer auf den nächsten gültigen header */ + +static void +SyncHeader(isdn_v110_stream * v) +{ + unsigned char *rbuf = v->decodebuf; + int len = v->decodelen; + + if (len == 0) + return; + for (rbuf++, len--; len > 0; len--, rbuf++) /* such den SyncHeader in buf ! */ + if ((*rbuf & v->key) == 0) /* erstes byte gefunden ? */ + break; /* jupp! */ + if (len) + memcpy(v->decodebuf, rbuf, len); + + v->decodelen = len; +#ifdef ISDN_V110_DEBUG + printk(KERN_DEBUG "isdn_v110: Header resync\n"); +#endif +} + +/* DecodeMatrix takes n (n>=1) matrices (v110 frames, 10 bytes) where + len is the number of matrix-lines. len must be a multiple of 10, i.e. + only complete matices must be given. + From these, netto data is extracted and returned in buf. The return-value + is the bytecount of the decoded data. + */ +static int +DecodeMatrix(isdn_v110_stream * v, unsigned char *m, int len, unsigned char *buf) +{ + int line = 0; + int buflen = 0; + int mbit = 64; + int introducer = v->introducer; + int dbit = v->dbit; + unsigned char b = v->b; + + while (line < len) { /* sind schon alle matrizenzeilen abgearbeitet? */ + if ((line % 10) == 0) { /* die 0. zeile der matrix ist immer null ! */ + if (m[line] != 0x00) { /* nicht 0 ? dann fehler! */ +#ifdef ISDN_V110_DEBUG + printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad Header\n"); +#endif + +/* + dann einen return zu machen, ist auch irgendwie nicht das richtige! :-( + v->introducer = 0; v->dbit = 1; v->b = 0; + return buflen; anzahl schon erzeugter daten zurückgeben! + */ + } + line++; /* sonst die nächste matrixzeile nehmen */ + continue; + } else if ((line % 10) == 5) { /* in zeile 5 stehen nur e-bits ! */ + if ((m[line] & 0x70) != 0x30) { /* 011 muß am anfang stehen! */ +#ifdef ISDN_V110_DEBUG + printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad 5th line\n"); +#endif +/* dann einen return zu machen, ist auch irgendwie nicht das richtige! :-( + v->introducer = 0; v->dbit = 1; v->b = 0; + return buflen; + */ + } + line++; /* alles klar, nächste zeile */ + continue; + } else if (!introducer) { /* every byte starts with 10 (stopbit, startbit) */ + introducer = (m[line] & mbit) ? 0 : 1; /* aktuelles bit der matrix */ + next_byte: + if (mbit > 2) { /* war es das letzte bit dieser matrixzeile ? */ + mbit >>= 1; /* nein, nimm das nächste in dieser zeile */ + continue; + } /* sonst links in der nächsten zeile anfangen */ + mbit = 64; + line++; + continue; + } else { /* sonst müssen wir ein datenbit setzen */ + if (m[line] & mbit) /* war das bit in der matrix gesetzt ? */ + b |= dbit; /* ja, dann setz es auch im datenbyte */ + else + b &= dbit - 1; /* nein, lösch bit im datenbyte */ + if (dbit < 128) /* haben wir schon ein ganzes byte voll ? */ + dbit <<= 1; /* nein, auf zum nächsten datenbit */ + else { /* ein ganzes datenbyte ist voll */ + buf[buflen++] = b; /* byte in den output buffer kopieren */ + introducer = b = 0; /* Init der Introsequenz und des datenbytes */ + dbit = 1; /* als nächstes suchen wir das nullte bit */ + } + goto next_byte; /* suche das nächste bit in der matrix */ + } + } + v->introducer = introducer; + v->dbit = dbit; + v->b = b; + return buflen; /* return anzahl der bytes im output buffer */ +} + +/* DecodeStream erhält vom input stream V110 kodierte Daten, die zu den + V110 frames zusammengepackt werden müssen. Die Daten können an diese + Schnittstelle so übergeben werden, wie sie von der Leitung kommen, ohne + darauf achten zu müssen, das frames usw. eingehalten werden. + */ +struct sk_buff * +isdn_v110_decode(isdn_v110_stream * v, struct sk_buff *skb) +{ + int i; + int j; + int len; + unsigned char *v110_buf; + unsigned char *rbuf; + + if (!skb) { + printk(KERN_WARNING "isdn_v110_decode called with NULL skb!\n"); + return NULL; + } + rbuf = skb->data; + len = skb->len; + if (v == NULL) { + /* invalid handle, no chance to proceed */ + printk(KERN_WARNING "isdn_v110_decode called with NULL stream!\n"); + dev_kfree_skb(skb); + return NULL; + } + if (v->decodelen == 0) /* cache empty? */ + for (; len > 0; len--, rbuf++) /* scan for SyncHeader in buf */ + if ((*rbuf & v->key) == 0) + break; /* found first byte */ + if (len == 0) { + dev_kfree_skb(skb); + return NULL; + } + /* copy new data to decode-buffer */ + memcpy(&(v->decodebuf[v->decodelen]), rbuf, len); + v->decodelen += len; + ReSync: + if (v->decodelen < v->nbytes) { /* got a new header ? */ + dev_kfree_skb(skb); + return NULL; /* no, try later */ + } + if (ValidHeaderBytes(v) != v->nbytes) { /* ist es ein ungültiger header ? */ + SyncHeader(v); /* nein, such einen header */ + goto ReSync; + } + len = (v->decodelen - (v->decodelen % (10 * v->nbytes))) / v->nbytes; + if ((v110_buf = kmalloc(len, GFP_ATOMIC)) == NULL) { + printk(KERN_WARNING "isdn_v110_decode: Couldn't allocate v110_buf\n"); + dev_kfree_skb(skb); + return NULL; + } + for (i = 0; i < len; i++) { + v110_buf[i] = 0; + for (j = 0; j < v->nbytes; j++) + v110_buf[i] |= (v->decodebuf[(i * v->nbytes) + j] & v->key) << (8 - ((j + 1) * v->nbits)); + v110_buf[i] = FlipBits(v110_buf[i], v->nbits); + } + v->decodelen = (v->decodelen % (10 * v->nbytes)); + memcpy(v->decodebuf, &(v->decodebuf[len * v->nbytes]), v->decodelen); + + skb_trim(skb, DecodeMatrix(v, v110_buf, len, skb->data)); + kfree(v110_buf); + if (skb->len) + return skb; + else { + kfree_skb(skb); + return NULL; + } +} + +/* EncodeMatrix takes input data in buf, len is the bytecount. + Data is encoded into v110 frames in m. Return value is the number of + matrix-lines generated. + */ +static int +EncodeMatrix(unsigned char *buf, int len, unsigned char *m, int mlen) +{ + int line = 0; + int i = 0; + int mbit = 128; + int dbit = 1; + int introducer = 3; + int ibit[] = {0, 1, 1}; + + while ((i < len) && (line < mlen)) { /* solange noch input da ist */ + switch (line % 10) { /* in welcher matrixzeile sind wir ? */ + case 0: + m[line++] = 0x00; /* zeile 0 ist immer 0 */ + mbit = 128; /* und es geht mit dem 7. bit weiter */ + break; + case 5: + m[line++] = 0xbf; /* zeile 5 ist immer 10111111 */ + mbit = 128; /* und es geht mit dem 7. bit weiter */ + break; + } + if (line >= mlen) { + printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n"); + return line; + } + next_bit: + switch (mbit) { /* ganz linkes oder rechtes bit ? */ + case 1: + line++; /* ganz rechts ! dann in die nächste */ + if (line >= mlen) { + printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n"); + return line; + } + case 128: + m[line] = 128; /* ganz links byte auf 1000000 setzen */ + mbit = 64; /* aktuelles bit in der matrixzeile */ + continue; + } + if (introducer) { /* 110 sequenz setzen ? */ + introducer--; /* ein digit weniger setzen */ + m[line] |= ibit[introducer] ? mbit : 0; /* entsprechendes bit setzen */ + mbit >>= 1; /* bit der matrixzeile >> 1 */ + goto next_bit; /* und dort weiter machen */ + } /* else datenbits in die matrix packen! */ + m[line] |= (buf[i] & dbit) ? mbit : 0; /* datenbit in matrix setzen */ + if (dbit == 128) { /* war es das letzte datenbit ? */ + dbit = 1; /* dann mach beim nächsten weiter */ + i++; /* nächste datenbyte des input buffers */ + if (i < len) /* war es schon das letzte ? */ + introducer = 3; /* nein, schreib den introducer 110 */ + else { /* war das letzte datenbyte ! */ + m[line] |= (mbit - 1) & 0xfe; /* setz restliche bits der zeile auf 1 */ + break; + } + } else /* nicht das letzte datenbit */ + dbit <<= 1; /* dann gehe zum nächsten datenbit */ + mbit >>= 1; /* und setz bit der matrix weiter */ + goto next_bit; + + } + /* evtl. noch restliche zeilen in der matrix generieren... */ + if ((line) && ((line + 10) < mlen)) + switch (++line % 10) { + case 1: + m[line++] = 0xfe; + case 2: + m[line++] = 0xfe; + case 3: + m[line++] = 0xfe; + case 4: + m[line++] = 0xfe; + case 5: + m[line++] = 0xbf; + case 6: + m[line++] = 0xfe; + case 7: + m[line++] = 0xfe; + case 8: + m[line++] = 0xfe; + case 9: + m[line++] = 0xfe; + } + return line; /* soviele matrixzeilen sind es */ +} + +/* + * Build a sync frame. + */ +static struct sk_buff * +isdn_v110_sync(isdn_v110_stream *v) +{ + struct sk_buff *skb; + + if (v == NULL) { + /* invalid handle, no chance to proceed */ + printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n"); + return NULL; + } + if ((skb = dev_alloc_skb(v->framelen + v->skbres))) { + skb_reserve(skb, v->skbres); + memcpy(skb_put(skb, v->framelen), v->OfflineFrame, v->framelen); + } + return skb; +} + +/* + * Build an idle frame. + */ +static struct sk_buff * +isdn_v110_idle(isdn_v110_stream *v) +{ + struct sk_buff *skb; + + if (v == NULL) { + /* invalid handle, no chance to proceed */ + printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n"); + return NULL; + } + if ((skb = dev_alloc_skb(v->framelen + v->skbres))) { + skb_reserve(skb, v->skbres); + memcpy(skb_put(skb, v->framelen), v->OnlineFrame, v->framelen); + } + return skb; +} + +struct sk_buff * +isdn_v110_encode(isdn_v110_stream * v, struct sk_buff *skb) +{ + int i; + int j; + int rlen; + int mlen; + int olen; + int size; + int sval1; + int sval2; + int nframes; + unsigned char *v110buf; + unsigned char *rbuf; + struct sk_buff *nskb; + + if (v == NULL) { + /* invalid handle, no chance to proceed */ + printk(KERN_WARNING "isdn_v110_encode called with NULL stream!\n"); + return NULL; + } + if (!skb) { + /* invalid skb, no chance to proceed */ + printk(KERN_WARNING "isdn_v110_encode called with NULL skb!\n"); + return NULL; + } + rlen = skb->len; + nframes = (rlen + 3) / 4; + v110buf = v->encodebuf; + if ((nframes * 40) > v->maxsize) { + size = v->maxsize; + rlen = v->maxsize / 40; + } else + size = nframes * 40; + if (!(nskb = dev_alloc_skb(size + v->skbres + sizeof(int)))) { + printk(KERN_WARNING "isdn_v110_encode: Couldn't alloc skb\n"); + return NULL; + } + skb_reserve(nskb, v->skbres + sizeof(int)); + if (skb->len == 0) { + memcpy(skb_put(nskb, v->framelen), v->OnlineFrame, v->framelen); + *((int *)skb_push(nskb, sizeof(int))) = 0; + return nskb; + } + mlen = EncodeMatrix(skb->data, rlen, v110buf, size); + /* jetzt noch jeweils 2 oder 4 bits auf den output stream verteilen! */ + rbuf = skb_put(nskb, size); + olen = 0; + sval1 = 8 - v->nbits; + sval2 = v->key << sval1; + for (i = 0; i < mlen; i++) { + v110buf[i] = FlipBits(v110buf[i], v->nbits); + for (j = 0; j < v->nbytes; j++) { + if (size--) + *rbuf++ = ~v->key | (((v110buf[i] << (j * v->nbits)) & sval2) >> sval1); + else { + printk(KERN_WARNING "isdn_v110_encode: buffers full!\n"); + goto buffer_full; + } + olen++; + } + } +buffer_full: + skb_trim(nskb, olen); + *((int *)skb_push(nskb, sizeof(int))) = rlen; + return nskb; +} + +int +isdn_v110_stat_callback(int idx, isdn_ctrl * c) +{ + isdn_v110_stream *v = NULL; + int i; + int ret; + + if (idx < 0) + return 0; + switch (c->command) { + case ISDN_STAT_BSENT: + /* Keep the send-queue of the driver filled + * with frames: + * If number of outstanding frames < 3, + * send down an Idle-Frame (or an Sync-Frame, if + * v->SyncInit != 0). + */ + if (!(v = dev->v110[idx])) + return 0; + atomic_inc(&dev->v110use[idx]); + if (v->skbidle > 0) { + v->skbidle--; + ret = 1; + } else { + if (v->skbuser > 0) + v->skbuser--; + ret = 0; + } + for (i = v->skbuser + v->skbidle; i < 2; i++) { + struct sk_buff *skb; + if (v->SyncInit > 0) + skb = isdn_v110_sync(v); + else + skb = isdn_v110_idle(v); + if (skb) { + if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) { + dev_kfree_skb(skb); + break; + } else { + if (v->SyncInit) + v->SyncInit--; + v->skbidle++; + } + } else + break; + } + atomic_dec(&dev->v110use[idx]); + return ret; + case ISDN_STAT_DHUP: + case ISDN_STAT_BHUP: + while (1) { + atomic_inc(&dev->v110use[idx]); + if (atomic_dec_and_test(&dev->v110use[idx])) { + isdn_v110_close(dev->v110[idx]); + dev->v110[idx] = NULL; + break; + } + sti(); + } + break; + case ISDN_STAT_BCONN: + if (dev->v110emu[idx] && (dev->v110[idx] == NULL)) { + int hdrlen = dev->drv[c->driver]->interface->hl_hdrlen; + int maxsize = dev->drv[c->driver]->interface->maxbufsize; + atomic_inc(&dev->v110use[idx]); + switch (dev->v110emu[idx]) { + case ISDN_PROTO_L2_V11096: + dev->v110[idx] = isdn_v110_open(V110_9600, hdrlen, maxsize); + break; + case ISDN_PROTO_L2_V11019: + dev->v110[idx] = isdn_v110_open(V110_19200, hdrlen, maxsize); + break; + case ISDN_PROTO_L2_V11038: + dev->v110[idx] = isdn_v110_open(V110_38400, hdrlen, maxsize); + break; + default: + } + if ((v = dev->v110[idx])) { + while (v->SyncInit) { + struct sk_buff *skb = isdn_v110_sync(v); + if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) { + dev_kfree_skb(skb); + /* Unable to send, try later */ + break; + } + v->SyncInit--; + v->skbidle++; + } + } else + printk(KERN_WARNING "isdn_v110: Couldn't open stream for chan %d\n", idx); + atomic_dec(&dev->v110use[idx]); + } + break; + default: + return 0; + } + return 0; +} diff --git a/drivers/isdn/isdn_v110.h b/drivers/isdn/isdn_v110.h new file mode 100644 index 000000000000..9ab5a93f3618 --- /dev/null +++ b/drivers/isdn/isdn_v110.h @@ -0,0 +1,45 @@ +/* $Id: isdn_v110.h,v 1.1 1998/02/20 17:32:11 fritz Exp $ + + * Linux ISDN subsystem, V.110 related functions (linklevel). + * + * Copyright by Thomas Pfeiffer (pfeiffer@pds.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: isdn_v110.h,v $ + * Revision 1.1 1998/02/20 17:32:11 fritz + * First checkin (not yet completely functionable). + * + */ +#ifndef _isdn_v110_h_ +#define _isdn_v110_h_ + +/* isdn_v110_encode erhält len Nettodaten in buf, kodiert diese und liefert + das Ergebnis wieder in buf. Wieviele Bytes kodiert wurden wird als + return zurück gegeben. Diese Daten können dann 1:1 auf die Leitung + gegeben werden. +*/ +extern struct sk_buff *isdn_v110_encode(isdn_v110_stream *, struct sk_buff *); + +/* isdn_v110_decode erhält vom input stream V110 kodierte Daten, die zu den + V110 frames zusammengepackt werden müssen. Die Daten können an diese + Schnittstelle so übergeben werden, wie sie von der Leitung kommen, ohne + darauf achten zu müssen, das frames usw. eingehalten werden. + */ +extern struct sk_buff *isdn_v110_decode(isdn_v110_stream *, struct sk_buff *); + +extern int isdn_v110_stat_callback(int, isdn_ctrl *); + +#endif diff --git a/drivers/isdn/isdn_x25iface.c b/drivers/isdn/isdn_x25iface.c new file mode 100644 index 000000000000..17d291cdfd8e --- /dev/null +++ b/drivers/isdn/isdn_x25iface.c @@ -0,0 +1,340 @@ +/* $Id: isdn_x25iface.c,v 1.3 1998/02/20 17:25:20 fritz Exp $ + * stuff needed to support the Linux X.25 PLP code on top of devices that + * can provide a lab_b service using the concap_proto mechanism. + * This module supports a network interface wich provides lapb_sematics + * -- as defined in ../../Documentation/networking/x25-iface.txt -- to + * the upper layer and assumes that the lower layer provides a reliable + * data link service by means of the the concap_device_ops callbacks. + * + * Only protocol specific stuff goes here. Device specific stuff + * goes to another -- device related -- concap_proto support source file. + * + * $Log: isdn_x25iface.c,v $ + * Revision 1.3 1998/02/20 17:25:20 fritz + * Changes for recent kernels. + * + * Revision 1.2 1998/01/31 22:49:22 keil + * correct comments + * + * Revision 1.1 1998/01/31 22:27:58 keil + * New files from Henner Eisen for X.25 support + * + */ + +/* #include */ +#include +#include +#include +#include "isdn_x25iface.h" + +/* for debugging messages not to cause an oops when device pointer is NULL*/ +#define MY_DEVNAME(dev) ( (dev) ? (dev)->name : "DEVICE UNSPECIFIED" ) + + +typedef struct isdn_x25iface_proto_data { + int magic; + enum wan_states state; + /* Private stuff, not to be accessed via proto_data. We provide the + other storage for the concap_proto instance here as well, + enabling us to allocate both with just one kmalloc(): */ + struct concap_proto priv; +} ix25_pdata_t; + + + +/* is now in header file (extern): struct concap_proto * isdn_x25iface_proto_new(void); */ +void isdn_x25iface_proto_del( struct concap_proto * ); +int isdn_x25iface_proto_close( struct concap_proto * ); +int isdn_x25iface_proto_restart( struct concap_proto *, + struct device *, + struct concap_device_ops *); +int isdn_x25iface_xmit( struct concap_proto *, struct sk_buff * ); +int isdn_x25iface_receive( struct concap_proto *, struct sk_buff * ); +int isdn_x25iface_connect_ind( struct concap_proto * ); +int isdn_x25iface_disconn_ind( struct concap_proto * ); + + +static struct concap_proto_ops ix25_pops = { + &isdn_x25iface_proto_new, + &isdn_x25iface_proto_del, + &isdn_x25iface_proto_restart, + &isdn_x25iface_proto_close, + &isdn_x25iface_xmit, + &isdn_x25iface_receive, + &isdn_x25iface_connect_ind, + &isdn_x25iface_disconn_ind +}; + +/* error message helper fuction */ +static void illegal_state_warn( unsigned state, unsigned char firstbyte) +{ + printk( KERN_WARNING "isdn_x25iface: firstbyte %x illegal in" + "current state %d\n",firstbyte, state ); +} + +/* check protocol data field for consistency */ +static int pdata_is_bad( ix25_pdata_t * pda ){ + + if( pda && pda -> magic == ISDN_X25IFACE_MAGIC ) return 0; + printk( KERN_WARNING + "isdn_x25iface_xxx: illegal pointer to proto data\n" ); + return 1; +} + +/* create a new x25 interface protocol instance + */ +struct concap_proto * isdn_x25iface_proto_new() +{ + ix25_pdata_t * tmp = kmalloc(sizeof(ix25_pdata_t),GFP_KERNEL); + IX25DEBUG("isdn_x25iface_proto_new\n"); + if( tmp ){ + tmp -> magic = ISDN_X25IFACE_MAGIC; + tmp -> state = WAN_UNCONFIGURED; + /* private data space used to hold the concap_proto data. + Only to be accessed via the returned pointer */ + tmp -> priv.dops = NULL; + tmp -> priv.net_dev = NULL; + tmp -> priv.pops = &ix25_pops; + tmp -> priv.flags = 0; + tmp -> priv.proto_data = tmp; + return( &(tmp -> priv) ); + } + return NULL; +}; + +/* close the x25iface encapsulation protocol + */ +int isdn_x25iface_proto_close(struct concap_proto *cprot){ + + ix25_pdata_t *tmp; + int ret = 0; + ulong flags; + + if( ! cprot ){ + printk( KERN_ERR "isdn_x25iface_proto_close: " + "invalid concap_proto pointer\n" ); + return -1; + } + IX25DEBUG( "isdn_x25iface_proto_close %s \n", MY_DEVNAME(cprot -> net_dev) ); + save_flags(flags); + cli(); /* avoid races with incoming events calling pops methods while + cprot members are inconsistent */ + cprot -> dops = NULL; + cprot -> net_dev = NULL; + tmp = cprot -> proto_data; + if( pdata_is_bad( tmp ) ){ + ret = -1; + } else { + tmp -> state = WAN_UNCONFIGURED; + } + restore_flags(flags); + + return ret; +} + +/* Delete the x25iface encapsulation protocol instance + */ +void isdn_x25iface_proto_del(struct concap_proto *cprot){ + + ix25_pdata_t * tmp; + + IX25DEBUG( "isdn_x25iface_proto_del \n" ); + if( ! cprot ){ + printk( KERN_ERR "isdn_x25iface_proto_del: " + "concap_proto pointer is NULL\n" ); + return; + } + tmp = cprot -> proto_data; + if( tmp == NULL ){ + printk( KERN_ERR "isdn_x25iface_proto_del: inconsistent " + "proto_data pointer (maybe already deleted?)\n"); + return; + } + /* close if the protocol is still open */ + if( cprot -> dops ) isdn_x25iface_proto_close(cprot); + /* freeing the storage should be sufficient now. But some additional + settings might help to catch wild pointer bugs */ + tmp -> magic = 0; + cprot -> proto_data = NULL; + + kfree( tmp ); + return; +} + +/* (re-)initialize the data structures for x25iface encapsulation + */ +int isdn_x25iface_proto_restart(struct concap_proto *cprot, + struct device *ndev, + struct concap_device_ops *dops) +{ + ix25_pdata_t * pda = cprot -> proto_data ; + ulong flags; + + IX25DEBUG( "isdn_x25iface_proto_restart %s \n", MY_DEVNAME(ndev) ); + + if ( pdata_is_bad( pda ) ) return -1; + + if( !( dops && dops -> data_req && dops -> connect_req + && dops -> disconn_req ) ){ + printk( KERN_WARNING "isdn_x25iface_restart: required dops" + " missing\n" ); + isdn_x25iface_proto_close(cprot); + return -1; + } + save_flags(flags); + cli(); /* avoid races with incoming events calling pops methods while + cprot members are inconsistent */ + cprot -> net_dev = ndev; + cprot -> pops = &ix25_pops; + cprot -> dops = dops; + pda -> state = WAN_DISCONNECTED; + restore_flags(flags); + return 0; +} + +/* deliver a dl_data frame received from i4l HL driver to the network layer + */ +int isdn_x25iface_receive(struct concap_proto *cprot, struct sk_buff *skb) +{ + IX25DEBUG( "isdn_x25iface_receive %s \n", MY_DEVNAME(cprot->net_dev) ); + if ( ( (ix25_pdata_t*) (cprot->proto_data) ) + -> state == WAN_CONNECTED ){ + skb -> dev = cprot -> net_dev; + skb -> protocol = htons(ETH_P_X25); + skb -> pkt_type = PACKET_HOST; + if( skb_push(skb, 1)){ + skb -> data[0]=0x00; + skb -> mac.raw = skb -> data; + netif_rx(skb); + return 0; + } + } + printk(KERN_WARNING "isdn_x25iface_receive %s: not connected, skb dropped\n", MY_DEVNAME(cprot->net_dev) ); + dev_kfree_skb(skb); + return -1; +} + +/* a connection set up is indicated by lower layer + */ +int isdn_x25iface_connect_ind(struct concap_proto *cprot) +{ + struct sk_buff * skb = dev_alloc_skb(1); + enum wan_states *state_p + = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state); + IX25DEBUG( "isdn_x25iface_connect_ind %s \n" + , MY_DEVNAME(cprot->net_dev) ); + if( *state_p == WAN_UNCONFIGURED ){ + printk(KERN_WARNING + "isdn_x25iface_connect_ind while unconfigured %s\n" + , MY_DEVNAME(cprot->net_dev) ); + return -1; + } + *state_p = WAN_CONNECTED; + if( skb ){ + *( skb_put(skb, 1) ) = 0x01; + skb -> mac.raw = skb -> data; + skb -> dev = cprot -> net_dev; + skb -> protocol = htons(ETH_P_X25); + skb -> pkt_type = PACKET_HOST; + netif_rx(skb); + return 0; + } else { + printk(KERN_WARNING "isdn_x25iface_connect_ind: " + " out of memory -- disconnecting\n"); + cprot -> dops -> disconn_req(cprot); + return -1; + } +} + +/* a disconnect is indicated by lower layer + */ +int isdn_x25iface_disconn_ind(struct concap_proto *cprot) +{ + struct sk_buff *skb; + enum wan_states *state_p + = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state); + IX25DEBUG( "isdn_x25iface_disconn_ind %s \n", MY_DEVNAME(cprot -> net_dev) ); + if( *state_p == WAN_UNCONFIGURED ){ + printk(KERN_WARNING + "isdn_x25iface_disconn_ind while unconfigured\n"); + return -1; + } + if(! cprot -> net_dev) return -1; + *state_p = WAN_DISCONNECTED; + skb = dev_alloc_skb(1); + if( skb ){ + *( skb_put(skb, 1) ) = 0x02; + skb -> mac.raw = skb -> data; + skb -> dev = cprot -> net_dev; + skb -> protocol = htons(ETH_P_X25); + skb -> pkt_type = PACKET_HOST; + netif_rx(skb); + return 0; + } else { + printk(KERN_WARNING "isdn_x25iface_disconn_ind:" + " out of memory\n"); + return -1; + } +} + +/* process a frame handed over to us from linux network layer. First byte + semantics as defined in ../../Documentation/networking/x25-iface.txt + */ +int isdn_x25iface_xmit(struct concap_proto *cprot, struct sk_buff *skb) +{ + unsigned char firstbyte = skb->data[0]; + unsigned *state = + &( ( (ix25_pdata_t*) (cprot -> proto_data) ) -> state ); + int ret = 0; + IX25DEBUG( "isdn_x25iface_xmit: %s first=%x state=%d \n", MY_DEVNAME(cprot -> net_dev), firstbyte, *state ); + switch ( firstbyte ){ + case 0x00: /* dl_data request */ + if( *state == WAN_CONNECTED ){ + skb_pull(skb, 1); + cprot -> net_dev -> trans_start = jiffies; + ret = ( cprot -> dops -> data_req(cprot, skb) ); + /* prepare for future retransmissions */ + if( ret ) skb_push(skb,1); + return ret; + } + illegal_state_warn( *state, firstbyte ); + break; + case 0x01: /* dl_connect request */ + if( *state == WAN_DISCONNECTED ){ + *state = WAN_CONNECTING; + cprot -> dops -> connect_req(cprot); + } else { + illegal_state_warn( *state, firstbyte ); + } + break; + case 0x02: /* dl_disconnect request */ + switch ( *state ){ + case WAN_DISCONNECTED: + /* Should not happen. However, give upper layer a + chance to recover from inconstistency but don't + trust the lower layer sending the disconn_confirm + when already disconnected */ + printk(KERN_WARNING "isdn_x25iface_xmit: disconnect " + " requested while disconnected\n" ); + isdn_x25iface_disconn_ind(cprot); + break; /* prevent infinite loops */ + case WAN_CONNECTING: + case WAN_CONNECTED: + *state = WAN_DISCONNECTED; + cprot -> dops -> disconn_req(cprot); + break; + default: + illegal_state_warn( *state, firstbyte ); + } + break; + case 0x03: /* changing lapb parameters requested */ + printk(KERN_WARNING "isdn_x25iface_xmit: setting of lapb" + " options not yet supported\n"); + break; + default: + printk(KERN_WARNING "isdn_x25iface_xmit: frame with illegal" + " first byte %x ignored:\n", firstbyte); + } + dev_kfree_skb(skb); + return 0; +} diff --git a/drivers/isdn/isdn_x25iface.h b/drivers/isdn/isdn_x25iface.h new file mode 100644 index 000000000000..146eeefff8e1 --- /dev/null +++ b/drivers/isdn/isdn_x25iface.h @@ -0,0 +1,32 @@ +/* $Id: isdn_x25iface.h,v 1.2 1998/01/31 22:49:23 keil Exp $ + */ +#ifndef _LINUX_ISDN_X25IFACE_H +#define _LINUX_ISDN_X25IFACE_H + +#define ISDN_X25IFACE_MAGIC 0x1e75a2b9 +/* #define DEBUG_ISDN_X25 if you want isdn_x25 debugging messages */ +#ifdef DEBUG_ISDN_X25 +# define IX25DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args) +#else +# define IX25DEBUG(fmt,args...) +#endif + +#include +#include +#include +#include + +extern struct concap_proto_ops * isdn_x25iface_concap_proto_ops_pt; +extern struct concap_proto * isdn_x25iface_proto_new(void); + + + +#endif + + + + + + + + diff --git a/drivers/isdn/isdnloop/Makefile b/drivers/isdn/isdnloop/Makefile new file mode 100644 index 000000000000..588d80760f30 --- /dev/null +++ b/drivers/isdn/isdnloop/Makefile @@ -0,0 +1,11 @@ +L_OBJS := +M_OBJS := + +ifeq ($(CONFIG_ISDN_DRV_LOOP),y) + L_OBJS += isdnloop.o +else + M_OBJS += isdnloop.o +endif + +include $(TOPDIR)/Rules.make + diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c new file mode 100644 index 000000000000..faf59ca6115a --- /dev/null +++ b/drivers/isdn/isdnloop/isdnloop.c @@ -0,0 +1,1619 @@ +/* $Id: isdnloop.c,v 1.4 1998/02/24 21:39:05 he Exp $ + + * ISDN low-level module implementing a dummy loop driver. + * + * Copyright 1997 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) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: isdnloop.c,v $ + * Revision 1.4 1998/02/24 21:39:05 he + * L2_PROT_X25DTE / DCE + * additional state 17 and new internal signal messages "BCON_I" + * (for reliable connect confirmation primitive as needed by x.25 upper layer) + * Changes for new LL-HL interface + * + * Revision 1.3 1998/02/20 17:33:30 fritz + * Changes for recent kernels. + * + * Revision 1.2 1997/10/01 09:22:03 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.1 1997/03/24 23:02:04 fritz + * Added isdnloop driver. + * + */ + +#include "isdnloop.h" + +static char +*revision = "$Revision: 1.4 $"; + +static int isdnloop_addcard(char *); + +/* + * Free queue completely. + * + * Parameter: + * card = pointer to card struct + * channel = channel number + */ +static void +isdnloop_free_queue(isdnloop_card * card, int channel) +{ + struct sk_buff_head *queue = &card->bqueue[channel]; + struct sk_buff *skb; + + while ((skb = skb_dequeue(queue))) + dev_kfree_skb(skb); + card->sndcount[channel] = 0; +} + +/* + * Send B-Channel data to another virtual card. + * This routine is called via timer-callback from isdnloop_pollbchan(). + * + * Parameter: + * card = pointer to card struct. + * ch = channel number (0-based) + */ +static void +isdnloop_bchan_send(isdnloop_card * card, int ch) +{ + isdnloop_card *rcard = card->rcard[ch]; + int rch = card->rch[ch], len, ack; + struct sk_buff *skb; + isdn_ctrl cmd; + + while (card->sndcount[ch]) { + if ((skb = skb_dequeue(&card->bqueue[ch]))) { + len = skb->len; + card->sndcount[ch] -= len; + ack = *(skb->head); /* used as scratch area */ + cmd.driver = card->myid; + cmd.arg = ch; + if (rcard){ + rcard->interface.rcvcallb_skb(rcard->myid, rch, skb); + } else { + printk(KERN_WARNING "isdnloop: no rcard, skb dropped\n"); + dev_kfree_skb(skb); + + cmd.command = ISDN_STAT_L1ERR; + cmd.parm.errcode = ISDN_STAT_L1ERR_SEND; + card->interface.statcallb(&cmd); + }; + cmd.command = ISDN_STAT_BSENT; + cmd.parm.length = len; + if ( ack ) card->interface.statcallb(&cmd); + } else + card->sndcount[ch] = 0; + } +} + +/* + * Send/Receive Data to/from the B-Channel. + * This routine is called via timer-callback. + * It schedules itself while any B-Channel is open. + * + * Parameter: + * data = pointer to card struct, set by kernel timer.data + */ +static void +isdnloop_pollbchan(unsigned long data) +{ + isdnloop_card *card = (isdnloop_card *) data; + unsigned long flags; + + if (card->flags & ISDNLOOP_FLAGS_B1ACTIVE) + isdnloop_bchan_send(card, 0); + if (card->flags & ISDNLOOP_FLAGS_B2ACTIVE) + isdnloop_bchan_send(card, 1); + if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE)) { + /* schedule b-channel polling again */ + save_flags(flags); + cli(); + card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD; + add_timer(&card->rb_timer); + card->flags |= ISDNLOOP_FLAGS_RBTIMER; + restore_flags(flags); + } else + card->flags &= ~ISDNLOOP_FLAGS_RBTIMER; +} + +/* + * Parse ICN-type setup string and fill fields of setup-struct + * with parsed data. + * + * Parameter: + * setup = setup string, format: [caller-id],si1,si2,[called-id] + * cmd = pointer to struct to be filled. + */ +static void +isdnloop_parse_setup(char *setup, isdn_ctrl * cmd) +{ + char *t = setup; + char *s = strpbrk(t, ","); + + *s++ = '\0'; + strncpy(cmd->parm.setup.phone, t, sizeof(cmd->parm.setup.phone)); + s = strpbrk(t = s, ","); + *s++ = '\0'; + if (!strlen(t)) + cmd->parm.setup.si1 = 0; + else + cmd->parm.setup.si1 = simple_strtoul(t, NULL, 10); + s = strpbrk(t = s, ","); + *s++ = '\0'; + if (!strlen(t)) + cmd->parm.setup.si2 = 0; + else + cmd->parm.setup.si2 = + simple_strtoul(t, NULL, 10); + strncpy(cmd->parm.setup.eazmsn, s, sizeof(cmd->parm.setup.eazmsn)); + cmd->parm.setup.plan = 0; + cmd->parm.setup.screen = 0; +} + +typedef struct isdnloop_stat { + char *statstr; + int command; + int action; +} isdnloop_stat; +/* *INDENT-OFF* */ +static isdnloop_stat isdnloop_stat_table[] = +{ + {"BCON_", ISDN_STAT_BCONN, 1}, /* B-Channel connected */ + {"BDIS_", ISDN_STAT_BHUP, 2}, /* B-Channel disconnected */ + {"DCON_", ISDN_STAT_DCONN, 0}, /* D-Channel connected */ + {"DDIS_", ISDN_STAT_DHUP, 0}, /* D-Channel disconnected */ + {"DCAL_I", ISDN_STAT_ICALL, 3}, /* Incoming call dialup-line */ + {"DSCA_I", ISDN_STAT_ICALL, 3}, /* Incoming call 1TR6-SPV */ + {"FCALL", ISDN_STAT_ICALL, 4}, /* Leased line connection up */ + {"CIF", ISDN_STAT_CINF, 5}, /* Charge-info, 1TR6-type */ + {"AOC", ISDN_STAT_CINF, 6}, /* Charge-info, DSS1-type */ + {"CAU", ISDN_STAT_CAUSE, 7}, /* Cause code */ + {"TEI OK", ISDN_STAT_RUN, 0}, /* Card connected to wallplug */ + {"NO D-CHAN", ISDN_STAT_NODCH, 0}, /* No D-channel available */ + {"E_L1: ACT FAIL", ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */ + {"E_L2: DATA LIN", ISDN_STAT_BHUP, 8}, /* Layer-2 data link lost */ + {"E_L1: ACTIVATION FAILED", + ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */ + {NULL, 0, -1} +}; +/* *INDENT-ON* */ + + +/* + * Parse Status message-strings from virtual card. + * Depending on status, call statcallb for sending messages to upper + * levels. Also set/reset B-Channel active-flags. + * + * Parameter: + * status = status string to parse. + * channel = channel where message comes from. + * card = card where message comes from. + */ +static void +isdnloop_parse_status(u_char * status, int channel, isdnloop_card * card) +{ + isdnloop_stat *s = isdnloop_stat_table; + int action = -1; + isdn_ctrl cmd; + + while (s->statstr) { + if (!strncmp(status, s->statstr, strlen(s->statstr))) { + cmd.command = s->command; + action = s->action; + break; + } + s++; + } + if (action == -1) + return; + cmd.driver = card->myid; + cmd.arg = channel; + switch (action) { + case 1: + /* BCON_x */ + card->flags |= (channel) ? + ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE; + break; + case 2: + /* BDIS_x */ + card->flags &= ~((channel) ? + ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE); + isdnloop_free_queue(card, channel); + break; + case 3: + /* DCAL_I and DSCA_I */ + isdnloop_parse_setup(status + 6, &cmd); + break; + case 4: + /* FCALL */ + sprintf(cmd.parm.setup.phone, "LEASED%d", card->myid); + sprintf(cmd.parm.setup.eazmsn, "%d", channel + 1); + cmd.parm.setup.si1 = 7; + cmd.parm.setup.si2 = 0; + cmd.parm.setup.plan = 0; + cmd.parm.setup.screen = 0; + break; + case 5: + /* CIF */ + strncpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num) - 1); + break; + case 6: + /* AOC */ + sprintf(cmd.parm.num, "%d", + (int) simple_strtoul(status + 7, NULL, 16)); + break; + case 7: + /* CAU */ + status += 3; + if (strlen(status) == 4) + sprintf(cmd.parm.num, "%s%c%c", + status + 2, *status, *(status + 1)); + else + strncpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num) - 1); + break; + case 8: + /* Misc Errors on L1 and L2 */ + card->flags &= ~ISDNLOOP_FLAGS_B1ACTIVE; + isdnloop_free_queue(card, 0); + cmd.arg = 0; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + cmd.command = ISDN_STAT_DHUP; + cmd.arg = 0; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + cmd.command = ISDN_STAT_BHUP; + card->flags &= ~ISDNLOOP_FLAGS_B2ACTIVE; + isdnloop_free_queue(card, 1); + cmd.arg = 1; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + cmd.command = ISDN_STAT_DHUP; + cmd.arg = 1; + cmd.driver = card->myid; + break; + } + card->interface.statcallb(&cmd); +} + +/* + * Store a cwcharacter into ringbuffer for reading from /dev/isdnctrl + * + * Parameter: + * card = pointer to card struct. + * c = char to store. + */ +static void +isdnloop_putmsg(isdnloop_card * card, unsigned char c) +{ + ulong flags; + + save_flags(flags); + cli(); + *card->msg_buf_write++ = (c == 0xff) ? '\n' : c; + if (card->msg_buf_write == card->msg_buf_read) { + if (++card->msg_buf_read > card->msg_buf_end) + card->msg_buf_read = card->msg_buf; + } + if (card->msg_buf_write > card->msg_buf_end) + card->msg_buf_write = card->msg_buf; + restore_flags(flags); +} + +/* + * Poll a virtual cards message queue. + * If there are new status-replies from the card, copy them to + * ringbuffer for reading on /dev/isdnctrl and call + * isdnloop_parse_status() for processing them. Watch for special + * Firmware bootmessage and parse it, to get the D-Channel protocol. + * If there are B-Channels open, initiate a timer-callback to + * isdnloop_pollbchan(). + * This routine is called periodically via timer interrupt. + * + * Parameter: + * data = pointer to card struct + */ +static void +isdnloop_polldchan(unsigned long data) +{ + isdnloop_card *card = (isdnloop_card *) data; + struct sk_buff *skb; + int avail; + int left; + u_char c; + int ch; + int flags; + u_char *p; + isdn_ctrl cmd; + + if ((skb = skb_dequeue(&card->dqueue))) + avail = skb->len; + else + avail = 0; + for (left = avail; left > 0; left--) { + c = *skb->data; + skb_pull(skb, 1); + isdnloop_putmsg(card, c); + card->imsg[card->iptr] = c; + if (card->iptr < 59) + card->iptr++; + if (!skb->len) { + avail++; + isdnloop_putmsg(card, '\n'); + card->imsg[card->iptr] = 0; + card->iptr = 0; + if (card->imsg[0] == '0' && card->imsg[1] >= '0' && + card->imsg[1] <= '2' && card->imsg[2] == ';') { + ch = (card->imsg[1] - '0') - 1; + p = &card->imsg[3]; + isdnloop_parse_status(p, ch, card); + } else { + p = card->imsg; + if (!strncmp(p, "DRV1.", 5)) { + printk(KERN_INFO "isdnloop: (%s) %s\n", CID, p); + if (!strncmp(p + 7, "TC", 2)) { + card->ptype = ISDN_PTYPE_1TR6; + card->interface.features |= ISDN_FEATURE_P_1TR6; + printk(KERN_INFO + "isdnloop: (%s) 1TR6-Protocol loaded and running\n", CID); + } + if (!strncmp(p + 7, "EC", 2)) { + card->ptype = ISDN_PTYPE_EURO; + card->interface.features |= ISDN_FEATURE_P_EURO; + printk(KERN_INFO + "isdnloop: (%s) Euro-Protocol loaded and running\n", CID); + } + continue; + + } + } + } + } + if (avail) { + cmd.command = ISDN_STAT_STAVAIL; + cmd.driver = card->myid; + cmd.arg = avail; + card->interface.statcallb(&cmd); + } + if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE)) + if (!(card->flags & ISDNLOOP_FLAGS_RBTIMER)) { + /* schedule b-channel polling */ + card->flags |= ISDNLOOP_FLAGS_RBTIMER; + save_flags(flags); + cli(); + del_timer(&card->rb_timer); + card->rb_timer.function = isdnloop_pollbchan; + card->rb_timer.data = (unsigned long) card; + card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD; + add_timer(&card->rb_timer); + restore_flags(flags); + } + /* schedule again */ + save_flags(flags); + cli(); + card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD; + add_timer(&card->st_timer); + restore_flags(flags); +} + +/* + * Append a packet to the transmit buffer-queue. + * + * Parameter: + * channel = Number of B-channel + * skb = packet to send. + * card = pointer to card-struct + * Return: + * Number of bytes transferred, -E??? on error + */ +static int +isdnloop_sendbuf(int channel, struct sk_buff *skb, isdnloop_card * card) +{ + int len = skb->len; + unsigned long flags; + struct sk_buff *nskb; + + if (len > 4000) { + printk(KERN_WARNING + "isdnloop: Send packet too large\n"); + return -EINVAL; + } + if (len) { + if (!(card->flags & (channel) ? ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE)) + return 0; + if (card->sndcount[channel] > ISDNLOOP_MAX_SQUEUE) + return 0; + save_flags(flags); + cli(); + nskb = skb_clone(skb, GFP_ATOMIC); + if (nskb) { + skb_queue_tail(&card->bqueue[channel], nskb); + dev_kfree_skb(skb); + } else + len = 0; + card->sndcount[channel] += len; + restore_flags(flags); + } + return len; +} + +/* + * Read the messages from the card's ringbuffer + * + * Parameter: + * buf = pointer to buffer. + * len = number of bytes to read. + * user = flag, 1: called from userlevel 0: called from kernel. + * card = pointer to card struct. + * Return: + * number of bytes actually transferred. + */ +static int +isdnloop_readstatus(u_char * buf, int len, int user, isdnloop_card * card) +{ + int count; + u_char *p; + + for (p = buf, count = 0; count < len; p++, count++) { + if (card->msg_buf_read == card->msg_buf_write) + return count; + if (user) + put_user(*card->msg_buf_read++, p); + else + *p = *card->msg_buf_read++; + if (card->msg_buf_read > card->msg_buf_end) + card->msg_buf_read = card->msg_buf; + } + return count; +} + +/* + * Simulate a card's response by appending it to the cards + * message queue. + * + * Parameter: + * card = pointer to card struct. + * s = pointer to message-string. + * ch = channel: 0 = generic messages, 1 and 2 = D-channel messages. + * Return: + * 0 on success, 1 on memory squeeze. + */ +static int +isdnloop_fake(isdnloop_card * card, char *s, int ch) +{ + struct sk_buff *skb; + int len = strlen(s) + ((ch >= 0) ? 3 : 0); + + if (!(skb = dev_alloc_skb(len))) { + printk(KERN_WARNING "isdnloop: Out of memory in isdnloop_fake\n"); + return 1; + } + if (ch >= 0) + sprintf(skb_put(skb, 3), "%02d;", ch); + memcpy(skb_put(skb, strlen(s)), s, strlen(s)); + skb_queue_tail(&card->dqueue, skb); + return 0; +} +/* *INDENT-OFF* */ +static isdnloop_stat isdnloop_cmd_table[] = +{ + {"BCON_R", 0, 1}, /* B-Channel connect */ + {"BCON_I", 0, 17}, /* B-Channel connect ind */ + {"BDIS_R", 0, 2}, /* B-Channel disconnect */ + {"DDIS_R", 0, 3}, /* D-Channel disconnect */ + {"DCON_R", 0, 16}, /* D-Channel connect */ + {"DSCA_R", 0, 4}, /* Dial 1TR6-SPV */ + {"DCAL_R", 0, 5}, /* Dial */ + {"EAZC", 0, 6}, /* Clear EAZ listener */ + {"EAZ", 0, 7}, /* Set EAZ listener */ + {"SEEAZ", 0, 8}, /* Get EAZ listener */ + {"MSN", 0, 9}, /* Set/Clear MSN listener */ + {"MSALL", 0, 10}, /* Set multi MSN listeners */ + {"SETSIL", 0, 11}, /* Set SI list */ + {"SEESIL", 0, 12}, /* Get SI list */ + {"SILC", 0, 13}, /* Clear SI list */ + {"LOCK", 0, -1}, /* LOCK channel */ + {"UNLOCK", 0, -1}, /* UNLOCK channel */ + {"FV2ON", 1, 14}, /* Leased mode on */ + {"FV2OFF", 1, 15}, /* Leased mode off */ + {NULL, 0, -1} +}; +/* *INDENT-ON* */ + + +/* + * Simulate an error-response from a card. + * + * Parameter: + * card = pointer to card struct. + */ +static void +isdnloop_fake_err(isdnloop_card * card) +{ + char buf[60]; + + sprintf(buf, "E%s", card->omsg); + isdnloop_fake(card, buf, -1); + isdnloop_fake(card, "NAK", -1); +} + +static u_char ctable_eu[] = +{0x00, 0x11, 0x01, 0x12}; +static u_char ctable_1t[] = +{0x00, 0x3b, 0x01, 0x3a}; + +/* + * Assemble a simplified cause message depending on the + * D-channel protocol used. + * + * Parameter: + * card = pointer to card struct. + * loc = location: 0 = local, 1 = remote. + * cau = cause: 1 = busy, 2 = nonexistent callerid, 3 = no user responding. + * Return: + * Pointer to buffer containing the assembled message. + */ +static char * +isdnloop_unicause(isdnloop_card * card, int loc, int cau) +{ + static char buf[6]; + + switch (card->ptype) { + case ISDN_PTYPE_EURO: + sprintf(buf, "E%02X%02X", (loc) ? 4 : 2, ctable_eu[cau]); + break; + case ISDN_PTYPE_1TR6: + sprintf(buf, "%02X44", ctable_1t[cau]); + break; + default: + return ("0000"); + } + return (buf); +} + +/* + * Release a virtual connection. Called from timer interrupt, when + * called party did not respond. + * + * Parameter: + * card = pointer to card struct. + * ch = channel (0-based) + */ +static void +isdnloop_atimeout(isdnloop_card * card, int ch) +{ + unsigned long flags; + char buf[60]; + + save_flags(flags); + cli(); + if (card->rcard) { + isdnloop_fake(card->rcard[ch], "DDIS_I", card->rch[ch] + 1); + card->rcard[ch]->rcard[card->rch[ch]] = NULL; + card->rcard[ch] = NULL; + } + isdnloop_fake(card, "DDIS_I", ch + 1); + /* No user responding */ + sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 3)); + isdnloop_fake(card, buf, ch + 1); + restore_flags(flags); +} + +/* + * Wrapper for isdnloop_atimeout(). + */ +static void +isdnloop_atimeout0(unsigned long data) +{ + isdnloop_card *card = (isdnloop_card *) data; + isdnloop_atimeout(card, 0); +} + +/* + * Wrapper for isdnloop_atimeout(). + */ +static void +isdnloop_atimeout1(unsigned long data) +{ + isdnloop_card *card = (isdnloop_card *) data; + isdnloop_atimeout(card, 1); +} + +/* + * Install a watchdog for a user, not responding. + * + * Parameter: + * card = pointer to card struct. + * ch = channel to watch for. + */ +static void +isdnloop_start_ctimer(isdnloop_card * card, int ch) +{ + unsigned long flags; + + save_flags(flags); + cli(); + init_timer(&card->c_timer[ch]); + card->c_timer[ch].expires = jiffies + ISDNLOOP_TIMER_ALERTWAIT; + if (ch) + card->c_timer[ch].function = isdnloop_atimeout1; + else + card->c_timer[ch].function = isdnloop_atimeout0; + card->c_timer[ch].data = (unsigned long) card; + add_timer(&card->c_timer[ch]); + restore_flags(flags); +} + +/* + * Kill a pending channel watchdog. + * + * Parameter: + * card = pointer to card struct. + * ch = channel (0-based). + */ +static void +isdnloop_kill_ctimer(isdnloop_card * card, int ch) +{ + unsigned long flags; + + save_flags(flags); + cli(); + del_timer(&card->c_timer[ch]); + restore_flags(flags); +} + +static u_char si2bit[] = +{0, 1, 0, 0, 0, 2, 0, 4, 0, 0}; +static u_char bit2si[] = +{1, 5, 7}; + +/* + * Try finding a listener for an outgoing call. + * + * Parameter: + * card = pointer to calling card. + * p = pointer to ICN-type setup-string. + * lch = channel of calling card. + * cmd = pointer to struct to be filled when parsing setup. + * Return: + * 0 = found match, alerting should happen. + * 1 = found matching number but it is busy. + * 2 = no matching listener. + * 3 = found matching number but SI does not match. + */ +static int +isdnloop_try_call(isdnloop_card * card, char *p, int lch, isdn_ctrl * cmd) +{ + isdnloop_card *cc = cards; + unsigned long flags; + int ch; + int num_match; + int i; + char *e; + char nbuf[32]; + + isdnloop_parse_setup(p, cmd); + while (cc) { + for (ch = 0; ch < 2; ch++) { + /* Exclude ourself */ + if ((cc == card) && (ch == lch)) + continue; + num_match = 0; + switch (cc->ptype) { + case ISDN_PTYPE_EURO: + for (i = 0; i < 3; i++) + if (!(strcmp(cc->s0num[i], cmd->parm.setup.phone))) + num_match = 1; + break; + case ISDN_PTYPE_1TR6: + e = cc->eazlist[ch]; + while (*e) { + sprintf(nbuf, "%s%c", cc->s0num[0], *e); + if (!(strcmp(nbuf, cmd->parm.setup.phone))) + num_match = 1; + e++; + } + } + if (num_match) { + save_flags(flags); + cli(); + /* channel idle? */ + if (!(cc->rcard[ch])) { + /* Check SI */ + if (!(si2bit[cmd->parm.setup.si1] & cc->sil[ch])) { + restore_flags(flags); + return 3; + } + /* ch is idle, si and number matches */ + cc->rcard[ch] = card; + cc->rch[ch] = lch; + card->rcard[lch] = cc; + card->rch[lch] = ch; + restore_flags(flags); + return 0; + } else { + restore_flags(flags); + /* num matches, but busy */ + if (ch == 1) + return 1; + } + } + } + cc = cc->next; + } + return 2; +} + +/* + * Depending on D-channel protocol and caller/called, modify + * phone number. + * + * Parameter: + * card = pointer to card struct. + * phone = pointer phone number. + * caller = flag: 1 = caller, 0 = called. + * Return: + * pointer to new phone number. + */ +static char * +isdnloop_vstphone(isdnloop_card * card, char *phone, int caller) +{ + int i; + static char nphone[30]; + + switch (card->ptype) { + case ISDN_PTYPE_EURO: + if (caller) { + for (i = 0; i < 2; i++) + if (!(strcmp(card->s0num[i], phone))) + return (phone); + return (card->s0num[0]); + } + return (phone); + break; + case ISDN_PTYPE_1TR6: + if (caller) { + sprintf(nphone, "%s%c", card->s0num[0], phone[0]); + return (nphone); + } else + return (&phone[strlen(phone) - 1]); + break; + } + return ("\0"); +} + +/* + * Parse an ICN-type command string sent to the 'card'. + * Perform misc. actions depending on the command. + * + * Parameter: + * card = pointer to card struct. + */ +static void +isdnloop_parse_cmd(isdnloop_card * card) +{ + char *p = card->omsg; + isdn_ctrl cmd; + char buf[60]; + isdnloop_stat *s = isdnloop_cmd_table; + int action = -1; + int i; + int ch; + + if ((card->omsg[0] != '0') && (card->omsg[2] != ';')) { + isdnloop_fake_err(card); + return; + } + ch = card->omsg[1] - '0'; + if ((ch < 0) || (ch > 2)) { + isdnloop_fake_err(card); + return; + } + p += 3; + while (s->statstr) { + if (!strncmp(p, s->statstr, strlen(s->statstr))) { + action = s->action; + if (s->command && (ch != 0)) { + isdnloop_fake_err(card); + return; + } + break; + } + s++; + } + if (action == -1) + return; + switch (action) { + case 1: + /* 0x;BCON_R */ + if (card->rcard[ch - 1]) { + isdnloop_fake(card->rcard[ch - 1], "BCON_I", + card->rch[ch - 1] + 1); + } + break; + case 17: + /* 0x;BCON_I */ + if (card->rcard[ch - 1]) { + isdnloop_fake(card->rcard[ch - 1], "BCON_C", + card->rch[ch - 1] + 1); + } + break; + case 2: + /* 0x;BDIS_R */ + isdnloop_fake(card, "BDIS_C", ch); + if (card->rcard[ch - 1]) { + isdnloop_fake(card->rcard[ch - 1], "BDIS_I", + card->rch[ch - 1] + 1); + } + break; + case 16: + /* 0x;DCON_R */ + isdnloop_kill_ctimer(card, ch - 1); + if (card->rcard[ch - 1]) { + isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]); + isdnloop_fake(card->rcard[ch - 1], "DCON_C", + card->rch[ch - 1] + 1); + isdnloop_fake(card, "DCON_C", ch); + } + break; + case 3: + /* 0x;DDIS_R */ + isdnloop_kill_ctimer(card, ch - 1); + if (card->rcard[ch - 1]) { + isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]); + isdnloop_fake(card->rcard[ch - 1], "DDIS_I", + card->rch[ch - 1] + 1); + card->rcard[ch - 1] = NULL; + } + isdnloop_fake(card, "DDIS_C", ch); + break; + case 4: + /* 0x;DSCA_Rdd,yy,zz,oo */ + if (card->ptype != ISDN_PTYPE_1TR6) { + isdnloop_fake_err(card); + return; + } + /* Fall through */ + case 5: + /* 0x;DCAL_Rdd,yy,zz,oo */ + p += 6; + switch (isdnloop_try_call(card, p, ch - 1, &cmd)) { + case 0: + /* Alerting */ + sprintf(buf, "D%s_I%s,%02d,%02d,%s", + (action == 4) ? "SCA" : "CAL", + isdnloop_vstphone(card, cmd.parm.setup.eazmsn, 1), + cmd.parm.setup.si1, + cmd.parm.setup.si2, + isdnloop_vstphone(card->rcard[ch], + cmd.parm.setup.phone, 0)); + isdnloop_fake(card->rcard[ch - 1], buf, card->rch[ch - 1] + 1); + /* Fall through */ + case 3: + /* si1 does not match, dont alert but start timer */ + isdnloop_start_ctimer(card, ch - 1); + break; + case 1: + /* Remote busy */ + isdnloop_fake(card, "DDIS_I", ch); + sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 1)); + isdnloop_fake(card, buf, ch); + break; + case 2: + /* No such user */ + isdnloop_fake(card, "DDIS_I", ch); + sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 2)); + isdnloop_fake(card, buf, ch); + break; + } + break; + case 6: + /* 0x;EAZC */ + card->eazlist[ch - 1][0] = '\0'; + break; + case 7: + /* 0x;EAZ */ + p += 3; + strcpy(card->eazlist[ch - 1], p); + break; + case 8: + /* 0x;SEEAZ */ + sprintf(buf, "EAZ-LIST: %s", card->eazlist[ch - 1]); + isdnloop_fake(card, buf, ch + 1); + break; + case 9: + /* 0x;MSN */ + break; + case 10: + /* 0x;MSNALL */ + break; + case 11: + /* 0x;SETSIL */ + p += 6; + i = 0; + while (strchr("0157", *p)) { + if (i) + card->sil[ch - 1] |= si2bit[*p - '0']; + i = (*p++ == '0'); + } + if (*p) + isdnloop_fake_err(card); + break; + case 12: + /* 0x;SEESIL */ + sprintf(buf, "SIN-LIST: "); + p = buf + 10; + for (i = 0; i < 3; i++) + if (card->sil[ch - 1] & (1 << i)) + p += sprintf(p, "%02d", bit2si[i]); + isdnloop_fake(card, buf, ch + 1); + break; + case 13: + /* 0x;SILC */ + card->sil[ch - 1] = 0; + break; + case 14: + /* 00;FV2ON */ + break; + case 15: + /* 00;FV2OFF */ + break; + } +} + +/* + * Put command-strings into the of the 'card'. In reality, execute them + * right in place by calling isdnloop_parse_cmd(). Also copy every + * command to the read message ringbuffer, preceeding it with a '>'. + * These mesagges can be read at /dev/isdnctrl. + * + * Parameter: + * buf = pointer to command buffer. + * len = length of buffer data. + * user = flag: 1 = called form userlevel, 0 called from kernel. + * card = pointer to card struct. + * Return: + * number of bytes transfered (currently always equals len). + */ +static int +isdnloop_writecmd(const u_char * buf, int len, int user, isdnloop_card * card) +{ + int xcount = 0; + int ocount = 1; + isdn_ctrl cmd; + + while (len) { + int count = MIN(255, len); + u_char *p; + u_char msg[0x100]; + + if (user) + copy_from_user(msg, buf, count); + else + memcpy(msg, buf, count); + isdnloop_putmsg(card, '>'); + for (p = msg; count > 0; count--, p++) { + len--; + xcount++; + isdnloop_putmsg(card, *p); + card->omsg[card->optr] = *p; + if (*p == '\n') { + card->omsg[card->optr] = '\0'; + card->optr = 0; + isdnloop_parse_cmd(card); + if (len) { + isdnloop_putmsg(card, '>'); + ocount++; + } + } else { + if (card->optr < 59) + card->optr++; + } + ocount++; + } + } + cmd.command = ISDN_STAT_STAVAIL; + cmd.driver = card->myid; + cmd.arg = ocount; + card->interface.statcallb(&cmd); + return xcount; +} + +/* + * Delete card's pending timers, send STOP to linklevel + */ +static void +isdnloop_stopcard(isdnloop_card * card) +{ + unsigned long flags; + isdn_ctrl cmd; + + save_flags(flags); + cli(); + if (card->flags & ISDNLOOP_FLAGS_RUNNING) { + card->flags &= ~ISDNLOOP_FLAGS_RUNNING; + del_timer(&card->st_timer); + del_timer(&card->rb_timer); + del_timer(&card->c_timer[0]); + del_timer(&card->c_timer[1]); + cmd.command = ISDN_STAT_STOP; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + } + restore_flags(flags); +} + +/* + * Stop all cards before unload. + */ +static void +isdnloop_stopallcards(void) +{ + isdnloop_card *p = cards; + + while (p) { + isdnloop_stopcard(p); + p = p->next; + } +} + +/* + * Start a 'card'. Simulate card's boot message and set the phone + * number(s) of the virtual 'S0-Interface'. Install D-channel + * poll timer. + * + * Parameter: + * card = pointer to card struct. + * sdefp = pointer to struct holding ioctl parameters. + * Return: + * 0 on success, -E??? otherwise. + */ +static int +isdnloop_start(isdnloop_card * card, isdnloop_sdef * sdefp) +{ + unsigned long flags; + isdnloop_sdef sdef; + int i; + + if (card->flags & ISDNLOOP_FLAGS_RUNNING) + return -EBUSY; + copy_from_user((char *) &sdef, (char *) sdefp, sizeof(sdef)); + save_flags(flags); + cli(); + switch (sdef.ptype) { + case ISDN_PTYPE_EURO: + if (isdnloop_fake(card, "DRV1.23EC-Q.931-CAPI-CNS-BASIS-20.02.96", + -1)) { + restore_flags(flags); + return -ENOMEM; + } + card->sil[0] = card->sil[1] = 4; + if (isdnloop_fake(card, "TEI OK", 0)) { + restore_flags(flags); + return -ENOMEM; + } + for (i = 0; i < 3; i++) + strcpy(card->s0num[i], sdef.num[i]); + break; + case ISDN_PTYPE_1TR6: + if (isdnloop_fake(card, "DRV1.04TC-1TR6-CAPI-CNS-BASIS-29.11.95", + -1)) { + restore_flags(flags); + return -ENOMEM; + } + card->sil[0] = card->sil[1] = 4; + if (isdnloop_fake(card, "TEI OK", 0)) { + restore_flags(flags); + return -ENOMEM; + } + strcpy(card->s0num[0], sdef.num[0]); + card->s0num[1][0] = '\0'; + card->s0num[2][0] = '\0'; + break; + default: + restore_flags(flags); + printk(KERN_WARNING "isdnloop: Illegal D-channel protocol %d\n", + sdef.ptype); + return -EINVAL; + } + init_timer(&card->st_timer); + card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD; + card->st_timer.function = isdnloop_polldchan; + card->st_timer.data = (unsigned long) card; + add_timer(&card->st_timer); + card->flags |= ISDNLOOP_FLAGS_RUNNING; + restore_flags(flags); + return 0; +} + +/* + * Main handler for commands sent by linklevel. + */ +static int +isdnloop_command(isdn_ctrl * c, isdnloop_card * card) +{ + ulong a; + int i; + char cbuf[60]; + isdn_ctrl cmd; + isdnloop_cdef cdef; + + switch (c->command) { + case ISDN_CMD_IOCTL: + memcpy(&a, c->parm.num, sizeof(ulong)); + switch (c->arg) { + case ISDNLOOP_IOCTL_DEBUGVAR: + return (ulong) card; + case ISDNLOOP_IOCTL_STARTUP: + if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(isdnloop_sdef)))) + return i; + return (isdnloop_start(card, (isdnloop_sdef *) a)); + break; + case ISDNLOOP_IOCTL_ADDCARD: + if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(isdnloop_cdef)))) + return i; + copy_from_user((char *) &cdef, (char *) a, sizeof(cdef)); + return (isdnloop_addcard(cdef.id1)); + break; + case ISDNLOOP_IOCTL_LEASEDCFG: + if (a) { + if (!card->leased) { + card->leased = 1; + while (card->ptype == ISDN_PTYPE_UNKNOWN) { + current->timeout = jiffies + 10; + schedule(); + } + current->timeout = jiffies + 10; + schedule(); + sprintf(cbuf, "00;FV2ON\n01;EAZ1\n02;EAZ2\n"); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + printk(KERN_INFO + "isdnloop: (%s) Leased-line mode enabled\n", + CID); + cmd.command = ISDN_STAT_RUN; + cmd.driver = card->myid; + cmd.arg = 0; + card->interface.statcallb(&cmd); + } + } else { + if (card->leased) { + card->leased = 0; + sprintf(cbuf, "00;FV2OFF\n"); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + printk(KERN_INFO + "isdnloop: (%s) Leased-line mode disabled\n", + CID); + cmd.command = ISDN_STAT_RUN; + cmd.driver = card->myid; + cmd.arg = 0; + card->interface.statcallb(&cmd); + } + } + return 0; + default: + return -EINVAL; + } + break; + case ISDN_CMD_DIAL: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (card->leased) + break; + if ((c->arg & 255) < ISDNLOOP_BCH) { + char *p; + char dial[50]; + char dcode[4]; + + a = c->arg; + p = c->parm.setup.phone; + if (*p == 's' || *p == 'S') { + /* Dial for SPV */ + p++; + strcpy(dcode, "SCA"); + } else + /* Normal Dial */ + strcpy(dcode, "CAL"); + strcpy(dial, p); + sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1), + dcode, dial, c->parm.setup.si1, + c->parm.setup.si2, c->parm.setup.eazmsn); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_ACCEPTD: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + cbuf[0] = 0; + switch (card->l2_proto[a - 1]) { + case ISDN_PROTO_L2_X75I: + sprintf(cbuf, "%02d;BX75\n", (int) a); + break; +#ifdef CONFIG_ISDN_X25 + case ISDN_PROTO_L2_X25DTE: + sprintf(cbuf, "%02d;BX2T\n", (int) a); + break; + case ISDN_PROTO_L2_X25DCE: + sprintf(cbuf, "%02d;BX2C\n", (int) a); + break; +#endif + case ISDN_PROTO_L2_HDLC: + sprintf(cbuf, "%02d;BTRA\n", (int) a); + break; + } + if (strlen(cbuf)) + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + sprintf(cbuf, "%02d;DCON_R\n", (int) a); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_ACCEPTB: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + switch (card->l2_proto[a - 1]) { + case ISDN_PROTO_L2_X75I: + sprintf(cbuf, "%02d;BCON_R,BX75\n", (int) a); + break; +#ifdef CONFIG_ISDN_X25 + case ISDN_PROTO_L2_X25DTE: + sprintf(cbuf, "%02d;BCON_R,BX2T\n", (int) a); + break; + case ISDN_PROTO_L2_X25DCE: + sprintf(cbuf, "%02d;BCON_R,BX2C\n", (int) a); + break; +#endif + case ISDN_PROTO_L2_HDLC: + sprintf(cbuf, "%02d;BCON_R,BTRA\n", (int) a); + break; + default: + sprintf(cbuf, "%02d;BCON_R\n", (int) a); + } + printk(KERN_DEBUG "isdnloop writecmd '%s'\n", cbuf); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + break; + case ISDN_CMD_HANGUP: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_SETEAZ: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (card->leased) + break; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + if (card->ptype == ISDN_PTYPE_EURO) { + sprintf(cbuf, "%02d;MS%s%s\n", (int) a, + c->parm.num[0] ? "N" : "ALL", c->parm.num); + } else + sprintf(cbuf, "%02d;EAZ%s\n", (int) a, + c->parm.num[0] ? c->parm.num : "0123456789"); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_CLREAZ: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (card->leased) + break; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + if (card->ptype == ISDN_PTYPE_EURO) + sprintf(cbuf, "%02d;MSNC\n", (int) a); + else + sprintf(cbuf, "%02d;EAZC\n", (int) a); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_SETL2: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg & 255) < ISDNLOOP_BCH) { + a = c->arg; + switch (a >> 8) { + case ISDN_PROTO_L2_X75I: + sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1); + break; +#ifdef CONFIG_ISDN_X25 + case ISDN_PROTO_L2_X25DTE: + sprintf(cbuf, "%02d;BX2T\n", (int) (a & 255) + 1); + break; + case ISDN_PROTO_L2_X25DCE: + sprintf(cbuf, "%02d;BX2C\n", (int) (a & 255) + 1); + break; +#endif + case ISDN_PROTO_L2_HDLC: + sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1); + break; + default: + return -EINVAL; + } + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + card->l2_proto[a & 255] = (a >> 8); + } + break; + case ISDN_CMD_GETL2: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg & 255) < ISDNLOOP_BCH) + return card->l2_proto[c->arg & 255]; + else + return -ENODEV; + case ISDN_CMD_SETL3: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + return 0; + case ISDN_CMD_GETL3: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg & 255) < ISDNLOOP_BCH) + return ISDN_PROTO_L3_TRANS; + else + return -ENODEV; + case ISDN_CMD_GETEAZ: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + break; + case ISDN_CMD_SETSIL: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + break; + case ISDN_CMD_GETSIL: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + break; + case ISDN_CMD_LOCK: + MOD_INC_USE_COUNT; + break; + case ISDN_CMD_UNLOCK: + MOD_DEC_USE_COUNT; + break; + default: + return -EINVAL; + } + } + return 0; +} + +/* + * Find card with given driverId + */ +static inline isdnloop_card * +isdnloop_findcard(int driverid) +{ + isdnloop_card *p = cards; + + while (p) { + if (p->myid == driverid) + return p; + p = p->next; + } + return (isdnloop_card *) 0; +} + +/* + * Wrapper functions for interface to linklevel + */ +static int +if_command(isdn_ctrl * c) +{ + isdnloop_card *card = isdnloop_findcard(c->driver); + + if (card) + return (isdnloop_command(c, card)); + printk(KERN_ERR + "isdnloop: if_command called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_writecmd(const u_char * buf, int len, int user, int id, int channel) +{ + isdnloop_card *card = isdnloop_findcard(id); + + if (card) { + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + return (isdnloop_writecmd(buf, len, user, card)); + } + printk(KERN_ERR + "isdnloop: if_writecmd called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_readstatus(u_char * buf, int len, int user, int id, int channel) +{ + isdnloop_card *card = isdnloop_findcard(id); + + if (card) { + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + return (isdnloop_readstatus(buf, len, user, card)); + } + printk(KERN_ERR + "isdnloop: if_readstatus called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_sendbuf(int id, int channel, int ack, struct sk_buff *skb) +{ + isdnloop_card *card = isdnloop_findcard(id); + + if (card) { + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + /* ack request stored in skb scratch area */ + *(skb->head) = ack; + return (isdnloop_sendbuf(channel, skb, card)); + } + printk(KERN_ERR + "isdnloop: 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 isdnloop_card * +isdnloop_initcard(char *id) +{ + isdnloop_card *card; + int i; + + if (!(card = (isdnloop_card *) kmalloc(sizeof(isdnloop_card), GFP_KERNEL))) { + printk(KERN_WARNING + "isdnloop: (%s) Could not allocate card-struct.\n", id); + return (isdnloop_card *) 0; + } + memset((char *) card, 0, sizeof(isdnloop_card)); + card->interface.channels = ISDNLOOP_BCH; + card->interface.hl_hdrlen = 1; /* scratch area for storing ack flag*/ + 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 | +#ifdef CONFIG_ISDN_X25 + ISDN_FEATURE_L2_X25DTE | + ISDN_FEATURE_L2_X25DCE | +#endif + ISDN_FEATURE_L2_HDLC | + ISDN_FEATURE_L3_TRANS | + ISDN_FEATURE_P_UNKNOWN; + card->ptype = ISDN_PTYPE_UNKNOWN; + strncpy(card->interface.id, id, sizeof(card->interface.id) - 1); + card->msg_buf_write = card->msg_buf; + card->msg_buf_read = card->msg_buf; + card->msg_buf_end = &card->msg_buf[sizeof(card->msg_buf) - 1]; + for (i = 0; i < ISDNLOOP_BCH; i++) { + card->l2_proto[i] = ISDN_PROTO_L2_X75I; + skb_queue_head_init(&card->bqueue[i]); + } + skb_queue_head_init(&card->dqueue); + card->next = cards; + cards = card; + if (!register_isdn(&card->interface)) { + cards = cards->next; + printk(KERN_WARNING + "isdnloop: Unable to register %s\n", id); + kfree(card); + return (isdnloop_card *) 0; + } + card->myid = card->interface.channels; + return card; +} + +static int +isdnloop_addcard(char *id1) +{ + ulong flags; + isdnloop_card *card; + + save_flags(flags); + cli(); + if (!(card = isdnloop_initcard(id1))) { + restore_flags(flags); + return -EIO; + } + restore_flags(flags); + printk(KERN_INFO + "isdnloop: (%s) virtual card added\n", + card->interface.id); + return 0; +} + +#ifdef MODULE +#define isdnloop_init init_module +#else +void +isdnloop_setup(char *str, int *ints) +{ + static char sid[20]; + + if (strlen(str)) { + strcpy(sid, str); + isdnloop_id = sid; + } +} +#endif + +int +isdnloop_init(void) +{ + char *p; + char rev[10]; + + /* No symbols to export, hide all symbols */ + EXPORT_NO_SYMBOLS; + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else + strcpy(rev, " ??? "); + printk(KERN_NOTICE "isdnloop-ISDN-driver Rev%s\n", rev); + return (isdnloop_addcard(isdnloop_id)); +} + +#ifdef MODULE +void +cleanup_module(void) +{ + isdn_ctrl cmd; + isdnloop_card *card = cards; + isdnloop_card *last; + int i; + + isdnloop_stopallcards(); + while (card) { + cmd.command = ISDN_STAT_UNLOAD; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + for (i = 0; i < ISDNLOOP_BCH; i++) + isdnloop_free_queue(card, i); + card = card->next; + } + card = cards; + while (card) { + struct sk_buff *skb; + + last = card; + while ((skb = skb_dequeue(&card->dqueue))) + dev_kfree_skb(skb); + card = card->next; + kfree(last); + } + printk(KERN_NOTICE "isdnloop-ISDN-driver unloaded\n"); +} +#endif diff --git a/drivers/isdn/isdnloop/isdnloop.h b/drivers/isdn/isdnloop/isdnloop.h new file mode 100644 index 000000000000..2eb8d25835da --- /dev/null +++ b/drivers/isdn/isdnloop/isdnloop.h @@ -0,0 +1,145 @@ +/* $Id: isdnloop.h,v 1.2 1997/10/01 09:22:07 fritz Exp $ + + * Loopback lowlevel module for testing of linklevel. + * + * Copyright 1997 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) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: isdnloop.h,v $ + * Revision 1.2 1997/10/01 09:22:07 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.1 1997/03/24 23:02:05 fritz + * Added isdnloop driver. + * + */ + +#ifndef isdnloop_h +#define isdnloop_h + +#define ISDNLOOP_IOCTL_DEBUGVAR 0 +#define ISDNLOOP_IOCTL_ADDCARD 1 +#define ISDNLOOP_IOCTL_LEASEDCFG 2 +#define ISDNLOOP_IOCTL_STARTUP 3 + +/* Struct for adding new cards */ +typedef struct isdnloop_cdef { + char id1[10]; +} isdnloop_cdef; + +/* Struct for configuring cards */ +typedef struct isdnloop_sdef { + int ptype; + char num[3][20]; +} isdnloop_sdef; + +#if defined(__KERNEL__) || defined(__DEBUGVAR__) + +#ifdef __KERNEL__ +/* Kernel includes */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* __KERNEL__ */ + +#define ISDNLOOP_FLAGS_B1ACTIVE 1 /* B-Channel-1 is open */ +#define ISDNLOOP_FLAGS_B2ACTIVE 2 /* B-Channel-2 is open */ +#define ISDNLOOP_FLAGS_RUNNING 4 /* Cards driver activated */ +#define ISDNLOOP_FLAGS_RBTIMER 8 /* scheduling of B-Channel-poll */ +#define ISDNLOOP_TIMER_BCREAD 1 /* B-Channel poll-cycle */ +#define ISDNLOOP_TIMER_DCREAD (HZ/2) /* D-Channel poll-cycle */ +#define ISDNLOOP_TIMER_ALERTWAIT (10*HZ) /* Alert timeout */ +#define ISDNLOOP_MAX_SQUEUE 65536 /* Max. outstanding send-data */ +#define ISDNLOOP_BCH 2 /* channels per card */ + +/* + * Per card driver data + */ +typedef struct isdnloop_card { + struct isdnloop_card *next; /* Pointer to next device struct */ + struct isdnloop_card + *rcard[ISDNLOOP_BCH]; /* Pointer to 'remote' card */ + int rch[ISDNLOOP_BCH]; /* 'remote' channel */ + int myid; /* Driver-Nr. assigned by linklevel */ + int leased; /* Flag: This Adapter is connected */ + /* to a leased line */ + int sil[ISDNLOOP_BCH]; /* SI's to listen for */ + char eazlist[ISDNLOOP_BCH][11]; + /* EAZ's to listen for */ + char s0num[3][20]; /* 1TR6 base-number or MSN's */ + unsigned short flags; /* Statusflags */ + int ptype; /* Protocol type (1TR6 or Euro) */ + struct timer_list st_timer; /* Timer for Status-Polls */ + struct timer_list rb_timer; /* Timer for B-Channel-Polls */ + struct timer_list + c_timer[ISDNLOOP_BCH]; /* Timer for Alerting */ + int l2_proto[ISDNLOOP_BCH]; /* Current layer-2-protocol */ + isdn_if interface; /* Interface to upper layer */ + int iptr; /* Index to imsg-buffer */ + char imsg[60]; /* Internal buf for status-parsing */ + int optr; /* Index to omsg-buffer */ + char omsg[60]; /* Internal buf for cmd-parsing */ + char msg_buf[2048]; /* Buffer for status-messages */ + char *msg_buf_write; /* Writepointer for statusbuffer */ + char *msg_buf_read; /* Readpointer for statusbuffer */ + char *msg_buf_end; /* Pointer to end of statusbuffer */ + int sndcount[ISDNLOOP_BCH]; /* Byte-counters for B-Ch.-send */ + struct sk_buff_head + bqueue[ISDNLOOP_BCH]; /* B-Channel queues */ + struct sk_buff_head dqueue; /* D-Channel queue */ +} isdnloop_card; + +/* + * Main driver data + */ +#ifdef __KERNEL__ +static isdnloop_card *cards = (isdnloop_card *) 0; +static char *isdnloop_id = "\0"; + +#ifdef MODULE +MODULE_AUTHOR("Fritz Elfert"); +MODULE_PARM(isdnloop_id, "s"); +MODULE_PARM_DESC(isdnloop_id, "ID-String of first card"); +#endif + +#endif /* __KERNEL__ */ + +/* Utility-Macros */ + +#define CID (card->interface.id) +#define MIN(a,b) ((ab)?a:b) + +#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */ +#endif /* isdnloop_h */ diff --git a/drivers/isdn/pcbit/capi.c b/drivers/isdn/pcbit/capi.c index 790da879248d..ed681f375547 100644 --- a/drivers/isdn/pcbit/capi.c +++ b/drivers/isdn/pcbit/capi.c @@ -147,9 +147,6 @@ int capi_conn_resp(struct pcbit_chan* chan, struct sk_buff **skb) return -1; } - SET_SKB_FREE((*skb)); - - *((ushort*) skb_put(*skb, 2) ) = chan->callref; *(skb_put(*skb, 1)) = 0x01; /* ACCEPT_CALL */ *(skb_put(*skb, 1)) = 0; @@ -170,8 +167,6 @@ int capi_conn_active_req(struct pcbit_chan* chan, struct sk_buff **skb) return -1; } - SET_SKB_FREE((*skb)); - *((ushort*) skb_put(*skb, 2) ) = chan->callref; #ifdef DEBUG @@ -200,8 +195,6 @@ int capi_conn_active_resp(struct pcbit_chan* chan, struct sk_buff **skb) return -1; } - SET_SKB_FREE((*skb)); - *((ushort*) skb_put(*skb, 2) ) = chan->callref; return 2; @@ -222,8 +215,6 @@ int capi_select_proto_req(struct pcbit_chan *chan, struct sk_buff **skb, return -1; } - SET_SKB_FREE((*skb)); - *((ushort*) skb_put(*skb, 2) ) = chan->callref; /* Layer2 protocol */ @@ -285,8 +276,6 @@ int capi_activate_transp_req(struct pcbit_chan *chan, struct sk_buff **skb) return -1; } - SET_SKB_FREE((*skb)); - *((ushort*) skb_put(*skb, 2) ) = chan->callref; @@ -338,8 +327,6 @@ int capi_tdata_resp(struct pcbit_chan *chan, struct sk_buff ** skb) return -1; } - SET_SKB_FREE((*skb)); - *((ushort*) skb_put(*skb, 2) ) = chan->callref; *(skb_put(*skb, 1)) = chan->layer2link; @@ -357,8 +344,6 @@ int capi_disc_req(ushort callref, struct sk_buff **skb, u_char cause) return -1; } - SET_SKB_FREE((*skb)); - *((ushort*) skb_put(*skb, 2) ) = callref; *(skb_put(*skb, 1)) = 2; /* Cause.Length = 2; */ @@ -382,8 +367,6 @@ int capi_disc_resp(struct pcbit_chan *chan, struct sk_buff **skb) return -1; } - SET_SKB_FREE((*skb)); - *((ushort*) skb_put(*skb, 2)) = chan->callref; return 2; diff --git a/drivers/isdn/pcbit/drv.c b/drivers/isdn/pcbit/drv.c index cacb714bee82..43c36fa9ee2c 100644 --- a/drivers/isdn/pcbit/drv.c +++ b/drivers/isdn/pcbit/drv.c @@ -61,7 +61,7 @@ static char* pcbit_devname[MAX_PCBIT_CARDS] = { int pcbit_command(isdn_ctrl* ctl); int pcbit_stat(u_char* buf, int len, int user, int, int); -int pcbit_xmit(int driver, int chan, struct sk_buff *skb); +int pcbit_xmit(int driver, int chan, int ack, struct sk_buff *skb); int pcbit_writecmd(const u_char*, int, int, int, int); static int set_protocol_running(struct pcbit_dev * dev); @@ -164,7 +164,6 @@ int pcbit_init_dev(int board, int mem_base, int irq) ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L2_TRANS ); dev_if->writebuf_skb = pcbit_xmit; - dev_if->writebuf = NULL; dev_if->hl_hdrlen = 10; dev_if->maxbufsize = MAXBUFSIZE; @@ -330,7 +329,7 @@ static void pcbit_block_timer(unsigned long data) } #endif -int pcbit_xmit(int driver, int chnum, struct sk_buff *skb) +int pcbit_xmit(int driver, int chnum, int ack, struct sk_buff *skb) { ushort hdrlen; int refnum, len; @@ -731,8 +730,6 @@ void pcbit_l3_receive(struct pcbit_dev * dev, ulong msg, #endif } - SET_SKB_FREE(skb); - kfree_skb(skb); } diff --git a/drivers/isdn/pcbit/layer2.c b/drivers/isdn/pcbit/layer2.c index 0ccb2b7c90a7..8283b7367033 100644 --- a/drivers/isdn/pcbit/layer2.c +++ b/drivers/isdn/pcbit/layer2.c @@ -380,10 +380,8 @@ pcbit_receive(struct pcbit_dev *dev) return; #else /* discard previous queued frame */ - if (dev->read_frame->skb) { - SET_SKB_FREE(dev->read_frame->skb); + if (dev->read_frame->skb) kfree_skb(dev->read_frame->skb); - } kfree(dev->read_frame); dev->read_frame = NULL; #endif @@ -648,10 +646,8 @@ pcbit_l2_err_recover(unsigned long data) dev->w_busy = dev->r_busy = 1; if (dev->read_frame) { - if (dev->read_frame->skb) { - SET_SKB_FREE(dev->read_frame->skb); + if (dev->read_frame->skb) kfree_skb(dev->read_frame->skb); - } kfree(dev->read_frame); dev->read_frame = NULL; } diff --git a/drivers/isdn/pcbit/module.c b/drivers/isdn/pcbit/module.c index 34f8fc1c54e6..33aee3360eb3 100644 --- a/drivers/isdn/pcbit/module.c +++ b/drivers/isdn/pcbit/module.c @@ -35,10 +35,8 @@ 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 @@ -87,11 +85,7 @@ 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/debug.c b/drivers/isdn/sc/debug.c index 3a814de931bb..c5312cd83e63 100644 --- a/drivers/isdn/sc/debug.c +++ b/drivers/isdn/sc/debug.c @@ -1,5 +1,5 @@ /* - * $Id: debug.c,v 1.2 1996/11/20 17:49:50 fritz Exp $ + * $Id: debug.c,v 1.3 1997/10/01 09:22:20 fritz Exp $ * Copyright (C) 1996 SpellCaster Telecommunications Inc. * * This program is free software; you can redistribute it and/or modify @@ -29,13 +29,8 @@ #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 +#define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e) +#define FREE_IRQ(a,b) free_irq(a,b) inline char *strcpy(char *, const char *); diff --git a/drivers/isdn/sc/event.c b/drivers/isdn/sc/event.c index 3452cbf36361..23cd53f07f37 100644 --- a/drivers/isdn/sc/event.c +++ b/drivers/isdn/sc/event.c @@ -1,5 +1,5 @@ /* - * $Id: event.c,v 1.3 1997/02/11 22:53:41 fritz Exp $ + * $Id: event.c,v 1.4 1997/10/09 22:30:58 fritz Exp $ * Copyright (C) 1996 SpellCaster Telecommunications Inc. * * This program is free software; you can redistribute it and/or modify @@ -62,10 +62,16 @@ int indicate_status(int card, int event,ulong Channel,char *Data) 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); + switch (event) { + case ISDN_STAT_BSENT: + memcpy(&cmd.parm.length, Data, sizeof(cmd.parm.length)); + break; + case ISDN_STAT_ICALL: + memcpy(&cmd.parm.setup, Data, sizeof(cmd.parm.setup)); + break; + default: + strcpy(cmd.parm.num, Data); + } } cmd.command = event; diff --git a/drivers/isdn/sc/hardware.h b/drivers/isdn/sc/hardware.h index 4a769822567b..b0f07ac3c9cd 100644 --- a/drivers/isdn/sc/hardware.h +++ b/drivers/isdn/sc/hardware.h @@ -16,6 +16,11 @@ 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 */ diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c index c9eb24035e65..d34dd03b9f88 100644 --- a/drivers/isdn/sc/init.c +++ b/drivers/isdn/sc/init.c @@ -20,7 +20,7 @@ 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 sndpkt(int, int, int, struct sk_buff *); extern int command(isdn_ctrl *); extern int indicate_status(int, int, ulong, char*); extern int reset(int); @@ -38,12 +38,10 @@ int irq_supported(int irq_x) } #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 /* diff --git a/drivers/isdn/sc/interrupt.c b/drivers/isdn/sc/interrupt.c index 6b5b369e363b..25964752ba69 100644 --- a/drivers/isdn/sc/interrupt.c +++ b/drivers/isdn/sc/interrupt.c @@ -1,5 +1,5 @@ /* - * $Id: interrupt.c,v 1.3 1997/02/11 22:53:43 fritz Exp $ + * $Id: interrupt.c,v 1.4 1998/01/31 22:10:52 keil Exp $ * Copyright (C) 1996 SpellCaster Telecommunications Inc. * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/isdn/sc/message.c b/drivers/isdn/sc/message.c index e104fada6466..2cbdcdae895e 100644 --- a/drivers/isdn/sc/message.c +++ b/drivers/isdn/sc/message.c @@ -1,5 +1,5 @@ /* - * $Id: message.c,v 1.2 1996/11/20 17:49:54 fritz Exp $ + * $Id: message.c,v 1.3 1998/01/31 22:10:55 keil Exp $ * Copyright (C) 1996 SpellCaster Telecommunications Inc. * * message.c - functions for sending and receiving control messages @@ -33,7 +33,6 @@ #include "hardware.h" #include "message.h" #include "card.h" -#include extern board *adapter[]; extern unsigned int cinst; @@ -203,7 +202,7 @@ int sendmessage(int card, * wait for an empty slot in the queue */ while (!(inb(adapter[card]->ioport[FIFO_STATUS]) & WF_NOT_FULL)) - __SLOW_DOWN_IO; + udelay(1); /* * Disable interrupts and map in shared memory diff --git a/drivers/isdn/sc/packet.c b/drivers/isdn/sc/packet.c index 563d1821db15..d75cb04d7f7b 100644 --- a/drivers/isdn/sc/packet.c +++ b/drivers/isdn/sc/packet.c @@ -1,5 +1,5 @@ /* - * $Id: packet.c,v 1.2 1996/11/20 17:49:55 fritz Exp $ + * $Id: packet.c,v 1.4 1998/02/12 23:08:50 keil Exp $ * Copyright (C) 1996 SpellCaster Telecommunications Inc. * * This program is free software; you can redistribute it and/or modify @@ -36,7 +36,7 @@ extern board *adapter[]; extern unsigned int cinst; extern int get_card_from_id(int); -extern int indicate_status(int, int,ulong,char*); +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, @@ -47,6 +47,7 @@ int sndpkt(int devId, int channel, struct sk_buff *data) LLData ReqLnkWrite; int status; int card; + unsigned long len; card = get_card_from_id(devId); @@ -89,6 +90,7 @@ int sndpkt(int devId, int channel, struct sk_buff *data) status = sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkWrite, channel+1, sizeof(LLData), (unsigned int*)&ReqLnkWrite); + len = data->len; if(status) { pr_debug("%s: Failed to send packet, status = %d\n", adapter[card]->devicename, status); return -1; @@ -101,9 +103,9 @@ int sndpkt(int devId, int channel, struct sk_buff *data) adapter[card]->channel[channel].next_sendbuf; pr_debug("%s: Packet sent successfully\n", adapter[card]->devicename); dev_kfree_skb(data); - indicate_status(card,ISDN_STAT_BSENT,channel,NULL); + indicate_status(card,ISDN_STAT_BSENT,channel, (char *)&len); } - return data->len; + return len; } void rcvpkt(int card, RspMessage *rcvmsg) diff --git a/drivers/misc/parport_pc.c b/drivers/misc/parport_pc.c index bd542b4fd761..012542b09446 100644 --- a/drivers/misc/parport_pc.c +++ b/drivers/misc/parport_pc.c @@ -482,8 +482,9 @@ static int parport_SPP_supported(struct parport *pb) */ static int parport_ECR_present(struct parport *pb) { - unsigned char r, octr = parport_pc_read_control(pb), - oecr = parport_pc_read_econtrol(pb); + unsigned char r, octr = parport_pc_read_control(pb); + unsigned char oecr = parport_pc_read_econtrol(pb); + unsigned char tmp; r = parport_pc_read_control(pb); if ((parport_pc_read_econtrol(pb) & 0x3) == (r & 0x3)) { @@ -500,12 +501,14 @@ static int parport_ECR_present(struct parport *pb) return 0; parport_pc_write_econtrol(pb, 0x34); - if (parport_pc_read_econtrol(pb) != 0x35) - return 0; + tmp = parport_pc_read_econtrol(pb); parport_pc_write_econtrol(pb, oecr); parport_pc_write_control(pb, octr); + if (tmp != 0x35) + return 0; + return PARPORT_MODE_PCECR; } @@ -735,6 +738,7 @@ static int irq_probe_EPP(struct parport *pb) { int irqs; unsigned char octr = parport_pc_read_control(pb); + unsigned char oecr = parport_pc_read_econtrol(pb); #ifndef ADVANCED_DETECT return PARPORT_IRQ_NONE; @@ -758,6 +762,7 @@ static int irq_probe_EPP(struct parport *pb) udelay(20); pb->irq = close_intr_election(irqs); + parport_pc_write_econtrol(pb, oecr); parport_pc_write_control(pb, octr); return pb->irq; } @@ -766,6 +771,7 @@ static int irq_probe_SPP(struct parport *pb) { int irqs; unsigned char octr = parport_pc_read_control(pb); + unsigned char oecr = parport_pc_read_econtrol(pb); #ifndef ADVANCED_DETECT return PARPORT_IRQ_NONE; @@ -794,6 +800,7 @@ static int irq_probe_SPP(struct parport *pb) if (pb->irq <= 0) pb->irq = PARPORT_IRQ_NONE; /* No interrupt detected */ + parport_pc_write_econtrol(pb, oecr); parport_pc_write_control(pb, octr); return pb->irq; } @@ -815,7 +822,7 @@ static int parport_irq_probe(struct parport *pb) if (pb->irq == PARPORT_IRQ_NONE && (pb->modes & PARPORT_MODE_PCECPEPP)) { - int oecr = parport_pc_read_econtrol(pb); + unsigned char oecr = parport_pc_read_econtrol(pb); parport_pc_write_econtrol(pb, 0x80); pb->irq = irq_probe_EPP(pb); parport_pc_write_econtrol(pb, oecr); diff --git a/drivers/net/smc-mca.c b/drivers/net/smc-mca.c index 7a4ceca36b6f..79d6d8076853 100644 --- a/drivers/net/smc-mca.c +++ b/drivers/net/smc-mca.c @@ -306,9 +306,9 @@ static int ultramca_close_card(struct device *dev) #define MAX_ULTRAMCA_CARDS 4 /* Max number of Ultra cards per module */ #define NAMELEN 8 /* # of chars for storing dev->name */ -static char namelist[NAMELEN * MAX_ULTRA_CARDS] = { 0, }; +static char namelist[NAMELEN * MAX_ULTRAMCA_CARDS] = { 0, }; -static struct device dev_ultra[MAX_ULTRA_CARDS] = +static struct device dev_ultra[MAX_ULTRAMCA_CARDS] = { { NULL, /* assign a chunk of namelist[] below */ @@ -318,11 +318,11 @@ static struct device dev_ultra[MAX_ULTRA_CARDS] = }, }; -static int io[MAX_ULTRA_CARDS] = { 0, }; -static int irq[MAX_ULTRA_CARDS] = { 0, }; +static int io[MAX_ULTRAMCA_CARDS] = { 0, }; +static int irq[MAX_ULTRAMCA_CARDS] = { 0, }; -MODULE_PARM(io, "1-" __MODULE_STRING(MAX_ULTRA_CARDS) "i"); -MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ULTRA_CARDS) "i"); +MODULE_PARM(io, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i"); +MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i"); /* This is set up so that only a single autoprobe takes place per call. ISA device autoprobes on a running machine are not recommended. */ @@ -331,7 +331,7 @@ int init_module(void) { int this_dev, found = 0; - for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) + for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++) { struct device *dev = &dev_ultra[this_dev]; dev->name = namelist+(NAMELEN*this_dev); @@ -360,7 +360,7 @@ void cleanup_module(void) { int this_dev; - for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) + for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++) { struct device *dev = &dev_ultra[this_dev]; if (dev->priv != NULL) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 1d14136fb40c..b5d76f7e52f4 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -28,6 +28,8 @@ #include #include +int sg_big_buff = SG_BIG_BUFF; /* for now, sg_big_buff is read-only through sysctl */ + static int sg_init(void); static int sg_attach(Scsi_Device *); static int sg_detect(Scsi_Device *); diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index be58c11eddf6..7da69373229d 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -362,8 +362,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs current->mm->rss = 0; current->mm->mmap = NULL; - current->suid = current->euid = current->fsuid = bprm->e_uid; - current->sgid = current->egid = current->fsgid = bprm->e_gid; + compute_creds(bprm); current->flags &= ~PF_FORKNOEXEC; #ifdef __sparc__ if (N_MAGIC(ex) == NMAGIC) { diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 50f071faf46d..ff4f5971086d 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -705,8 +705,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) #ifndef VM_STACK_FLAGS current->executable = dget(bprm->dentry); #endif - current->suid = current->euid = current->fsuid = bprm->e_uid; - current->sgid = current->egid = current->fsgid = bprm->e_gid; + compute_creds(bprm); current->flags &= ~PF_FORKNOEXEC; bprm->p = (unsigned long) create_elf_tables((char *)bprm->p, diff --git a/fs/buffer.c b/fs/buffer.c index 1d58146ae2a5..9aefbe8f3ae0 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1141,6 +1141,9 @@ struct buffer_head * breada(kdev_t dev, int block, int bufsize, return NULL; } +/* + * Note: the caller should wake up the buffer_wait list if needed. + */ static void put_unused_buffer_head(struct buffer_head * bh) { if (nr_unused_buffer_heads >= MAX_UNUSED_BUFFERS) { @@ -1153,9 +1156,6 @@ static void put_unused_buffer_head(struct buffer_head * bh) nr_unused_buffer_heads++; bh->b_next_free = unused_list; unused_list = bh; - if (!waitqueue_active(&buffer_wait)) - return; - wake_up(&buffer_wait); } /* @@ -1165,18 +1165,26 @@ static void put_unused_buffer_head(struct buffer_head * bh) * fields after the final unlock. So, the device driver puts them on * the reuse_list instead once IO completes, and we recover these to * the unused_list here. + * + * Note that we don't do a wakeup here, but return a flag indicating + * whether we got any buffer heads. A task ready to sleep can check + * the returned value, and any tasks already sleeping will have been + * awakened when the buffer heads were added to the reuse list. */ -static inline void recover_reusable_buffer_heads(void) +static inline int recover_reusable_buffer_heads(void) { - struct buffer_head *head; - - head = xchg(&reuse_list, NULL); + struct buffer_head *head = xchg(&reuse_list, NULL); + int found = 0; - while (head) { - struct buffer_head *bh = head; - head = head->b_next_free; - put_unused_buffer_head(bh); + if (head) { + do { + struct buffer_head *bh = head; + head = head->b_next_free; + put_unused_buffer_head(bh); + } while (head); + found = 1; } + return found; } /* @@ -1275,11 +1283,15 @@ try_again: * In case anything failed, we just free everything we got. */ no_grow: - bh = head; - while (bh) { - head = bh; - bh = bh->b_this_page; - put_unused_buffer_head(head); + if (head) { + do { + bh = head; + head = head->b_this_page; + put_unused_buffer_head(bh); + } while (head); + + /* Wake up any waiters ... */ + wake_up(&buffer_wait); } /* @@ -1305,8 +1317,8 @@ no_grow: */ add_wait_queue(&buffer_wait, &wait); current->state = TASK_UNINTERRUPTIBLE; - recover_reusable_buffer_heads(); - schedule(); + if (!recover_reusable_buffer_heads()) + schedule(); remove_wait_queue(&buffer_wait, &wait); current->state = TASK_RUNNING; goto try_again; @@ -1333,14 +1345,24 @@ static inline void after_unlock_page (struct page * page) */ static inline void free_async_buffers (struct buffer_head * bh) { - struct buffer_head * tmp; + struct buffer_head *tmp, *tail; - tmp = bh; - do { - tmp->b_next_free = xchg(&reuse_list, NULL); - reuse_list = tmp; - tmp = tmp->b_this_page; - } while (tmp != bh); + /* + * Link all the buffers into the b_next_free list, + * so we only have to do one xchg() operation ... + */ + tail = bh; + while ((tmp = tail->b_this_page) != bh) { + tail->b_next_free = tmp; + tail = tmp; + }; + + /* Update the reuse list */ + tail->b_next_free = xchg(&reuse_list, NULL); + reuse_list = bh; + + /* Wake up any waiters ... */ + wake_up(&buffer_wait); } static void end_buffer_io_async(struct buffer_head * bh, int uptodate) @@ -1390,7 +1412,6 @@ static void end_buffer_io_async(struct buffer_head * bh, int uptodate) clear_bit(PG_locked, &page->flags); wake_up(&page->wait); after_unlock_page(page); - wake_up(&buffer_wait); return; still_busy: @@ -1636,6 +1657,7 @@ int try_to_free_buffer(struct buffer_head * bh, struct buffer_head ** bhp, return 0; tmp = tmp->b_this_page; } while (tmp != bh); + tmp = bh; do { p = tmp; @@ -1649,6 +1671,9 @@ int try_to_free_buffer(struct buffer_head * bh, struct buffer_head ** bhp, remove_from_queues(p); put_unused_buffer_head(p); } while (tmp != bh); + /* Wake up anyone waiting for buffer heads */ + wake_up(&buffer_wait); + buffermem -= PAGE_SIZE; mem_map[MAP_NR(page)].buffers = NULL; free_page(page); diff --git a/fs/dcache.c b/fs/dcache.c index 28cc277f5bfd..801db79fb6a6 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -19,6 +19,8 @@ #include #include +#include + #define DCACHE_PARANOIA 1 /* #define DCACHE_DEBUG 1 */ @@ -669,7 +671,7 @@ void d_add(struct dentry * entry, struct inode * inode) d_instantiate(entry, inode); } -#define switch(x,y) do { \ +#define do_switch(x,y) do { \ __typeof__ (x) __tmp = x; \ x = y; y = __tmp; } while (0) @@ -705,10 +707,10 @@ void d_move(struct dentry * dentry, struct dentry * target) list_del(&target->d_child); /* Switch the parents and the names.. */ - switch(dentry->d_parent, target->d_parent); - switch(dentry->d_name.name, target->d_name.name); - switch(dentry->d_name.len, target->d_name.len); - switch(dentry->d_name.hash, target->d_name.hash); + do_switch(dentry->d_parent, target->d_parent); + do_switch(dentry->d_name.name, target->d_name.name); + do_switch(dentry->d_name.len, target->d_name.len); + do_switch(dentry->d_name.hash, target->d_name.hash); list_add(&target->d_child, &target->d_parent->d_subdirs); list_add(&dentry->d_child, &dentry->d_parent->d_subdirs); } @@ -757,6 +759,41 @@ char * d_path(struct dentry *dentry, char *buffer, int buflen) return retval; } +/* + * NOTE! The user-level library version returns a + * character pointer. The kernel system call just + * returns the length of the buffer filled (which + * includes the ending '\0' character), or a negative + * error value. So libc would do something like + * + * char *getcwd(char * buf, size_t size) + * { + * int retval; + * + * retval = sys_getcwd(buf, size); + * if (retval >= 0) + * return buf; + * errno = -retval; + * return NULL; + * } + */ +asmlinkage int sys_getcwd(char *buf, unsigned long size) +{ + int error; + unsigned long len; + char * page = (char *) __get_free_page(GFP_USER); + char * cwd = d_path(current->fs->pwd, page, PAGE_SIZE); + + error = -ERANGE; + len = PAGE_SIZE + page - cwd; + if (len <= size) { + error = len; + if (copy_to_user(buf, cwd, len)) + error = -EFAULT; + } + return error; +} + /* * Test whether new_dentry is a subdirectory of old_dentry. * diff --git a/fs/exec.c b/fs/exec.c index dfdd96826ee5..835e4e9fdb99 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -605,6 +605,45 @@ int prepare_binprm(struct linux_binprm *bprm) id_change = 1; } + /* We don't have VFS support for capabilities yet */ + cap_clear(bprm->cap_inheritable); + cap_clear(bprm->cap_permitted); + cap_clear(bprm->cap_effective); + + /* To support inheritance of root-permissions and suid-root + * executables under compatibility mode, we raise the + * effective and inherited bitmasks of the executable file + * (translation: we set the executable "capability dumb" and + * set the allowed set to maximum). We don't set any forced + * bits. + * + * If only the real uid is 0, we only raise the inheritable + * bitmask of the executable file (translation: we set the + * allowed set to maximum and the application to "capability + * smart"). + */ + + if (!issecure(SECURE_NOROOT)) { + if (bprm->e_uid == 0 || current->uid == 0) + cap_set_full(bprm->cap_inheritable); + if (bprm->e_uid == 0) + cap_set_full(bprm->cap_effective); + } + + /* We use a conservative definition of suid for capabilities. + * The process is suid if the permitted set is not a subset of + * the current permitted set after the exec call. + * new permitted set = forced | (allowed & inherited) + * pP' = fP | (fI & pI) + */ + + if ((bprm->cap_permitted.cap | + (current->cap_inheritable.cap & + bprm->cap_inheritable.cap)) & + ~current->cap_permitted.cap) { + id_change = 1; + } + if (id_change) { /* We can't suid-execute if we're sharing parts of the executable */ /* or if we're being traced (or if suid execs are not allowed) */ @@ -623,6 +662,45 @@ int prepare_binprm(struct linux_binprm *bprm) return read_exec(bprm->dentry,0,bprm->buf,128,1); } +/* + * This function is used to produce the new IDs and capabilities + * from the old ones and the file's capabilities. + * + * The formula used for evolving capabilities is: + * + * pI' = pI + * pP' = fP | (fI & pI) + * pE' = pP' & fE [NB. fE is 0 or ~0] + * + * I=Inheritable, P=Permitted, E=Effective // p=process, f=file + * ' indicates post-exec(). + */ + +void compute_creds(struct linux_binprm *bprm) +{ + int new_permitted = bprm->cap_permitted.cap | + (bprm->cap_inheritable.cap & current->cap_inheritable.cap); + + current->cap_permitted.cap = new_permitted; + current->cap_effective.cap = new_permitted & bprm->cap_effective.cap; + + /* XXX - Audit candidate */ + if (!cap_isclear(current->cap_effective)) { + printk(KERN_NOTICE + "raising capabilities on `%s'(pid=%d) [%04x]:%lu\n", + current->comm, current->pid, + kdev_t_to_nr(bprm->dentry->d_inode->i_dev), + bprm->dentry->d_inode->i_ino); + } + + current->suid = current->euid = current->fsuid = bprm->e_uid; + current->sgid = current->egid = current->fsgid = bprm->e_gid; + if (current->euid != current->uid || current->egid != current->gid || + !cap_isclear(current->cap_permitted)) + current->dumpable = 0; +} + + void remove_arg_zero(struct linux_binprm *bprm) { if (bprm->argc) { diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index f752229ce6ab..8280d0736508 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -648,7 +648,7 @@ int ext2_notify_change(struct dentry *dentry, struct iattr *iattr) (ATTR_FLAG_APPEND | ATTR_FLAG_IMMUTABLE)) ^ (inode->u.ext2_i.i_flags & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) { - if (securelevel > 0 || !fsuser()) + if (!fsuser()) goto out; } else if ((current->fsuid != inode->i_uid) && !fsuser()) goto out; diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c index c0514c01e14e..4ba722b8540b 100644 --- a/fs/ext2/ioctl.c +++ b/fs/ext2/ioctl.c @@ -37,7 +37,7 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, (inode->u.ext2_i.i_flags & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) { /* This test looks nicer. Thanks to Pauline Middelink */ - if (!fsuser() || securelevel > 0) + if (!fsuser()) return -EPERM; } else if ((current->fsuid != inode->i_uid) && !fsuser()) diff --git a/fs/proc/fd.c b/fs/proc/fd.c index ab9948a62c91..04da4f4126ca 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -50,7 +50,7 @@ struct inode_operations proc_fd_inode_operations = { NULL, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ - NULL /* permission */ + proc_permission /* permission */ }; /* diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 16ee84225581..53ac522cb935 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -129,6 +129,108 @@ static int parse_options(char *options,uid_t *uid,gid_t *gid) return 1; } +/* + * The standard rules, copied from fs/namei.c:permission(). + */ +static int standard_permission(struct inode *inode, int mask) +{ + int mode = inode->i_mode; + + if ((mask & S_IWOTH) && IS_RDONLY(inode) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) + return -EROFS; /* Nobody gets write access to a read-only fs */ + else if ((mask & S_IWOTH) && IS_IMMUTABLE(inode)) + return -EACCES; /* Nobody gets write access to an immutable file */ + else if (current->fsuid == inode->i_uid) + mode >>= 6; + else if (in_group_p(inode->i_gid)) + mode >>= 3; + if (((mode & mask & 0007) == mask) || fsuser()) + return 0; + return -EACCES; +} + +/* + * Set up permission rules for processes looking at other processes. + * You're not allowed to see a process unless it has the same or more + * restricted root than your own. This prevents a chrooted processes + * from escaping through the /proc entries of less restricted + * processes, and thus allows /proc to be safely mounted in a chrooted + * area. + * + * Note that root (uid 0) doesn't get permission for this either, + * since chroot is stronger than root. + * + * XXX TODO: use the dentry mechanism to make off-limits procs simply + * invisible rather than denied? Does each namespace root get its own + * dentry tree? + * + * This also applies the default permissions checks, as it only adds + * restrictions. + * + * Jeremy Fitzhardinge + */ +int proc_permission(struct inode *inode, int mask) +{ + struct task_struct *p; + unsigned long ino = inode->i_ino; + unsigned long pid; + struct dentry *de, *base; + + if (standard_permission(inode, mask) != 0) + return -EACCES; + + /* + * Find the root of the processes being examined (if any). + * XXX Surely there's a better way of doing this? + */ + if (ino >= PROC_OPENPROM_FIRST && + ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM) + return 0; /* already allowed */ + + pid = ino >> 16; + if (pid == 0) + return 0; /* already allowed */ + + de = NULL; + base = current->fs->root; + + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + + if (p != NULL) + de = p->fs->root; + read_unlock(&tasklist_lock); + + if (p == NULL) + return -EACCES; /* ENOENT? */ + + if (de == NULL) + { + /* kswapd and bdflush don't have proper root or cwd... */ + return -EACCES; + } + + /* XXX locking? */ + for(;;) + { + struct dentry *parent; + + if (de == base) + return 0; /* already allowed */ + + de = de->d_covers; + parent = de->d_parent; + + if (de == parent) + break; + + de = parent; + } + + return -EACCES; /* incompatible roots */ +} + struct inode * proc_get_inode(struct super_block * sb, int ino, struct proc_dir_entry * de) { diff --git a/fs/proc/link.c b/fs/proc/link.c index 2f4abc945543..5ee0fd30a36c 100644 --- a/fs/proc/link.c +++ b/fs/proc/link.c @@ -58,7 +58,7 @@ struct inode_operations proc_link_inode_operations = { NULL, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ - NULL /* permission */ + proc_permission /* permission */ }; static struct dentry * proc_follow_link(struct dentry *dentry, diff --git a/fs/proc/mem.c b/fs/proc/mem.c index 1cbdbad9aaf2..6478dab77a9a 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -336,5 +337,5 @@ struct inode_operations proc_mem_inode_operations = { NULL, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ - NULL /* permission */ + proc_permission /* permission */ }; diff --git a/include/asm-i386/bugs.h b/include/asm-i386/bugs.h index 0a5c60bf532e..d947f77332cf 100644 --- a/include/asm-i386/bugs.h +++ b/include/asm-i386/bugs.h @@ -148,7 +148,7 @@ __initfunc(static void check_popad(void)) } /* - * B step AMD K6 before B 9729AIJW have hardware bugs that can cause + * B step AMD K6 before B 9730xxxx have hardware bugs that can cause * misexecution of code under Linux. Owners of such processors should * contact AMD for precise details and a CPU swap. * @@ -195,10 +195,10 @@ __initfunc(static void check_amd_k6(void)) printk(KERN_INFO "AMD K6 stepping B detected - "); /* -- cut here -- */ if (d > 20*K6_BUG_LOOP) - printk(KERN_INFO "system stability may be impaired when more than 32 MB are used.\n"); + printk("system stability may be impaired when more than 32 MB are used.\n"); else - printk(KERN_INFO "probably OK (after B9730xxxx).\n"); - printk(KERN_INFO "Please see http://www.chorus.com/bpc/k6bug.html\n"); + printk("probably OK (after B9730xxxx).\n"); + printk(KERN_INFO "Please see http://www.chorus.com/poulot/k6bug.html\n"); } } diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h index 068be0f29547..6819f825b252 100644 --- a/include/asm-i386/unistd.h +++ b/include/asm-i386/unistd.h @@ -188,6 +188,7 @@ #define __NR_pread 180 #define __NR_pwrite 181 #define __NR_chown 182 +#define __NR_getcwd 183 /* user-visible error numbers are in the range -1 - -122: see */ diff --git a/include/linux/b1lli.h b/include/linux/b1lli.h index 401933c3f031..f564ae89884d 100644 --- a/include/linux/b1lli.h +++ b/include/linux/b1lli.h @@ -1,11 +1,25 @@ /* - * $Id: b1lli.h,v 1.1 1997/03/04 21:27:32 calle Exp $ + * $Id: b1lli.h,v 1.3 1998/01/31 10:54:37 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.3 1998/01/31 10:54:37 calle + * include changes for PCMCIA cards from 2.0 version + * + * Revision 1.2 1997/12/10 19:38:42 calle + * get changes from 2.0 tree + * + * Revision 1.1.2.2 1997/11/26 16:57:26 calle + * more changes for B1/M1/T1. + * + * Revision 1.1.2.1 1997/11/26 10:47:01 calle + * prepared for M1 (Mobile) and T1 (PMX) cards. + * prepared to set configuration after load to support other D-channel + * protocols, point-to-point and leased lines. + * * Revision 1.1 1997/03/04 21:27:32 calle * First version in isdn4linux * @@ -32,10 +46,22 @@ typedef struct avmb1_loaddef { avmb1_t4file t4file; } avmb1_loaddef; +typedef struct avmb1_loadandconfigdef { + int contr; + avmb1_t4file t4file; + avmb1_t4file t4config; +} avmb1_loadandconfigdef; + typedef struct avmb1_resetdef { int contr; } avmb1_resetdef; +typedef struct avmb1_getdef { + int contr; + int cardtype; + int cardstate; +} avmb1_getdef; + /* * struct for adding new cards */ @@ -44,25 +70,39 @@ typedef struct avmb1_carddef { int irq; } avmb1_carddef; -#define AVMB1_LOAD 0 /* load image to card */ -#define AVMB1_ADDCARD 1 /* add a new card */ -#define AVMB1_RESETCARD 2 /* reset a card */ +#define AVM_CARDTYPE_B1 0 +#define AVM_CARDTYPE_T1 1 +#define AVM_CARDTYPE_M1 2 +#define AVM_CARDTYPE_M2 3 +typedef struct avmb1_extcarddef { + int port; + int irq; + int cardtype; +} avmb1_extcarddef; + +#define AVMB1_LOAD 0 /* load image to card */ +#define AVMB1_ADDCARD 1 /* add a new card */ +#define AVMB1_RESETCARD 2 /* reset a card */ +#define AVMB1_LOAD_AND_CONFIG 3 /* load image and config to card */ +#define AVMB1_ADDCARD_WITH_TYPE 4 /* add a new card, with cardtype */ +#define AVMB1_GET_CARDINFO 5 /* get cardtype */ -#ifdef __KERNEL__ /* * card states for startup */ -#define CARD_NONE 0 +#define CARD_FREE 0 #define CARD_DETECTED 1 #define CARD_LOADING 2 #define CARD_INITSTATE 4 #define CARD_RUNNING 5 #define CARD_ACTIVE 6 +#ifdef __KERNEL__ + #define AVMB1_PORTLEN 0x1f #define AVM_MAXVERSION 8 @@ -81,6 +121,7 @@ typedef struct avmb1_card { int cnr; unsigned short port; unsigned irq; + int cardtype; volatile unsigned short cardstate; int interrupt; int blocked; @@ -108,14 +149,15 @@ typedef struct avmb1_card { /* b1lli.c */ -int B1_detect(unsigned short base); +int B1_detect(unsigned short base, int cardtype); void B1_reset(unsigned short base); int B1_load_t4file(unsigned short base, avmb1_t4file * t4file); +int B1_load_config(unsigned short base, avmb1_t4file * config); int B1_loaded(unsigned short base); -unsigned char B1_assign_irq(unsigned short base, unsigned irq); +unsigned char B1_assign_irq(unsigned short base, unsigned irq, int cardtype); unsigned char B1_enable_irq(unsigned short base); unsigned char B1_disable_irq(unsigned short base); -int B1_valid_irq(unsigned irq); +int B1_valid_irq(unsigned irq, int cardtype); void B1_handle_interrupt(avmb1_card * card); void B1_send_init(unsigned short port, unsigned int napps, unsigned int nncci, unsigned int cardnr); @@ -133,8 +175,17 @@ void avmb1_handle_free_ncci(avmb1_card * card, void avmb1_handle_capimsg(avmb1_card * card, __u16 appl, struct sk_buff *skb); void avmb1_card_ready(avmb1_card * card); -int avmb1_addcard(int port, int irq); -int avmb1_probecard(int port, int irq); +/* standard calls, with check and allocation of resources */ +int avmb1_addcard(int port, int irq, int cardtype); +int avmb1_probecard(int port, int irq, int cardtype); + + +int avmb1_resetcard(int cardnr); + +/* calls for pcmcia driver */ +int avmb1_detectcard(int port, int irq, int cardtype); +int avmb1_registercard(int port, int irq, int cardtype, int allocio); +int avmb1_unregistercard(int cnr, int freeio); #endif /* __KERNEL__ */ diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index c6a15bb34fca..4d90c2eefae2 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -2,6 +2,7 @@ #define _LINUX_BINFMTS_H #include +#include /* * MAX_ARG_PAGES defines the number of pages allocated for arguments @@ -21,6 +22,7 @@ struct linux_binprm{ int java; /* Java binary, prevent recursive invocation */ struct dentry * dentry; int e_uid, e_gid; + kernel_cap_t cap_inheritable, cap_permitted, cap_effective; int argc, envc; char * filename; /* Name of binary */ unsigned long loader, exec; @@ -63,6 +65,8 @@ extern unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm extern unsigned long copy_strings(int argc,char ** argv,unsigned long *page, unsigned long p, int from_kmem); +extern void compute_creds(struct linux_binprm *binprm); + /* this eventually goes away */ #define change_ldt(a,b) setup_arg_pages(a,b) diff --git a/include/linux/capability.h b/include/linux/capability.h new file mode 100644 index 000000000000..a702a6eceb51 --- /dev/null +++ b/include/linux/capability.h @@ -0,0 +1,206 @@ +/* + * This is + * + * Andrew G. Morgan + * Alexander Kjeldaas + * with help from Aleph1, Roland Buresund and Andrew Main. + */ + +#ifndef _LINUX_CAPABILITY_H +#define _LINUX_CAPABILITY_H + +#include +#include + +/* User-level do most of the mapping between kernel and user + capabilities based on the version tag given by the kernel. The + kernel might be somewhat backwards compatible, but don't bet on + it. */ + +#define _LINUX_CAPABILITY_VERSION 0x19980330 + +typedef struct _user_cap_struct { + __u32 version; + __u32 size; + __u8 cap[1]; +} *cap_t; + +#ifdef __KERNEL__ + +typedef struct kernel_cap_struct { + int cap; +} kernel_cap_t; + +#endif + + +/** + ** POSIX-draft defined capabilities. + **/ + +/* In a system with the [_POSIX_CHOWN_RESTRICTED] option defined, this + overrides the restriction of changing file ownership and group + ownership. */ + +#define CAP_CHOWN 0 + +/* Override all DAC access, including ACL execute access if + [_POSIX_ACL] is defined. Excluding DAC access covered by + CAP_LINUX_IMMUTABLE */ + +#define CAP_DAC_OVERRIDE 1 + +/* Overrides all DAC restrictions regarding read and search on files + and directories, including ACL restrictions if [_POSIX_ACL] is + defined. Excluding DAC access covered by CAP_LINUX_IMMUTABLE */ + +#define CAP_DAC_READ_SEARCH 2 + +/* Overrides all restrictions about allowed operations on files, where + file owner ID must be equal to the user ID, except where CAP_FSETID + is applicable. It doesn't override MAC and DAC restrictions. */ + +#define CAP_FOWNER 3 + +/* Overrides the following restrictions that the effective user ID + shall match the file owner ID when setting the S_ISUID and S_ISGID + bits on that file; that the effective group ID (or one of the + supplementary group IDs shall match the file owner ID when setting + the S_ISGID bit on that file; that the S_ISUID and S_ISGID bits are + cleared on successful return from chown(2). */ + +#define CAP_FSETID 4 + +/* Used to decide between falling back on the old suser() or fsuser(). */ + +#define CAP_FS_MASK 0x1f + +/* Overrides the restriction that the real or effective user ID of a + process sending a signal must match the real or effective user ID + of the process receiving the signal. */ + +#define CAP_KILL 5 + +/* Allows setgid(2) manipulation */ + +#define CAP_SETGID 6 + +/* Allows setuid(2) manipulation */ + +#define CAP_SETUID 7 + + +/** + ** Linux-specific capabilities + **/ + +/* Transfer any capability in your permitted set to any pid, + remove any capability in your permitted set from any pid */ + +#define CAP_SETPCAP 8 + +/* Allow modification of S_IMMUTABLE and S_APPEND file attributes */ + +#define CAP_LINUX_IMMUTABLE 9 + +/* Allows binding to TCP/UDP sockets below 1024 */ + +#define CAP_NET_BIND_SERVICE 10 + +/* Allow broadcasting, listen to multicast */ + +#define CAP_NET_BROADCAST 11 + +/* Allow interface configuration */ +/* Allow configuring of firewall stuff */ +/* Allow setting debug option on sockets */ +/* Allow modification of routing tables */ + +#define CAP_NET_ADMIN 12 + +/* Allow use of RAW sockets */ +/* Allow use of PACKET sockets */ + +#define CAP_NET_RAW 13 + +/* Allow locking of segments in memory */ + +#define CAP_IPC_LOCK 14 + +/* Override IPC ownership checks */ + +#define CAP_IPC_OWNER 15 + +/* Insert and remove kernel modules */ + +#define CAP_SYS_MODULE 16 + +/* Allow ioperm/iopl access */ + +#define CAP_SYS_RAWIO 17 + +/* Allow use of chroot() */ + +#define CAP_SYS_CHROOT 18 + +/* Allow ptrace() of any process */ + +#define CAP_SYS_PTRACE 19 + +/* Allow configuration of process accounting */ + +#define CAP_SYS_PACCT 20 + +/* Allow configuration of the secure attention key */ +/* Allow administration of the random device */ +/* Allow device administration */ +/* Allow examination and configuration of disk quotas */ +/* System Admin functions: mount et al */ + +#define CAP_SYS_ADMIN 21 + +/* Allow use of reboot() */ + +#define CAP_SYS_BOOT 22 + +/* Allow use of renice() on others, and raising of priority */ + +#define CAP_SYS_NICE 23 + +/* Override resource limits */ + +#define CAP_SYS_RESOURCE 24 + +/* Allow manipulation of system clock */ + +#define CAP_SYS_TIME 25 + +/* Allow configuration of tty devices */ + +#define CAP_SYS_TTY_CONFIG 26 + +#ifdef __KERNEL__ + +/* + * Internal kernel functions only + */ + +#define CAP_EMPTY_SET { 0 } +#define CAP_FULL_SET { ~0 } + +#define CAP_TO_MASK(x) (1 << (x)) +#define cap_raise(c, flag) (c.cap |= CAP_TO_MASK(flag)) +#define cap_lower(c, flag) (c.cap &= ~CAP_TO_MASK(flag)) +#define cap_raised(c, flag) (c.cap & CAP_TO_MASK(flag)) + +#define cap_isclear(c) (!c.cap) + +#define cap_copy(dest,src) do { (dest).cap = (src).cap; } while(0) +#define cap_clear(c) do { c.cap = 0; } while(0) +#define cap_set_full(c) do { c.cap = ~0; } while(0) + +#define cap_is_fs_cap(c) ((c) & CAP_FS_MASK) + +#endif /* __KERNEL__ */ + +#endif /* !_LINUX_CAPABILITY_H */ diff --git a/include/linux/concap.h b/include/linux/concap.h new file mode 100644 index 000000000000..05f74d820c89 --- /dev/null +++ b/include/linux/concap.h @@ -0,0 +1,113 @@ +/* $Id: concap.h,v 1.1 1998/02/01 00:15:11 keil Exp $ +*/ +#ifndef _LINUX_CONCAP_H +#define _LINUX_CONCAP_H +#ifdef __KERNEL__ +#include +#include + +/* Stuff to support encapsulation protocols genericly. The encapsulation + protocol is processed at the uppermost layer of the network interface. + + (c) 1997 by Henner Eisen + This software is subject to the GNU General Public License. + + Based on a ideas developed in a 'synchronous device' thread in the + linux-x25 mailing list contributed by Alan Cox, Thomasz Motylewski + and Jonathan Naylor. + + For more documetation on this refer to Documentation/isdn/README.concap + */ + +struct concap_proto_ops; +struct concap_device_ops; + +/* this manages all data needed by the encapsulation protocol + */ +struct concap_proto{ + struct device *net_dev; /* net device using our service */ + struct concap_device_ops *dops; /* callbacks provided by device */ + struct concap_proto_ops *pops; /* callbacks provided by us */ + int flags; + void *proto_data; /* protocol specific private data, to + be accessed via *pops methods only*/ + /* + : + whatever + : + */ +}; + +/* Operations to be supported by the net device. Called by the encapsulation + * protocol entity. No receive method is offered because the encapsulation + * protocol directly calls netif_rx(). + */ +struct concap_device_ops{ + + /* to request data is submitted by device*/ + int (*data_req)(struct concap_proto *, struct sk_buff *); + + /* Control methods must be set to NULL by devices which do not + support connection control.*/ + /* to request a connection is set up */ + int (*connect_req)(struct concap_proto *); + + /* to request a connection is released */ + int (*disconn_req)(struct concap_proto *); +}; + +/* Operations to be supported by the encapsulation protocol. Called by + * device driver. + */ +struct concap_proto_ops{ + + /* create a new encapsulation protocol instance of same type */ + struct concap_proto * (*proto_new) (void); + + /* delete encapsulation protocol instance and free all its resources. + cprot may no loger be referenced after calling this */ + void (*proto_del)(struct concap_proto *cprot); + + /* initialize the protocol's data. To be called at interface startup + or when the device driver resets the interface. All services of the + encapsulation protocol may be used after this*/ + int (*restart)(struct concap_proto *cprot, + struct device *ndev, + struct concap_device_ops *dops); + + /* inactivate an encapsulation protocol instance. The encapsulation + protocol may not call any *dops methods after this. */ + int (*close)(struct concap_proto *cprot); + + /* process a frame handed down to us by upper layer */ + int (*encap_and_xmit)(struct concap_proto *cprot, struct sk_buff *skb); + + /* to be called for each data entity received from lower layer*/ + int (*data_ind)(struct concap_proto *cprot, struct sk_buff *skb); + + /* to be called when a connection was set up/down. + Protocols that don't process these primitives might fill in + dummy methods here */ + int (*connect_ind)(struct concap_proto *cprot); + int (*disconn_ind)(struct concap_proto *cprot); + /* + Some network device support functions, like net_header(), rebuild_header(), + and others, that depend solely on the encapsulation protocol, might + be provided here, too. The net device would just fill them in its + corresponding fields when it is opened. + */ +}; + +/* dummy restart/close/connect/reset/disconn methods + */ +extern int concap_nop(struct concap_proto *cprot); + +/* dummy submit method + */ +extern int concap_drop_skb(struct concap_proto *cprot, struct sk_buff *skb); +#endif +#endif + + + + diff --git a/include/linux/console.h b/include/linux/console.h index 8c0854b0b854..e287e64d5fe9 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -80,7 +80,7 @@ extern struct console_cmdline console_list[MAX_CMDLINECONSOLES]; */ #define CON_PRINTBUFFER (1) -#define CON_FIRST (2) +#define CON_CONSDEV (2) /* Last on the command line */ #define CON_ENABLED (4) struct console diff --git a/include/linux/if_ppp.h b/include/linux/if_ppp.h index 6c55c89f1950..35eb83fa264e 100644 --- a/include/linux/if_ppp.h +++ b/include/linux/if_ppp.h @@ -65,6 +65,7 @@ #define SC_REJ_COMP_TCP 0x00000020 /* reject TCP (VJ) comp. on input */ #define SC_CCP_OPEN 0x00000040 /* Look at CCP packets */ #define SC_CCP_UP 0x00000080 /* May send/recv compressed packets */ +#define SC_ENABLE_IP 0x00000100 /* IP packets may be exchanged */ #define SC_COMP_RUN 0x00001000 /* compressor has been inited */ #define SC_DECOMP_RUN 0x00002000 /* decompressor has been inited */ #define SC_DEBUG 0x00010000 /* enable debug messages */ diff --git a/include/linux/isdn.h b/include/linux/isdn.h index f7b4747d38a0..e061cabaa13c 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -1,4 +1,10 @@ -/* $Id: isdn.h,v 1.29 1997/05/27 15:18:02 fritz Exp $ +/* Changes for X.25 support: + Added ISDN_NET_ENCAP_X25IFACE macro. + Additional field in isdn_net_dev_s and isdn_net_local to support + generic encapsulation protocols. +*/ + +/* $Id: isdn.h,v 1.37 1998/02/22 19:45:24 fritz Exp $ * * Main header for the Linux ISDN subsystem (linklevel). * @@ -21,6 +27,37 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn.h,v $ + * Revision 1.37 1998/02/22 19:45:24 fritz + * Some changes regarding V.110 + * + * Revision 1.36 1998/02/20 17:35:55 fritz + * Added V.110 stuff. + * + * Revision 1.35 1998/01/31 22:14:14 keil + * changes for 2.1.82 + * + * Revision 1.34 1997/10/09 21:28:11 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * New L1 error status (not yet in use). + * Cleaned up obsolete structures. + * Implemented Cisco-SLARP. + * Changed local net-interface data to be dynamically allocated. + * Removed old 2.0 compatibility stuff. + * + * Revision 1.33 1997/08/21 14:44:22 fritz + * Moved triggercps to end of struct for backwards-compatibility. + * + * Revision 1.32 1997/08/21 09:49:46 fritz + * Increased NET_DV + * + * Revision 1.31 1997/06/22 11:57:07 fritz + * Added ability to adjust slave triggerlevel. + * + * 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 @@ -181,13 +218,15 @@ #define IIOCDRVCTL _IO('I',128) /* Packet encapsulations for net-interfaces */ -#define ISDN_NET_ENCAP_ETHER 0 -#define ISDN_NET_ENCAP_RAWIP 1 -#define ISDN_NET_ENCAP_IPTYP 2 -#define ISDN_NET_ENCAP_CISCOHDLC 3 -#define ISDN_NET_ENCAP_SYNCPPP 4 -#define ISDN_NET_ENCAP_UIHDLC 5 - +#define ISDN_NET_ENCAP_ETHER 0 +#define ISDN_NET_ENCAP_RAWIP 1 +#define ISDN_NET_ENCAP_IPTYP 2 +#define ISDN_NET_ENCAP_CISCOHDLC 3 /* Without SLARP and keepalive */ +#define ISDN_NET_ENCAP_SYNCPPP 4 +#define ISDN_NET_ENCAP_UIHDLC 5 +#define ISDN_NET_ENCAP_CISCOHDLCK 6 /* With SLARP and keepalive */ +#define ISDN_NET_ENCAP_X25IFACE 7 /* Documentation/networking/x25-iface.txt*/ +#define ISDN_NET_ENCAP_MAX_ENCAP ISDN_NET_ENCAP_X25IFACE /* Facility which currently uses an ISDN-channel */ #define ISDN_USAGE_NONE 0 #define ISDN_USAGE_RAW 1 @@ -219,7 +258,7 @@ typedef struct { int outgoing; } isdn_net_ioctl_phone; -#define NET_DV 0x02 /* Data version for net_cfg */ +#define NET_DV 0x04 /* Data version for net_cfg */ #define TTY_DV 0x04 /* Data version for iprofd etc. */ typedef struct { @@ -244,6 +283,7 @@ typedef struct { int cbhup; /* Flag: Reject Call before Callback */ int pppbind; /* ippp device for bindings */ int chargeint; /* Use fixed charge interval length */ + int triggercps; /* BogoCPS needed for triggering slave */ } isdn_net_ioctl_cfg; #ifdef __KERNEL__ @@ -287,6 +327,10 @@ typedef struct { #include #endif +#ifdef CONFIG_ISDN_X25 +# include +#endif + #include #define ISDN_DRVIOCTL_MASK 0x7f /* Mask for Device-ioctl */ @@ -317,21 +361,23 @@ typedef struct { ((x & ISDN_USAGE_MASK)==ISDN_USAGE_VOICE) ) /* Timer-delays and scheduling-flags */ -#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 -#define ISDN_TIMER_MODEMXMIT 8 -#define ISDN_TIMER_NETDIAL 16 -#define ISDN_TIMER_NETHANGUP 32 -#define ISDN_TIMER_IPPP 64 +#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_KEEPINT 10 /* Cisco-Keepalive = ISDN_TIMER_1SEC * this factor */ +#define ISDN_TIMER_MODEMREAD 1 +#define ISDN_TIMER_MODEMPLUS 2 +#define ISDN_TIMER_MODEMRING 4 +#define ISDN_TIMER_MODEMXMIT 8 +#define ISDN_TIMER_NETDIAL 16 +#define ISDN_TIMER_NETHANGUP 32 +#define ISDN_TIMER_IPPP 64 +#define ISDN_TIMER_KEEPALIVE 128 /* Cisco-Keepalive */ #define ISDN_TIMER_FAST (ISDN_TIMER_MODEMREAD | ISDN_TIMER_MODEMPLUS | \ ISDN_TIMER_MODEMXMIT) #define ISDN_TIMER_SLOW (ISDN_TIMER_MODEMRING | ISDN_TIMER_NETHANGUP | \ - ISDN_TIMER_NETDIAL) + ISDN_TIMER_NETDIAL | ISDN_TIMER_KEEPALIVE) /* Timeout-Values for isdn_net_dial() */ #define ISDN_TIMER_DTIMEOUT10 (10*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1))) @@ -362,6 +408,15 @@ typedef struct { char num[ISDN_MSNLEN]; } isdn_net_phone; +/* + Principles when extending structures for generic encapsulation protocol + ("concap") support: + - Stuff which is hardware specific (here i4l-specific) goes in + the netdev -> local structure (here: isdn_net_local) + - Stuff which is encapsulation protocol specific goes in the structure + which holds the linux device structure (here: isdn_net_device) +*/ + /* Local interface-data */ typedef struct isdn_net_local_s { ulong magic; @@ -413,6 +468,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 */ @@ -425,16 +481,21 @@ 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*/ - /* Ptr to orig. hard_header_cache */ - int (*org_hhc)(struct neighbour *neigh, + int (*org_hhc)( + struct neighbour *neigh, struct hh_cache *hh); - /* Ptr to orig. header_cache_update */ void (*org_hcu)(struct hh_cache *, struct device *, unsigned char *); int pppbind; /* ippp device for bindings */ +#ifdef CONFIG_ISDN_X25 + struct concap_device_ops *dops; /* callbacks used by encapsulator */ +#endif + int cisco_loop; /* Loop counter for Cisco-SLARP */ + ulong cisco_myseq; /* Local keepalive seq. for Cisco */ + ulong cisco_yourseq; /* Remote keepalive seq. for Cisco */ } isdn_net_local; #ifdef CONFIG_ISDN_PPP @@ -451,14 +512,18 @@ struct ippp_bundle { /* the interface itself */ typedef struct isdn_net_dev_s { - isdn_net_local local; + isdn_net_local *local; isdn_net_local *queue; void *next; /* Pointer to next isdn-interface */ - struct device dev; /* interface to upper levels */ + struct device dev; /* interface to upper levels */ #ifdef CONFIG_ISDN_PPP struct mpqueue *mp_last; struct ippp_bundle ib; #endif +#ifdef CONFIG_ISDN_X25 + struct concap_proto *cprot; /* connection oriented encapsulation protocol */ +#endif + } isdn_net_dev; /*===================== End of ip-over-ISDN stuff ===========================*/ @@ -477,7 +542,8 @@ typedef struct isdn_net_dev_s { #define ISDN_ASYNC_PGRP_LOCKOUT 0x0200 /* Lock cua opens on pgrp */ #define ISDN_ASYNC_CALLOUT_NOHUP 0x0400 /* No hangup for cui */ #define ISDN_ASYNC_SPLIT_TERMIOS 0x0008 /* Sep. termios for dialin/out */ -#define ISDN_SERIAL_XMIT_SIZE 4000 /* Maximum bufsize for write */ +#define ISDN_SERIAL_XMIT_SIZE 1024 /* Default bufsize for write */ +#define ISDN_SERIAL_XMIT_MAX 4000 /* Maximum bufsize for write */ #define ISDN_SERIAL_TYPE_NORMAL 1 #define ISDN_SERIAL_TYPE_CALLOUT 2 @@ -499,18 +565,19 @@ typedef struct isdn_audio_skb { /* 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 */ + 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 */ + 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 + */ - char mdmcmd[255]; /* Modem-Commandbuffer */ + int mdmcmdl; /* Length of Modem-Commandbuffer */ + int pluscount; /* Counter for +++ sequence */ + int lastplus; /* Timestamp of last + */ + char mdmcmd[255]; /* Modem-Commandbuffer */ + unsigned int charge; /* Charge units of current connection */ } atemu; /* Private data (similar to async_struct in ) */ @@ -590,8 +657,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; }; @@ -599,7 +666,7 @@ struct sqqueue { struct mpqueue { struct mpqueue *next; struct mpqueue *last; - int sqno; + long sqno; struct sk_buff *skb; int BEbyte; unsigned long time; @@ -638,18 +705,44 @@ struct ippp_struct { struct slcompress *slcomp; #endif unsigned long debug; - struct isdn_ppp_compressor *compressor; + struct isdn_ppp_compressor *compressor,*link_compressor; + void *decomp_stat,*comp_stat,*link_decomp_stat,*link_comp_stat; }; #endif /*======================== End of sync-ppp stuff ===========================*/ +/*======================== Start of V.110 stuff ============================*/ +#define V110_BUFSIZE 1024 + +typedef struct { + int nbytes; /* 1 Matrixbyte -> nbytes in stream */ + int nbits; /* Number of used bits in streambyte */ + unsigned char key; /* Bitmask in stream eg. 11 (nbits=2) */ + int decodelen; /* Amount of data in decodebuf */ + int SyncInit; /* Number of sync frames to send */ + unsigned char *OnlineFrame; /* Precalculated V110 idle frame */ + unsigned char *OfflineFrame; /* Precalculated V110 sync Frame */ + int framelen; /* Length of frames */ + int skbuser; /* Number of unacked userdata skbs */ + int skbidle; /* Number of unacked idle/sync skbs */ + int introducer; /* Local vars for decoder */ + int dbit; + unsigned char b; + int skbres; /* space to reserve in outgoing skb */ + int maxsize; /* maxbufsize of lowlevel driver */ + unsigned char *encodebuf; /* temporary buffer for encoding */ + unsigned char decodebuf[V110_BUFSIZE]; /* incomplete V110 matrices */ +} isdn_v110_stream; + +/*========================= End of V.110 stuff =============================*/ + /*======================= Start of general stuff ===========================*/ typedef struct { - char *next; - char *private; + char *next; + char *private; } infostruct; /* Description of hardware-level-driver */ @@ -704,6 +797,9 @@ typedef struct isdn_devt { isdn_net_dev *st_netdev[ISDN_MAX_CHANNELS]; /* stat netdev-pointers */ ulong ibytes[ISDN_MAX_CHANNELS]; /* Statistics incoming bytes */ ulong obytes[ISDN_MAX_CHANNELS]; /* Statistics outgoing bytes */ + int v110emu[ISDN_MAX_CHANNELS];/* V.110 emulator-mode 0=none */ + atomic_t v110use[ISDN_MAX_CHANNELS];/* Usage-Semaphore for stream */ + isdn_v110_stream *v110[ISDN_MAX_CHANNELS]; /* V.110 private data */ } isdn_dev; extern isdn_dev *dev; diff --git a/include/linux/isdn_ppp.h b/include/linux/isdn_ppp.h index 71bbc557a917..177646520470 100644 --- a/include/linux/isdn_ppp.h +++ b/include/linux/isdn_ppp.h @@ -1,23 +1,8 @@ #ifndef _LINUX_ISDN_PPP_H #define _LINUX_ISDN_PPP_H -struct isdn_ppp_compressor -{ - struct isdn_ppp_compressor *next,*prev; - int num; /* proto num */ - void *priv; /* private data for compressor */ - int (*open)(struct isdn_ppp_compressor *); - int (*close)(struct isdn_ppp_compressor *); - int (*reset)(struct isdn_ppp_compressor *,int type); - int (*config)(struct isdn_ppp_compressor *,void *data,int data_len); - struct sk_buff *(*compress)(struct isdn_ppp_compressor *,struct sk_buff *); - struct sk_buff *(*uncompress)(struct isdn_ppp_compressor *,struct sk_buff *); -}; - extern int isdn_ppp_dial_slave(char *); extern int isdn_ppp_hangup_slave(char *); -extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *); -extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *); #define CALLTYPE_INCOMING 0x1 #define CALLTYPE_OUTGOING 0x2 @@ -31,18 +16,17 @@ struct pppcallinfo int charge_units; }; - #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 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 @@ -52,4 +36,36 @@ struct pppcallinfo #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 a3f6773a46dd..e7a7f247c086 100644 --- a/include/linux/isdnif.h +++ b/include/linux/isdnif.h @@ -1,4 +1,8 @@ -/* $Id: isdnif.h,v 1.20 1997/05/27 15:18:06 fritz Exp $ +/* X25 changes: + Added constants ISDN_PROTO_L2_X25DTE/DCE and corresponding ISDN_FEATURE_.. + */ + +/* $Id: isdnif.h,v 1.23 1998/02/20 17:36:52 fritz Exp $ * * Linux ISDN subsystem * @@ -22,6 +26,22 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdnif.h,v $ + * Revision 1.23 1998/02/20 17:36:52 fritz + * Added L2-protocols for V.110, changed FEATURE-Flag-constants. + * + * Revision 1.22 1998/01/31 22:14:12 keil + * changes for 2.1.82 + * + * Revision 1.21 1997/10/09 21:28:13 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * New L1 error status (not yet in use). + * Cleaned up obsolete structures. + * Implemented Cisco-SLARP. + * Changed local net-interface data to be dynamically allocated. + * Removed old 2.0 compatibility stuff. + * * Revision 1.20 1997/05/27 15:18:06 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -104,20 +124,28 @@ #define ISDN_PTYPE_EURO 2 /* EDSS1-protocol */ #define ISDN_PTYPE_LEASED 3 /* for leased lines */ #define ISDN_PTYPE_NI1 4 /* US NI-1 protocol */ +#define ISDN_PTYPE_MAX 7 /* Max. 8 Protocols */ /* * Values for Layer-2-protocol-selection */ -#define ISDN_PROTO_L2_X75I 0 /* X75/LAPB with I-Frames */ -#define ISDN_PROTO_L2_X75UI 1 /* X75/LAPB with UI-Frames */ -#define ISDN_PROTO_L2_X75BUI 2 /* X75/LAPB with UI-Frames */ -#define ISDN_PROTO_L2_HDLC 3 /* HDLC */ -#define ISDN_PROTO_L2_TRANS 4 /* Transparent (Voice) */ +#define ISDN_PROTO_L2_X75I 0 /* X75/LAPB with I-Frames */ +#define ISDN_PROTO_L2_X75UI 1 /* X75/LAPB with UI-Frames */ +#define ISDN_PROTO_L2_X75BUI 2 /* X75/LAPB with UI-Frames */ +#define ISDN_PROTO_L2_HDLC 3 /* HDLC */ +#define ISDN_PROTO_L2_TRANS 4 /* Transparent (Voice) */ +#define ISDN_PROTO_L2_X25DTE 5 /* X25/LAPB DTE mode */ +#define ISDN_PROTO_L2_X25DCE 6 /* X25/LAPB DCE mode */ +#define ISDN_PROTO_L2_V11096 7 /* V.110 bitrate adaption 9600 Baud */ +#define ISDN_PROTO_L2_V11019 8 /* V.110 bitrate adaption 19200 Baud */ +#define ISDN_PROTO_L2_V11038 9 /* V.110 bitrate adaption 38400 Baud */ +#define ISDN_PROTO_L2_MAX 15 /* Max. 16 Protocols */ /* * Values for Layer-3-protocol-selection */ #define ISDN_PROTO_L3_TRANS 0 /* Transparent */ +#define ISDN_PROTO_L3_MAX 7 /* Max. 8 Protocols */ #ifdef __KERNEL__ @@ -127,7 +155,7 @@ * Commands from linklevel to lowlevel * */ -#define ISDN_CMD_IOCTL 0 /* Perform ioctl */ +#define ISDN_CMD_IOCTL 0 /* Perform ioctl */ #define ISDN_CMD_DIAL 1 /* Dial out */ #define ISDN_CMD_ACCEPTD 2 /* Accept an incoming call on D-Chan. */ #define ISDN_CMD_ACCEPTB 3 /* Request B-Channel connect. */ @@ -166,6 +194,13 @@ #define ISDN_STAT_NODCH 268 /* Signal no D-Channel */ #define ISDN_STAT_ADDCH 269 /* Add more Channels */ #define ISDN_STAT_CAUSE 270 /* Cause-Message */ +#define ISDN_STAT_L1ERR 271 /* Signal Layer-1 Error */ + +/* + * Values for errcode field + */ +#define ISDN_STAT_L1ERR_SEND 1 +#define ISDN_STAT_L1ERR_RECV 2 /* * Values for feature-field of interface-struct. @@ -176,15 +211,29 @@ #define ISDN_FEATURE_L2_X75BUI (0x0001 << ISDN_PROTO_L2_X75BUI) #define ISDN_FEATURE_L2_HDLC (0x0001 << ISDN_PROTO_L2_HDLC) #define ISDN_FEATURE_L2_TRANS (0x0001 << ISDN_PROTO_L2_TRANS) +#define ISDN_FEATURE_L2_X25DTE (0x0001 << ISDN_PROTO_L2_X25DTE) +#define ISDN_FEATURE_L2_X25DCE (0x0001 << ISDN_PROTO_L2_X25DCE) +#define ISDN_FEATURE_L2_V11096 (0x0001 << ISDN_PROTO_L2_V11096) +#define ISDN_FEATURE_L2_V11019 (0x0001 << ISDN_PROTO_L2_V11019) +#define ISDN_FEATURE_L2_V11038 (0x0001 << ISDN_PROTO_L2_V11038) + +#define ISDN_FEATURE_L2_MASK (0x0FFFF) /* Max. 16 protocols */ +#define ISDN_FEATURE_L2_SHIFT (0) /* Layer 3 */ -#define ISDN_FEATURE_L3_TRANS (0x0100 << ISDN_PROTO_L3_TRANS) +#define ISDN_FEATURE_L3_TRANS (0x10000 << ISDN_PROTO_L3_TRANS) + +#define ISDN_FEATURE_L3_MASK (0x0FF0000) /* Max. 8 Protocols */ +#define ISDN_FEATURE_L3_SHIFT (16) /* Signaling */ -#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) +#define ISDN_FEATURE_P_UNKNOWN (0x1000000 << ISDN_PTYPE_UNKNOWN) +#define ISDN_FEATURE_P_1TR6 (0x1000000 << ISDN_PTYPE_1TR6) +#define ISDN_FEATURE_P_EURO (0x1000000 << ISDN_PTYPE_EURO) +#define ISDN_FEATURE_P_NI1 (0x1000000 << ISDN_PTYPE_NI1) + +#define ISDN_FEATURE_P_MASK (0x0FF000000) /* Max. 8 Protocols */ +#define ISDN_FEATURE_P_SHIFT (24) typedef struct setup_parm { char phone[32]; /* Remote Phone-Number */ @@ -204,6 +253,8 @@ typedef struct { int command; /* Command or Status (see above) */ ulong arg; /* Additional Data */ union { + ulong errcode; /* Type of error with STAT_L1ERR */ + int length; /* Amount of bytes sent with STAT_BSENT */ char num[50]; /* Additional Data */ setup_parm setup; } parm; @@ -238,19 +289,6 @@ typedef struct { */ unsigned short hl_hdrlen; - /* Receive-Callback - * Parameters: - * int Driver-ID - * int local channel-number (0 ...) - * u_char pointer to received data (in Kernel-Space, volatile) - * int length of data - * - * NOTE: This callback is obsolete, and will be removed when all - * current LL-drivers support rcvcall_skb. Do NOT use for new - * drivers. - */ - void (*rcvcallb)(int, int, u_char*, int); - /* * Receive-Callback using sk_buff's * Parameters: @@ -269,6 +307,7 @@ typedef struct { * num = depending on status-type. */ int (*statcallb)(isdn_ctrl*); + /* Send command * Parameters: * isdn_ctrl* @@ -278,31 +317,16 @@ typedef struct { * num = depending on command. */ int (*command)(isdn_ctrl*); - /* Send Data - * Parameters: - * int driverId - * int local channel-number (0 ...) - * u_char pointer to data - * int length of data - * int Flag: 0 = Call form Kernel-Space (use memcpy, - * no schedule allowed) - * 1 = Data is in User-Space (use memcpy_fromfs, - * may schedule) - * - * NOTE: This call is obsolete, and will be removed when all - * current LL-drivers support writebuf_skb. Do NOT use for new - * drivers. - */ - int (*writebuf)(int, int, const u_char*, int, int); /* * Send data using sk_buff's * Parameters: * int driverId * int local channel-number (0...) + * int Flag: Need ACK for this packet. * struct sk_buff *skb Data to send */ - int (*writebuf_skb) (int, int, struct sk_buff *); + int (*writebuf_skb) (int, int, int, struct sk_buff *); /* Send raw D-Channel-Commands * Parameters: @@ -316,6 +340,7 @@ typedef struct { * int local channel-number (0 ...) */ int (*writecmd)(const u_char*, int, int, int, int); + /* Read raw Status replies * u_char pointer data (volatile) * int length of buffer @@ -327,6 +352,7 @@ typedef struct { * int local channel-number (0 ...) */ int (*readstat)(u_char*, int, int, int, int); + char id[20]; } isdn_if; @@ -339,8 +365,7 @@ typedef struct { * supporting sk_buff's should set this to 0. * command Address of Command-Handler. * features Bitwise coded Features of this driver. (use ISDN_FEATURE_...) - * writebuf Address of Send-Command-Handler. OBSOLETE do NOT use anymore. - * writebuf_skb Address of Skbuff-Send-Handler. (NULL if not supported) + * writebuf_skb Address of Skbuff-Send-Handler. * writecmd " " D-Channel " which accepts raw D-Ch-Commands. * readstat " " D-Channel " which delivers raw Status-Data. * @@ -348,72 +373,16 @@ typedef struct { * * channels Driver-ID assigned to this driver. (Must be used on all * subsequent callbacks. - * rcvcallb Address of handler for received data. OBSOLETE, do NOT use anymore. - * rcvcallb_skb Address of handler for received Skbuff's. (NULL if not supp.) + * rcvcallb_skb Address of handler for received Skbuff's. * statcallb " " " for status-changes. * */ extern int register_isdn(isdn_if*); -/* Compatibility Linux-2.0.X <-> Linux-2.1.X */ - #ifndef LINUX_VERSION_CODE #include #endif -#if (LINUX_VERSION_CODE < 0x020100) -#include - -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 -#define RWARG int -#define LSARG off_t -#else #include -#define GET_USER get_user -#define PUT_USER put_user -#define RWTYPE ssize_t -#define LSTYPE long long -#define RWARG size_t -#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/proc_fs.h b/include/linux/proc_fs.h index baec070117c6..e72a3df89e90 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -327,6 +327,7 @@ extern struct inode * proc_get_inode(struct super_block *, int, struct proc_dir_ extern int proc_statfs(struct super_block *, struct statfs *, int); extern void proc_read_inode(struct inode *); extern void proc_write_inode(struct inode *); +extern int proc_permission(struct inode *, int); extern int proc_match(int, const char *,struct proc_dir_entry *); diff --git a/include/linux/sched.h b/include/linux/sched.h index b5f1be07e71b..ca8016313012 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -20,6 +20,8 @@ extern unsigned long event; #include #include #include +#include +#include /* * cloning flags: @@ -220,8 +222,6 @@ struct task_struct { pid_t session; /* boolean value for session group leader */ int leader; - int ngroups; - gid_t groups[NGROUPS]; /* * pointers to (original) parent process, youngest child, younger sibling, * older sibling, respectively. (p->father can be replaced with @@ -237,8 +237,6 @@ struct task_struct { struct task_struct **tarray_ptr; struct wait_queue *wait_chldexit; /* for wait4() */ - uid_t uid,euid,suid,fsuid; - gid_t gid,egid,sgid,fsgid; unsigned long timeout, policy, rt_priority; unsigned long it_real_value, it_prof_value, it_virt_value; unsigned long it_real_incr, it_prof_incr, it_virt_incr; @@ -253,6 +251,12 @@ struct task_struct { unsigned long old_maj_flt; /* old value of maj_flt */ unsigned long dec_flt; /* page fault count of the last time */ unsigned long swap_cnt; /* number of pages to swap on next pass */ +/* process credentials */ + uid_t uid,euid,suid,fsuid; + gid_t gid,egid,sgid,fsgid; + int ngroups; + gid_t groups[NGROUPS]; + kernel_cap_t cap_effective, cap_inheritable, cap_permitted; /* limits */ struct rlimit rlim[RLIM_NLIMITS]; unsigned short used_math; @@ -334,18 +338,20 @@ struct task_struct { /* schedlink */ &init_task,&init_task, &init_task, &init_task, \ /* ec,brk... */ 0,0,0,0,0,0, \ /* pid etc.. */ 0,0,0,0,0, \ -/* suppl grps*/ 0, {0,}, \ /* proc links*/ &init_task,&init_task,NULL,NULL,NULL, \ /* pidhash */ NULL, NULL, \ /* tarray */ &task[0], \ /* chld wait */ NULL, \ -/* uid etc */ 0,0,0,0,0,0,0,0, \ /* timeout */ 0,SCHED_OTHER,0,0,0,0,0,0,0, \ /* timer */ { NULL, NULL, 0, 0, it_real_fn }, \ /* utime */ {0,0,0,0},0, \ /* per cpu times */ {0, }, {0, }, \ /* flt */ 0,0,0,0,0,0, \ /* swp */ 0,0,0,0,0, \ +/* process credentials */ \ +/* uid etc */ 0,0,0,0,0,0,0,0, \ +/* suppl grps*/ 0, {0,}, \ +/* caps */ CAP_FULL_SET, CAP_FULL_SET, CAP_FULL_SET, \ /* rlimits */ INIT_RLIMITS, \ /* math */ 0, \ /* comm */ "swapper", \ @@ -444,8 +450,6 @@ extern unsigned int * prof_buffer; extern unsigned long prof_len; extern unsigned long prof_shift; -extern int securelevel; /* system security level */ - #define CURRENT_TIME (xtime.tv_sec) extern void FASTCALL(__wake_up(struct wait_queue ** p, unsigned int mode)); @@ -530,10 +534,13 @@ extern void free_irq(unsigned int irq, void *dev_id); * For correctness, the above considerations need to be extended to * fsuser(). This is done, along with moving fsuser() checks to be * last. + * + * These will be removed, but in the mean time, when the SECURE_NOROOT + * flag is set, uids don't grant privilege. */ extern inline int suser(void) { - if (current->euid == 0) { + if (!issecure(SECURE_NOROOT) && current->euid == 0) { current->flags |= PF_SUPERPRIV; return 1; } @@ -542,7 +549,27 @@ extern inline int suser(void) extern inline int fsuser(void) { - if (current->fsuid == 0) { + if (!issecure(SECURE_NOROOT) && current->fsuid == 0) { + current->flags |= PF_SUPERPRIV; + return 1; + } + return 0; +} + +/* + * capable() checks for a particular capability. + * New privilege checks should use this interface, rather than suser() or + * fsuser(). See include/linux/capability.h for defined capabilities. + */ + +extern inline int capable(int cap) +{ +#if 0 /* not yet */ + if (cap_raised(current->cap_effective, cap)) +#else + if (cap_is_fs_cap(cap) ? current->fsuid == 0 : current->euid == 0) +#endif + { current->flags |= PF_SUPERPRIV; return 1; } diff --git a/include/linux/securebits.h b/include/linux/securebits.h new file mode 100644 index 000000000000..1e10badcbdba --- /dev/null +++ b/include/linux/securebits.h @@ -0,0 +1,30 @@ +#ifndef _LINUX_SECUREBITS_H +#define _LINUX_SECUREBITS_H 1 + +#define SECUREBITS_DEFAULT 0x00000000 + +extern unsigned securebits; + +/* When set UID 0 has no special privileges. When unset, we support + inheritance of root-permissions and suid-root executablew under + compatibility mode. We raise the effective and inheritable bitmasks + *of the executable file* if the effective uid of the new process is + 0. If the real uid is 0, we raise the inheritable bitmask of the + executable file. */ +#define SECURE_NOROOT 0 + +/* When set, setuid to/from uid 0 does not trigger capability-"fixes" + to be compatible with old programs relying on set*uid to loose + privileges. When unset, setuid doesn't change privileges. */ +#define SECURE_NO_SETUID_FIXUP 2 + +/* Each securesetting is implemented using two bits. One bit specify + whether the setting is on or off. The other bit specify whether the + setting is fixed or not. A setting which is fixed cannot be changed + from user-level. */ + +#define issecure(X) ( (1 << (X+1)) & SECUREBITS_DEFAULT ? \ + (1 << (X)) & SECUREBITS_DEFAULT : \ + (1 << (X)) & securebits ) + +#endif /* !_LINUX_SECUREBITS_H */ diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 0ccd625cfd30..79a7faf2a016 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -75,8 +75,10 @@ struct sk_buff { struct dst_entry *dst; -#if (defined(__alpha__) || defined(__sparc64__)) && (defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)) +#if (defined(__alpha__) || defined(__sparc_v9__)) && (defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)) char cb[48]; /* sorry. 64bit pointers have a price */ +#elif (defined(__alpha__) || defined(__sparc_v9__)) + char cb[40]; #else char cb[36]; #endif @@ -370,12 +372,17 @@ extern __inline__ void skb_insert(struct sk_buff *old, struct sk_buff *newsk) * Place a packet after a given packet in a list. */ +extern __inline__ void __skb_append(struct sk_buff *old, struct sk_buff *newsk) +{ + __skb_insert(newsk, old, old->next, old->list); +} + extern __inline__ void skb_append(struct sk_buff *old, struct sk_buff *newsk) { unsigned long flags; spin_lock_irqsave(&skb_queue_lock, flags); - __skb_insert(newsk, old, old->next, old->list); + __skb_append(old, newsk); spin_unlock_irqrestore(&skb_queue_lock, flags); } diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 3a8f31e77770..53b265f49ec6 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -153,7 +153,6 @@ enum NET_IPV4_TCP_TIMESTAMPS, NET_IPV4_TCP_WINDOW_SCALING, NET_IPV4_TCP_SACK, - NET_IPV4_TCP_VEGAS_CONG_AVOID, NET_IPV4_DEFAULT_TTL, NET_IPV4_AUTOCONFIG, NET_IPV4_NO_PMTU_DISC, diff --git a/include/linux/types.h b/include/linux/types.h index a990773af62f..a53a4ccc10a7 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -69,6 +69,28 @@ typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; +#ifndef __BIT_TYPES_DEFINED__ +#define __BIT_TYPES_DEFINED__ + +typedef __u8 u_int8_t; +typedef __s8 int8_t; +typedef __u16 u_int16_t; +typedef __s16 int16_t; +typedef __u32 u_int32_t; +typedef __s32 int32_t; + +#endif /* !(__BIT_TYPES_DEFINED__) */ + +typedef __u8 uint8_t; +typedef __u16 uint16_t; +typedef __u32 uint32_t; + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +typedef __u64 uint64_t; +typedef __u64 u_int64_t; +typedef __s64 int64_t; +#endif + #endif /* __KERNEL_STRICT_NAMES */ /* diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index 40072ab47cdc..efa2e5b3e649 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -16,7 +16,7 @@ struct vm_struct { struct vm_struct * get_vm_area(unsigned long size); void vfree(void * addr); void * vmalloc(unsigned long size); -int vread(char *buf, char *addr, int count); +long vread(char *buf, char *addr, unsigned long count); void vmfree_area_pages(unsigned long address, unsigned long size); int vmalloc_area_pages(unsigned long address, unsigned long size); diff --git a/include/net/sock.h b/include/net/sock.h index d94469da9ffa..6b6f782eacb3 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -83,12 +83,8 @@ #include -/* - * The AF_UNIX specific socket options - */ - -struct unix_opt -{ +/* The AF_UNIX specific socket options */ +struct unix_opt { int family; char * name; int locks; @@ -105,8 +101,7 @@ struct unix_opt #ifdef CONFIG_NETLINK struct netlink_callback; -struct netlink_opt -{ +struct netlink_opt { pid_t pid; unsigned groups; pid_t dst_pid; @@ -117,13 +112,9 @@ struct netlink_opt }; #endif -/* - * Once the IPX ncpd patches are in these are going into protinfo - */ - +/* Once the IPX ncpd patches are in these are going into protinfo. */ #if defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE) -struct ipx_opt -{ +struct ipx_opt { ipx_address dest_addr; ipx_interface *intrfc; unsigned short port; @@ -141,8 +132,7 @@ struct ipx_opt #endif #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) -struct ipv6_pinfo -{ +struct ipv6_pinfo { struct in6_addr saddr; struct in6_addr rcv_saddr; struct in6_addr daddr; @@ -213,7 +203,7 @@ struct tcp_opt { __u32 lrcvtime; /* timestamp of last received data packet*/ __u32 srtt; /* smothed round trip time << 3 */ - __u32 ato; /* delayed ack timeout */ + __u32 ato; /* delayed ack timeout */ __u32 snd_wl1; /* Sequence for window update */ __u32 snd_wl2; /* Ack sequence for update */ @@ -231,12 +221,11 @@ struct tcp_opt { __u32 packets_out; /* Packets which are "in flight" */ __u32 fackets_out; /* Non-retrans SACK'd packets */ __u32 retrans_out; /* Fast-retransmitted packets out */ - __u32 high_seq; /* highest sequence number sent by onset of congestion */ + __u32 high_seq; /* snd_nxt at onset of congestion */ /* * Slow start and congestion control (see also Nagle, and Karn & Partridge) */ __u32 snd_ssthresh; /* Slow start size threshold */ - __u16 snd_cwnd_cnt; __u8 dup_acks; /* Consequetive duplicate acks seen from other end */ __u8 delayed_acks; @@ -276,7 +265,6 @@ struct tcp_opt { struct tcp_sack_block selective_acks[4]; /* The SACKS themselves*/ struct timer_list probe_timer; /* Probes */ - __u32 basertt; /* Vegas baseRTT */ __u32 window_clamp; /* XXX Document this... -DaveM */ __u32 probes_out; /* unanswered 0 window probes */ __u32 syn_seq; @@ -287,7 +275,6 @@ struct tcp_opt { struct open_request *syn_wait_queue; struct open_request **syn_wait_last; int syn_backlog; - }; @@ -336,12 +323,7 @@ struct tcp_opt { #define SOCK_DEBUG(sk, msg...) do { } while (0) #endif -/* - * TCP will start to use the new protinfo while *still using the old* fields - */ - -struct sock -{ +struct sock { /* This must be first. */ struct sock *sklist_next; struct sock *sklist_prev; @@ -350,28 +332,29 @@ struct sock struct sock *bind_next; struct sock **bind_pprev; - /* Main hash linkage for various protocol lookup tables. */ - struct sock *next; - struct sock **pprev; - /* Socket demultiplex comparisons on incoming packets. */ __u32 daddr; /* Foreign IPv4 addr */ __u32 rcv_saddr; /* Bound local IPv4 addr */ - int bound_dev_if; /* Bound device index if != 0 */ + __u16 dport; /* Destination port */ unsigned short num; /* Local port */ + int bound_dev_if; /* Bound device index if != 0 */ + + /* Main hash linkage for various protocol lookup tables. */ + struct sock *next; + struct sock **pprev; + volatile unsigned char state, /* Connection state */ zapped; /* In ax25 & ipx means not linked */ __u16 sport; /* Source port */ - __u16 dport; /* Destination port */ - unsigned short family; - unsigned char reuse, - nonagle; + unsigned short family; /* Address family */ + unsigned char reuse, /* SO_REUSEADDR setting */ + nonagle; /* Disable Nagle algorithm? */ - int sock_readers; /* user count */ - int rcvbuf; + int sock_readers; /* User count */ + int rcvbuf; /* Size of receive buffer in bytes */ - struct wait_queue **sleep; + struct wait_queue **sleep; /* Sock wait queue */ struct dst_entry *dst_cache; /* Destination cache */ atomic_t rmem_alloc; /* Receive queue bytes committed */ struct sk_buff_head receive_queue; /* Incoming packets */ @@ -380,13 +363,12 @@ struct sock atomic_t omem_alloc; /* "o" is "option" or "other" */ __u32 saddr; /* Sending source */ unsigned int allocation; /* Allocation mode */ - int sndbuf; + int sndbuf; /* Size of send buffer in bytes */ struct sock *prev; - /* - * Not all are volatile, but some are, so we - * might as well say they all are. - */ + /* Not all are volatile, but some are, so we might as well say they all are. + * XXX Make this a flag word -DaveM + */ volatile char dead, done, urginline, @@ -409,9 +391,9 @@ struct sock struct proto *prot; -/* - * mss is min(mtu, max_window) - */ + /* mss is min(mtu, max_window) + * XXX Fix this, mtu only used in one TCP place and that is it -DaveM + */ unsigned short mtu; /* mss negotiated in the syn's */ unsigned short mss; /* current eff. mss - can change */ unsigned short user_mss; /* mss requested by user in ioctl */ @@ -451,13 +433,10 @@ struct sock struct sock_filter *filter_data; #endif /* CONFIG_FILTER */ -/* - * This is where all the private (optional) areas that don't - * overlap will eventually live. - */ - - union - { + /* This is where all the private (optional) areas that don't + * overlap will eventually live. + */ + union { void *destruct_hook; struct unix_opt af_unix; #if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE) @@ -489,9 +468,7 @@ struct sock #endif } protinfo; -/* - * IP 'private area' or will be eventually - */ + /* IP 'private area' or will be eventually. */ int ip_ttl; /* TTL setting */ int ip_tos; /* TOS */ unsigned ip_cmsg_flags; @@ -505,31 +482,18 @@ struct sock __u32 ip_mc_addr; struct ip_mc_socklist *ip_mc_list; /* Group array */ -/* - * This part is used for the timeout functions (timer.c). - */ - + /* This part is used for the timeout functions (timer.c). */ int timeout; /* What are we waiting for? */ - struct timer_list timer; /* This is the TIME_WAIT/receive timer - * when we are doing IP - */ + struct timer_list timer; /* This is the sock cleanup timer. */ struct timeval stamp; - /* - * Identd - */ - + /* Identd */ struct socket *socket; - /* - * RPC layer private data - */ + /* RPC layer private data */ void *user_data; - /* - * Callbacks - */ - + /* Callbacks */ void (*state_change)(struct sock *sk); void (*data_ready)(struct sock *sk,int bytes); void (*write_space)(struct sock *sk); @@ -540,14 +504,11 @@ struct sock void (*destruct)(struct sock *sk); }; -/* - * IP protocol blocks we attach to sockets. - * socket layer -> transport layer interface - * transport -> network interface is defined by struct inet_proto +/* IP protocol blocks we attach to sockets. + * socket layer -> transport layer interface + * transport -> network interface is defined by struct inet_proto */ - -struct proto -{ +struct proto { /* These must be first. */ struct sock *sklist_next; struct sock *sklist_prev; @@ -609,16 +570,10 @@ struct proto #define TIME_DONE 7 /* Used to absorb those last few packets */ #define TIME_PROBE0 8 -/* - * About 10 seconds - */ - +/* About 10 seconds */ #define SOCK_DESTROY_TIME (10*HZ) -/* - * Sockets 0-1023 can't be bound to unless you are superuser - */ - +/* Sockets 0-1023 can't be bound to unless you are superuser */ #define PROT_SOCK 1024 #define SHUTDOWN_MASK 3 diff --git a/include/net/tcp.h b/include/net/tcp.h index b5af990237c2..01a95b5789ba 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -154,16 +154,16 @@ struct tcp_tw_bucket { struct sock *sklist_prev; struct sock *bind_next; struct sock **bind_pprev; - struct sock *next; - struct sock **pprev; __u32 daddr; __u32 rcv_saddr; - int bound_dev_if; + __u16 dport; unsigned short num; + int bound_dev_if; + struct sock *next; + struct sock **pprev; unsigned char state, zapped; __u16 sport; - __u16 dport; unsigned short family; unsigned char reuse, nonagle; @@ -181,6 +181,43 @@ struct tcp_tw_bucket { extern kmem_cache_t *tcp_timewait_cachep; +/* Socket demux engine toys. */ +#ifdef __BIG_ENDIAN +#define TCP_COMBINED_PORTS(__sport, __dport) \ + (((__u32)(__sport)<<16) | (__u32)(__dport)) +#else /* __LITTLE_ENDIAN */ +#define TCP_COMBINED_PORTS(__sport, __dport) \ + (((__u32)(__dport)<<16) | (__u32)(__sport)) +#endif + +#if defined(__alpha__) || defined(__sparc_v9__) +#ifdef __BIG_ENDIAN +#define TCP_V4_ADDR_COOKIE(__name, __saddr, __daddr) \ + __u64 __name = (((__u64)(__saddr))<<32)|((__u64)(__daddr)); +#else /* __LITTLE_ENDIAN */ +#define TCP_V4_ADDR_COOKIE(__name, __saddr, __daddr) \ + __u64 __name = (((__u64)(__daddr))<<32)|((__u64)(__saddr)); +#endif /* __BIG_ENDIAN */ +#define TCP_IPV4_MATCH(__sk, __cookie, __saddr, __daddr, __ports, __dif)\ + (((*((__u64 *)&((__sk)->daddr)))== (__cookie)) && \ + ((*((__u32 *)&((__sk)->dport)))== (__ports)) && \ + (!((__sk)->bound_dev_if) || ((__sk)->bound_dev_if == (__dif)))) +#else /* 32-bit arch */ +#define TCP_V4_ADDR_COOKIE(__name, __saddr, __daddr) +#define TCP_IPV4_MATCH(__sk, __cookie, __saddr, __daddr, __ports, __dif)\ + (((__sk)->daddr == (__saddr)) && \ + ((__sk)->rcv_saddr == (__daddr)) && \ + ((*((__u32 *)&((__sk)->dport)))== (__ports)) && \ + (!((__sk)->bound_dev_if) || ((__sk)->bound_dev_if == (__dif)))) +#endif /* 64-bit arch */ + +#define TCP_IPV6_MATCH(__sk, __saddr, __daddr, __ports, __dif) \ + (((*((__u32 *)&((__sk)->dport)))== (__ports)) && \ + ((__sk)->family == AF_INET6) && \ + !ipv6_addr_cmp(&(__sk)->net_pinfo.af_inet6.daddr, (__saddr)) && \ + !ipv6_addr_cmp(&(__sk)->net_pinfo.af_inet6.rcv_saddr, (__daddr)) && \ + (!((__sk)->bound_dev_if) || ((__sk)->bound_dev_if == (__dif)))) + /* tcp_ipv4.c: These sysctl variables need to be shared between v4 and v6 * because the v6 tcp code to intialize a connection needs to interoperate * with the v4 code using the same variables. @@ -303,14 +340,6 @@ static __inline__ int tcp_sk_listen_hashfn(struct sock *sk) #define TCPOLEN_SACK_BASE_ALIGNED 4 #define TCPOLEN_SACK_PERBLOCK 8 -/* - * TCP Vegas constants - */ - -#define TCP_VEGAS_ALPHA 2 /* v_cong_detect_top_nseg */ -#define TCP_VEGAS_BETA 4 /* v_cong_detect_bot_nseg */ -#define TCP_VEGAS_GAMMA 1 /* v_exp_inc_nseg */ - struct open_request; struct or_calltable { @@ -682,6 +711,12 @@ struct tcp_skb_cb { #define TCP_SKB_CB(__skb) ((struct tcp_skb_cb *)&((__skb)->cb[0])) +/* We store the congestion window as a packet count, shifted by + * a factor so that implementing the 1/2 MSS ssthresh rules + * is easy. + */ +#define TCP_CWND_SHIFT 1 + /* This determines how many packets are "in the network" to the best * or our knowledge. In many cases it is conservative, but where * detailed information is available from the receiver (via SACK @@ -726,7 +761,8 @@ static __inline__ int tcp_snd_test(struct sock *sk, struct sk_buff *skb) !(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_URG)) nagle_check = 0; - return (nagle_check && tcp_packets_in_flight(tp) < tp->snd_cwnd && + return (nagle_check && + (tcp_packets_in_flight(tp) < (tp->snd_cwnd>>TCP_CWND_SHIFT)) && !after(TCP_SKB_CB(skb)->end_seq, tp->snd_una + tp->snd_wnd) && tp->retransmits == 0); } diff --git a/init/main.c b/init/main.c index 5bb9e7ff8868..c9e0fff8d13e 100644 --- a/init/main.c +++ b/init/main.c @@ -1144,6 +1144,13 @@ static int init(void * unused) smp_begin(); #endif +#ifdef CONFIG_KMOD + { + extern int kmod_init(void); + kmod_init(); + } +#endif + #ifdef CONFIG_UMSDOS_FS { /* @@ -1179,13 +1186,6 @@ static int init(void * unused) } #endif -#ifdef CONFIG_KMOD - { - extern int kmod_init(void); - kmod_init(); - } -#endif - setup(1); if (open("/dev/console", O_RDWR, 0) < 0) diff --git a/kernel/kmod.c b/kernel/kmod.c index c2ff9c85640f..379c27695578 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -14,7 +14,6 @@ */ int kmod_unload_delay = 60; char modprobe_path[256] = "/sbin/modprobe"; -static int kmod_running = 0; static char module_name[64] = ""; static char * argv[] = { "modprobe", "-k", module_name, NULL, }; static char * envp[] = { "HOME=/", "TERM=linux", NULL, }; @@ -42,7 +41,6 @@ int kmod_thread(void * data) current->pgrp = 1; sprintf(current->comm, "kmod"); sigfillset(¤t->blocked); - kmod_running = 1; /* This is the main kmod_thread loop. It first sleeps, then @@ -135,9 +133,6 @@ int request_module(const char * name) the module into module_name. Once that is done, wake up kmod_thread. */ - if(!kmod_running) - return 0; - strncpy(module_name, name, sizeof(module_name)); module_name[sizeof(module_name)-1] = '\0'; wake_up(&kmod_queue); diff --git a/kernel/ksyms.c b/kernel/ksyms.c index ad3c4a706907..6f1101319f08 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -389,7 +389,6 @@ EXPORT_SYMBOL(is_bad_inode); EXPORT_SYMBOL(event); EXPORT_SYMBOL(__down); EXPORT_SYMBOL(__up); -EXPORT_SYMBOL(securelevel); /* all busmice */ EXPORT_SYMBOL(add_mouse_randomness); diff --git a/kernel/printk.c b/kernel/printk.c index afc178c5ee5d..d8242a428c79 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -54,7 +54,7 @@ static char log_buf[LOG_BUF_LEN]; static unsigned long log_start = 0; static unsigned long logged_chars = 0; struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES]; -static int selected_console = 0; +static int preferred_console = -1; /* * Setup a list of consoles. Called from init/main.c @@ -95,11 +95,13 @@ __initfunc(void console_setup(char *str, int *ints)) */ for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++) if (strcmp(console_cmdline[i].name, name) == 0 && - console_cmdline[i].index == idx) + console_cmdline[i].index == idx) { + preferred_console = i; return; + } if (i == MAX_CMDLINECONSOLES) return; - selected_console = 1; + preferred_console = i; c = &console_cmdline[i]; memcpy(c->name, name, sizeof(c->name)); c->options = options; @@ -336,13 +338,13 @@ void register_console(struct console * console) * didn't select a console we take the first one * that registers here. */ - if (selected_console == 0) { + if (preferred_console < 0) { if (console->index < 0) console->index = 0; if (console->setup == NULL || console->setup(console, NULL) == 0) { - console->flags |= CON_ENABLED | CON_FIRST; - selected_console = 1; + console->flags |= CON_ENABLED | CON_CONSDEV; + preferred_console = 0; } } @@ -362,8 +364,8 @@ void register_console(struct console * console) break; console->flags |= CON_ENABLED; console->index = console_cmdline[i].index; - if (i == 0) - console->flags |= CON_FIRST; + if (i == preferred_console) + console->flags |= CON_CONSDEV; break; } @@ -374,7 +376,7 @@ void register_console(struct console * console) * Put this console in the list - keep the * preferred driver at the head of the list. */ - if ((console->flags & CON_FIRST) || console_drivers == NULL) { + if ((console->flags & CON_CONSDEV) || console_drivers == NULL) { console->next = console_drivers; console_drivers = console; } else { diff --git a/kernel/sched.c b/kernel/sched.c index 8d857680807b..f9246e78533b 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -47,7 +47,7 @@ * kernel variables */ -int securelevel = 0; /* system security level */ +unsigned securebits = SECUREBITS_DEFAULT; /* systemwide security settings */ long tick = (1000000 + HZ/2) / HZ; /* timer interrupt period */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 0da7a07c8828..ed2d8fd6987c 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -45,6 +45,9 @@ extern int sysctl_overcommit_memory; extern char modprobe_path[]; extern int kmod_unload_delay; #endif +#ifdef CONFIG_CHR_DEV_SG +extern int sg_big_buff; +#endif #ifdef __sparc__ extern char reboot_command []; @@ -52,8 +55,6 @@ extern char reboot_command []; static int parse_table(int *, int, void *, size_t *, void *, size_t, ctl_table *, void **); -static int do_securelevel_strategy (ctl_table *, int *, int, void *, size_t *, - void *, size_t, void **); static ctl_table root_table[]; @@ -156,8 +157,6 @@ static ctl_table kern_table[] = { 0644, NULL, &proc_dointvec}, {KERN_DENTRY, "dentry-state", &dentry_stat, 6*sizeof(int), 0444, NULL, &proc_dointvec}, - {KERN_SECURELVL, "securelevel", &securelevel, sizeof(int), - 0444, NULL, &proc_dointvec, (ctl_handler *)&do_securelevel_strategy}, {KERN_PANIC, "panic", &panic_timeout, sizeof(int), 0644, NULL, &proc_dointvec}, #ifdef CONFIG_BLK_DEV_INITRD @@ -183,6 +182,10 @@ static ctl_table kern_table[] = { 0644, NULL, &proc_dostring, &sysctl_string }, {KERN_KMOD_UNLOAD_DELAY, "kmod_unload_delay", &kmod_unload_delay, sizeof(int), 0644, NULL, &proc_dointvec}, +#endif +#ifdef CONFIG_CHR_DEV_SG + {KERN_NRFILE, "sg-big-buff", &sg_big_buff, sizeof (int), + 0444, NULL, &proc_dointvec}, #endif {0} }; @@ -408,29 +411,6 @@ int do_sysctl_strategy (ctl_table *table, return 0; } -/* - * This function only checks permission for changing the security level - * If the tests are successful, the actual change is done by - * do_sysctl_strategy - */ -static int do_securelevel_strategy (ctl_table *table, - int *name, int nlen, - void *oldval, size_t *oldlenp, - void *newval, size_t newlen, void **context) -{ - int level; - - if (newval && newlen) { - if (newlen != sizeof (int)) - return -EINVAL; - if(copy_from_user (&level, newval, newlen)) - return -EFAULT; - if (level < securelevel && current->pid != 1) - return -EPERM; - } - return 0; -} - struct ctl_table_header *register_sysctl_table(ctl_table * table, int insert_at_head) { diff --git a/mm/vmalloc.c b/mm/vmalloc.c index d0270d58677a..6b262b403df4 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -208,12 +208,16 @@ void * vmalloc(unsigned long size) return addr; } -int vread(char *buf, char *addr, int count) +long vread(char *buf, char *addr, unsigned long count) { struct vm_struct **p, *tmp; char *vaddr, *buf_start = buf; int n; + /* Don't allow overflow */ + if ((unsigned long) addr + count < count) + count = -(unsigned long) addr; + for (p = &vmlist; (tmp = *p) ; p = &tmp->next) { vaddr = (char *) tmp->addr; while (addr < vaddr) { diff --git a/net/bridge/br.c b/net/bridge/br.c index 2961ff3c6d00..014453f8cec8 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -13,8 +13,31 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Fixes: - * Yury Shevchuk : Bridge with non bridging ports + * Fixes: + * Yury Shevchuk : Bridge with non bridging ports + * Jean-Rene Peulve: jr.peulve@aix.pacwan.net Jan/Feb 98 + * support Linux 2.0 + * Handle Receive config bpdu + * kick mark_bh to send Spanning Tree pdus + * bridgeId comparison using htonl() + * make STP interoperable with other vendors + * wrong test in root_selection() + * add more STP debug info + * some performance improvments + * do not clear bridgeId.mac while setting priority + * do not reset port priority when starting bridge + * make port priority from user value and port number + * maintains user port state out of device state + * broacast/multicast storm limitation + * forwarding statistics + * stop br_tick when bridge is turn off + * add local MACs in avl_tree to forward up stack + * fake receive on right port for IP/ARP + * ages tree even if packet does not cross bridge + * add BRCMD_DISPLAY_FDB (ioctl for now) + * + * Alan Cox: Merged Jean-Rene's stuff, reformatted stuff a bit + * so blame me first if its broken ;) * * Todo: * Don't bring up devices automatically. Start ports disabled @@ -42,11 +65,17 @@ #include #include #include +#include +#include #include #include #include #include +#ifndef min +#define min(a, b) (((a) <= (b)) ? (a) : (b)) +#endif + static void transmit_config(int port_no); static int root_bridge(void); static int supersedes_port_info(int port_no, Config_bpdu *config); @@ -80,7 +109,7 @@ static void br_init_port(int port_no); static void enable_port(int port_no); static void disable_port(int port_no); static void set_bridge_priority(bridge_id_t *new_bridge_id); -static void set_port_priority(int port_no, unsigned short new_port_id); +static void set_port_priority(int port_no); static void set_path_cost(int port_no, unsigned short path_cost); static void start_hello_timer(void); static void stop_hello_timer(void); @@ -104,11 +133,12 @@ static int br_device_event(struct notifier_block *dnot, unsigned long event, voi static void br_tick(unsigned long arg); static int br_forward(struct sk_buff *skb, int port); /* 3.7 */ static int br_port_cost(struct device *dev); /* 4.10.2 */ -static void br_bpdu(struct sk_buff *skb); /* consumes skb */ +static void br_bpdu(struct sk_buff *skb, int port); /* consumes skb */ static int br_cmp(unsigned int *a, unsigned int *b); static int send_tcn_bpdu(int port_no, Tcn_bpdu *bpdu); static int send_config_bpdu(int port_no, Config_bpdu *config_bpdu); static int find_port(struct device *dev); +static void br_add_local_mac(unsigned char *mac); static int br_flood(struct sk_buff *skb, int port); static int br_drop(struct sk_buff *skb); static int br_learn(struct sk_buff *skb, int port); /* 3.8 */ @@ -116,8 +146,21 @@ static int br_learn(struct sk_buff *skb, int port); /* 3.8 */ static unsigned char bridge_ula[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; static Bridge_data bridge_info; /* (4.5.3) */ Port_data port_info[All_ports]; /* (4.5.5) */ -Config_bpdu config_bpdu[All_ports]; -Tcn_bpdu tcn_bpdu[All_ports]; + +/* JRP: fdb cache 1/port save kmalloc/kfree on every frame */ +struct fdb *newfdb[All_ports]; +int allocated_fdb_cnt = 0; + +/* broacast/multicast storm limitation */ +int max_mcast_per_period = MAX_MCAST_PER_PERIOD; +int mcast_hold_time = MCAST_HOLD_TIME; + +/* JRP: next two bpdu are copied to skbuff so we need only 1 of each */ +static Config_bpdu config_bpdu; +static Tcn_bpdu tcn_bpdu; +static unsigned char port_priority[All_ports]; +static unsigned char user_port_state[All_ports]; + static Timer hello_timer; /* (4.5.4.1) */ static Timer tcn_timer; /* (4.5.4.2) */ static Timer topology_change_timer; /* (4.5.4.3) */ @@ -129,6 +172,7 @@ static Timer hold_timer[All_ports]; /* (4.5.6.3) */ unsigned int fdb_aging_time = FDB_TIMEOUT; struct br_stat br_stats; +#define br_stats_cnt br_stats.packet_cnts static struct timer_list tl; /* for 1 second timer... */ @@ -154,23 +198,28 @@ static struct notifier_block br_dev_notifier={ #define BR_PROTOCOL_HASH(x) (x % BR_MAX_PROTOCOLS) /* Checks if that protocol type is to be bridged */ + int br_protocol_ok(unsigned short protocol) { unsigned x; /* See if protocol statistics are to be kept */ if (br_stats.flags & BR_PROT_STATS) - { for(x=0;xmax_age; /* (4.6.3.3) */ bridge_info.hello_time = config->hello_time; bridge_info.forward_delay = config->forward_delay; - if (config->flags & TOPOLOGY_CHANGE) - bridge_info.top_change = 1; + bridge_info.top_change = config->top_change; } static void config_bpdu_generation(void) @@ -353,8 +403,8 @@ static void transmit_tcn(void) int port_no; port_no = bridge_info.root_port; - tcn_bpdu[port_no].type = BPDU_TYPE_TOPO_CHANGE; - send_tcn_bpdu(port_no, &tcn_bpdu[bridge_info.root_port]); /* (4.6.6.3) */ + tcn_bpdu.type = BPDU_TYPE_TOPO_CHANGE; + send_tcn_bpdu(port_no, &tcn_bpdu); /* (4.6.6.3) */ } static void configuration_update(void) /* (4.6.7) */ @@ -420,7 +470,7 @@ static void root_selection(void) ) /* (4.6.8.3.1(4)) */ || ((port_info[port_no].designated_port - = port_info[root_port].designated_port +/* JRP: was missing an "=" ! */ == port_info[root_port].designated_port ) && (port_info[port_no].port_id @@ -433,6 +483,10 @@ static void root_selection(void) bridge_info.root_port = root_port; /* (4.6.8.3.1) */ if (root_port == No_port) { /* (4.6.8.3.2) */ +#ifdef DEBUG_STP + if (br_stats.flags & BR_DEBUG) + printk(KERN_DEBUG "root_selection: becomes root\n"); +#endif bridge_info.designated_root = bridge_info.bridge_id; /* (4.6.8.3.2(1)) */ bridge_info.root_path_cost = Zero;/* (4.6.8.3.2(2)) */ @@ -450,6 +504,8 @@ static void designated_port_selection(void) int port_no; for (port_no = One; port_no <= No_of_ports; port_no++) { /* (4.6.9.3) */ + if(port_info[port_no].state == Disabled) + continue; if (designated_port(port_no) /* (4.6.9.3.1) */ || ( @@ -498,19 +554,32 @@ static void become_designated_port(int port_no) static void port_state_selection(void) { /* (4.6.11) */ int port_no; + char *state_str; for (port_no = One; port_no <= No_of_ports; port_no++) { + + if(port_info[port_no].state == Disabled) + continue; if (port_no == bridge_info.root_port) { /* (4.6.11.3.1) */ - port_info[port_no].config_pending = FALSE; /* (4.6.11.3~1(1)) */ + state_str = "root"; + port_info[port_no].config_pending = FALSE; /* (4.6.11.3.1(1)) */ port_info[port_no].top_change_ack = 0; make_forwarding(port_no); /* (4.6.11.3.1(2)) */ } else if (designated_port(port_no)) { /* (4.6.11.3.2) */ + state_str = "designated"; stop_message_age_timer(port_no); /* (4.6.11.3.2(1)) */ make_forwarding(port_no); /* (4.6.11.3.2(2)) */ } else { /* (4.6.11.3.3) */ + state_str = "blocking"; port_info[port_no].config_pending = FALSE; /* (4.6.11.3.3(1)) */ port_info[port_no].top_change_ack = 0; make_blocking(port_no); /* (4.6.11.3.3(2)) */ } +#ifdef DEBUG_STP + if (br_stats.flags & BR_DEBUG) + printk(KERN_DEBUG "port_state_selection: becomes %s port %d\n", + state_str, port_no); +#endif + } } @@ -525,6 +594,11 @@ static void make_forwarding(int port_no) static void topology_change_detection(void) { /* (4.6.14) */ +#ifdef DEBUG_STP + if ((br_stats.flags & BR_DEBUG) + && (bridge_info.top_change_detected == 0)) + printk(KERN_DEBUG "topology_change_detected\n"); +#endif if (root_bridge()) { /* (4.6.14.3.1) */ bridge_info.top_change = 1; start_topology_change_timer(); /* (4.6.14.3.1(2)) */ @@ -532,12 +606,16 @@ static void topology_change_detection(void) transmit_tcn(); /* (4.6.14.3.2(1)) */ start_tcn_timer(); /* (4.6.14.3.2(2)) */ } - bridge_info.top_change = 1; + bridge_info.top_change_detected = 1; /* (4.6.14.3.3) */ } static void topology_change_acknowledged(void) { /* (4.6.15) */ - bridge_info.top_change_detected = 0; +#ifdef DEBUG_STP + if (br_stats.flags & BR_DEBUG) + printk(KERN_DEBUG "topology_change_acked\n"); +#endif + bridge_info.top_change_detected = 0; /* (4.6.15.3.1) */ stop_tcn_timer(); /* (4.6.15.3.2) */ } @@ -574,10 +652,16 @@ static void set_port_state(int port_no, int state) static void received_config_bpdu(int port_no, Config_bpdu *config) /* (4.7.1) */ { - int root; + int root; root = root_bridge(); if (port_info[port_no].state != Disabled) { + +#ifdef DEBUG_STP + if (br_stats.flags & BR_DEBUG) + printk(KERN_DEBUG "received_config_bpdu: port %d\n", + port_no); +#endif if (supersedes_port_info(port_no, config)) { /* (4.7.1.1) *//* (4. * 6.2.2) */ record_config_information(port_no, config); /* (4.7.1.1.1) */ @@ -588,7 +672,7 @@ static void received_config_bpdu(int port_no, Config_bpdu *config) /* (4.7.1) /* (4.6.11.2.1) */ if ((!root_bridge()) && root) { /* (4.7.1.1.4) */ stop_hello_timer(); - if (bridge_info.top_change_detected) { /* (4.7.1.1.5~ */ + if (bridge_info.top_change_detected) { /* (4.7.1.1.5 */ stop_topology_change_timer(); transmit_tcn(); /* (4.6.6.1) */ start_tcn_timer(); @@ -598,7 +682,7 @@ static void received_config_bpdu(int port_no, Config_bpdu *config) /* (4.7.1) record_config_timeout_values(config); /* (4.7.1.1.6) */ /* (4.6.3.2) */ config_bpdu_generation(); /* (4.6.4.2.1) */ - if (config->flags & TOPOLOGY_CHANGE_ACK) { /* (4.7.1.1.7) */ + if (config->top_change_ack) { /* (4.7.1.1.7) */ topology_change_acknowledged(); /* (4.6.15.2) */ } } @@ -612,6 +696,11 @@ static void received_config_bpdu(int port_no, Config_bpdu *config) /* (4.7.1) static void received_tcn_bpdu(int port_no, Tcn_bpdu *tcn) /* (4.7.2) */ { if (port_info[port_no].state != Disabled) { +#ifdef DEBUG_STP + if (br_stats.flags & BR_DEBUG) + printk(KERN_DEBUG "received_tcn_bpdu: port %d\n", + port_no); +#endif if (designated_port(port_no)) { topology_change_detection(); /* (4.7.2.1) */ /* (4.6.14.2.1) */ @@ -628,9 +717,14 @@ static void hello_timer_expiry(void) static void message_age_timer_expiry(int port_no) /* (4.7.4) */ { - int root; + int root; root = root_bridge(); +#ifdef DEBUG_STP + if (br_stats.flags & BR_DEBUG) + printk(KERN_DEBUG "message_age_timer_expiry: port %d\n", + port_no); +#endif become_designated_port(port_no); /* (4.7.4.1) */ /* (4.6.10.2.1) */ configuration_update(); /* (4.7.4.2) */ @@ -653,12 +747,17 @@ static void message_age_timer_expiry(int port_no) /* (4.7.4) */ static void forward_delay_timer_expiry(int port_no) /* (4.7.5) */ { - if (port_info[port_no].state == Listening) { /* (4.7.5.1) */ + if (port_info[port_no].state == Listening) + { /* (4.7.5.1) */ set_port_state(port_no, Learning); /* (4.7.5.1.1) */ start_forward_delay_timer(port_no); /* (4.7.5.1.2) */ - } else if (port_info[port_no].state == Learning) { /* (4.7.5.2) */ + } + else if (port_info[port_no].state == Learning) + { + /* (4.7.5.2) */ set_port_state(port_no, Forwarding); /* (4.7.5.2.1) */ - if (designated_for_some_port()) { /* (4.7.5.2.2) */ + if (designated_for_some_port()) + { /* (4.7.5.2.2) */ topology_change_detection(); /* (4.6.14.2.2) */ } @@ -667,13 +766,15 @@ static void forward_delay_timer_expiry(int port_no) /* (4.7.5) */ static int designated_for_some_port(void) { - int port_no; - + int port_no; - for (port_no = One; port_no <= No_of_ports; port_no++) { + for (port_no = One; port_no <= No_of_ports; port_no++) + { + if(port_info[port_no].state == Disabled) + continue; if ((br_cmp(port_info[port_no].designated_bridge.BRIDGE_ID, - bridge_info.bridge_id.BRIDGE_ID) == 0) - ) { + bridge_info.bridge_id.BRIDGE_ID) == 0)) + { return (TRUE); } } @@ -688,26 +789,38 @@ static void tcn_timer_expiry(void) static void topology_change_timer_expiry(void) { /* (4.7.7) */ - bridge_info.top_change_detected = 0; + bridge_info.top_change_detected = 0; /* (4.7.7.1) */ bridge_info.top_change = 0; /* (4.7.7.2) */ } static void hold_timer_expiry(int port_no) /* (4.7.8) */ { - if (port_info[port_no].config_pending) { + if (port_info[port_no].config_pending) + { transmit_config(port_no); /* (4.7.8.1) */ } /* (4.6.1.2.3) */ } __initfunc(void br_init(void)) { /* (4.8.1) */ - int port_no; + int port_no; + + printk(KERN_INFO "Ethernet Bridge 005 for NET3.037 (Linux 2.1)\n"); + + /* + * Form initial topology change time. + * The topology change timer is only used if this is the root bridge. + */ + + bridge_info.topology_change_time = BRIDGE_MAX_AGE + BRIDGE_FORWARD_DELAY; /* (4.5.3.13) */ - printk(KERN_INFO "Ethernet Bridge 003 for NET3.037 (Linux 2.1)\n"); bridge_info.designated_root = bridge_info.bridge_id; /* (4.8.1.1) */ bridge_info.root_path_cost = Zero; bridge_info.root_port = No_port; +#ifdef DEBUG_STP + printk(KERN_INFO "br_init: becomes root\n"); +#endif bridge_info.bridge_max_age = BRIDGE_MAX_AGE; bridge_info.bridge_hello_time = BRIDGE_HELLO_TIME; @@ -722,17 +835,22 @@ __initfunc(void br_init(void)) bridge_info.top_change = 0; stop_tcn_timer(); stop_topology_change_timer(); + memset(newfdb, 0, sizeof(newfdb)); for (port_no = One; port_no <= No_of_ports; port_no++) { /* (4.8.1.4) */ + /* initial state = Enable */ + user_port_state[port_no] = ~Disabled; + port_priority[port_no] = 128; br_init_port(port_no); disable_port(port_no); } +#if 0 /* JRP: We are not UP ! Wait for the start command */ port_state_selection(); /* (4.8.1.5) */ config_bpdu_generation(); /* (4.8.1.6) */ - /* initialize system timer */ tl.expires = jiffies+HZ; /* 1 second */ tl.function = br_tick; add_timer(&tl); +#endif register_netdevice_notifier(&br_dev_notifier); br_stats.flags = 0; /*BR_UP | BR_DEBUG*/; /* enable bridge */ @@ -741,8 +859,14 @@ __initfunc(void br_init(void)) /*start_hello_timer();*/ } +static inline unsigned short make_port_id(int port_no) +{ + return (port_priority[port_no] << 8) | port_no; +} + static void br_init_port(int port_no) { + port_info[port_no].port_id = make_port_id(port_no); become_designated_port(port_no); /* (4.8.1.4.1) */ set_port_state(port_no, Blocking); /* (4.8.1.4.2) */ port_info[port_no].top_change_ack = 0; @@ -787,10 +911,12 @@ static void set_bridge_priority(bridge_id_t *new_bridge_id) /* (4.8.4) */ { - int root; - int port_no; + int root; + int port_no; root = root_bridge(); for (port_no = One; port_no <= No_of_ports; port_no++) { /* (4.8.4.2) */ + if(port_info[port_no].state == Disabled) + continue; if (designated_port(port_no)) { port_info[port_no].designated_bridge = *new_bridge_id; } @@ -810,9 +936,10 @@ static void set_bridge_priority(bridge_id_t *new_bridge_id) } } -static void set_port_priority(int port_no, unsigned short new_port_id) +static void set_port_priority(int port_no) /* (4.8.5) */ -{ +{int new_port_id = make_port_id(port_no); + if (designated_port(port_no)) { /* (4.8.5.2) */ port_info[port_no].designated_port = new_port_id; } @@ -825,7 +952,8 @@ static void set_port_priority(int port_no, unsigned short new_port_id) < port_info[port_no].designated_port ) - ) { + ) + { become_designated_port(port_no); /* (4.8.5.4.1) */ port_state_selection(); /* (4.8.5.4.2) */ } @@ -841,27 +969,33 @@ static void set_path_cost(int port_no, unsigned short path_cost) static void br_tick(unsigned long arg) { - int port_no; + int port_no; + + if(!(br_stats.flags & BR_UP)) + return; /* JRP: we have been shot down */ - if (hello_timer_expired()) { + if (hello_timer_expired()) hello_timer_expiry(); - } - if (tcn_timer_expired()) { + + if (tcn_timer_expired()) tcn_timer_expiry(); - } - if (topology_change_timer_expired()) { + + if (topology_change_timer_expired()) topology_change_timer_expiry(); - } - for (port_no = One; port_no <= No_of_ports; port_no++) { - if (forward_delay_timer_expired(port_no)) { + + for (port_no = One; port_no <= No_of_ports; port_no++) + { + if(port_info[port_no].state == Disabled) + continue; + + if (forward_delay_timer_expired(port_no)) forward_delay_timer_expiry(port_no); - } - if (message_age_timer_expired(port_no)) { + + if (message_age_timer_expired(port_no)) message_age_timer_expiry(port_no); - } - if (hold_timer_expired(port_no)) { + + if (hold_timer_expired(port_no)) hold_timer_expiry(port_no); - } } /* call me again sometime... */ tl.expires = jiffies+HZ; /* 1 second */ @@ -882,7 +1016,8 @@ static void stop_hello_timer(void) static int hello_timer_expired(void) { - if (hello_timer.active && (++hello_timer.value >= bridge_info.hello_time)) { + if (hello_timer.active && (++hello_timer.value >= bridge_info.hello_time)) + { hello_timer.active = FALSE; return (TRUE); } @@ -902,8 +1037,8 @@ static void stop_tcn_timer(void) static int tcn_timer_expired(void) { - if (tcn_timer.active && (++tcn_timer.value >= - bridge_info.bridge_hello_time)) { + if (tcn_timer.active && (++tcn_timer.value >= bridge_info.bridge_hello_time)) + { tcn_timer.active = FALSE; return (TRUE); } @@ -925,9 +1060,8 @@ static void stop_topology_change_timer(void) static int topology_change_timer_expired(void) { if (topology_change_timer.active - && (++topology_change_timer.value - >= bridge_info.topology_change_time - )) { + && (++topology_change_timer.value >= bridge_info.topology_change_time )) + { topology_change_timer.active = FALSE; return (TRUE); } @@ -947,8 +1081,8 @@ static void stop_message_age_timer(int port_no) static int message_age_timer_expired(int port_no) { - if (message_age_timer[port_no].active && - (++message_age_timer[port_no].value >= bridge_info.max_age)) { + if (message_age_timer[port_no].active && (++message_age_timer[port_no].value >= bridge_info.max_age)) + { message_age_timer[port_no].active = FALSE; return (TRUE); } @@ -968,12 +1102,12 @@ static void stop_forward_delay_timer(int port_no) static int forward_delay_timer_expired(int port_no) { - if (forward_delay_timer[port_no].active && - (++forward_delay_timer[port_no].value >= bridge_info.forward_delay)) { - forward_delay_timer[port_no].active = FALSE; - return (TRUE); - } - return (FALSE); + if (forward_delay_timer[port_no].active && (++forward_delay_timer[port_no].value >= bridge_info.forward_delay)) + { + forward_delay_timer[port_no].active = FALSE; + return (TRUE); + } + return (FALSE); } static void start_hold_timer(int port_no) @@ -990,7 +1124,8 @@ static void stop_hold_timer(int port_no) static int hold_timer_expired(int port_no) { if (hold_timer[port_no].active && - (++hold_timer[port_no].value >= bridge_info.hold_time)) { + (++hold_timer[port_no].value >= bridge_info.hold_time)) + { hold_timer[port_no].active = FALSE; return (TRUE); } @@ -998,113 +1133,112 @@ static int hold_timer_expired(int port_no) } -static int send_config_bpdu(int port_no, Config_bpdu *config_bpdu) +static struct sk_buff *alloc_bridge_skb(int port_no, int pdu_size, char *pdu_name) { struct sk_buff *skb; struct device *dev = port_info[port_no].dev; - int size; struct ethhdr *eth; - - if (port_info[port_no].state == Disabled) { - printk(KERN_DEBUG "send_config_bpdu: port %i not valid\n",port_no); - return(-1); + int size = dev->hard_header_len + BRIDGE_LLC1_HS + pdu_size; + unsigned char *llc_buffer; + int pad_size = 60 - size; + + size = 60; /* minimum Ethernet frame - CRC */ + + if (port_info[port_no].state == Disabled) + { + printk(KERN_DEBUG "send_%s_bpdu: port %i not valid\n", pdu_name, port_no); + return NULL; } + + skb = alloc_skb(size, GFP_ATOMIC); + if (skb == NULL) + { + printk(KERN_DEBUG "send_%s_bpdu: no skb available\n", pdu_name); + return NULL; + } + skb->dev = dev; + skb->mac.raw = skb->h.raw = skb_put(skb,size); + memset(skb->h.raw + 60 - pad_size, 0xa5, pad_size); + eth = skb->mac.ethernet; + memcpy(eth->h_dest, bridge_ula, ETH_ALEN); + memcpy(eth->h_source, dev->dev_addr, ETH_ALEN); + + if (br_stats.flags & BR_DEBUG) + printk("send_%s_bpdu: port %i src %02x:%02x:%02x:%02x:%02x:%02x\n", + pdu_name, + port_no, + eth->h_source[0], + eth->h_source[1], + eth->h_source[2], + eth->h_source[3], + eth->h_source[4], + eth->h_source[5]); +#if 0 + /* 8038 is used in older DEC spanning tree protocol which uses a + * different pdu layout as well + */ + eth->h_proto = htons(0x8038); +#endif + eth->h_proto = htons(pdu_size + BRIDGE_LLC1_HS); + + skb->h.raw += skb->dev->hard_header_len; + llc_buffer = skb->h.raw; + *llc_buffer++ = BRIDGE_LLC1_DSAP; + *llc_buffer++ = BRIDGE_LLC1_SSAP; + *llc_buffer++ = BRIDGE_LLC1_CTRL; + /* set h.raw to where the bpdu starts */ + skb->h.raw += BRIDGE_LLC1_HS; + + /* mark that we've been here... */ + skb->pkt_bridged = IS_BRIDGED; + return skb; +} + +static int send_config_bpdu(int port_no, Config_bpdu *config_bpdu) +{ + struct sk_buff *skb; - if (br_stats.flags & BR_DEBUG) - printk("send_config_bpdu: "); /* - * create and send the message + * Create and send the message */ - size = dev->hard_header_len + sizeof(Config_bpdu); - skb = alloc_skb(size, GFP_ATOMIC); - if (skb == NULL) - { - printk(KERN_DEBUG "send_config_bpdu: no skb available\n"); + + skb = alloc_bridge_skb(port_no, BRIDGE_BPDU_8021_CONFIG_SIZE, + "config"); + if (skb == NULL) return(-1); - } - skb->dev = dev; - skb->mac.raw = skb->h.raw = skb_put(skb, size); - eth = skb->mac.ethernet; - memcpy(eth->h_dest, bridge_ula, ETH_ALEN); - memcpy(eth->h_source, dev->dev_addr, ETH_ALEN); - if (br_stats.flags & BR_DEBUG) - printk("port %i src %02x:%02x:%02x:%02x:%02x:%02x\ - dest %02x:%02x:%02x:%02x:%02x:%02x\n", - port_no, - eth->h_source[0], - eth->h_source[1], - eth->h_source[2], - eth->h_source[3], - eth->h_source[4], - eth->h_source[5], - eth->h_dest[0], - eth->h_dest[1], - eth->h_dest[2], - eth->h_dest[3], - eth->h_dest[4], - eth->h_dest[5]); - eth->h_proto = htons(0x8038); - skb->h.raw += skb->dev->hard_header_len; - memcpy(skb->h.raw, config_bpdu, sizeof(Config_bpdu)); + /* copy fields before "flags" */ + memcpy(skb->h.raw, config_bpdu, BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET); - /* won't get bridged again... */ - skb->pkt_bridged = IS_BRIDGED; - skb->dev=dev; - dev_queue_xmit(skb); - return(0); -} + /* build the "flags" field */ + *(skb->h.raw+BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET) = 0; + if (config_bpdu->top_change_ack) + *(skb->h.raw+BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET) |= 0x80; + if (config_bpdu->top_change) + *(skb->h.raw+BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET) |= 0x01; + + config_bpdu_hton(config_bpdu); + /* copy the rest */ + memcpy(skb->h.raw+BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET+1, + (char*)&(config_bpdu->root_id), + BRIDGE_BPDU_8021_CONFIG_SIZE-1-BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET); + dev_queue_xmit(skb); + return(0); +} + static int send_tcn_bpdu(int port_no, Tcn_bpdu *bpdu) { struct sk_buff *skb; - struct device *dev = port_info[port_no].dev; - int size; - struct ethhdr *eth; - - if (port_info[port_no].state == Disabled) { - printk(KERN_DEBUG "send_tcn_bpdu: port %i not valid\n",port_no); - return(-1); - } - if (br_stats.flags & BR_DEBUG) - printk("send_tcn_bpdu: "); - size = sizeof(Tcn_bpdu) + dev->hard_header_len; - skb = alloc_skb(size, GFP_ATOMIC); - if (skb == NULL) { - printk(KERN_DEBUG "send_tcn_bpdu: no skb available\n"); - return(-1); - } - skb->dev = dev; - skb->mac.raw = skb->h.raw = skb_put(skb,size); - eth = skb->mac.ethernet; - memcpy(eth->h_dest, bridge_ula, ETH_ALEN); - memcpy(eth->h_source, dev->dev_addr, ETH_ALEN); - if (br_stats.flags & BR_DEBUG) - printk("port %i src %02x:%02x:%02x:%02x:%02x:%02x\ - dest %02x:%02x:%02x:%02x:%02x:%02x\n", - port_no, - eth->h_source[0], - eth->h_source[1], - eth->h_source[2], - eth->h_source[3], - eth->h_source[4], - eth->h_source[5], - eth->h_dest[0], - eth->h_dest[1], - eth->h_dest[2], - eth->h_dest[3], - eth->h_dest[4], - eth->h_dest[5]); - eth->h_proto = htons(0x8038); - - skb->h.raw += skb->dev->hard_header_len; - memcpy(skb->h.raw, bpdu, sizeof(Tcn_bpdu)); - - /* mark that we've been here... */ - skb->pkt_bridged = IS_BRIDGED; - skb->dev=dev; - dev_queue_xmit(skb); - return(0); + + skb = alloc_bridge_skb(port_no, sizeof(Tcn_bpdu), "tcn"); + if (skb == NULL) + return(-1); + + memcpy(skb->h.raw, bpdu, sizeof(Tcn_bpdu)); + + dev_queue_xmit(skb); + return(0); } static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr) @@ -1116,52 +1250,59 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v if (dev->flags & IFF_LOOPBACK) return(NOTIFY_DONE); - switch (event) { - case NETDEV_DOWN: - if (br_stats.flags & BR_DEBUG) - printk("br_device_event: NETDEV_DOWN...\n"); - /* find our device and mark it down */ - for (i = One; i <= No_of_ports; i++) { - if (port_info[i].dev == dev) { - disable_port(i); - return NOTIFY_DONE; - break; + switch (event) + { + case NETDEV_DOWN: + if (br_stats.flags & BR_DEBUG) + printk(KERN_DEBUG "br_device_event: NETDEV_DOWN...\n"); + /* find our device and mark it down */ + for (i = One; i <= No_of_ports; i++) + { + if (port_info[i].dev == dev) + { + disable_port(i); + return NOTIFY_DONE; + break; + } } - } - break; - case NETDEV_UP: - if (br_stats.flags & BR_DEBUG) - printk("br_device_event: NETDEV_UP...\n"); - /* Only handle ethernet ports */ - if(dev->type!=ARPHRD_ETHER && dev->type!=ARPHRD_LOOPBACK) - return NOTIFY_DONE; - /* look up an unused device and enable it */ - for (i = One; i <= No_of_ports; i++) { - if ((port_info[i].dev == (struct device *)0) || - (port_info[i].dev == dev)) { - port_info[i].dev = dev; - enable_port(i); - set_path_cost(i, br_port_cost(dev)); - set_port_priority(i, 128); - port_info[i].port_id = i; - /* set bridge addr from 1st device addr */ - if ((bridge_info.bridge_id.BRIDGE_ID[0] == 0) && - (bridge_info.bridge_id.BRIDGE_ID[1] == 0)) { - memcpy(bridge_info.bridge_id.BRIDGE_ID_ULA, dev->dev_addr, 6); - bridge_info.bridge_id.BRIDGE_PRIORITY = port_info[i].port_id; - set_bridge_priority(&bridge_info.bridge_id); - } - make_forwarding(i); + break; + case NETDEV_UP: + if (br_stats.flags & BR_DEBUG) + printk(KERN_DEBUG "br_device_event: NETDEV_UP...\n"); + /* Only handle ethernet ports */ + if(dev->type!=ARPHRD_ETHER && dev->type!=ARPHRD_LOOPBACK) return NOTIFY_DONE; - break; + /* look up an unused device and enable it */ + for (i = One; i <= No_of_ports; i++) + { + if (port_info[i].dev == NULL || port_info[i].dev == dev) + { + port_info[i].dev = dev; + port_info[i].port_id = i; + /* set bridge addr from 1st device addr */ + if (((htonl(bridge_info.bridge_id.BRIDGE_ID[0])&0xffff) == 0) && + (bridge_info.bridge_id.BRIDGE_ID[1] == 0)) + { + memcpy(bridge_info.bridge_id.BRIDGE_ID_ULA, dev->dev_addr, 6); + if(bridge_info.bridge_id.BRIDGE_PRIORITY == 0) + bridge_info.bridge_id.BRIDGE_PRIORITY = htons(32768); + set_bridge_priority(&bridge_info.bridge_id); + } + br_add_local_mac(dev->dev_addr); + if((br_stats.flags & BR_UP) && + (user_port_state[i] != Disabled)) + { + /* don't start if user said so */ + enable_port(i); + set_path_cost(i, br_port_cost(dev)); + set_port_priority(i); + make_forwarding(i); + } + return NOTIFY_DONE; + break; + } } - } - break; -#if 0 - default: - printk("br_device_event: unknown event [%x]\n", - (unsigned int)event); -#endif + break; } return NOTIFY_DONE; } @@ -1175,10 +1316,9 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v int br_receive_frame(struct sk_buff *skb) /* 3.5 */ { int port; + Port_data *p; struct ethhdr *eth; - if (br_stats.flags & BR_DEBUG) - printk("br_receive_frame: "); /* sanity */ if (!skb) { printk(KERN_CRIT "br_receive_frame: no skb!\n"); @@ -1189,88 +1329,79 @@ int br_receive_frame(struct sk_buff *skb) /* 3.5 */ /* check for loopback */ if (skb->dev->flags & IFF_LOOPBACK) - return(0); + return 0 ; port = find_port(skb->dev); + if(!port) + return 0; + skb->h.raw = skb->mac.raw; eth = skb->mac.ethernet; - if (br_stats.flags & BR_DEBUG) - printk("port %i src %02x:%02x:%02x:%02x:%02x:%02x\ - dest %02x:%02x:%02x:%02x:%02x:%02x\n", - port, - eth->h_source[0], - eth->h_source[1], - eth->h_source[2], - eth->h_source[3], - eth->h_source[4], - eth->h_source[5], - eth->h_dest[0], - eth->h_dest[1], - eth->h_dest[2], - eth->h_dest[3], - eth->h_dest[4], - eth->h_dest[5]); + p = &port_info[port]; + + if(p->state == Disabled) + { + /* We are here if BR_UP even if this port is Disabled. + * Send everything up + */ + skb->pkt_type = PACKET_HOST; + ++br_stats_cnt.port_disable_up_stack; + return(0); /* pass frame up our stack (this will */ + /* happen in net_bh() in dev.c) */ + } + + /* Here only if not disable. + * Remark: only frames going up will show up in NIT (tcpdump) + */ - if (!port) { - if(br_stats.flags&BR_DEBUG) - printk("\nbr_receive_frame: no port!\n"); - return(0); + /* JRP: even if port is Blocking we need to process the Spanning Tree + * frames to keep the port in that state + */ + if (memcmp(eth->h_dest, bridge_ula, ETH_ALEN) == 0) + { + ++br_stats_cnt.rcv_bpdu; + br_bpdu(skb, port); /* br_bpdu consumes skb */ + return(1); } - - switch (port_info[port].state) + switch (p->state) { case Learning: - (void) br_learn(skb, port); /* 3.8 */ + if(br_learn(skb, port)) + { /* 3.8 */ + ++br_stats_cnt.drop_multicast; + return br_drop(skb); + } /* fall through */ case Listening: - /* process BPDUs */ - if (memcmp(eth->h_dest, bridge_ula, 6) == 0) { - br_bpdu(skb); - return(1); /* br_bpdu consumes skb */ - } /* fall through */ case Blocking: - /* fall through */ - case Disabled: - /* should drop frames, but for now, we let - * them get passed up to the next higher layer + ++br_stats_cnt.notForwarding; return(br_drop(skb)); - */ - return(0); /* pass frame up stack */ + /* + case Disabled: is now handled before this switch ! + Keep the break to allow GCC to use a jmp table. + */ break; case Forwarding: - (void) br_learn(skb, port); /* 3.8 */ - /* process BPDUs */ - if (memcmp(eth->h_dest, bridge_ula, - ETH_ALEN) == 0) - { - /*printk("frame bpdu processor for me!!!\n");*/ - br_bpdu(skb); - return(1); /* br_bpdu consumes skb */ - } - /* is frame for me? */ - if (memcmp(eth->h_dest, - port_info[port].dev->dev_addr, - ETH_ALEN) == 0) - { - /* Packet is for us */ - skb->pkt_type = PACKET_HOST; - return(0); /* pass frame up our stack (this will */ - /* happen in net_bh() in dev.c) */ + if(br_learn(skb, port)) { /* 3.8 */ + ++br_stats_cnt.drop_multicast; + return br_drop(skb); } /* Now this frame came from one of bridged - ports, and it appears to be not for me; - this means we should attempt to forward it. - But actually this frame can still be for me - [as well] if it is destined to one of our - multicast groups. br_forward() will not - consume the frame if this is the case */ + ports this means we should attempt to forward it. + JRP: local addresses are now in the AVL tree, + br_forward will pass frames up if it matches + one of our local MACs or if it is a multicast + group address. + br_forward() will not consume the frame if this + is the case */ return(br_forward(skb, port)); default: printk(KERN_DEBUG "br_receive_frame: port [%i] unknown state [%i]\n", - port, port_info[port].state); - return(0); /* pass frame up stack? */ + port, p->state); + ++br_stats_cnt.unknown_state; + return(br_drop(skb)); /* discard frame */ } } @@ -1304,15 +1435,17 @@ int br_tx_frame(struct sk_buff *skb) /* 3.5 */ /* if bridging is not enabled on the port we are going to send to, we have nothing to do with this frame, hands off */ - if (! find_port(skb->dev)) + if (((port=find_port(skb->dev))==0)||(port_info[port].state==Disabled)) { + ++br_stats_cnt.port_disable; return(0); - + } + ++br_stats_cnt.port_not_disable; skb->mac.raw = skb->h.raw = skb->data; eth = skb->mac.ethernet; - port = 0; /* an impossible port */ + port = 0; /* an impossible port (locally generated) */ if (br_stats.flags & BR_DEBUG) - printk("br_tx_fr : port %i src %02x:%02x:%02x:%02x:%02x:%02x\ - dest %02x:%02x:%02x:%02x:%02x:%02x\n", + printk("br_tx_fr : port %i src %02x:%02x:%02x:%02x:%02x:%02x" + " dest %02x:%02x:%02x:%02x:%02x:%02x\n", port, eth->h_source[0], eth->h_source[1], @@ -1329,55 +1462,135 @@ int br_tx_frame(struct sk_buff *skb) /* 3.5 */ return(br_forward(skb, port)); } +static void br_add_local_mac(unsigned char *mac) +{ + struct fdb *f; + f = (struct fdb *)kmalloc(sizeof(struct fdb), GFP_ATOMIC); + if (!f) + { + printk(KERN_CRIT "br_add_local_mac: unable to malloc fdb\n"); + return; + } + f->port = 0; /* dest port == 0 =>local */ + memcpy(f->ula, mac, 6); + f->timer = 0; /* will not aged anyway */ + f->flags = 0; /* not valid => br_forward special route */ + /* + * add entity to AVL tree. If entity already + * exists in the tree, update the fields with + * what we have here. + */ + if (br_avl_insert(f) != NULL) + { + /* Already in */ + kfree(f); + } +} + +/* Avoid broadcast loop by limiting the number of broacast frames per + * period. The idea is to limit this per source + * returns: 0 if limit is not reached + * 1 if frame should be dropped + */ + +static inline int mcast_quench(struct fdb *f) +{ + if(f->mcast_count++ == 0) /* first time */ + f->mcast_timer = jiffies; + else { + if(f->mcast_count > max_mcast_per_period) { + if(jiffies > (f->mcast_timer + mcast_hold_time)) + f->mcast_count = 0; + else return 1; + } + } + return 0; +} + /* * this routine returns 0 when it learns (or updates) from the - * frame, and -1 if the frame is simply discarded due to port - * state or lack of resources... + * frame, and 1 if we must dropped the frame. */ static int br_learn(struct sk_buff *skb, int port) /* 3.8 */ { - struct fdb *f; + struct fdb *f, *oldfdb; + Port_data *p = &port_info[port]; + struct ethhdr *eth = skb->mac.ethernet; - switch (port_info[port].state) { - case Listening: - case Blocking: - case Disabled: - default: - return(-1); - /* break; */ - case Learning: - case Forwarding: - /* don't keep group addresses in the tree */ - if (skb->mac.ethernet->h_source[0] & 0x01) - return(-1); - - f = (struct fdb *)kmalloc(sizeof(struct fdb), - GFP_ATOMIC); + /* JRP: no reason to check port state again. We are called by + * br_receive_frame() only when in Learning or Forwarding + * Remark: code not realigned yet to keep diffs smaller + */ - if (!f) { - printk(KERN_DEBUG "br_learn: unable to malloc fdb\n"); - return(-1); - } - f->port = port; /* source port */ - memcpy(f->ula, skb->mac.ethernet->h_source, 6); - f->timer = CURRENT_TIME; - f->flags = FDB_ENT_VALID; - /* - * add entity to AVL tree. If entity already - * exists in the tree, update the fields with - * what we have here. - */ - if (br_avl_insert(f) == 0) { /* update */ - kfree(f); - return(0); - } - /* add to head of port chain */ - f->fdb_next = port_info[port].fdb; - port_info[port].fdb = f; - return(0); - /* break */ + /* don't keep group addresses in the tree */ + if (eth->h_source[0] & 0x01) + return 0; + + if((f= newfdb[port]) == NULL) + { + newfdb[port] = f = (struct fdb *)kmalloc(sizeof(struct fdb), GFP_ATOMIC); + if (!f) + { + printk(KERN_DEBUG "br_learn: unable to malloc fdb\n"); + return(-1); /* this drop the frame */ + } + } + f->port = port; /* source port */ + memcpy(f->ula, eth->h_source, 6); + f->timer = CURRENT_TIME; + f->flags = FDB_ENT_VALID; + /* + * add entity to AVL tree. If entity already + * exists in the tree, update the fields with + * what we have here. + */ + if ((oldfdb = br_avl_insert(f))) + { + /* update if !NULL */ + if((eth->h_dest[0] & 0x01) && /* multicast */ mcast_quench(oldfdb)) + return 1; + return 0; } + newfdb[port] = NULL; /* force kmalloc next time */ + f->mcast_count = 0; + /* add to head of port chain */ + f->fdb_next = p->fdb; + p->fdb = f; + allocated_fdb_cnt++; + return 0; +} + +/* JRP: always called under br_receive_frame(). No need for Q protection. */ + +void requeue_fdb(struct fdb *node, int new_port) +{ + Port_data *p = &port_info[node->port]; + + /* dequeue */ + if(p->fdb == node) + p->fdb = node->fdb_next; + else + { + struct fdb *prev; + + for(prev = p->fdb; prev; prev = prev->fdb_next) + if (prev->fdb_next == node) + break; + + if(prev != NULL) + prev->fdb_next = node->fdb_next; + else + { + /* Forget about this update. */ + printk(KERN_ERR "br:requeue_fdb\n"); + return; + } + } + /* enqueue */ + node->port = new_port; + node->fdb_next = port_info[new_port].fdb; + port_info[new_port].fdb = node; } /* @@ -1429,26 +1642,43 @@ static int br_forward(struct sk_buff *skb, int port) /* 3.7 */ * This probably should be dropped since the flood will * have sent it anyway. */ - if (port == 0) /* locally generated */ + if (port == 0) + { + /* Locally generated */ + ++br_stats_cnt.local_multicast; return(br_dev_drop(skb)); + } + ++br_stats_cnt.forwarded_multicast; return(0); - } else { - /* locate port to forward to */ + } + else + { + /* unicast frame, locate port to forward to */ f = br_avl_find_addr(skb->mac.ethernet->h_dest); /* * Send flood and drop. */ - if (!f || !(f->flags & FDB_ENT_VALID)) { - /* not found; flood all ports */ + if (!f || !(f->flags & FDB_ENT_VALID)) + { + if(f && (f->port == 0)) + { + skb->pkt_type = PACKET_HOST; + ++br_stats_cnt.forwarded_unicast_up_stack; + return(0); + } + /* not found or too old; flood all ports */ + ++br_stats_cnt.flood_unicast; br_flood(skb, port); return(br_dev_drop(skb)); } /* * Sending */ - if (f->port!=port && port_info[f->port].state == Forwarding) { - /* has entry expired? */ - if (f->timer + fdb_aging_time < CURRENT_TIME) { + if (f->port!=port && port_info[f->port].state == Forwarding) + { + /* Has entry expired? */ + if (f->timer + fdb_aging_time < CURRENT_TIME) + { /* timer expired, invalidate entry */ f->flags &= ~FDB_ENT_VALID; if (br_stats.flags & BR_DEBUG) @@ -1456,9 +1686,11 @@ static int br_forward(struct sk_buff *skb, int port) /* 3.7 */ /* * Send flood and drop original */ + ++br_stats_cnt.aged_flood_unicast; br_flood(skb, port); return(br_dev_drop(skb)); } + ++br_stats_cnt.forwarded_unicast; /* mark that's we've been here... */ skb->pkt_bridged = IS_BRIDGED; @@ -1477,7 +1709,25 @@ static int br_forward(struct sk_buff *skb, int port) /* 3.7 */ skb->priority = 1; dev_queue_xmit(skb); return(1); /* skb has been consumed */ - } else { + } + else + { + /* JRP: Needs to aged entry as well, if topology changes + * the entry would not age. Got this while swapping + * two cables ! + * + * Has entry expired? + */ + + if (f->timer + fdb_aging_time < CURRENT_TIME) + { + /* timer expired, invalidate entry */ + f->flags &= ~FDB_ENT_VALID; + if (br_stats.flags & BR_DEBUG) + printk("fdb entry expired...\n"); + ++br_stats_cnt.drop_same_port_aged; + } + else ++br_stats_cnt.drop_same_port; /* * Arrived on the right port, we discard */ @@ -1499,7 +1749,7 @@ static int br_flood(struct sk_buff *skb, int port) for (i = One; i <= No_of_ports; i++) { - if (i == port) + if (i == port) /* don't send back where we got it */ continue; if (port_info[i].state == Forwarding) { @@ -1515,8 +1765,12 @@ static int br_flood(struct sk_buff *skb, int port) /* printk("Flood to port %d\n",i);*/ nskb->h.raw = nskb->data + ETH_HLEN; +#if LINUX_VERSION_CODE >= 0x20100 nskb->priority = 1; dev_queue_xmit(nskb); +#else + dev_queue_xmit(nskb,nskb->dev,1); +#endif } } return(0); @@ -1527,12 +1781,16 @@ static int find_port(struct device *dev) int i; for (i = One; i <= No_of_ports; i++) - if ((port_info[i].dev == dev) && - (port_info[i].state != Disabled)) + if (port_info[i].dev == dev) return(i); return(0); } +/* + * FIXME: This needs to come from the device structs, eg for + * 10,100,1Gbit ethernet. + */ + static int br_port_cost(struct device *dev) /* 4.10.2 */ { if (strncmp(dev->name, "eth", 3) == 0) /* ethernet */ @@ -1546,43 +1804,103 @@ static int br_port_cost(struct device *dev) /* 4.10.2 */ * this routine always consumes the skb */ -static void br_bpdu(struct sk_buff *skb) /* consumes skb */ +static void br_bpdu(struct sk_buff *skb, int port) /* consumes skb */ { - Tcn_bpdu *bpdu; - int port; - - port = find_port(skb->dev); - if (port == 0) { /* unknown port */ - br_drop(skb); - return; - } - - bpdu = (Tcn_bpdu *) (skb->data + ETH_HLEN); - switch (bpdu->type) { - case BPDU_TYPE_CONFIG: - received_config_bpdu(port, (Config_bpdu *)bpdu); - break; - case BPDU_TYPE_TOPO_CHANGE: - received_tcn_bpdu(port, bpdu); - break; - default: - printk(KERN_DEBUG "br_bpdu: received unknown bpdu, type = %i\n", - bpdu->type); + char *bufp = skb->data + ETH_HLEN; + Tcn_bpdu *bpdu = (Tcn_bpdu *) (bufp + BRIDGE_LLC1_HS); + Config_bpdu rcv_bpdu; + + if((*bufp++ == BRIDGE_LLC1_DSAP) && (*bufp++ == BRIDGE_LLC1_SSAP) && + (*bufp++ == BRIDGE_LLC1_CTRL) && + (bpdu->protocol_id == BRIDGE_BPDU_8021_PROTOCOL_ID) && + (bpdu->protocol_version_id == BRIDGE_BPDU_8021_PROTOCOL_VERSION_ID)) + { + + switch (bpdu->type) + { + case BPDU_TYPE_CONFIG: + /* realign for portability to RISC */ + memcpy((char*)&rcv_bpdu, bufp, + BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET); + bufp+= BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET; + rcv_bpdu.top_change_ack = + (*bufp & TOPOLOGY_CHANGE_ACK) != 0; + rcv_bpdu.top_change = + (*bufp & TOPOLOGY_CHANGE) != 0; + bufp++; + memcpy((char*)&rcv_bpdu.root_id, bufp, + BRIDGE_BPDU_8021_CONFIG_SIZE-1 + -BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET); + config_bpdu_ntoh(&rcv_bpdu); + received_config_bpdu(port, &rcv_bpdu); + break; + + case BPDU_TYPE_TOPO_CHANGE: + received_tcn_bpdu(port, bpdu); + break; + default: + printk(KERN_DEBUG "br_bpdu: received unknown bpdu, type = %i\n", bpdu->type); /* break; */ + } } br_drop(skb); } +struct fdb_info *get_fdb_info(int user_buf_size, int *copied,int *notcopied) +{ + int fdb_size, i, built = 0; + struct fdb_info *fdbi, *fdbis; + + *copied = user_buf_size - sizeof(struct fdb_info_hdr); + *copied /= sizeof(struct fdb_info); + *copied = min(*copied, allocated_fdb_cnt); + *notcopied = allocated_fdb_cnt - *copied; + if(*copied == 0) + return NULL; + fdb_size = *copied * sizeof(struct fdb_info); + fdbis = kmalloc(fdb_size, GFP_KERNEL); + if(fdbis == NULL) + return NULL; + fdbi = fdbis; + + for(i=One; i<=No_of_ports;i++) + { + struct fdb *fdb; + + cli(); + fdb = port_info[i].fdb; + while(fdb) + { + memcpy(fdbi->ula, fdb->ula, ETH_ALEN); + fdbi->port = fdb->port; + fdbi->flags = fdb->flags; + fdbi->timer = fdb->timer; + fdbi++; + if(++built == *copied) + { + sti(); + return fdbis; + } + fdb = fdb->fdb_next; + } + sti(); + } + printk(KERN_DEBUG "get_fdb_info: built=%d\n", built); + return fdbis; +} + int br_ioctl(unsigned int cmd, void *arg) { - int err; + int err, i; struct br_cf bcf; + bridge_id_t new_id; switch(cmd) { case SIOCGIFBR: /* get bridging control blocks */ memcpy(&br_stats.bridge_data, &bridge_info, sizeof(Bridge_data)); memcpy(&br_stats.port_data, &port_info, sizeof(Port_data)*No_of_ports); + err = copy_to_user(arg, &br_stats, sizeof(struct br_stat)); if (err) { @@ -1590,17 +1908,33 @@ int br_ioctl(unsigned int cmd, void *arg) } return err; case SIOCSIFBR: - if (!suser()) - return -EPERM; err = copy_from_user(&bcf, arg, sizeof(struct br_cf)); if (err) return -EFAULT; - switch (bcf.cmd) { + if (bcf.cmd != BRCMD_DISPLAY_FDB && !suser()) + return -EPERM; + switch (bcf.cmd) + { case BRCMD_BRIDGE_ENABLE: if (br_stats.flags & BR_UP) return(-EALREADY); printk(KERN_DEBUG "br: enabling bridging function\n"); br_stats.flags |= BR_UP; /* enable bridge */ + for(i=One;i<=No_of_ports; i++) + { + /* don't start if user said so */ + if((user_port_state[i] != Disabled) + && port_info[i].dev) + { + enable_port(i); + } + } + port_state_selection(); /* (4.8.1.5) */ + config_bpdu_generation(); /* (4.8.1.6) */ + /* initialize system timer */ + tl.expires = jiffies+HZ; /* 1 second */ + tl.function = br_tick; + add_timer(&tl); start_hello_timer(); break; case BRCMD_BRIDGE_DISABLE: @@ -1609,35 +1943,41 @@ int br_ioctl(unsigned int cmd, void *arg) printk(KERN_DEBUG "br: disabling bridging function\n"); br_stats.flags &= ~BR_UP; /* disable bridge */ stop_hello_timer(); -#if 0 for (i = One; i <= No_of_ports; i++) if (port_info[i].state != Disabled) disable_port(i); -#endif break; case BRCMD_PORT_ENABLE: if (port_info[bcf.arg1].dev == 0) return(-EINVAL); - if (port_info[bcf.arg1].state != Disabled) + if (user_port_state[bcf.arg1] != Disabled) return(-EALREADY); printk(KERN_DEBUG "br: enabling port %i\n",bcf.arg1); - enable_port(bcf.arg1); + user_port_state[bcf.arg1] = ~Disabled; + if(br_stats.flags & BR_UP) + enable_port(bcf.arg1); break; case BRCMD_PORT_DISABLE: if (port_info[bcf.arg1].dev == 0) return(-EINVAL); - if (port_info[bcf.arg1].state == Disabled) + if (user_port_state[bcf.arg1] == Disabled) return(-EALREADY); printk(KERN_DEBUG "br: disabling port %i\n",bcf.arg1); - disable_port(bcf.arg1); + user_port_state[bcf.arg1] = Disabled; + if(br_stats.flags & BR_UP) + disable_port(bcf.arg1); break; case BRCMD_SET_BRIDGE_PRIORITY: - set_bridge_priority((bridge_id_t *)&bcf.arg1); + new_id = bridge_info.bridge_id; + new_id.BRIDGE_PRIORITY = htons(bcf.arg1); + set_bridge_priority(&new_id); break; case BRCMD_SET_PORT_PRIORITY: - if (port_info[bcf.arg1].dev == 0) + if((port_info[bcf.arg1].dev == 0) + || (bcf.arg2 & ~0xff)) return(-EINVAL); - set_port_priority(bcf.arg1, bcf.arg2); + port_priority[bcf.arg1] = bcf.arg2; + set_port_priority(bcf.arg1); break; case BRCMD_SET_PATH_COST: if (port_info[bcf.arg1].dev == 0) @@ -1664,6 +2004,36 @@ int br_ioctl(unsigned int cmd, void *arg) memset(&br_stats.prot_id,0,sizeof(br_stats.prot_id)); memset(&br_stats.prot_counter,0,sizeof(br_stats.prot_counter)); break; + case BRCMD_DISPLAY_FDB: + { + struct fdb_info_hdr *user_buf = (void*) bcf.arg1; + struct fdb_info *u_fdbs, *fdbis; + int copied, notcopied; + u32 j = CURRENT_TIME; + + if(bcf.arg2cmd_time); + if(allocated_fdb_cnt == 0) + { + put_user(0, &user_buf->copied); + put_user(0, &user_buf->not_copied); + return 0; + } + fdbis = get_fdb_info(bcf.arg2, &copied, ¬copied); + put_user(copied, &user_buf->copied); + put_user(notcopied, &user_buf->not_copied); + if(!fdbis) + return -ENOMEM; + u_fdbs = (struct fdb_info *) (user_buf+1); + err = copy_to_user(u_fdbs, fdbis, copied*sizeof(struct fdb_info)); + kfree(fdbis); + if (err) + { + err = -EFAULT; + } + return err; + } default: return -EINVAL; } @@ -1680,12 +2050,13 @@ static int br_cmp(unsigned int *a, unsigned int *b) int i; for (i=0; i<2; i++) { - if (a[i] == b[i]) - continue; - if (a[i] < b[i]) - return(1); - if (a[i] > b[i]) + /* JRP: compares prty then MAC address in memory byte order + * OK optimizer does htonl() only once per long ! + */ + if (htonl(a[i]) < htonl(b[i])) return(-1); + if (htonl(a[i]) > htonl(b[i])) + return(1); } return(0); } diff --git a/net/bridge/br_tree.c b/net/bridge/br_tree.c index 8234249c5094..709bafb2b511 100644 --- a/net/bridge/br_tree.c +++ b/net/bridge/br_tree.c @@ -1,6 +1,7 @@ /* - * this code is derived from the avl functions in mmap.c + * This code is derived from the avl functions in mmap.c */ + #include #include #include @@ -16,6 +17,10 @@ * Written by Bruno Haible . * Taken from mmap.c, extensively modified by John Hayes * + * 98-02 Modified by Jean-Rene Peulve jr.peulve@aix.pacwan.net + * update port number when topology change + * return oldfdb when updating, for broadcast storm checking + * call addr_cmp once per node */ static struct fdb fdb_head; @@ -50,8 +55,7 @@ static int addr_cmp(unsigned char *a1, unsigned char *a2); * foreach node in tree->fdb_avl_right: node->fdb_avl_key >= tree->fdb_avl_key. */ -static int -fdb_init(void) +static int fdb_init(void) { fdb_head.fdb_avl_height = 0; fdb_head.fdb_avl_left = (struct fdb *)0; @@ -109,7 +113,6 @@ struct fdb *br_avl_find_addr(unsigned char addr[6]) } -#if (0) /* * Rebalance a tree. * After inserting or deleting a node of a tree we have a sequence of subtrees @@ -196,10 +199,14 @@ static void br_avl_rebalance (struct fdb *** nodeplaces_ptr, int count) printk_avl(&fdb_head); #endif /* DEBUG_AVL */ } -#endif /* (0) */ -/* Insert a node into a tree. */ -int br_avl_insert (struct fdb * new_node) +/* Insert a node into a tree. + * Performance improvement: + * call addr_cmp() only once per node and use result in a switch. + * Return old node address if we knew that MAC address already + * Return NULL if we insert the new node + */ +struct fdb *br_avl_insert (struct fdb * new_node) { struct fdb ** nodeplace = fhpp; struct fdb ** stack[avl_maxheight]; @@ -214,15 +221,38 @@ int br_avl_insert (struct fdb * new_node) if (node == avl_br_empty) break; *stack_ptr++ = nodeplace; stack_count++; - if (addr_cmp(new_node->ula, node->ula) == 0) { /* update */ + switch(addr_cmp(new_node->ula, node->ula)) { + case 0: /* update */ + if (node->port == new_node->port) { node->flags = new_node->flags; node->timer = new_node->timer; - return(0); - } - if (addr_cmp(new_node->ula, node->ula) < 0) { - nodeplace = &node->fdb_avl_left; - } else { - nodeplace = &node->fdb_avl_right; + } else if (!(node->flags & FDB_ENT_VALID) && + node->port) { + /* update fdb but never for local interfaces */ +#if (DEBUG_AVL) + printk("node 0x%x:port changed old=%d new=%d\n", + (unsigned int)node, node->port,new_node->port); +#endif + /* JRP: update port as well if the topology change ! + * Don't do this while entry is still valid otherwise + * a broadcast that we flooded and is reentered by another + * port would mess up the good port number. + * The fdb list per port needs to be updated as well. + */ + requeue_fdb(node, new_node->port); + node->flags = new_node->flags; + node->timer = new_node->timer; +#if (DEBUG_AVL) + printk_avl(&fdb_head); +#endif /* DEBUG_AVL */ + } + return node; /* pass old fdb to caller */ + + case 1: /* new_node->ula > node->ula */ + nodeplace = &node->fdb_avl_right; + break; + default: /* -1 => new_node->ula < node->ula */ + nodeplace = &node->fdb_avl_left; } } #if (DEBUG_AVL) @@ -239,17 +269,14 @@ int br_avl_insert (struct fdb * new_node) new_node->fdb_avl_right = avl_br_empty; new_node->fdb_avl_height = 1; *nodeplace = new_node; -#if (0) br_avl_rebalance(stack_ptr,stack_count); -#endif /* (0) */ #ifdef DEBUG_AVL printk_avl(&fdb_head); #endif /* DEBUG_AVL */ - return(1); + return NULL; /* this is a new node */ } -#if (0) /* Removes a node out of a tree. */ static int br_avl_remove (struct fdb * node_to_delete) { @@ -302,7 +329,6 @@ static int br_avl_remove (struct fdb * node_to_delete) br_avl_rebalance(stack_ptr,stack_count); return(0); } -#endif /* (0) */ #ifdef DEBUG_AVL @@ -311,13 +337,14 @@ static void printk_avl (struct fdb * tree) { if (tree != avl_br_empty) { printk("("); - printk("%02x:%02x:%02x:%02x:%02x:%02x", + printk("%02x:%02x:%02x:%02x:%02x:%02x(%d)", tree->ula[0], tree->ula[1], tree->ula[2], tree->ula[3], tree->ula[4], - tree->ula[5]); + tree->ula[5], + tree->port); if (tree->fdb_avl_left != avl_br_empty) { printk_avl(tree->fdb_avl_left); printk("<"); @@ -330,7 +357,6 @@ static void printk_avl (struct fdb * tree) } } -#if (0) static char *avl_check_point = "somewhere"; /* check a tree's consistency and balancing */ @@ -387,7 +413,6 @@ static void avl_checkorder (struct fdb * tree) avl_checkright(tree->fdb_avl_right,tree->fdb_avl_key); } -#endif /* (0) */ #endif /* DEBUG_AVL */ static int addr_cmp(unsigned char a1[], unsigned char a2[]) diff --git a/net/ipv4/ip_masq_app.c b/net/ipv4/ip_masq_app.c index ebd7a887a073..7b4f9c844e48 100644 --- a/net/ipv4/ip_masq_app.c +++ b/net/ipv4/ip_masq_app.c @@ -506,7 +506,7 @@ static struct sk_buff * skb_replace(struct sk_buff *skb, int pri, char *o_buf, i struct sk_buff *n_skb; int offset; - maxsize = skb->truesize - sizeof(struct sk_buff); + maxsize = skb->truesize; diff = n_len - o_len; o_offset = o_buf - (char*) skb->data; @@ -547,7 +547,6 @@ static struct sk_buff * skb_replace(struct sk_buff *skb, int pri, char *o_buf, i offset = n_skb->data - skb->data; n_skb->nh.raw = skb->nh.raw + offset; n_skb->h.raw = skb->h.raw + offset; - n_skb->when = skb->when; n_skb->dev = skb->dev; n_skb->mac.raw = skb->mac.raw + offset; n_skb->pkt_type = skb->pkt_type; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 52a250b957b2..1d7315481d41 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -1,7 +1,7 @@ /* * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem. * - * $Id: sysctl_net_ipv4.c,v 1.30 1998/03/23 23:56:29 davem Exp $ + * $Id: sysctl_net_ipv4.c,v 1.31 1998/03/30 08:41:41 davem Exp $ * * Begun April 1, 1996, Mike Shaver. * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS] @@ -70,9 +70,6 @@ extern int sysctl_icmp_echoreply_time; int tcp_retr1_max = 255; -extern int tcp_sysctl_congavoid(ctl_table *ctl, int write, struct file * filp, - void *buffer, size_t *lenp); - struct ipv4_config ipv4_config; extern ctl_table ipv4_route_table[]; @@ -108,9 +105,6 @@ ctl_table ipv4_table[] = { {NET_IPV4_TCP_SACK, "tcp_sack", &sysctl_tcp_sack, sizeof(int), 0644, NULL, &proc_dointvec}, - {NET_IPV4_TCP_VEGAS_CONG_AVOID, "tcp_vegas_cong_avoid", - &sysctl_tcp_cong_avoidance, sizeof(int), 0644, - NULL, &tcp_sysctl_congavoid }, {NET_IPV4_FORWARD, "ip_forward", &ipv4_devconf.forwarding, sizeof(int), 0644, NULL, &ipv4_sysctl_forward}, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 86595e1c31e4..e9fcec0476e9 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp.c,v 1.107 1998/03/28 00:55:28 davem Exp $ + * Version: $Id: tcp.c,v 1.108 1998/03/29 08:43:51 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -1407,7 +1407,7 @@ void tcp_close(struct sock *sk, unsigned long timeout) * descriptor close, not protocol-sourced closes, because the * reader process may not have drained the data yet! */ - while((skb=skb_dequeue(&sk->receive_queue))!=NULL) { + while((skb=__skb_dequeue(&sk->receive_queue))!=NULL) { data_was_unread++; kfree_skb(skb); } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index d9c5a95846f2..7dab8ff8874d 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_input.c,v 1.100 1998/03/28 00:55:31 davem Exp $ + * Version: $Id: tcp_input.c,v 1.103 1998/03/30 08:41:12 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -50,15 +50,6 @@ #include #include -typedef void (*tcp_sys_cong_ctl_t)(struct sock *sk, - u32 seq, u32 ack, - u32 seq_rtt); - -static void tcp_cong_avoid_vanj(struct sock *sk, u32 seq, u32 ack, - u32 seq_rtt); -static void tcp_cong_avoid_vegas(struct sock *sk, u32 seq, u32 ack, - u32 seq_rtt); - #ifdef CONFIG_SYSCTL #define SYNC_INIT 0 /* let the user enable it */ #else @@ -80,8 +71,6 @@ int sysctl_tcp_syncookies = SYNC_INIT; int sysctl_tcp_stdurg; int sysctl_tcp_rfc1337; -static tcp_sys_cong_ctl_t tcp_sys_cong_ctl_f = &tcp_cong_avoid_vanj; - /* There is something which you must keep in mind when you analyze the * behavior of the tp->ato delayed ack timeout interval. When a * connection starts up, we want to ack as quickly as possible. The @@ -164,7 +153,7 @@ static __inline__ void tcp_rtt_estimator(struct tcp_opt *tp, __u32 mrtt) static __inline__ void tcp_set_rto(struct tcp_opt *tp) { tp->rto = (tp->srtt >> 3) + tp->mdev; - tp->rto += (tp->rto >> 2) + (tp->rto >> (tp->snd_cwnd-1)); + tp->rto += (tp->rto >> 2) + (tp->rto >> ((tp->snd_cwnd>>TCP_CWND_SHIFT)-1)); } @@ -450,7 +439,7 @@ static void tcp_compute_tsack(struct sock *sk, struct tcp_opt *tp) static __inline__ void clear_fast_retransmit(struct tcp_opt *tp) { if (tp->dup_acks > 3) - tp->snd_cwnd = tp->snd_ssthresh; + tp->snd_cwnd = (tp->snd_ssthresh << TCP_CWND_SHIFT); tp->dup_acks = 0; } @@ -490,8 +479,8 @@ static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup) tp->dup_acks++; if ((tp->fackets_out > 3) || (tp->dup_acks == 3)) { tp->dup_acks++; - tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2); - tp->snd_cwnd = tp->snd_ssthresh + 3; + tp->snd_ssthresh = max(tp->snd_cwnd >> (TCP_CWND_SHIFT + 1), 2); + tp->snd_cwnd = (tp->snd_ssthresh + 3) << TCP_CWND_SHIFT; tp->high_seq = tp->snd_nxt; if(!tp->fackets_out) tcp_retransmit_skb(sk, skb_peek(&sk->write_queue)); @@ -511,7 +500,7 @@ static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup) */ if (tp->dup_acks > 3) { if(!tp->fackets_out) { - tp->snd_cwnd++; + tp->snd_cwnd += (1 << TCP_CWND_SHIFT); } else { /* Fill any further holes which may have appeared. * We may want to change this to run every further @@ -572,121 +561,25 @@ static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup) } } -/* - * TCP slow start and congestion avoidance in two flavors: - * RFC 1122 and TCP Vegas. +/* This is Jacobson's slow start and congestion avoidance. + * SIGCOMM '88, p. 328. * - * This is a /proc/sys configurable option. + * FIXME: What happens when the congestion window gets larger + * than the maximum receiver window by some large factor + * Suppose the pipeline never looses packets for a long + * period of time, then traffic increases causing packet loss. + * The congestion window should be reduced, but what it should + * be reduced to is not clear, since 1/2 the old window may + * still be larger than the maximum sending rate we ever achieved. */ - -#define SHIFT_FACTOR 16 - -static void tcp_cong_avoid_vegas(struct sock *sk, u32 seq, u32 ack, - u32 seq_rtt) +static void tcp_cong_avoid(struct tcp_opt *tp, u32 seq, u32 ack, u32 seq_rtt) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - unsigned int actual, expected; - unsigned int inv_rtt, inv_basertt, inv_basebd; - u32 snt_bytes; - - /* From: - * TCP Vegas: New Techniques for Congestion - * Detection and Avoidance. - * - * Warning: This code is a scratch implementation taken - * from the paper only. The code they distribute seams - * to have improved several things over the initial spec. - */ - - if (!seq_rtt) - seq_rtt = 1; - - if (tp->basertt) - tp->basertt = min(seq_rtt, tp->basertt); - else - tp->basertt = seq_rtt; - - /* actual = throughput for this segment. - * expected = number_of_bytes in transit / BaseRTT - */ - - snt_bytes = ack - seq; - - inv_rtt = (1 << SHIFT_FACTOR) / seq_rtt; - inv_basertt = (1 << SHIFT_FACTOR) / tp->basertt; - - actual = snt_bytes * inv_rtt; - - expected = (tp->snd_nxt - tp->snd_una) * inv_basertt; - - inv_basebd = sk->mss * inv_basertt; - - /* Slow Start */ - if (tp->snd_cwnd < tp->snd_ssthresh && - (seq == tp->snd_nxt || - (expected - actual <= TCP_VEGAS_GAMMA * inv_basebd))) { - /* "Vegas allows exponential growth only every other RTT" */ - if (tp->snd_cwnd_cnt++) { - tp->snd_cwnd++; - tp->snd_cwnd_cnt = 0; - } - } else { - /* Congestion Avoidance */ - if (expected - actual <= TCP_VEGAS_ALPHA * inv_basebd) { - /* Increase Linearly */ - if (tp->snd_cwnd_cnt++ >= tp->snd_cwnd) { - tp->snd_cwnd++; - tp->snd_cwnd_cnt = 0; - } - } - - if (expected - actual >= TCP_VEGAS_BETA * inv_basebd) { - /* Decrease Linearly */ - if (tp->snd_cwnd_cnt++ >= tp->snd_cwnd) { - tp->snd_cwnd--; - tp->snd_cwnd_cnt = 0; - } - - /* Never less than 2 segments. */ - if (tp->snd_cwnd < 2) - tp->snd_cwnd = 2; - } - } -} - -static void tcp_cong_avoid_vanj(struct sock *sk, u32 seq, u32 ack, u32 seq_rtt) -{ - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - - /* This is Jacobson's slow start and congestion avoidance. - * SIGCOMM '88, p. 328. Because we keep cong_window in - * integral mss's, we can't do cwnd += 1 / cwnd. - * Instead, maintain a counter and increment it once every - * cwnd times. - * FIXME: Check to be sure the mathematics works out right - * on this trick when we have to reduce the congestion window. - * The snd_cwnd_cnt has to be reset properly when reduction events - * happen. - * FIXME: What happens when the congestion window gets larger - * than the maximum receiver window by some large factor - * Suppose the pipeline never looses packets for a long - * period of time, then traffic increases causing packet loss. - * The congestion window should be reduced, but what it should - * be reduced to is not clear, since 1/2 the old window may - * still be larger than the maximum sending rate we ever achieved. - */ - if (tp->snd_cwnd <= tp->snd_ssthresh) { + if ((tp->snd_cwnd>>TCP_CWND_SHIFT) <= tp->snd_ssthresh) { /* In "safe" area, increase. */ - tp->snd_cwnd++; + tp->snd_cwnd += (1 << TCP_CWND_SHIFT); } else { - /* In dangerous area, increase slowly. In theory this is - * tp->snd_cwnd += 1 / tp->snd_cwnd - */ - if (tp->snd_cwnd_cnt >= tp->snd_cwnd) { - tp->snd_cwnd++; - tp->snd_cwnd_cnt = 0; - } else - tp->snd_cwnd_cnt++; + /* In dangerous area, increase slowly. */ + tp->snd_cwnd += 1; } } @@ -738,7 +631,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, __u32 ack, tp->packets_out--; *seq = TCP_SKB_CB(skb)->seq; *seq_rtt = now - TCP_SKB_CB(skb)->when; - skb_unlink(skb); + __skb_unlink(skb, skb->list); kfree_skb(skb); } @@ -791,7 +684,7 @@ static void tcp_ack_saw_tstamp(struct sock *sk, struct tcp_opt *tp, } else { tcp_set_rto(tp); if (flag & FLAG_DATA_ACKED) - (*tcp_sys_cong_ctl_f)(sk, seq, ack, seq_rtt); + tcp_cong_avoid(tp, seq, ack, seq_rtt); } /* NOTE: safe here so long as cong_ctl doesn't use rto */ tcp_bound_rto(tp); @@ -910,7 +803,7 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, tcp_set_rto(tp); tcp_bound_rto(tp); } - (*tcp_sys_cong_ctl_f)(sk, seq, ack, seq_rtt); + tcp_cong_avoid(tp, seq, ack, seq_rtt); } } } @@ -1326,9 +1219,13 @@ static void tcp_sack_remove_skb(struct tcp_opt *tp, struct sk_buff *skb) int num_sacks = tp->num_sacks; int this_sack; - /* We know this removed SKB will eat from the front of a SACK. */ + /* This is an in order data segment _or_ an out-of-order SKB being + * moved to the receive queue, so we know this removed SKB will eat + * from the front of a SACK. + */ for(this_sack = 0; this_sack < num_sacks; this_sack++, sp++) { - if(sp->start_seq == TCP_SKB_CB(skb)->seq) + if(!after(sp->start_seq, TCP_SKB_CB(skb)->seq) && + before(sp->start_seq, TCP_SKB_CB(skb)->end_seq)) break; } @@ -1380,7 +1277,7 @@ static void tcp_ofo_queue(struct sock *sk) if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) { SOCK_DEBUG(sk, "ofo packet was already received \n"); - skb_unlink(skb); + __skb_unlink(skb, skb->list); kfree_skb(skb); continue; } @@ -1390,8 +1287,8 @@ static void tcp_ofo_queue(struct sock *sk) if(tp->sack_ok) tcp_sack_remove_skb(tp, skb); - skb_unlink(skb); - skb_queue_tail(&sk->receive_queue, skb); + __skb_unlink(skb, skb->list); + __skb_queue_tail(&sk->receive_queue, skb); tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; if(skb->h.th->fin) tcp_fin(skb, sk, skb->h.th); @@ -1411,7 +1308,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) /* Ok. In sequence. */ queue_and_out: dst_confirm(sk->dst_cache); - skb_queue_tail(&sk->receive_queue, skb); + __skb_queue_tail(&sk->receive_queue, skb); tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; if(skb->h.th->fin) { tcp_fin(skb, sk, skb->h.th); @@ -1468,7 +1365,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) tp->selective_acks[0].start_seq = TCP_SKB_CB(skb)->seq; tp->selective_acks[0].end_seq = TCP_SKB_CB(skb)->end_seq; } - skb_queue_head(&tp->out_of_order_queue,skb); + __skb_queue_head(&tp->out_of_order_queue,skb); } else { for(skb1=tp->out_of_order_queue.prev; ; skb1 = skb1->prev) { /* Already there. */ @@ -1476,8 +1373,8 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) if (skb->len >= skb1->len) { if(tp->sack_ok) tcp_sack_extend(tp, skb1, skb); - skb_append(skb1, skb); - skb_unlink(skb1); + __skb_append(skb1, skb); + __skb_unlink(skb1, skb1->list); kfree_skb(skb1); } else { /* A duplicate, smaller than what is in the @@ -1489,7 +1386,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) } if (after(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb1)->seq)) { - skb_append(skb1,skb); + __skb_append(skb1, skb); if(tp->sack_ok) tcp_sack_new_ofo_skb(sk, skb); break; @@ -1497,7 +1394,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) /* See if we've hit the start. If so insert. */ if (skb1 == skb_peek(&tp->out_of_order_queue)) { - skb_queue_head(&tp->out_of_order_queue,skb); + __skb_queue_head(&tp->out_of_order_queue,skb); if(tp->sack_ok) tcp_sack_new_ofo_skb(sk, skb); break; @@ -1550,7 +1447,7 @@ static void tcp_data_snd_check(struct sock *sk) if ((skb = tp->send_head)) { if (!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una + tp->snd_wnd) && - tcp_packets_in_flight(tp) < tp->snd_cwnd) { + tcp_packets_in_flight(tp) < (tp->snd_cwnd >> TCP_CWND_SHIFT)) { /* Put more data onto the wire. */ tcp_write_xmit(sk); } else if (tp->packets_out == 0 && !tp->pending) { @@ -1695,7 +1592,7 @@ static void prune_queue(struct sock *sk) /* Start with the end because there are probably the least * useful packets (crossing fingers). */ - while ((skb = skb_dequeue_tail(&tp->out_of_order_queue))) { + while ((skb = __skb_dequeue_tail(&tp->out_of_order_queue))) { kfree_skb(skb); if (atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) return; @@ -1715,7 +1612,7 @@ static void prune_queue(struct sock *sk) tp->copied_seq, TCP_SKB_CB(skb)->end_seq, tp->last_ack_sent); break; } - skb_unlink(skb); + __skb_unlink(skb, skb->list); tp->rcv_nxt = TCP_SKB_CB(skb)->seq; SOCK_DEBUG(sk, "prune_queue: removing %x-%x (c=%x)\n", TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq, @@ -1796,7 +1693,7 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, /* DO NOT notify forward progress here. * It saves dozen of CPU instructions in fast path. --ANK */ - skb_queue_tail(&sk->receive_queue, skb); + __skb_queue_tail(&sk->receive_queue, skb); tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; /* FIN bit check is not done since if FIN is set in @@ -2249,26 +2146,3 @@ discard: } return 0; } - -int tcp_sysctl_congavoid(ctl_table *ctl, int write, struct file * filp, - void *buffer, size_t *lenp) -{ - int val = sysctl_tcp_cong_avoidance; - int retv; - static tcp_sys_cong_ctl_t tab[] = { - tcp_cong_avoid_vanj, - tcp_cong_avoid_vegas - }; - - retv = proc_dointvec(ctl, write, filp, buffer, lenp); - - if (write) { - if ((unsigned)sysctl_tcp_cong_avoidance > 1) { - retv = -EINVAL; - sysctl_tcp_cong_avoidance = val; - } else { - tcp_sys_cong_ctl_f = tab[sysctl_tcp_cong_avoidance]; - } - } - return retv; -} diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 0d3b957cf908..408fa3e8735e 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.123 1998/03/28 00:55:30 davem Exp $ + * Version: $Id: tcp_ipv4.c,v 1.127 1998/03/30 08:41:25 davem Exp $ * * IPv4 specific functions * @@ -347,7 +347,9 @@ static inline struct sock *__tcp_v4_lookup(struct tcphdr *th, u32 saddr, u16 sport, u32 daddr, u16 dport, int dif) { - unsigned short hnum = ntohs(dport); + TCP_V4_ADDR_COOKIE(acookie, saddr, daddr) + __u16 hnum = ntohs(dport); + __u32 ports = TCP_COMBINED_PORTS(sport, hnum); struct sock *sk; int hash; @@ -359,12 +361,7 @@ static inline struct sock *__tcp_v4_lookup(struct tcphdr *th, /* Check TCP register quick cache first. */ sk = TCP_RHASH(sport); - if(sk && - sk->daddr == saddr && /* remote address */ - sk->dport == sport && /* remote port */ - sk->num == hnum && /* local port */ - sk->rcv_saddr == daddr && /* local address */ - (!sk->bound_dev_if || sk->bound_dev_if == dif)) + if(sk && TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif)) goto hit; /* Optimize here for direct hit, only listening connections can @@ -372,25 +369,16 @@ static inline struct sock *__tcp_v4_lookup(struct tcphdr *th, */ hash = tcp_hashfn(daddr, hnum, saddr, sport); for(sk = tcp_established_hash[hash]; sk; sk = sk->next) { - if(sk->daddr == saddr && /* remote address */ - sk->dport == sport && /* remote port */ - sk->num == hnum && /* local port */ - sk->rcv_saddr == daddr && /* local address */ - (!sk->bound_dev_if || sk->bound_dev_if == dif)) { + if(TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif)) { if (sk->state == TCP_ESTABLISHED) TCP_RHASH(sport) = sk; goto hit; /* You sunk my battleship! */ } } /* Must check for a TIME_WAIT'er before going to listener hash. */ - for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) { - if(sk->daddr == saddr && /* remote address */ - sk->dport == sport && /* remote port */ - sk->num == hnum && /* local port */ - sk->rcv_saddr == daddr && /* local address */ - (!sk->bound_dev_if || sk->bound_dev_if == dif)) + for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) + if(TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif)) goto hit; - } #ifdef USE_QUICKSYNS listener_shortcut: #endif @@ -771,8 +759,8 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len) switch (type) { case ICMP_SOURCE_QUENCH: #ifndef OLD_SOURCE_QUENCH /* This is deprecated */ - tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2); - tp->snd_cwnd = tp->snd_ssthresh; + tp->snd_ssthresh = max(tp->snd_cwnd >> (1 + TCP_CWND_SHIFT), 2); + tp->snd_cwnd = (tp->snd_ssthresh << TCP_CWND_SHIFT); tp->high_seq = tp->snd_nxt; #endif return; @@ -1203,14 +1191,13 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, newtp->last_ack_sent = req->rcv_isn + 1; newtp->backoff = 0; newtp->mdev = TCP_TIMEOUT_INIT; - newtp->snd_cwnd = 1; + newtp->snd_cwnd = (1 << TCP_CWND_SHIFT); newtp->rto = TCP_TIMEOUT_INIT; newtp->packets_out = 0; newtp->fackets_out = 0; newtp->retrans_out = 0; newtp->high_seq = 0; newtp->snd_ssthresh = 0x7fffffff; - newtp->snd_cwnd_cnt = 0; newtp->dup_acks = 0; newtp->delayed_acks = 0; init_timer(&newtp->retransmit_timer); @@ -1659,7 +1646,7 @@ static int tcp_v4_init_sock(struct sock *sk) /* See draft-stevens-tcpca-spec-01 for discussion of the * initialization of these values. */ - tp->snd_cwnd = 1; + tp->snd_cwnd = (1 << TCP_CWND_SHIFT); tp->snd_ssthresh = 0x7fffffff; /* Infinity */ sk->priority = 1; @@ -1687,11 +1674,11 @@ static int tcp_v4_destroy_sock(struct sock *sk) tcp_dec_slow_timer(TCP_SLT_KEEPALIVE); /* Cleanup up the write buffer. */ - while((skb = skb_dequeue(&sk->write_queue)) != NULL) + while((skb = __skb_dequeue(&sk->write_queue)) != NULL) kfree_skb(skb); /* Cleans up our, hopefuly empty, out_of_order_queue. */ - while((skb = skb_dequeue(&tp->out_of_order_queue)) != NULL) + while((skb = __skb_dequeue(&tp->out_of_order_queue)) != NULL) kfree_skb(skb); /* Clean up a locked TCP bind bucket, this only happens if a diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index dea48c176ec9..664231f23e9f 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_output.c,v 1.79 1998/03/28 00:55:33 davem Exp $ + * Version: $Id: tcp_output.c,v 1.81 1998/03/30 08:41:36 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -138,7 +138,7 @@ void tcp_send_skb(struct sock *sk, struct sk_buff *skb, int force_queue) /* Advance write_seq and place onto the write_queue. */ tp->write_seq += (TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq); - skb_queue_tail(&sk->write_queue, skb); + __skb_queue_tail(&sk->write_queue, skb); if (!force_queue && tp->send_head == NULL && tcp_snd_test(sk, skb)) { /* Send it out now. */ @@ -215,7 +215,7 @@ static int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len) skb->csum = csum_partial(skb->data, skb->len, 0); /* Link BUFF into the send queue. */ - skb_append(skb, buff); + __skb_append(skb, buff); return 0; } @@ -396,7 +396,7 @@ static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *skb, int m return; /* Ok. We will be able to collapse the packet. */ - skb_unlink(next_skb); + __skb_unlink(next_skb, next_skb->list); if(skb->len % 4) { /* Must copy and rechecksum all data. */ @@ -548,7 +548,7 @@ void tcp_xmit_retransmit_queue(struct sock *sk) /* Stop retransmitting if we've hit the congestion * window limit. */ - if (tp->retrans_out >= tp->snd_cwnd) + if (tp->retrans_out >= (tp->snd_cwnd >> TCP_CWND_SHIFT)) break; } update_retrans_head(sk); @@ -577,7 +577,7 @@ void tcp_fack_retransmit(struct sock *sk) if(tcp_retransmit_skb(sk, skb)) break; - if(tcp_packets_in_flight(tp) >= tp->snd_cwnd) + if(tcp_packets_in_flight(tp) >= (tp->snd_cwnd >> TCP_CWND_SHIFT)) break; next_packet: packet_cnt++; @@ -687,7 +687,7 @@ int tcp_send_synack(struct sock *sk) /* SYN eats a sequence byte. */ TCP_SKB_CB(skb)->seq = tp->snd_una; TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + 1; - skb_queue_tail(&sk->write_queue, skb); + __skb_queue_tail(&sk->write_queue, skb); TCP_SKB_CB(skb)->when = jiffies; tp->packets_out++; tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC)); @@ -838,7 +838,7 @@ void tcp_connect(struct sock *sk, struct sk_buff *buff, int mss) tp->retrans_out = 0; /* Send it off. */ - skb_queue_tail(&sk->write_queue, buff); + __skb_queue_tail(&sk->write_queue, buff); TCP_SKB_CB(buff)->when = jiffies; tp->packets_out++; tcp_transmit_skb(sk, skb_clone(buff, GFP_KERNEL)); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 00d021415762..78fcad3fefa5 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_timer.c,v 1.44 1998/03/27 04:07:43 davem Exp $ + * Version: $Id: tcp_timer.c,v 1.45 1998/03/30 08:41:31 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -382,9 +382,8 @@ void tcp_retransmit_timer(unsigned long data) * "one half of the current window but at least 2 segments" */ tp->retrans_out = 0; - tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2); - tp->snd_cwnd_cnt = 0; - tp->snd_cwnd = 1; + tp->snd_ssthresh = max(tp->snd_cwnd >> (1 + TCP_CWND_SHIFT), 2); + tp->snd_cwnd = (1 << TCP_CWND_SHIFT); } tp->retransmits++; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 0f1c710d3625..e92b4e878fb9 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque * - * $Id: ip6_output.c,v 1.10 1998/03/20 09:12:17 davem Exp $ + * $Id: ip6_output.c,v 1.11 1998/03/28 08:29:39 davem Exp $ * * Based on linux/net/ipv4/ip_output.c * @@ -265,7 +265,6 @@ static int ip6_frag_xmit(struct sock *sk, inet_getfrag_t getfrag, return err; last_skb->dst = dst_clone(dst); - last_skb->when = jiffies; skb_reserve(last_skb, (dst->dev->hard_header_len + 15) & ~15); @@ -461,8 +460,6 @@ int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data, dev = dst->dev; skb->dst = dst_clone(dst); - skb->when = jiffies; - skb_reserve(skb, (dev->hard_header_len + 15) & ~15); hdr = (struct ipv6hdr *) skb->tail; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 5e3446b49df7..e4162fac9a4b 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque * - * $Id: tcp_ipv6.c,v 1.69 1998/03/28 00:55:36 davem Exp $ + * $Id: tcp_ipv6.c,v 1.72 1998/03/30 08:41:52 davem Exp $ * * Based on: * linux/net/ipv4/tcp.c @@ -240,8 +240,9 @@ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th, struct in6_addr *daddr, u16 dport, int dif) { - unsigned short hnum = ntohs(dport); struct sock *sk; + __u16 hnum = ntohs(dport); + __u32 ports = TCP_COMBINED_PORTS(sport, hnum); int hash; #ifdef USE_QUICKSYNS @@ -252,13 +253,7 @@ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th, /* Check TCP register quick cache first. */ sk = TCP_RHASH(sport); - if(sk && - sk->num == hnum && /* local port */ - sk->family == AF_INET6 && /* address family */ - sk->dport == sport && /* remote port */ - !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr) && - !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr) && - (!sk->bound_dev_if || sk->bound_dev_if == dif)) + if(sk && TCP_IPV6_MATCH(sk, saddr, daddr, ports, dif)) goto hit; /* Optimize here for direct hit, only listening connections can @@ -267,28 +262,23 @@ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th, hash = tcp_v6_hashfn(daddr, hnum, saddr, sport); for(sk = tcp_established_hash[hash]; sk; sk = sk->next) { /* For IPV6 do the cheaper port and family tests first. */ - if(sk->num == hnum && /* local port */ - sk->family == AF_INET6 && /* address family */ - sk->dport == sport && /* remote port */ - !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr) && - !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr) && - (!sk->bound_dev_if || sk->bound_dev_if == dif)) { + if(TCP_IPV6_MATCH(sk, saddr, daddr, ports, dif)) { if (sk->state == TCP_ESTABLISHED) TCP_RHASH(sport) = sk; goto hit; /* You sunk my battleship! */ } } /* Must check for a TIME_WAIT'er before going to listener hash. */ - for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) - if(sk->num == hnum && /* local port */ - sk->family == AF_INET6 && /* address family */ - sk->dport == sport) { /* remote port */ + for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) { + if(*((__u32 *)&(sk->dport)) == ports && + sk->family == AF_INET6) { struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk; if(!ipv6_addr_cmp(&tw->v6_daddr, saddr) && !ipv6_addr_cmp(&tw->v6_rcv_saddr, daddr) && (!sk->bound_dev_if || sk->bound_dev_if == dif)) goto hit; } + } #ifdef USE_QUICKSYNS listener_shortcut: #endif @@ -1302,7 +1292,7 @@ static int tcp_v6_init_sock(struct sock *sk) /* See draft-stevens-tcpca-spec-01 for discussion of the * initialization of these values. */ - tp->snd_cwnd = 1; + tp->snd_cwnd = (1 << TCP_CWND_SHIFT); tp->snd_ssthresh = 0x7fffffff; sk->priority = 1; @@ -1333,14 +1323,14 @@ static int tcp_v6_destroy_sock(struct sock *sk) * Cleanup up the write buffer. */ - while((skb = skb_dequeue(&sk->write_queue)) != NULL) + while((skb = __skb_dequeue(&sk->write_queue)) != NULL) kfree_skb(skb); /* * Cleans up our, hopefuly empty, out_of_order_queue */ - while((skb = skb_dequeue(&tp->out_of_order_queue)) != NULL) + while((skb = __skb_dequeue(&tp->out_of_order_queue)) != NULL) kfree_skb(skb); /* diff --git a/net/netsyms.c b/net/netsyms.c index 8bc8ca237d15..4c469e2a2a5f 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -73,7 +73,6 @@ extern void destroy_8023_client(struct datalink_proto *); /* Skbuff symbols. */ EXPORT_SYMBOL(skb_push_errstr); EXPORT_SYMBOL(skb_put_errstr); -EXPORT_SYMBOL(skb_queue_lock); /* Socket layer registration */ EXPORT_SYMBOL(sock_register); @@ -121,6 +120,7 @@ EXPORT_SYMBOL(put_cmsg); EXPORT_SYMBOL(net_families); EXPORT_SYMBOL(sock_kmalloc); EXPORT_SYMBOL(sock_kfree_s); +EXPORT_SYMBOL(skb_queue_lock); #ifdef CONFIG_FILTER EXPORT_SYMBOL(sk_run_filter); diff --git a/scripts/Configure b/scripts/Configure index 5ca0e83662ff..3b05916e6a3f 100644 --- a/scripts/Configure +++ b/scripts/Configure @@ -50,6 +50,9 @@ # 300397 Phil Blundell (pjb27@cam.ac.uk) - added support for min/max # arguments to "int", allow dep_tristate to take a list of dependencies # rather than just one. +# +# 090398 Axel Boldt (boldt@math.ucsb.edu) - allow for empty lines in help +# texts. # # Make sure we're really running bash. @@ -89,16 +92,18 @@ function help () { var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g') #now pick out the right help text: text=$(sed -n "/^$var[ ]*\$/,\${ - /^$var[ ]*\$/b - /^#.*/b - /^[ ]*\$/q + /^$var[ ]*\$/c\\ +${var}:\\ + + /^#/b + /^[^ ]/q p }" Documentation/Configure.help) if [ -z "$text" ] then echo; echo " Sorry, no help available for this option yet.";echo else - (echo; echo "$text"; echo) | ${PAGER:-more} + (echo; echo "$text") | ${PAGER:-more} fi else echo; diff --git a/scripts/Menuconfig b/scripts/Menuconfig index 0679ae8cfe41..6f112d1e5f1c 100644 --- a/scripts/Menuconfig +++ b/scripts/Menuconfig @@ -44,6 +44,9 @@ # # 160198 Michael Chastain (mec@shout.net) - fix bug with 'c' command # (complement existing value) when used on virgin uninitialized variables. +# +# 090398 Axel Boldt (boldt@math.ucsb.edu) - allow for empty lines in help +# texts. #---------------------------------------------------------------------------- @@ -278,9 +281,11 @@ function extract_help () { var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g') #now pick out the right help text: text=$(sed -n "/^$var[ ]*\$/,\${ - /^$var[ ]*\$/d - /^#.*/d - /^[ ]*\$/q + /^$var[ ]*\$/c\\ +${var}:\\ + + /^#/b + /^[^ ]/q s/^ // p }" Documentation/Configure.help) diff --git a/scripts/header.tk b/scripts/header.tk index 6e4a1f2cc032..76b576097a02 100644 --- a/scripts/header.tk +++ b/scripts/header.tk @@ -36,14 +36,14 @@ if { [cget .ref -disabledforeground] == "" } { # Define some macros we will need to parse the config.in file. # proc mainmenu_name { text } { - message .header.message -width 400 -relief raised -text "$text" - pack .header.label .header.message -side left -padx 15 - wm title . "$text" + message .header.message -width 400 -text "$text" + pack .header.message -side left -padx 15 + wm title . "$text" } proc menu_option { w menu_num text } { button .f0.x$menu_num -text "$text" -width 50 -command "$w .$w \"$text\"" - pack .f0.x$menu_num -pady 1 -expand on + pack .f0.x$menu_num -pady 0 -expand on } # @@ -370,21 +370,19 @@ proc dohelp {w var } { if { [file readable Documentation/Configure.help] == 1} then { set filefound 1 + # First escape sed regexp special characters in var: + set var [exec echo "$var" | sed s/\[\]\[\/.^$*\]/\\\\&/g] + # Now pick out right help text: set message [exec sed -n " /^$var\[ \]*\$/,\${ /^$var\[ \]*\$/c\\ ${var}:\\ - /^#.*/d - /^\[ \]*\$/bL - H + /^#/b + /^\[^ \]/q + s/^ // + p } - d - :L x - s/\\n // - s/\\n / /g - p - q " Documentation/Configure.help] set found [expr [string length "$message"] > 0] } @@ -402,8 +400,10 @@ ${var}:\\ label $w.f1.bm -bitmap error wm title $w "RTFM" } else { - message $w.f1.m -width 400 -aspect 300 -text $message \ - -relief flat + text $w.f1.m -width 73 -relief flat -wrap word + $w.f1.m insert 0.0 $message + $w.f1.m conf -state disabled -height [$w.f1.m index end] + label $w.f1.bm -bitmap info wm title $w "Configuration help" } @@ -416,7 +416,7 @@ ${var}:\\ frame $w.f2 button $w.f2.ok -text "OK" \ -width 10 -command "destroy $w; focus $oldFocus" - pack $w.f2.ok -side bottom -pady 10 -anchor s + pack $w.f2.ok -side bottom -pady 6 -anchor n pack $w.f2 -side bottom -padx 10 -anchor s # Finish off the window @@ -454,7 +454,6 @@ proc wrapup {w } { # buttons which we will stick down at the bottom. # frame .header -label .header.label frame .f0 diff --git a/scripts/tail.tk b/scripts/tail.tk index ee2edae7ecd9..2a727fe895b5 100644 --- a/scripts/tail.tk +++ b/scripts/tail.tk @@ -1,7 +1,3 @@ - -pack .header -side top -padx 10 -pady 10 -expand on -pack .f0 -side top -padx 15 -pady 10 -fill y -expand on - # # Misc buttons to save/restore state and so forth. # @@ -46,13 +42,19 @@ button .f0_bot.l.load -text "Load Configuration from File" -width 25 -command { load_configfile .load "Load Configuration from file" read_config_file } -pack .f0_bot.r.save .f0_bot.r.quit -padx 25 -ipadx 10 -ipady 2 -expand on -pack .f0_bot.l.load .f0_bot.l.store -padx 25 -ipadx 10 -ipady 2 -expand on +# +# Now pack everything, important things first because of small screens. +# +pack .f0_bot.r.save .f0_bot.r.quit -padx 25 -ipadx 10 -expand on +pack .f0_bot.l.load .f0_bot.l.store -padx 25 -ipadx 10 -expand on + +pack .f0_bot.r -side left -padx 15 -expand on -fill y +pack .f0_bot.l -side right -padx 15 -expand on -fill y -pack .f0_bot.r -side left -padx 15 -pady 10 -expand on -fill y -pack .f0_bot.l -side right -padx 15 -pady 10 -expand on -fill y +pack .f0_bot -side bottom -fill both -expand on -pady 4 +pack .f0 -side bottom -padx 15 -pady 0 -fill y -expand on +pack .header -padx 10 -pady 7 -expand on -pack .f0_bot -fill both -expand on # # If we cannot write our config files, disable the write button. diff --git a/scripts/tkgen.c b/scripts/tkgen.c index dcf025e2e568..040c1fe80f9e 100644 --- a/scripts/tkgen.c +++ b/scripts/tkgen.c @@ -61,6 +61,14 @@ * user switches from one configuration method to * another. * + * 1998 03 09 + * Axel Boldt - Smaller layout of main menu - it's still too big for 800x600. + * - Display help in text window to allow for cut and paste. + * - Allow for empty lines in help texts. + * - update_define should not set all variables unconditionally to + * 0: they may have been set to 1 elsewhere. CONFIG_NETLINK is + * an example. + * * TO DO: * - clean up - there are useless ifdef's everywhere. * - better comments throughout - C code generating tcl is really cryptic. @@ -73,9 +81,6 @@ * - make choice and int/hex input types line up vertically with * bool/tristate. * - general speedups - how? The canvas seems to slow it down a lot. - * - choice buttons should default to the first menu option, rather than a - * blank. Also look up the right variable when the help button - * is pressed. * - clean up +/- 16 confusion for enabling/disabling variables; causes * (theoretical, at the moment) problems with dependencies. * @@ -1124,7 +1129,7 @@ void dump_tk_script(struct kconfig *scfg) for(cfg = scfg; cfg != NULL; cfg = cfg->next) { if( cfg->tok != tok_define ) continue; - printf("\tglobal %s; set %s 0\n", cfg->optionname, cfg->optionname); + printf("\tglobal %s\n", cfg->optionname); cfg->flags |= GLOBAL_WRITTEN; } for(cfg = scfg; cfg != NULL; cfg = cfg->next)