From a55f926ce2385eda391b1a87b674be11c4392588 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 23 Nov 2007 15:11:53 -0500 Subject: [PATCH] Linux 2.0.36pre2 ftp://ftp.linux.org.uk/pub/linux/alan/.. This is a bit bigger than pre patch 1 but most of the bulk is the isdn driver updates to reflect what everyone is actually using. I don't have ISDN here - thats a hint to the ISDN using people to test this ok ;) Lesser items: o Tlan 1.0 driver o >64Mb memory recognition o Full cyrix and other CPU recognition (should fix the TI problems) o Syscall return trap fix o Delay slightly more stable o IDE floppy knows about Iomega zip firmware 23.D o Cyclades update o Traffic Shaper o Etherexpress should now handle Compaq LTE base stations o EATA update o SCSI blacklist update o SCSI medium changers recognized o Ultrastor 14f/34f update o MPU401 DMA reporting o Sequencer busy fix o ISOfs boundary case fix o NFS unload fix o SMBfs time saving fix o mmap append only file fix o Socket leak fix --- Documentation/isdn/README.HiSax | 286 +- Documentation/isdn/README.TimRu.De | 296 ++ arch/i386/boot/compressed/misc.c | 11 + arch/i386/boot/setup.S | 23 + arch/i386/kernel/Makefile | 2 +- arch/i386/kernel/setup.c | 325 +- arch/i386/kernel/traps.c | 6 +- arch/i386/lib/delay.S | 8 +- drivers/block/ide-floppy.c | 4 +- drivers/char/cyclades.c | 6013 +++++++++++++++++++--------- drivers/isdn/Config.in | 24 +- drivers/isdn/avmb1/b1capi.c | 507 ++- drivers/isdn/avmb1/b1lli.c | 602 ++- drivers/isdn/avmb1/b1pci.c | 17 +- drivers/isdn/avmb1/capidrv.c | 574 ++- drivers/isdn/avmb1/capidrv.h | 70 +- drivers/isdn/hisax/Makefile | 95 +- drivers/isdn/hisax/arcofi.c | 75 + drivers/isdn/hisax/arcofi.h | 23 + drivers/isdn/hisax/asuscom.c | 406 ++ drivers/isdn/hisax/avm_a1.c | 968 +---- drivers/isdn/hisax/callc.c | 2036 +++++----- drivers/isdn/hisax/config.c | 537 ++- drivers/isdn/hisax/diva.c | 605 +++ drivers/isdn/hisax/elsa.c | 2031 ++++------ drivers/isdn/hisax/elsa_ser.c | 684 ++++ drivers/isdn/hisax/fsm.c | 74 +- drivers/isdn/hisax/hfc_2bds0.c | 1275 ++++++ drivers/isdn/hisax/hfc_2bds0.h | 133 + drivers/isdn/hisax/hfc_2bs0.c | 608 +++ drivers/isdn/hisax/hfc_2bs0.h | 65 + drivers/isdn/hisax/hisax.h | 986 +++-- drivers/isdn/hisax/hscx.c | 300 ++ drivers/isdn/hisax/hscx.h | 53 + drivers/isdn/hisax/hscx_irq.c | 321 ++ drivers/isdn/hisax/ipac.h | 36 + drivers/isdn/hisax/isac.c | 703 ++++ drivers/isdn/hisax/isac.h | 84 + drivers/isdn/hisax/isdnl1.c | 1782 +++++---- drivers/isdn/hisax/isdnl1.h | 58 +- drivers/isdn/hisax/isdnl2.c | 1740 ++++---- drivers/isdn/hisax/isdnl3.c | 437 +- drivers/isdn/hisax/isdnl3.h | 36 +- drivers/isdn/hisax/ix1_micro.c | 922 +---- drivers/isdn/hisax/l3_1tr6.c | 647 +-- drivers/isdn/hisax/l3_1tr6.h | 21 +- drivers/isdn/hisax/l3dss1.c | 1632 ++++++-- drivers/isdn/hisax/l3dss1.h | 75 + drivers/isdn/hisax/lmgr.c | 67 + drivers/isdn/hisax/mic.c | 278 ++ drivers/isdn/hisax/netjet.c | 1189 ++++++ drivers/isdn/hisax/niccy.c | 406 ++ drivers/isdn/hisax/q931.c | 54 +- drivers/isdn/hisax/s0box.c | 277 ++ drivers/isdn/hisax/sedlbauer.c | 353 ++ drivers/isdn/hisax/sportster.c | 286 ++ drivers/isdn/hisax/tei.c | 605 ++- drivers/isdn/hisax/teleint.c | 378 ++ drivers/isdn/hisax/teles0.c | 996 +---- drivers/isdn/hisax/teles3.c | 1146 ++---- drivers/isdn/hisax/teles3c.c | 201 + drivers/isdn/hisax/telespci.c | 374 ++ drivers/isdn/icn/icn.c | 77 +- drivers/isdn/isdn_common.c | 25 +- drivers/isdn/isdn_common.h | 7 +- drivers/isdn/isdn_ppp.c | 20 +- drivers/isdn/isdn_tty.c | 46 +- drivers/isdn/isdnloop/Makefile | 11 + drivers/isdn/isdnloop/isdnloop.c | 1560 ++++++++ drivers/isdn/isdnloop/isdnloop.h | 142 + drivers/net/3c59x.c | 6 +- drivers/net/Config.in | 3 + drivers/net/Makefile | 8 + drivers/net/eexpress.c | 14 +- drivers/net/shaper.c | 661 +++ drivers/scsi/eata.c | 91 +- drivers/scsi/eata.h | 7 +- drivers/scsi/scsi.c | 2 + drivers/scsi/u14-34f.c | 45 +- drivers/scsi/u14-34f.h | 3 +- drivers/sound/sb_common.c | 2 + drivers/sound/sequencer.c | 4 +- fs/isofs/inode.c | 7 +- fs/nfs/inode.c | 3 + fs/nfs/nfsiod.c | 6 +- fs/proc/array.c | 2 +- fs/smbfs/proc.c | 4 +- include/asm-i386/processor.h | 47 + include/asm-i386/smp.h | 1 + include/linux/b1lli.h | 106 +- include/linux/cyclades.h | 507 ++- include/linux/if_shaper.h | 62 + include/linux/isdn.h | 76 +- include/linux/isdnif.h | 11 +- include/linux/serial.h | 3 +- include/linux/skbuff.h | 11 + init/main.c | 6 + kernel/ksyms.c | 5 + mm/mmap.c | 2 +- 99 files changed, 27664 insertions(+), 10755 deletions(-) create mode 100644 Documentation/isdn/README.TimRu.De create mode 100644 drivers/isdn/hisax/arcofi.c create mode 100644 drivers/isdn/hisax/arcofi.h create mode 100644 drivers/isdn/hisax/asuscom.c create mode 100644 drivers/isdn/hisax/diva.c create mode 100644 drivers/isdn/hisax/elsa_ser.c create mode 100644 drivers/isdn/hisax/hfc_2bds0.c create mode 100644 drivers/isdn/hisax/hfc_2bds0.h create mode 100644 drivers/isdn/hisax/hfc_2bs0.c create mode 100644 drivers/isdn/hisax/hfc_2bs0.h create mode 100644 drivers/isdn/hisax/hscx.c create mode 100644 drivers/isdn/hisax/hscx.h create mode 100644 drivers/isdn/hisax/hscx_irq.c create mode 100644 drivers/isdn/hisax/ipac.h create mode 100644 drivers/isdn/hisax/isac.c create mode 100644 drivers/isdn/hisax/isac.h create mode 100644 drivers/isdn/hisax/l3dss1.h create mode 100644 drivers/isdn/hisax/lmgr.c create mode 100644 drivers/isdn/hisax/mic.c create mode 100644 drivers/isdn/hisax/netjet.c create mode 100644 drivers/isdn/hisax/niccy.c create mode 100644 drivers/isdn/hisax/s0box.c create mode 100644 drivers/isdn/hisax/sedlbauer.c create mode 100644 drivers/isdn/hisax/sportster.c create mode 100644 drivers/isdn/hisax/teleint.c create mode 100644 drivers/isdn/hisax/teles3c.c create mode 100644 drivers/isdn/hisax/telespci.c create mode 100644 drivers/isdn/isdnloop/Makefile create mode 100644 drivers/isdn/isdnloop/isdnloop.c create mode 100644 drivers/isdn/isdnloop/isdnloop.h create mode 100644 drivers/net/shaper.c create mode 100644 include/linux/if_shaper.h diff --git a/Documentation/isdn/README.HiSax b/Documentation/isdn/README.HiSax index 20b578d83221..593994f1767a 100644 --- a/Documentation/isdn/README.HiSax +++ b/Documentation/isdn/README.HiSax @@ -23,24 +23,45 @@ Supported cards --------------- Teles 8.0/16.0/16.3 and compatible ones +Teles 16.3c Teles S0/PCMCIA +Teles PCI +Teles S0Box +Creatix S0Box 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 Quickstep 3000PCI ELSA PCMCIA ITK ix1-micro Rev.2 +Eicon.Diehl Diva 2.0 ISA and PCI (S0 and U interface, no PRO version) +Eicon.Diehl Diva Piccola +ASUSCOM NETWORK INC. ISDNLink 128K PC adapter (order code I-IN100-ST-D) +Dynalink IS64PH (OEM version of ASUSCOM NETWORK INC. ISDNLink 128K adapter) +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 + Teles PCI is EXPERIMENTAL + Teles S0Box is EXPERIMENTAL + Eicon.Diehl Diva U interface not tested If you know other passive cards with the Siemens chipset, please let me know. To use the PNP cards you need the isapnptools. You can combine any card, if there is no conflict between the ressources -(io, mem, irq), with one exception: The ELSA PCMCIA cannot work with an other -non PCMCIA ELSA card at the same time. You cannot select ELSA ISA and ELSA -PCMCIA support at the same time during kernel config. +(io, mem, irq). Configuring the driver @@ -113,13 +134,30 @@ 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 + 21 Teles PCI no parameter + 22 Sedlbauer Speed Star (PCMCIA) irq, io (set with card manager) + 24 Dr. Neuhaus Niccy PnP irq, io0, io1 (from isapnp setup) + 24 Dr. Neuhaus Niccy PCI no parameter + 25 Teles S0Box irq, io (of the used lpt port) + -At the moment IRQ sharing is not possible. Please make sure that your IRQ -is free and enabled for ISA use. +At the moment IRQ sharing is only possible with PCI cards. Please make sure +that your IRQ is free and enabled for ISA use. Note: For using the ELSA PCMCIA you need the cardmanager under MSDOS for enabling in the moment, then boot linux with loadlin. @@ -181,17 +219,32 @@ 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 Teles PCI no parameter + 22 Sedlbauer Speed Star (PCMCIA) pa=irq, pb=io (set with card manager) + 24 Dr. Neuhaus Niccy PnP ONLY WORKS AS A MODULE ! + 24 Dr. Neuhaus Niccy PCI no parameter + 25 Teles S0Box irq, io (of the used lpt port) Running the driver ------------------ @@ -200,7 +253,7 @@ When you insmod isdn.o and hisax.o (or with the in-kernel version, during boot time), a few lines should appear in your syslog. Look for something like: Apr 13 21:01:59 kke01 kernel: HiSax: Driver for Siemens chip set ISDN cards -Apr 13 21:01:59 kke01 kernel: HiSax: Version 2.1 +Apr 13 21:01:59 kke01 kernel: HiSax: Version 2.9 Apr 13 21:01:59 kke01 kernel: HiSax: Revisions 1.14/1.9/1.10/1.25/1.8 Apr 13 21:01:59 kke01 kernel: HiSax: Total 1 card defined Apr 13 21:01:59 kke01 kernel: HiSax: Card 1 Protocol EDSS1 Id=HiSax1 (0) @@ -215,8 +268,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 +279,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 +324,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 +344,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 +361,22 @@ 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 + Enrik Berkhan (enrik@starfleet.inka.de) for S0BOX specific stuff + Ton van Rosmalen for Teles PCI and more people who are hunting bugs. (If I forgot somebody, please send me a mail). 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 +393,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.TimRu.De b/Documentation/isdn/README.TimRu.De new file mode 100644 index 000000000000..e3f86ace2cca --- /dev/null +++ b/Documentation/isdn/README.TimRu.De @@ -0,0 +1,296 @@ +--( Version: 11.10.1997 )-- + +TimRu-Erweiterungen: +==================== + +1. erfolglose Anwahlversuche per ICMP zurueckmelden +--------------------------------------------------- + +isdnctrl dialtimeout + name: Name des Interfaces + timeout: -1: kein Waehl-Timeout + 0: jede Telefonnummer dialmax-mal probieren + >0: Zeitraum in Sekunden, in dem die Anwahl versucht wird + + isdnctrl dialtimeout bewirkt, dass die Anwahl der Gegenstelle im Fehler- + fall nicht unbegrenzt versucht wird, sondern entweder nach einer bestimmten + Anzahl von Versuchen oder nach einem bestimmten Zeitraum abgebrochen wird + und alle ausstehenden Pakete fuer den durch isdnctrl dialwait bestimmten + Zeitraum mit ICMP_NET_UNREACHABLE beantwortet werden. + + +isdnctrl dialwait + name: Name des Interfaces + seconds: 0: keine Waehl-Unterdrueckung im Fehlerfall + >0: Zeit in Sekunden, in der nach einem erfolglosen Anwahl- + versuch Pakete mit ICMP_NET_UNREACHABLE beantwortet werden + + +1.1 einzelne Interfaces stoppen / starten +----------------------------------------- + +isdnctrl status + name: Name des Interfaces + status: on: Interface einschalten + off: Interface ausschalten + + Dieser Befehl wirkt wie 'isdnctrl system on/off' auf Interface-Ebene. + + +2. bessere Kontrolle ueber Aufbau und Hangup einer Verbindung +------------------------------------------------------------- + +isdnctrl bringup +isdnctrl keepup in +isdnctrl keepup out +isdnctrl keepup both + cmd: addrule: Regel am Ende der Regelliste anfuegen + insrule: Regel am Anfang der Regelliste einfuegen + delrule: Regel loeschen + default: Was tun, wenn keine Regel passt? + showrules: alle Regeln anzeigen + flushrules: alle Regeln einer Art loeschen (bringup, ...) + flushallrules: alle Regeln loeschen + + name: Name des Interfaces + seconds: Mindester Hangup-Timeout ab jetzt + rule: Regel, auf die ein Paket passen muss, damit die Verbindung + aufgebaut oder der Hangup-Timeout verlaengert wird + + + Eine passende Bringup-Regel erlaubt den Aufbau der Verbindung, + wenn ihr Timeout > 0 ist. Eine Bringup-Regel mit einen Timeout == 0 + verhindert einen Verbindungsaufbau. Dieser Timeout muss eigentlich nur + so gross sein, dass die Verbindung zum Gegenrechner erfolgreich + zustande kommt, da das ausloesende Datenpaket danach durch die + Keepup-Logik 'geht' und der Hangup-Timeout erneut berechnet wird. + + Eine passende Keepup-Regel verlaengert den Hangup-Timeout, wobei + nach eingehenden und ausgehenden Paketen unterschieden werden kann. + + Die Kontrolle eines Paketes stoppt bei der ersten passenden Regel, falls + keine Regel anwendbar ist, gilt die Default-Regel. + + Die Regeln haben folgenden Aufbau: + ip/icmp / / + ip/tcp / / + ip/udp / / + ip/* / / + ip/any / / + + ipx/* + ipx/any + + ppp/ipcp + ppp/ipxcp + ppp/ccp + ppp/lcp + ppp/chap + ppp/pap + ppp/lqr + ppp/* + ppp/any + + */* + any + + + src: Absender-Adresse des Paketes als Nummer oder Name + dst: Empfaenger-Adresse des Paketes als Nummer oder Name + mask: Subnet-Maske als Anzahl Bits oder als Maske + type: ICMP-Message-Type als Nummer + port: Portnummer als Nummer oder Name + + + Wildcards ('*', 'any') sind ueberall erlaubt. + + Fuer und gilt: + : Host-Adresse als Nummer oder Name, + Subnet-Mask /32 + /: Host-Adresse als Nummer oder Name, + Subnet-Mask als Anzahl Bits oder Maske + : Netzwerk-Adresse als Nummer oder Name, + Subnet-Mask /32 + /: Host-Adresse als Nummer oder Name, + Subnet-Mask als Anzahl Bits oder Maske + 0.0.0.0/0 + 0/0 + * + any: Wildcard, passt auf jede Adresse. + + Fuer gilt: + -: alle Ports von bis , numerische Angabe + -: alle Ports von bis 65535, numerische Angabe + -: alle Ports von 0 bis , numerische Angabe + : ein Port als Nummer oder Name + -, * + oder any: alle Ports + + Beginnt die Regel mit einem '!' oder mit 'not', so dreht sich ihre Aussage um: + falls sie NICHT passt, wird die Verbindung aufgebaut, bzw. der + Hangup-Timeout verlaengert. + + + Default-Regeln werden folgendermassen angegeben: + + isdnctrl default bringup + name: Name des Interfaces + timeout: 0: Verbindung wird nicht aufgebaut + >0: Verbindung wird aufgebaut, anfaenglicher Timeout + ist . + + isdnctrl default keepup in + isdnctrl default keepup out + isdnctrl default keepup both + name: Name des Interfaces + seconds: Mindester Hangup-Timeout + + +3. Budget-Erweiterungen +----------------------- + +Diese Erweiterung erlaubt es, bestimmte 'Verbrauchswerte' pro +Zeitraum zu limitieren. Falls ein Budget aufgebraucht ist, findet +keine Anwahl mehr statt und eine bestehende Verbindung wird unterbrochen. +Sobald wieder alle Budgets verfuegbar sind, werden wieder Verbindungen +zugelassen. +Die Gebuehrenimpuls-Zaehlung setzt momentan auf der Uebertragung der +CINF-Pakete, also der Gebuehreninformation waehrend der Verbindung auf. +Unmittelbar nach Aufbau der Verbindung wird der erste Gebuehrenimpuls +'angenommen'. +Fuer ISDN-Anschluesse, bei denen die Gebuehren erst am Ende der Ver- +bindung uebertragen werden, faellt uns bestimmt auch noch was ein ;-). + +isdnctrl budget + setzt die Werte eines bestimmten Budgets. Ein aufgebrauchtes + Budget wird unmittelbar wieder aktiviert. + + budget-type: dial: Anzahl der Anwahlversuche (erfolgreich oder + erfolglos) pro Periode + charge: Anzahl der Gebuehreneinheiten pro Periode + online: Online-Zeit pro Periode + + amount: Hoehe des Budgets. Bei 'dial' oder 'charge' + ist das die Anzahl Anwahlen oder Einheiten, bei 'online' + eine Zeitangabe (s.u.) + + period: Dauer der Periode in folgendem Format ( steht fuer eine + natuerliche Zahl): + + + s + sec, Sekunden + + m + min, Minuten + + h + hour, Stunden + + d + day, Tage + + w + week, Wochen (a 7 Tage) + + M + month, Monate (a 30 Tage) + + y + year, Jahre (a 365 Tage) + + Diese Angaben koennen miteinander kombiniert werden, z.B.: + + 2h30m15 + 2hour,30min,15sec + 1M2week,1day,8h + + +isdnctrl budget off + schaltet die Kontrolle des entsprechenden Budgets aus + +isdnctrl budget showbudgets + zeigt die momentaten Budgets an + +isdnctrl budget savebudgets + gibt die momentaten Budgets in einen Format aus, das vom Befehl 'restore- + budgets' wieder eingelesen kann + +isdnctrl budget restorebudgets ... + setzt die Budgets wieder auf die vorher mit 'savebudgets' ausgegebenen + Werte. Damit koennen Budgets auch ueber den Reboot der Maschine hinaus + Gueltigkeit haben. + + +Hier ein Beispiel fuer die TimRu-Erweiterung: + + # Alle Regeln loeschen + isdnctrl flushallrules ippp0 + + # Default: jeder Datenverkehr setzt den Hangup-Timeout auf 60 Sek. + isdnctrl default ippp0 keepup both 60 + + # FTP und Telnet erhoehen den Timeout auf 5 Minuten + isdnctrl addrule ippp0 keepup both 300 ip/tcp 0/0 20-23 0/0 - + + # Anzeige der Regeln: + isdnctrl showrules ippp0 + + # ... erzeugt folgende Ausgabe: + +Timeout rules for interface ippp0: +Default bringup policy: true +Default huptimeout for incoming packets: 60 sec. +Default huptimeout for outgoing packets: 60 sec. + +Current huptimeout: 60 sec. +Time until hangup: 45 sec. + +Keepup-rules for incoming ip-packets: +1-1-0 keepup_in 300 ip/tcp 0/0 20-23 0/0 * + +Keepup-rules for outgoing ip-packets: +2-1-0 keepup_out 300 ip/tcp 0/0 * 0/0 20-23 + + +Hier ein Beispiel fuer die Budget-Erweiterung: + + # Hoechstens 60 Anwahlversuche pro Stunde + isdnctrl budget ippp0 dial 60 1h + + # Hoechstens 3000 Einheiten pro Monat + isdnctrl budget ippp0 charge 3000 1M + + # Hoechstens 8 Stunden online pro Tag + isdnctrl budget ippp0 online 8h 1d + + # Anzeige der Budgets: + isdnctrl showbudgets ippp0 + + # ... erzeugt nach kurzer Zeit folgende Ausgabe: + + +Budgets for interface ippp0: + +TYPE AMOUNT PERIOD USED SINCE +dial 60 1h 1 01.07.1997, 17:43:40 +charge 3000 1M 2 01.07.1997, 17:43:49 +online 8h 1d 2m26s 01.07.1997, 17:43:57 + + +Zum Nachfuehren der Budgets ueber einen laengeren Zeitraum koennte man +folgende Methode verwenden: + +beim Herunterfahren oder per 'cron' jede Minute: + + INTERFACES="ippp0 ippp1 ippp2" + for i in $INTERFACES; do + isdnctrl savebudgets $i > /var/isdn/saved-budgets/$i + done + + +und dann beim Neustart: + + for f in /var/isdn/saved-budgets/*; do + isdnctrl restorebudgets ${f##*/} `cat $f` + done diff --git a/arch/i386/boot/compressed/misc.c b/arch/i386/boot/compressed/misc.c index 5d9676ecffa9..c5e31878aecc 100644 --- a/arch/i386/boot/compressed/misc.c +++ b/arch/i386/boot/compressed/misc.c @@ -97,6 +97,9 @@ struct screen_info { * This is set up by the setup-routine at boot-time */ #define EXT_MEM_K (*(unsigned short *)0x90002) +#ifndef STANDARD_MEMORY_BIOS_CALL +#define ALT_MEM_K (*(unsigned long *) 0x901e0) +#endif #define DRIVE_INFO (*(struct drive_info *)0x90080) #define SCREEN_INFO (*(struct screen_info *)0x90000) #define RAMDISK_SIZE (*(unsigned short *)0x901F8) @@ -313,7 +316,11 @@ struct { void setup_normal_output_buffer() { +#ifdef STANDARD_MEMORY_BIOS_CALL if (EXT_MEM_K < 1024) error("Less than 2MB of memory.\n"); +#else + if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < 1024) error("Less than 2MB of memory.\n"); +#endif output_data = (char *)0x100000; /* Points to 1M */ } @@ -325,7 +332,11 @@ struct moveparams { void setup_output_buffer_if_we_run_high(struct moveparams *mv) { high_buffer_start = (uch *)(((ulg)&end) + HEAP_SIZE); +#ifdef STANDARD_MEMORY_BIOS_CALL if (EXT_MEM_K < (3*1024)) error("Less than 4MB of memory.\n"); +#else + if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < (3*1024)) error("Less than 4MB of memory.\n"); +#endif mv->low_buffer_start = output_data = (char *)LOW_BUFFER_START; high_loaded = 1; free_mem_end_ptr = (long)high_buffer_start; diff --git a/arch/i386/boot/setup.S b/arch/i386/boot/setup.S index 201a2936b25c..f6afafc39063 100644 --- a/arch/i386/boot/setup.S +++ b/arch/i386/boot/setup.S @@ -232,6 +232,29 @@ loader_panic_mess: loader_ok: ! Get memory size (extended mem, kB) +#ifndef STANDARD_MEMORY_BIOS_CALL + push ebx + + xor ebx,ebx ! preload new memory slot with 0k + mov [0x1e0], ebx + + mov ax,#0xe801 + int 0x15 + jc oldstylemem + + and ebx, #0xffff ! clear sign extend + shl ebx, 6 ! and go from 64k to 1k chunks + mov [0x1e0],ebx ! store extended memory size + + and eax, #0xffff ! clear sign extend + add [0x1e0],eax ! and add lower memory into total size. + + ! and fall into the old memory detection code to populate the + ! compatability slot. + +oldstylemem: + pop ebx +#endif mov ah,#0x88 int 0x15 mov [2],ax diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index 123dd1207786..1aca286047f9 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -62,6 +62,6 @@ trampoline32.s: trampoline32.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Mak $(CPP) -D__SMP__ -traditional $< -o $@ clean: - rm -f trampoline hexify + rm -f trampoline trampoline.hex hexify include $(TOPDIR)/Rules.make diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 084cd21a1f46..923e1aa7ef8a 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -42,15 +42,21 @@ char x86 = 0; /* set by kernel/head.S to 3..6 */ char x86_model = 0; /* set by kernel/head.S */ char x86_mask = 0; /* set by kernel/head.S */ int x86_capability = 0; /* set by kernel/head.S */ +int x86_ext_capability = 0; /* newer CPUs have this */ int fdiv_bug = 0; /* set if Pentium(TM) with FP bug */ int pentium_f00f_bug = 0; /* set if Pentium(TM) with F00F bug */ int have_cpuid = 0; /* set if CPUID instruction works */ +int ext_cpuid = 0; /* if != 0, highest available CPUID value */ -char x86_vendor_id[13] = "unknown"; +char x86_vendor_id[13] = "GenuineIntel";/* default */ -unsigned char Cx86_step = 0; -static const char *Cx86_type[] = { - "unknown", "1.3", "1.4", "1.5", "1.6", "2.4", "2.5", "2.6", "2.7 or 3.7", "4.2" +static char *Cx86_step = "unknown"; /* stepping info for Cyrix CPUs */ + +static unsigned char Cx86_mult = 0; /* clock multiplier for Cyrix CPUs */ + +static const char *x86_clkmult[] = { + "unknown", "1", "1.5", "2", "2.5", "3", "3.5", "4", "4.5", "5", "5.5", + "6", "6.5", "7", "7.5", "8" }; char ignore_irq13 = 0; /* set if exception 16 works */ @@ -89,6 +95,9 @@ extern char empty_zero_page[PAGE_SIZE]; */ #define PARAM empty_zero_page #define EXT_MEM_K (*(unsigned short *) (PARAM+2)) +#ifndef STANDARD_MEMORY_BIOS_CALL +#define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0)) +#endif #ifdef CONFIG_APM #define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+64)) #endif @@ -116,6 +125,9 @@ void setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p) { unsigned long memory_start, memory_end; +#ifndef STANDARD_MEMORY_BIOS_CALL + unsigned long memory_alt_end; +#endif char c = ' ', *to = command_line, *from = COMMAND_LINE; int len = 0; static unsigned char smptrap=0; @@ -134,6 +146,15 @@ void setup_arch(char **cmdline_p, #endif aux_device_present = AUX_DEVICE_INFO; memory_end = (1<<20) + (EXT_MEM_K<<10); +#ifndef STANDARD_MEMORY_BIOS_CALL + memory_alt_end = (1<<20) + (ALT_MEM_K<<10); + if (memory_alt_end > memory_end) { + printk("Memory: sized by int13 0e801h\n"); + memory_end = memory_alt_end; + } + else + printk("Memory: sized by int13 088h\n"); +#endif memory_end &= PAGE_MASK; #ifdef CONFIG_BLK_DEV_RAM rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; @@ -211,95 +232,107 @@ void setup_arch(char **cmdline_p, request_region(0xf0,0x10,"npu"); } -static const char * i486model(unsigned int nr) -{ - static const char *model[] = { - "0","DX","SX","DX/2","4","SX/2","6","DX/2-WB","DX/4","DX/4-WB", - "10","11","12","13","Am5x86-WT","Am5x86-WB" - }; - if (nr < sizeof(model)/sizeof(char *)) - return model[nr]; - return NULL; -} - -static const char * i586model(unsigned int nr) +static const char * IDTmodel(void) +/* Right now IDT has a single CPU model in production: the C6. + * Adjust this when IDT/Centaur comes out with a new CPU model. + * Stepping information is correctly reported in x86_mask. + */ { static const char *model[] = { - "0", "Pentium 60/66","Pentium 75+","OverDrive PODP5V83", - "Pentium MMX", NULL, NULL, "Mobile Pentium 75+", - "Mobile Pentium MMX" + "C6", "C6-3D" }; - if (nr < sizeof(model)/sizeof(char *)) - return model[nr]; - return NULL; + return model[0]; } static const char * Cx86model(void) +/* We know our CPU is a Cyrix now (see bugs.h), so we can use the DIR0/DIR1 + * mechanism to figure out the model, bus clock multiplier and stepping. + * For the newest CPUs (GXm and MXi) we use the Extended CPUID function. + */ { - unsigned char nr6x86 = 0; - static const char *model[] = { - "unknown", "6x86", "6x86L", "6x86MX", "MII" - }; - switch (x86) { - case 5: - nr6x86 = ((x86_capability & (1 << 8)) ? 2 : 1); /* cx8 flag only on 6x86L */ - break; - case 6: - nr6x86 = 3; - break; - default: - nr6x86 = 0; + unsigned char nr6x86 = 0; + unsigned char cx_dir0 = 0; /* Model and bus clock multiplier */ + unsigned char cx_dir1 = 0; /* Stepping info */ + unsigned int flags; + static const char *model[] = { + "unknown", "Cx486", "5x86", "MediaGX", "6x86", "6x86L", "6x86MX", + "M II" + }; + + if (x86_model == -1) { /* is this an old Cx486 without DIR0/DIR1? */ + nr6x86 = 1; /* Cx486 */ + Cx86_mult = 0; /* unknown multiplier */ + } + else { + + /* Get DIR0, DIR1 since all other Cyrix CPUs have them */ + + save_flags(flags); + cli(); + cx_dir0 = getCx86(CX86_DIR0); /* we use the access macros */ + cx_dir1 = getCx86(CX86_DIR1); /* defined in processor.h */ + restore_flags(flags); + + /* Now cook; the recipe is by Channing Corn, from Cyrix. + * We do the same thing for each generation: we work out + * the model, multiplier and stepping. + */ + + if (cx_dir0 < 0x20) { + nr6x86 = 1; /* Cx486 */ + Cx86_mult = 0; /* unknown multiplier */ + sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f); } - - /* We must get the stepping number by reading DIR1 */ - outb(0xff, 0x22); x86_mask=inb(0x23); - - switch (x86_mask) { - case 0x03: - Cx86_step = 1; /* 6x86MX Rev 1.3 */ - break; - case 0x04: - Cx86_step = 2; /* 6x86MX Rev 1.4 */ - break; - case 0x05: - Cx86_step = 3; /* 6x86MX Rev 1.5 */ - break; - case 0x06: - Cx86_step = 4; /* 6x86MX Rev 1.6 */ - break; - case 0x14: - Cx86_step = 5; /* 6x86 Rev 2.4 */ - break; - case 0x15: - Cx86_step = 6; /* 6x86 Rev 2.5 */ - break; - case 0x16: - Cx86_step = 7; /* 6x86 Rev 2.6 */ + + if ((cx_dir0 > 0x20) && (cx_dir0 < 0x30)) { + nr6x86 = 2; /* 5x86 */ + Cx86_mult = ((cx_dir0 & 0x04) ? 5 : 3); /* either 3x or 2x */ + sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f); + } + + if ((cx_dir0 >= 0x30) && (cx_dir0 < 0x38)) { + nr6x86 = ((x86_capability & (1 << 8)) ? 5 : 4); /* 6x86(L) */ + Cx86_mult = ((cx_dir0 & 0x04) ? 5 : 3); /* either 3x or 2x */ + sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 3), cx_dir1 & 0x0f); + } + + if ((cx_dir0 >= 0x40) && (cx_dir0 < 0x50)) { + if (x86 == 4) { /* MediaGX */ + nr6x86 = 3; + Cx86_mult = ((cx_dir0 & 0x01) ? 5 : 7); /* either 3x or 4x */ + switch (cx_dir1 >> 4) { + case (0) : + case (1) : + sprintf(Cx86_step, "2.%d", cx_dir1 & 0x0f); break; - case 0x17: - Cx86_step = 8; /* 6x86 Rev 2.7 or 3.7 */ + case (2) : + sprintf(Cx86_step, "1.%d", cx_dir1 & 0x0f); break; - case 0x22: - Cx86_step = 9; /* 6x86L Rev 4.2 */ + default : break; - default: - Cx86_step = 0; + } + } /* endif MediaGX */ + if (x86 == 5) { /* GXm */ + char GXm_mult[8] = {7,11,7,11,13,15,13,9}; /* 4 to 8 */ + ext_cpuid = 0x80000005; /* available */ + Cx86_mult = GXm_mult[cx_dir0 & 0x0f]; + sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) - 1, cx_dir1 & 0x0f); + } /* endif GXm */ + } + + if ((cx_dir0 >= 0x50) && (cx_dir0 < 0x60)) { + nr6x86 = ((cx_dir1 > 7) ? 7 : 6); /* 6x86Mx or M II */ + Cx86_mult = (cx_dir0 & 0x07) + 2; /* 2 to 5 in 0.5 steps */ + if (((cx_dir1 & 0x0f) > 4) || ((cx_dir1 >> 4) == 2)) cx_dir1 += 0x10; + sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f); } - return model[nr6x86]; -} - -static const char * i686model(unsigned int nr) -{ - static const char *model[] = { - "PPro A-step", "Pentium Pro" - }; - if (nr < sizeof(model)/sizeof(char *)) - return model[nr]; - return NULL; + } + x86_mask = 1; /* we don't use it, but has to be set to something */ + return model[nr6x86]; } struct cpu_model_info { - int x86; + int cpu_x86; char *model_names[16]; }; @@ -308,10 +341,7 @@ static struct cpu_model_info amd_models[] = { { NULL, NULL, NULL, "DX/2", NULL, NULL, NULL, "DX/2-WB", "DX/4", "DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", "Am5x86-WB" }}, { 5, - { "K5/SSA5 (PR-75, PR-90, PR-100)", "K5 (PR-120, PR-133)", - "K5 (PR-166)", "K5 (PR-200)", NULL, NULL, - "K6 (166 - 266)", "K6 (166 - 300)", "K6-2 (200 - 450)", - "K6-3D-Plus (200 - 450)", NULL, NULL, NULL, NULL, NULL, NULL }}, + { "K5/SSA5 (PR-75, PR-90, PR-100)"}}, }; static const char * AMDmodel(void) @@ -319,41 +349,81 @@ static const char * AMDmodel(void) const char *p=NULL; int i; - if (x86_model < 16) + if ((x86_model == 0) || (x86 == 4)) { for (i=0; i= 6)) { len += sprintf(buffer+len, - "stepping\t: %d\n", - CD(x86_mask)); + "stepping\t: %c\n", + x86_mask + 'A'); } - else { /* we have a Cyrix */ + else if (strncmp(x86_vendor_id, "Cy", 2) == 0) { len += sprintf(buffer+len, - "stepping\t: %s\n", - Cx86_type[Cx86_step]); + "stepping\t: %s, core/bus clock ratio: %sx\n", + Cx86_step, x86_clkmult[Cx86_mult]); + } + else { + len += sprintf(buffer+len, + "stepping\t: %d\n", + CD(x86_mask)); } else len += sprintf(buffer+len, @@ -432,6 +513,10 @@ int get_cpuinfo(char * buffer) len += sprintf(buffer+len, " %s", x86_cap_flags[i]); } + else if ( CD(x86_ext_capability) & (1 << i) ) { + len += sprintf(buffer+len, " %s", + x86_ext_cap_flags[i]); + } } len += sprintf(buffer+len, "\nbogomips\t: %lu.%02lu\n", diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 19cbf8a1c373..b7cd6470da9d 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -192,9 +192,9 @@ DO_ERROR(12, SIGBUS, "stack segment", stack_segment, current) DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current) DO_ERROR(18, SIGSEGV, "reserved", reserved, current) -/* signal_return is directly after ret_from_sys_call in entry.S */ +/* divide_error is after ret_from_sys_call in entry.S */ asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call"); -asmlinkage void signal_return(void) __asm__("signal_return"); +asmlinkage void divide_error(void) __asm__("divide_error"); asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) { @@ -210,7 +210,7 @@ asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) */ if ((regs->cs & 3) != 3) { if (regs->eip >= (unsigned long)ret_from_sys_call && - regs->eip < (unsigned long)signal_return) { + regs->eip < (unsigned long)divide_error) { static int moancount = 0; if (moancount < 5) { printk(KERN_INFO "Ignoring GPF attempt from program \"%s\" (pid %d).\n", diff --git a/arch/i386/lib/delay.S b/arch/i386/lib/delay.S index c6079a74efea..9d36420e1955 100644 --- a/arch/i386/lib/delay.S +++ b/arch/i386/lib/delay.S @@ -6,8 +6,12 @@ */ ENTRY(__do_delay) -1: decl %eax - jns 1b + jmp 1f +.align 16 +1: jmp 2f +.align 16 +2: decl %eax + jns 2b ret diff --git a/drivers/block/ide-floppy.c b/drivers/block/ide-floppy.c index d4622cee4ee9..5c1710576898 100644 --- a/drivers/block/ide-floppy.c +++ b/drivers/block/ide-floppy.c @@ -28,6 +28,7 @@ * Issue START cmd only if TEST_UNIT_READY fails * Add CDROMEJECT ioctl * Clean up error messages a bit + * Ver 0.72 Jul 8 98 Limit max sectors only on IOMEGA ZIP 23.D */ #include @@ -1376,7 +1377,8 @@ void idefloppy_setup (ide_drive_t *drive) if (gcw.drq_type == 1) set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags); if (strcmp(drive->id->model, "IOMEGA ZIP 100 ATAPI") == 0 && - strcmp(drive->id->fw_rev, "21.D") == 0) + ((strcmp(drive->id->fw_rev, "21.D") == 0) || + (strcmp(drive->id->fw_rev, "23.D") == 0))) floppy->max_sectors = 64; else floppy->max_sectors = IDEFLOPPY_MAX_SECTORS; diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 235b33e4e93e..77603e60b420 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1,13 +1,17 @@ +#define BLOCKMOVE +#define Z_WAKE static char rcsid[] = -"$Revision: 1.36.3.9 $$Date: 1996/10/07 19:47:13 $"; +"$Revision: 2.1.1.4 $$Date: 1998/06/01 18:01:19 $"; + /* * linux/drivers/char/cyclades.c * * This file contains the driver for the Cyclades Cyclom-Y multiport * serial boards. * - * Maintained by Marcio Saito (marcio@cyclades.com) and - * Randolph Bentson (bentson@grieg.seaslug.org) + * Maintained by Ivan Passos (ivan@cyclades.com), + * Marcio Saito (marcio@cyclades.com) and + * Randolph Bentson (bentson@grieg.seaslug.org). * * For Technical support and installation problems, please send e-mail * to support@cyclades.com. @@ -22,11 +26,212 @@ static char rcsid[] = * This module exports the following rs232 io functions: * int cy_init(void); * int cy_open(struct tty_struct *tty, struct file *filp); + * and the following functions for modularization. + * int init_module(void); + * void cleanup_module(void); * * $Log: cyclades.c,v $ - * Revision 1.36.3.9 1996/10/07 19:47:13 bentson - * add MOD_DEC_USE_COUNT in one return from cy_close (as - * noted by Jon Lewis ) + * Revision 2.1.1.4 1998/06/01 18:01:19 ivan + * data loss prevention revisited (cy_wait_until_sent created); + * MOD_COUNT bug (which caused the usage decrementing not to work + * properly) fixed. + * + * Revision 2.1.1.3b 1998/05/14 10:14:00 ivan + * temporary workaround for losing SIGHUPs in PPP connections + * (Cyclom-Y only). + * + * Revision 2.1.1.3a 1998/03/16 18:01:12 ivan + * cleaned up the data loss fix; + * fixed XON/XOFF handling once more (Cyclades-Z); + * general revision in the driver routines; + * introduction of a mechanism to prevent data loss with slow + * printers, by forcing a delay before closing the port. + * + * Revision 2.1.1.2a 1998/02/17 16:50:00 ivan + * fixed detection/handling of new CD1400 in Ye boards; + * fixed XON/XOFF handling (Cyclades-Z); + * fixed data loss caused by a premature port close; + * introduction of a flag that holds the CD1400 version ID per port + * (used by the CYGETCD1400VER new ioctl). + * + * Revision 2.1.1.1a 1997/12/03 17:31:19 ivan + * Code review for the module cleanup routine; + * fixed RTS and DTR status report for new CD1400's in get_modem_info; + * includes anonymous changes regarding signal_pending. + * + * Revision 2.1 1997/11/01 17:42:41 ivan + * Changes in the driver to support Alpha systems (except 8Zo V_1); + * BREAK fix for the Cyclades-Z boards; + * driver inactivity control by FW implemented; + * introduction of flag that allows driver to take advantage of + * a special CD1400 feature related to HW flow control; + * added support for the CD1400 rev. J (Cyclom-Y boards); + * introduction of ioctls to: + * - control the rtsdtr_inv flag (Cyclom-Y); + * - control the rflow flag (Cyclom-Y); + * - adjust the polling interval (Cyclades-Z); + * + * Revision 1.36.4.33 1997/06/27 19:00:00 ivan + * Fixes related to kernel version conditional + * compilation. + * + * Revision 1.36.4.32 1997/06/14 19:30:00 ivan + * Compatibility issues between kernels 2.0.x and + * 2.1.x (mainly related to clear_bit function). + * + * Revision 1.36.4.31 1997/06/03 15:30:00 ivan + * Changes to define the memory window according to the + * board type. + * + * Revision 1.36.4.30 1997/05/16 15:30:00 daniel + * Changes to suport new cycladesZ boards. + * + * Revision 1.36.4.29 1997/05/12 11:30:00 daniel + * Merge of Bentson's and Daniel's version 1.36.4.28. + * Corrects bug in cy_detect_pci: check if there are more + * ports than the number of static structs allocated. + * Warning message during initialization if this driver is + * used with the new generation of cycladesZ boards. Those + * will be supported only in next release of the driver. + * Corrects bug in cy_detect_pci and cy_detect_isa that + * returned wrong number of VALID boards, when a cyclomY + * was found with no serial modules connected. + * Changes to use current (2.1.x) kernel subroutine names + * and created macros for compilation with 2.0.x kernel, + * instead of the other way around. + * + * Revision 1.36.4.28 1997/05/?? ??:00:00 bentson + * Change queue_task_irq_off to queue_task_irq. + * The inline function queue_task_irq_off (tqueue.h) + * was removed from latest releases of 2.1.x kernel. + * Use of macro __initfunc to mark the initialization + * routines, so memory can be reused. + * Also incorporate implementation of critical region + * in function cleanup_module() created by anonymous + * linuxer. + * + * Revision 1.36.4.28 1997/04/25 16:00:00 daniel + * Change to support new firmware that solves DCD problem: + * application could fail to receive SIGHUP signal when DCD + * varying too fast. + * + * Revision 1.36.4.27 1997/03/26 10:30:00 daniel + * Changed for suport linux versions 2.1.X. + * Backward compatible with linux versions 2.0.X. + * Corrected illegal use of filler field in + * CH_CTRL struct. + * Deleted some debug messages. + * + * Revision 1.36.4.26 1997/02/27 12:00:00 daniel + * Included check for NULL tty pointer in cyz_poll. + * + * Revision 1.36.4.25 1997/02/26 16:28:30 bentson + * Bill Foster at Blarg! Online services noticed that + * some of the switch elements of -Z modem control + * lacked a closing "break;" + * + * Revision 1.36.4.24 1997/02/24 11:00:00 daniel + * Changed low water threshold for buffer xmit_buf + * + * Revision 1.36.4.23 1996/12/02 21:50:16 bentson + * Marcio provided fix to modem status fetch for -Z + * + * Revision 1.36.4.22 1996/10/28 22:41:17 bentson + * improve mapping of -Z control page (thanks to Steve + * Price for help on this) + * + * Revision 1.36.4.21 1996/09/10 17:00:10 bentson + * shift from cpu-bound to memcopy in cyz_polling operation + * + * Revision 1.36.4.20 1996/09/09 18:30:32 Bentson + * Added support to set and report higher speeds. + * + * Revision 1.36.4.19c 1996/08/09 10:00:00 Marcio Saito + * Some fixes in the HW flow control for the BETA release. + * Don't try to register the IRQ. + * + * Revision 1.36.4.19 1996/08/08 16:23:18 Bentson + * make sure "cyc" appears in all kernel messages; all soft interrupts + * handled by same routine; recognize out-of-band reception; comment + * out some diagnostic messages; leave RTS/CTS flow control to hardware; + * fix race condition in -Z buffer management; only -Y needs to explictly + * flush chars; tidy up some startup messages; + * + * Revision 1.36.4.18 1996/07/25 18:57:31 bentson + * shift MOD_INC_USE_COUNT location to match + * serial.c; purge some diagnostic messages; + * + * Revision 1.36.4.17 1996/07/25 18:01:08 bentson + * enable modem status messages and fetch & process them; note + * time of last activity type for each port; set_line_char now + * supports more than line 0 and treats 0 baud correctly; + * get_modem_info senses rs_status; + * + * Revision 1.36.4.16 1996/07/20 08:43:15 bentson + * barely works--now's time to turn on + * more features 'til it breaks + * + * Revision 1.36.4.15 1996/07/19 22:30:06 bentson + * check more -Z board status; shorten boot message + * + * Revision 1.36.4.14 1996/07/19 22:20:37 bentson + * fix reference to ch_ctrl in startup; verify return + * values from cyz_issue_cmd and cyz_update_channel; + * more stuff to get modem control correct; + * + * Revision 1.36.4.13 1996/07/11 19:53:33 bentson + * more -Z stuff folded in; re-order changes to put -Z stuff + * after -Y stuff (to make changes clearer) + * + * Revision 1.36.4.12 1996/07/11 15:40:55 bentson + * Add code to poll Cyclades-Z. Add code to get & set RS-232 control. + * Add code to send break. Clear firmware ID word at startup (so + * that other code won't talk to inactive board). + * + * Revision 1.36.4.11 1996/07/09 05:28:29 bentson + * add code for -Z in set_line_char + * + * Revision 1.36.4.10 1996/07/08 19:28:37 bentson + * fold more -Z stuff (or in some cases, error messages) + * into driver; add text to "don't know what to do" messages. + * + * Revision 1.36.4.9 1996/07/08 18:38:38 bentson + * moved compile-time flags near top of file; cosmetic changes + * to narrow text (to allow 2-up printing); changed many declarations + * to "static" to limit external symbols; shuffled code order to + * coalesce -Y and -Z specific code, also to put internal functions + * in order of tty_driver structure; added code to recognize -Z + * ports (and for moment, do nothing or report error); add cy_startup + * to parse boot command line for extra base addresses for ISA probes; + * + * Revision 1.36.4.8 1996/06/25 17:40:19 bentson + * reorder some code, fix types of some vars (int vs. long), + * add cy_setup to support user declared ISA addresses + * + * Revision 1.36.4.7 1996/06/21 23:06:18 bentson + * dump ioctl based firmware load (it's now a user level + * program); ensure uninitialzed ports cannot be used + * + * Revision 1.36.4.6 1996/06/20 23:17:19 bentson + * rename vars and restructure some code + * + * Revision 1.36.4.5 1996/06/14 15:09:44 bentson + * get right status back after boot load + * + * Revision 1.36.4.4 1996/06/13 19:51:44 bentson + * successfully loads firmware + * + * Revision 1.36.4.3 1996/06/13 06:08:33 bentson + * add more of the code for the boot/load ioctls + * + * Revision 1.36.4.2 1996/06/11 21:00:51 bentson + * start to add Z functionality--starting with ioctl + * for loading firmware + * + * Revision 1.36.4.1 1996/06/10 18:03:02 bentson + * added code to recognize Z/PCI card at initialization; report + * presence, but card is not initialized (because firmware needs + * to be loaded) * * Revision 1.36.3.8 1996/06/07 16:29:00 bentson * starting minor number at zero; added missing verify_area @@ -261,6 +466,63 @@ static char rcsid[] = * */ +/* If you need to install more boards than NR_CARDS, change the constant + in the definition below. No other change is necessary to support up to + eight boards. Beyond that you'll have to extend cy_isa_addresses. */ + +#define NR_CARDS 4 + +/* + If the total number of ports is larger than NR_PORTS, change this + constant in the definition below. No other change is necessary to + support more boards/ports. */ + +#define NR_PORTS 128 + +#define ZE_V1_NPORTS 64 +#define ZO_V1 0 +#define ZO_V2 1 +#define ZE_V1 2 + +#define SERIAL_PARANOIA_CHECK +#undef CY_DEBUG_OPEN +#undef CY_DEBUG_THROTTLE +#undef CY_DEBUG_OTHER +#undef CY_DEBUG_IO +#undef CY_DEBUG_COUNT +#undef CY_DEBUG_DTR +#undef CY_DEBUG_WAIT_UNTIL_SENT +#undef CY_16Y_HACK +#undef CY_ENABLE_MONITORING +#undef CY_PCI_DEBUG + +#if 0 +#define PAUSE __asm__("nop"); +#else +#define PAUSE ; +#endif + +#define cy_min(a,b) (((a)<(b))?(a):(b)) + +#if 0 +/******** + * For the next two macros, it is assumed that the buffer size is a + * power of 2 + ********/ + +#define CHARS_IN_BUF(buf_ctrl) \ + ((cy_readl(&buf_ctrl->rx_put) - \ + cy_readl(&buf_ctrl->rx_get) + \ + cy_readl(&buf_ctrl->rx_bufsize)) & \ + (cy_readl(&buf_ctrl->rx_bufsize) - 1)) + +#define SPACE_IN_BUF(buf_ctrl) \ + ((cy_readl(&buf_ctrl->tx_get) - \ + cy_readl(&buf_ctrl->tx_put) + \ + cy_readl(&buf_ctrl->tx_bufsize) - 1) & \ + (cy_readl(&buf_ctrl->tx_bufsize) - 1)) +#endif + #include #include @@ -289,31 +551,38 @@ static char rcsid[] = #include #include -#define small_delay(x) for(j=0;j -#define SERIAL_PARANOIA_CHECK -#undef SERIAL_DEBUG_OPEN -#undef SERIAL_DEBUG_THROTTLE -#undef SERIAL_DEBUG_OTHER -#undef SERIAL_DEBUG_IO -#undef SERIAL_DEBUG_COUNT -#undef SERIAL_DEBUG_DTR -#undef CYCLOM_16Y_HACK -#undef CYCLOM_ENABLE_MONITORING +#define __initfunc(__arginit) __arginit +#define copy_from_user memcpy_fromfs +#define copy_to_user memcpy_tofs +#define cy_get_user get_fs_long +#define cy_put_user put_fs_long +#define ioremap vremap #ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif -#define WAKEUP_CHARS 256 +#define IS_CYC_Z(card) ((card).num_chips == 1) + +#define Z_FPGA_CHECK(card) \ + ((cy_readl(&((struct RUNTIME_9060 *) \ + ((card).ctl_addr))->init_ctrl) & (1<<17)) != 0) + +#define ISZLOADED(card) (((ZO_V1==cy_readl(&((struct RUNTIME_9060 *) \ + ((card).ctl_addr))->mail_box_0)) || \ + Z_FPGA_CHECK(card)) && \ + (ZFIRM_ID==cy_readl(&((struct FIRM_ID *) \ + ((card).base_addr+ID_ADDRESS))->signature))) + +#define WAKEUP_CHARS (SERIAL_XMIT_SIZE-256) #define STD_COM_FLAGS (0) #define SERIAL_TYPE_NORMAL 1 #define SERIAL_TYPE_CALLOUT 2 - DECLARE_TASK_QUEUE(tq_cyclades); struct tty_driver cy_serial_driver, cy_callout_driver; @@ -321,75 +590,42 @@ struct tty_driver cy_serial_driver, cy_callout_driver; static volatile int cy_irq_triggered; static volatile int cy_triggered; static int cy_wild_int_mask; -static unsigned char *intr_base_addr; +static volatile ucchar *intr_base_addr; -/* This is the address lockup table. The driver will probe for Cyclom-Y/ISA - boards at all addresses in here. If you want the driver to probe addresses - in a different address, add it to this table. - If the driver is probing some other board and causing problems, remove the - address from this table. */ +/* This is the address lookup table. The driver will probe for + Cyclom-Y/ISA boards at all addresses in here. If you want the + driver to probe addresses at a different address, add it to + this table. If the driver is probing some other board and + causing problems, remove the offending address from this table. + The cy_setup function extracts additional addresses from the + boot options line. The form is "cyclades=address,address..." +*/ static unsigned char *cy_isa_addresses[] = { - (unsigned char *) 0xD0000, - (unsigned char *) 0xD2000, - (unsigned char *) 0xD4000, - (unsigned char *) 0xD6000, - (unsigned char *) 0xD8000, - (unsigned char *) 0xDA000, - (unsigned char *) 0xDC000, - (unsigned char *) 0xDE000, + (unsigned char *) 0xD0000, + (unsigned char *) 0xD2000, + (unsigned char *) 0xD4000, + (unsigned char *) 0xD6000, + (unsigned char *) 0xD8000, + (unsigned char *) 0xDA000, + (unsigned char *) 0xDC000, + (unsigned char *) 0xDE000, + 0,0,0,0,0,0,0,0 }; -#define NR_ISA_ADDRESSES (sizeof(cy_isa_addresses)/sizeof(unsigned char *)) +#define NR_ISA_ADDRS (sizeof(cy_isa_addresses)/sizeof(unsigned char*)) /* This is the per-card data structure containing address, irq, number of - channels, etc. This driver supports a maximum of NR_CARDS cards. If - you need to install more boards, change this constant in the definition - below. No other change is necessary to support more boards. */ - -#define NR_CARDS 4 - + channels, etc. This driver supports a maximum of NR_CARDS cards. +*/ static struct cyclades_card cy_card[NR_CARDS]; /* This is the per-channel data structure containing pointers, flags - and variables for the port. This driver supports a maximum of NR_PORTS. - If the total number of ports is larger than NR_PORTS, change this - constant in the definition below. No other change is necessary to - support more boards/ports. */ - -#define NR_PORTS 64 - + and variables for the port. This driver supports a maximum of NR_PORTS. +*/ static struct cyclades_port cy_port[NR_PORTS]; -/* The Cyclom-Ye has placed the sequential chips in non-sequential - * address order. This look-up table overcomes that problem. - */ -static int cy_chip_offset [] = - { 0x0000, - 0x0400, - 0x0800, - 0x0C00, - 0x0200, - 0x0600, - 0x0A00, - 0x0E00 - }; - -/* PCI related definitions */ - -static unsigned short cy_pci_nboard = 0; -static unsigned short cy_isa_nboard = 0; -static unsigned short cy_nboard = 0; -static unsigned short cy_pci_dev_id[] = { - PCI_DEVICE_ID_CYCLOM_Y_Lo,/* PCI below 1Mb */ - PCI_DEVICE_ID_CYCLOM_Y_Hi,/* PCI above 1Mb */ - 0 /* end of table */ - }; - -int cy_detect_isa(void); -int cy_detect_pci(void); - -static int cy_next_channel = 0; /* next minor available */ +static int cy_next_channel = 0; /* next minor available */ static int serial_refcount; @@ -400,17 +636,18 @@ static struct termios *serial_termios_locked[NR_PORTS]; /* This is the per-irq data structure, it maps an irq to the corresponding card */ -struct cyclades_card *IRQ_cards[16]; +static struct cyclades_card *IRQ_cards[16]; /* * tmp_buf is used as a temporary buffer by serial_write. We need to - * lock it in case the memcpy_fromfs blocks while swapping in a page, + * lock it in case the copy_from_user blocks while swapping in a page, * and some other program tries to do a serial write at the same time. * Since the lock will only come under contention when the system is * swapping and available memory is low, it makes sense to share one * buffer across all the serial ports, since it significantly saves - * memory if large numbers of serial ports are open. + * memory if large numbers of serial ports are open. This buffer is + * allocated when the first cy_open occurs. */ static unsigned char *tmp_buf = 0; static struct semaphore tmp_buf_sem = MUTEX; @@ -419,83 +656,157 @@ static struct semaphore tmp_buf_sem = MUTEX; * This is used to look up the divisor speeds and the timeouts * We're normally limited to 15 distinct baud rates. The extra * are accessed via settings in info->flags. - * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - * HI VHI + * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + * HI VHI + * 20 */ static int baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, - 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800,115200,150000, - 0}; + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, + 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800,115200,150000, + 230400, 0}; + +static char baud_co_25[] = { /* 25 MHz clock option table */ + /* value => 00 01 02 03 04 */ + /* divide by 8 32 128 512 2048 */ + 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, + 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static char baud_bpr_25[] = { /* 25 MHz baud rate period table */ + 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, + 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15}; + +static char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */ + /* value => 00 01 02 03 04 */ + /* divide by 8 32 128 512 2048 */ + 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, + 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00}; + +static char baud_bpr_60[] = { /* 60 MHz baud rate period table (CD1400 J) */ + 0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62, + 0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32, + 0x21}; -static char baud_co[] = { /* 25 MHz clock option table */ - /* value => 00 01 02 03 04 */ - /* divide by 8 32 128 512 2048 */ - 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, - 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static char baud_cor3[] = { /* receive threshold */ + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, + 0x07}; -static char baud_bpr[] = { /* 25 MHz baud rate period table */ - 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, - 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15}; +/* + * The Cyclades driver implements HW flow control as any serial driver. + * The cyclades_port structure member rflow and the vector rflow_thr + * allows us to take advantage of a special feature in the CD1400 to avoid + * data loss even when the system interrupt latency is too high. These flags + * are to be used only with very special applications. Setting these flags + * requires the use of a special cable (DTR and RTS reversed). In the new + * CD1400-based boards (rev. 6.00 or later), there is no need for special + * cables. + */ -static char baud_cor3[] = { /* receive threshold */ - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07}; +static char rflow_thr[] = { /* rflow threshold */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a}; +/* The Cyclom-Ye has placed the sequential chips in non-sequential + * address order. This look-up table overcomes that problem. + */ +static int cy_chip_offset [] = + { 0x0000, + 0x0400, + 0x0800, + 0x0C00, + 0x0200, + 0x0600, + 0x0A00, + 0x0E00 + }; +/* PCI related definitions */ -static void shutdown(struct cyclades_port *); -static int startup (struct cyclades_port *); -static void cy_throttle(struct tty_struct *); -static void cy_unthrottle(struct tty_struct *); -static void config_setup(struct cyclades_port *); -#ifdef CYCLOM_SHOW_STATUS +static unsigned short cy_pci_nboard = 0; +static unsigned short cy_isa_nboard = 0; +static unsigned short cy_nboard = 0; +static unsigned short cy_pci_dev_id[] = { + PCI_DEVICE_ID_CYCLOM_Y_Lo,/* PCI below 1Mb */ + PCI_DEVICE_ID_CYCLOM_Y_Hi,/* PCI above 1Mb */ + PCI_DEVICE_ID_CYCLOM_Z_Lo,/* PCI below 1Mb */ + PCI_DEVICE_ID_CYCLOM_Z_Hi,/* PCI above 1Mb */ + 0 /* end of table */ + }; + + +static void cy_start(struct tty_struct *); +static void set_line_char(struct cyclades_port *); +static void cy_probe(int, void *, struct pt_regs *); +static void cyz_poll(unsigned long); +#ifdef CY_SHOW_STATUS static void show_status(int); #endif +/* The Cyclades-Z polling cycle is defined by this variable */ +static long cyz_polling_cycle = CZ_DEF_POLL; + +static int cyz_timeron = 0; +static struct timer_list +cyz_timerlist = { + NULL, NULL, 0, 0, cyz_poll +}; + +/************************************************** +error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long)); +copy_to_user (to, from, count); +*************************************************************** +error = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned long *)); +copy_from_user(to, from, count); +**************************************************/ + static inline int serial_paranoia_check(struct cyclades_port *info, - kdev_t device, const char *routine) + kdev_t device, const char *routine) { #ifdef SERIAL_PARANOIA_CHECK static const char *badmagic = - "Warning: bad magic number for serial struct (%s) in %s\n"; + "cyc Warning: bad magic number for serial struct (%s) in %s\n"; static const char *badinfo = - "Warning: null cyclades_port for (%s) in %s\n"; + "cyc Warning: null cyclades_port for (%s) in %s\n"; static const char *badrange = - "Warning: cyclades_port out of range for (%s) in %s\n"; + "cyc Warning: cyclades_port out of range for (%s) in %s\n"; if (!info) { - printk(badinfo, kdevname(device), routine); - return 1; + printk(badinfo, kdevname(device), routine); + return 1; } if( (long)info < (long)(&cy_port[0]) || (long)(&cy_port[NR_PORTS]) < (long)info ){ - printk(badrange, kdevname(device), routine); - return 1; + printk(badrange, kdevname(device), routine); + return 1; } if (info->magic != CYCLADES_MAGIC) { - printk(badmagic, kdevname(device), routine); - return 1; + printk(badmagic, kdevname(device), routine); + return 1; } #endif - return 0; + return 0; } /* serial_paranoia_check */ + /* The following diagnostic routines allow the driver to spew information on the screen, even (especially!) during interrupts. */ -void +static void SP(char *data){ unsigned long flags; save_flags(flags); cli(); console_print(data); restore_flags(flags); -} -void +}/* SP */ + +static void CP(char data){ unsigned long flags; char scrn[2]; @@ -506,111 +817,17 @@ CP(char data){ restore_flags(flags); }/* CP */ -void CP1(int data) { (data<10)? CP(data+'0'): CP(data+'A'-10); }/* CP1 */ -void CP2(int data) { CP1((data>>4) & 0x0f); CP1( data & 0x0f); }/* CP2 */ -void CP4(int data) { CP2((data>>8) & 0xff); CP2(data & 0xff); }/* CP4 */ -void CP8(long data) { CP4((data>>16) & 0xffff); CP4(data & 0xffff); }/* CP8 */ - -/* This routine waits up to 1000 micro-seconds for the previous - command to the Cirrus chip to complete and then issues the - new command. An error is returned if the previous command - didn't finish within the time limit. - */ -u_short -write_cy_cmd(u_char *base_addr, u_char cmd, int index) -{ - unsigned long flags; - volatile int i; - - save_flags(flags); cli(); - /* Check to see that the previous command has completed */ - for(i = 0 ; i < 100 ; i++){ - if (base_addr[CyCCR<driver_data; - unsigned char *base_addr; - int chip,channel,index; - unsigned long flags; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_stop ttyC%d\n", info->line); /* */ -#endif - - if (serial_paranoia_check(info, tty->device, "cy_stop")) - return; - - cinfo = &cy_card[info->card]; - index = cinfo->bus_index; - channel = info->line - cinfo->first_line; - chip = channel>>2; - channel &= 0x03; - base_addr = (unsigned char*) - (cy_card[info->card].base_addr + (cy_chip_offset[chip]<driver_data; - unsigned char *base_addr; - int chip,channel,index; - unsigned long flags; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_start ttyC%d\n", info->line); /* */ +static void CP4(int data) + { (data<10)? CP(data+'0'): CP(data+'A'-10); }/* CP4 */ +static void CP8(int data) + { CP4((data>>4) & 0x0f); CP4( data & 0x0f); }/* CP8 */ +#if 0 +static void CP16(int data) + { CP8((data>>8) & 0xff); CP8(data & 0xff); }/* CP16 */ +static void CP32(long data) + { CP16((data>>16) & 0xffff); CP16(data & 0xffff); }/* CP32 */ #endif - if (serial_paranoia_check(info, tty->device, "cy_start")) - return; - - cinfo = &cy_card[info->card]; - index = cinfo->bus_index; - channel = info->line - cinfo->first_line; - chip = channel>>2; - channel &= 0x03; - base_addr = (unsigned char*) - (cy_card[info->card].base_addr + (cy_chip_offset[chip]<event |= 1 << event; /* remember what kind of event and who */ - queue_task_irq_off(&info->tqueue, &tq_cyclades); /* it belongs to */ + queue_task(&info->tqueue, &tq_cyclades); /* it belongs to */ mark_bh(CYCLADES_BH); /* then trigger event */ } /* cy_sched_event */ -static int probe_ready; - /* - * This interrupt routine is used - * while we are probing for submarines. + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * cy#/_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using cy_sched_event(), and they get done here. + * + * This is done through one level of indirection--the task queue. + * When a hardware interrupt service routine wants service by the + * driver's bottom half, it enqueues the appropriate tq_struct (one + * per port) to the tq_cyclades work queue and sets a request flag + * via mark_bh for processing that queue. When the time is right, + * do_cyclades_bh is called (because of the mark_bh) and it requests + * that the work queue be processed. + * + * Although this may seem unwieldy, it gives the system a way to + * pass an argument (in this case the pointer to the cyclades_port + * structure) to the bottom half of the driver. Previous kernels + * had to poll every port to see if that port needed servicing. */ static void -cy_probe(int irq, void *dev_id, struct pt_regs *regs) +do_cyclades_bh(void) { - int save_xir, save_car; - int index = 0; /* probing interrupts is only for ISA */ + run_task_queue(&tq_cyclades); +} /* do_cyclades_bh */ - if (!probe_ready) { - *(intr_base_addr + (Cy_ClrIntr<tty; + if (!tty) return; + + if (clear_bit(Cy_EVENT_HANGUP, &info->event)) { + tty_hangup(info->tty); + wake_up_interruptible(&info->open_wait); + info->flags &= ~(ASYNC_NORMAL_ACTIVE| + ASYNC_CALLOUT_ACTIVE); + } + if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { + wake_up_interruptible(&info->open_wait); + } + if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { + if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup){ + (tty->ldisc.write_wakeup)(tty); + } + wake_up_interruptible(&tty->write_wait); } +#ifdef Z_WAKE + if (clear_bit(Cy_EVENT_SHUTDOWN_WAKEUP, &info->event)) { + wake_up_interruptible(&info->shutdown_wait); + } +#endif +} /* do_softint */ - cy_irq_triggered = irq; - cy_triggered |= 1 << irq; - if(intr_base_addr[CySVRR<= jiffies) + ; + + cy_triggered = 0; /* Reset after letting things settle */ + + timeout = jiffies+(HZ/10); + while (timeout >= jiffies) + ; + + for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { + if ((cy_triggered & (1 << i)) && + (irq_lines & (1 << i))) { + wild_interrupts |= mask; + } + } + free_all_interrupts(irq_lines); + restore_flags(flags); + return wild_interrupts; +} /* check_wild_interrupts */ + +/* + * This routine is called by do_auto_irq(); it attempts to determine + * which interrupt a serial port is configured to use. It is not + * fool-proof, but it works a large part of the time. + */ +static int +get_auto_irq(volatile ucchar *address) +{ + unsigned long timeout; + volatile ucchar *base_addr; + int index; + unsigned long flags; + + index = 0; /* IRQ probing is only for ISA */ + base_addr = address; + intr_base_addr = address; + + /* + * Enable interrupts and see who answers + */ + cy_irq_triggered = 0; + save_flags(flags); cli(); + cy_writeb((u_long)base_addr+(CyCAR<= jiffies) { + if (cy_irq_triggered) + break; + } + probe_ready = 0; + return(cy_irq_triggered); +} /* get_auto_irq */ + +/* + * Calls get_auto_irq() multiple times, to make sure we don't get + * faked out by random interrupts + */ +static int +do_auto_irq(volatile ucchar *address) +{ + int irq_lines = 0; + int irq_try_1 = 0, irq_try_2 = 0; + int retries; + unsigned long flags; + + /* Turn on interrupts (they may be off) */ + save_flags(flags); sti(); + + probe_ready = 0; + + cy_wild_int_mask = check_wild_interrupts(); + + irq_lines = grab_all_interrupts(cy_wild_int_mask); + + for (retries = 0; retries < 5; retries++) { + if (!irq_try_1) + irq_try_1 = get_auto_irq(address); + if (!irq_try_2) + irq_try_2 = get_auto_irq(address); + if (irq_try_1 && irq_try_2) { + if (irq_try_1 == irq_try_2) + break; + irq_try_1 = irq_try_2 = 0; + } + } + restore_flags(flags); + free_all_interrupts(irq_lines); + return (irq_try_1 == irq_try_2) ? irq_try_1 : 0; +} /* do_auto_irq */ + + +/* + * This interrupt routine is used + * while we are probing for submarines. + */ +static void +cy_probe(int irq, void *dev_id, struct pt_regs *regs) +{ + int save_xir, save_car; + int index = 0; /* probing interrupts is only for ISA */ + + if (!probe_ready) { + cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<base_addr; index = cinfo->bus_index; + /* This loop checks all chips in the card. Make a note whenever _any_ chip had some work to do, as this is considered an indication that there will be more to do. Only when no chip @@ -702,224 +1178,255 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) do{ had_work = 0; for ( chip = 0 ; chip < cinfo->num_chips ; chip ++) { - base_addr = (unsigned char *) - (cinfo->base_addr + (cy_chip_offset[chip]<base_addr + (cy_chip_offset[chip]<first_line; info = &cy_port[i]; info->last_active = jiffies; - save_car = base_addr[CyCAR<tty == 0){ - j = (base_addr[CyRIVR<tty; - j = (base_addr[CyRIVR<ignore_status_mask){ continue; } if (tty->flip.count < TTY_FLIPBUF_SIZE){ - tty->flip.count++; - if (data & info->read_status_mask){ - if(data & CyBREAK){ - *tty->flip.flag_buf_ptr++ = - TTY_BREAK; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<flags & ASYNC_SAK){ - do_SAK(tty); - } - }else if(data & CyFRAME){ - *tty->flip.flag_buf_ptr++ = - TTY_FRAME; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<flip.flag_buf_ptr++ = - TTY_PARITY; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<flip.flag_buf_ptr++ = - TTY_OVERRUN; - *tty->flip.char_buf_ptr++ = 0; - /* If the flip buffer itself is - overflowing, we still loose - the next incoming character. - */ - if(tty->flip.count < TTY_FLIPBUF_SIZE){ - tty->flip.count++; - *tty->flip.flag_buf_ptr++ = - TTY_NORMAL; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<flip.flag_buf_ptr++ = 0; - *tty->flip.char_buf_ptr++ = 0; - } - }else{ - *tty->flip.flag_buf_ptr++ = 0; - *tty->flip.char_buf_ptr++ = 0; - } + tty->flip.count++; + if (data & info->read_status_mask){ + if(data & CyBREAK){ + *tty->flip.flag_buf_ptr++ = + TTY_BREAK; + *tty->flip.char_buf_ptr++ = + cy_readb(base_addr+(CyRDSR<flags & ASYNC_SAK){ + do_SAK(tty); + } + }else if(data & CyFRAME){ + *tty->flip.flag_buf_ptr++ = + TTY_FRAME; + *tty->flip.char_buf_ptr++ = + cy_readb(base_addr+(CyRDSR<flip.flag_buf_ptr++ = + TTY_PARITY; + *tty->flip.char_buf_ptr++ = + cy_readb(base_addr+(CyRDSR<flip.flag_buf_ptr++ = + TTY_OVERRUN; + *tty->flip.char_buf_ptr++ = 0; + /* If the flip buffer itself is + overflowing, we still loose + the next incoming character. + */ + if(tty->flip.count + < TTY_FLIPBUF_SIZE){ + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = + TTY_NORMAL; + *tty->flip.char_buf_ptr++ = + cy_readb(base_addr+(CyRDSR<flip.flag_buf_ptr++ = 0; + *tty->flip.char_buf_ptr++ = 0; + } + }else{ + *tty->flip.flag_buf_ptr++ = 0; + *tty->flip.char_buf_ptr++ = 0; + } }else{ - /* there was a software buffer overrun - and nothing could be done about it!!! */ + /* there was a software buffer + overrun and nothing could be + done about it!!! */ } } else { /* normal character reception */ - /* load # characters available from the chip */ - char_count = base_addr[CyRDCR<mon.int_count; - info->mon.char_count += char_count; - if (char_count > info->mon.char_max) - info->mon.char_max = char_count; - info->mon.char_last = char_count; + /* load # chars available from the chip */ + char_count = cy_readb(base_addr+(CyRDCR<mon.int_count; + info->mon.char_count += char_count; + if (char_count > info->mon.char_max) + info->mon.char_max = char_count; + info->mon.char_last = char_count; #endif while(char_count--){ - if (tty->flip.count >= TTY_FLIPBUF_SIZE){ + if (tty->flip.count >= TTY_FLIPBUF_SIZE){ break; } - tty->flip.count++; - data = base_addr[CyRDSR<flip.flag_buf_ptr++ = TTY_NORMAL; - *tty->flip.char_buf_ptr++ = data; -#ifdef CYCLOM_16Y_HACK - udelay(10L); + tty->flip.count++; + data = cy_readb(base_addr+(CyRDSR<flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = data; +#ifdef CY_16Y_HACK + udelay(10L); #endif } } - queue_task_irq_off(&tty->flip.tqueue, &tq_timer); + queue_task(&tty->flip.tqueue, &tq_timer); } /* end of service */ - base_addr[CyRIR<first_line; - save_car = base_addr[CyCAR<last_active = jiffies; if(info->tty == 0){ - base_addr[CySRER<xmit_fifo_size; if(info->x_char) { /* send special char */ outch = info->x_char; - base_addr[CyTDR<x_char = 0; } - if (info->x_break){ - /* The Cirrus chip requires the "Embedded Transmit - Commands" of start break, delay, and end break - sequences to be sent. The duration of the - break is given in TICs, which runs at HZ - (typically 100) and the PPR runs at 200 Hz, - so the delay is duration * 200/HZ, and thus a - break can run from 1/100 sec to about 5/4 sec. - */ - base_addr[CyTDR<x_break*200/HZ; - base_addr[CyTDR<x_break = 0; - } + if (info->x_break){ + /* The Cirrus chip requires the "Embedded + Transmit Commands" of start break, delay, + and end break sequences to be sent. The + duration of the break is given in TICs, + which runs at HZ (typically 100) and the + PPR runs at 200 Hz, so the delay is + duration * 200/HZ, and thus a break can + run from 1/100 sec to about 5/4 sec. + For CD1400 J or later, replace the 200 Hz + by 500 Hz. + */ + /* start break */ + cy_writeb((u_long)base_addr + (CyTDR<chip_rev >= CD1400_REV_J ) { + /* It is a CD1400 rev. J or later */ + cy_writeb((u_long)base_addr + (CyTDR<x_break*500/HZ); + } else { + cy_writeb((u_long)base_addr + (CyTDR<x_break*200/HZ); + } + /* finish break */ + cy_writeb((u_long)base_addr + (CyTDR<x_break = 0; + } + if (!info->xmit_cnt){ + cy_writeb((u_long)base_addr+(CySRER<xmit_buf == 0){ + cy_writeb((u_long)base_addr+(CySRER<tty->stopped || info->tty->hw_stopped){ + cy_writeb((u_long)base_addr+(CySRER< 0){ if (!info->xmit_cnt){ - base_addr[CySRER<xmit_buf == 0){ - base_addr[CySRER<tty->stopped || info->tty->hw_stopped){ - base_addr[CySRER<xmit_buf[info->xmit_tail]; if( outch ){ - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) - & (PAGE_SIZE - 1); - base_addr[CyTDR<xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) + & (SERIAL_XMIT_SIZE - 1); + cy_writeb((u_long)base_addr+(CyTDR< 1){ - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) - & (PAGE_SIZE - 1); - base_addr[CyTDR<xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) + & (SERIAL_XMIT_SIZE - 1); + cy_writeb((u_long)base_addr+(CyTDR<xmit_cnt < WAKEUP_CHARS) { cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); } - txend: /* end of service */ - base_addr[CyTIR<first_line]; + info = &cy_port[channel + chip * 4 + + cinfo->first_line]; info->last_active = jiffies; - save_car = base_addr[CyCAR<tty == 0){ /* nowhere to put the data, ignore it */ + if(info->tty == 0){/* no place for data, ignore it*/ ; }else{ if((mdm_change & CyDCD) && (info->flags & ASYNC_CHECK_CD)){ if(mdm_status & CyDCD){ - cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP); - }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE) - &&(info->flags & ASYNC_CALLOUT_NOHUP))){ - cy_sched_event(info, Cy_EVENT_HANGUP); + cy_sched_event(info, + Cy_EVENT_OPEN_WAKEUP); + }else if(!((info->flags + & ASYNC_CALLOUT_ACTIVE) + &&(info->flags + & ASYNC_CALLOUT_NOHUP))){ + cy_sched_event(info, + Cy_EVENT_HANGUP); } } if((mdm_change & CyCTS) && (info->flags & ASYNC_CTS_FLOW)){ if(info->tty->hw_stopped){ if(mdm_status & CyCTS){ - /* !!! cy_start isn't used because... */ + /* cy_start isn't used + because... !!! */ info->tty->hw_stopped = 0; - base_addr[CySRER<tty->hw_stopped = 1; - base_addr[CySRER<base_addr + ID_ADDRESS); + if (!ISZLOADED(*cinfo)){ + return (-1); + } + zfw_ctrl = (struct ZFW_CTRL *) + (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &zfw_ctrl->board_ctrl; + + loc_doorbell = cy_readl(&((struct RUNTIME_9060 *) + (cinfo->ctl_addr))->loc_doorbell); + if (loc_doorbell){ + *cmd = (char)(0xff & loc_doorbell); + *channel = cy_readl(&board_ctrl->fwcmd_channel); + *param = (uclong)cy_readl(&board_ctrl->fwcmd_param); + cy_writel(&((struct RUNTIME_9060 *)(cinfo->ctl_addr))->loc_doorbell, + 0xffffffff); + return 1; + } + return 0; +} /* cyz_fetch_msg */ - tty = info->tty; - if (!tty) - return; - if (clear_bit(Cy_EVENT_HANGUP, &info->event)) { - tty_hangup(info->tty); - wake_up_interruptible(&info->open_wait); - info->flags &= ~(ASYNC_NORMAL_ACTIVE| - ASYNC_CALLOUT_ACTIVE); - } - if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { - wake_up_interruptible(&info->open_wait); +static int +cyz_issue_cmd( struct cyclades_card *cinfo, + uclong channel, ucchar cmd, uclong param) +{ + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + volatile uclong *pci_doorbell; + int index; + + firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); + if (!ISZLOADED(*cinfo)){ + return (-1); } - if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { - if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup){ - (tty->ldisc.write_wakeup)(tty); - } - wake_up_interruptible(&tty->write_wait); + zfw_ctrl = (struct ZFW_CTRL *) + (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &zfw_ctrl->board_ctrl; + + index = 0; + pci_doorbell = (uclong *)(&((struct RUNTIME_9060 *) + (cinfo->ctl_addr))->pci_doorbell); + while( (cy_readl(pci_doorbell) & 0xff) != 0){ + if (index++ == 1000){ + return(-1); + } + udelay(50L); } -} /* do_softint */ + cy_writel((u_long)&board_ctrl->hcmd_channel, channel); + cy_writel((u_long)&board_ctrl->hcmd_param , param); + cy_writel((u_long)pci_doorbell, (long)cmd); + return(0); +} /* cyz_issue_cmd */ -/* - * Grab all interrupts in preparation for doing an automatic irq - * detection. dontgrab is a mask of irq's _not_ to grab. Returns a - * mask of irq's which were grabbed and should therefore be freed - * using free_all_interrupts(). - */ + +#if 0 static int -grab_all_interrupts(int dontgrab) -{ - int irq_lines = 0; - int i, mask; - - for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { - if (!(mask & dontgrab) - && !request_irq(i, cy_probe, SA_INTERRUPT, "serial probe", NULL)) { - irq_lines |= mask; - } +cyz_update_channel( struct cyclades_card *cinfo, + u_long channel, u_char mode, u_char cmd) +{ + struct FIRM_ID *firm_id = + (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); + struct ZFW_CTRL *zfw_ctrl; + struct CH_CTRL *ch_ctrl; + + if (!ISZLOADED(*cinfo)){ + return (-1); } - return irq_lines; -} /* grab_all_interrupts */ + zfw_ctrl = (struct ZFW_CTRL *) + (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); + ch_ctrl = zfw_ctrl->ch_ctrl; + + cy_writel(&ch_ctrl[channel].op_mode, (uclong)mode); + + return cyz_issue_cmd(cinfo, channel, cmd, 0L); + +} /* cyz_update_channel */ +#endif + -/* - * Release all interrupts grabbed by grab_all_interrupts - */ static void -free_all_interrupts(int irq_lines) +cyz_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - int i; - - for (i = 0; i < 16; i++) { - if (irq_lines & (1 << i)) - free_irq(i,NULL); - } -} /* free_all_interrupts */ +} /* cyz_interrupt */ -/* - * This routine returns a bitfield of "wild interrupts". Basically, - * any unclaimed interrupts which is flapping around. - */ -static int -check_wild_interrupts(void) -{ - int i, mask; - int wild_interrupts = 0; - int irq_lines; - unsigned long timeout; - unsigned long flags; - - /*Turn on interrupts (they may be off) */ - save_flags(flags); sti(); - irq_lines = grab_all_interrupts(0); - - /* - * Delay for 0.1 seconds -- we use a busy loop since this may - * occur during the bootup sequence - */ - timeout = jiffies+10; - while (timeout >= jiffies) - ; - - cy_triggered = 0; /* Reset after letting things settle */ - - timeout = jiffies+10; - while (timeout >= jiffies) - ; - - for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { - if ((cy_triggered & (1 << i)) && - (irq_lines & (1 << i))) { - wild_interrupts |= mask; +static void +cyz_poll(unsigned long arg) +{ + static volatile struct FIRM_ID *firm_id; + static volatile struct ZFW_CTRL *zfw_ctrl; + static volatile struct BOARD_CTRL *board_ctrl; + static volatile struct CH_CTRL *ch_ctrl; + static volatile struct BUF_CTRL *buf_ctrl; + struct cyclades_card *cinfo; + struct cyclades_port *info; + struct tty_struct *tty; + int card, port; + int char_count; +#ifdef BLOCKMOVE + int small_count; +#endif + char data; + uclong channel; + ucchar cmd; + uclong param; + uclong hw_ver, fw_ver; + volatile uclong tx_put, tx_get, tx_bufsize; + volatile uclong rx_put, rx_get, rx_bufsize; + + cyz_timerlist.expires = jiffies + (HZ); + for (card = 0 ; card < NR_CARDS ; card++){ + cinfo = &cy_card[card]; + if (!IS_CYC_Z(*cinfo)) continue; + + + firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); + if (!ISZLOADED(*cinfo)) { + cinfo->inact_ctrl = 0; + continue; + } + + zfw_ctrl = (struct ZFW_CTRL *) + (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &(zfw_ctrl->board_ctrl); + fw_ver = cy_readl(&board_ctrl->fw_version); + hw_ver = cy_readl(&((struct RUNTIME_9060 *) + (cinfo->ctl_addr))->mail_box_0); + + /* Enables the firmware inactivity control */ + if ((fw_ver > 0x00000310L) && (!cinfo->inact_ctrl)) { + param = cyz_issue_cmd( &cy_card[card], 0L, C_CM_TINACT, 0L); + cinfo->inact_ctrl = 1; + } + + while(cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1){ + char_count = 0; + info = &cy_port[ channel + cinfo->first_line ]; + if((tty = info->tty) == 0) continue; + ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); + buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); + info->jiffies[0] = jiffies; + + switch(cmd){ + case C_CM_PR_ERROR: + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_PARITY; + *tty->flip.char_buf_ptr++ = 0; + char_count++; + break; + case C_CM_FR_ERROR: + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_FRAME; + *tty->flip.char_buf_ptr++ = 0; + char_count++; + break; + case C_CM_RXBRK: + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + *tty->flip.char_buf_ptr++ = 0; + char_count++; + break; + case C_CM_MDCD: + if (info->flags & ASYNC_CHECK_CD){ + if ((fw_ver > 241 ? + ((u_long)param) : + cy_readl(&ch_ctrl[channel].rs_status)) & C_RS_DCD) { + /* SP("Open Wakeup\n"); */ + cy_sched_event(info, + Cy_EVENT_OPEN_WAKEUP); + }else if(!((info->flags + & ASYNC_CALLOUT_ACTIVE) + &&(info->flags + & ASYNC_CALLOUT_NOHUP))){ + /* SP("Hangup\n"); */ + cy_sched_event(info, + Cy_EVENT_HANGUP); + } + } + break; + case C_CM_MCTS: + if (info->flags & ASYNC_CTS_FLOW) { + if(info->tty->hw_stopped){ + if( cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD){ + /* cy_start isn't used because... + HW flow is handled by the board */ + /* SP("Write Wakeup\n"); */ + cy_sched_event(info, + Cy_EVENT_WRITE_WAKEUP); + } + }else{ + if(!(cy_readl(&ch_ctrl[channel].rs_status) & C_RS_CTS)){ + /* cy_stop isn't used because + HW flow is handled by the board */ + /* SP("Write stop\n"); */ + } + } + } + break; + case C_CM_MRI: + break; + case C_CM_MDSR: + break; +#ifdef Z_WAKE + case C_CM_IOCTLW: + cy_sched_event(info, Cy_EVENT_SHUTDOWN_WAKEUP); + break; +#endif + case C_CM_FATAL: + /* should do something with this !!! */ + break; + } + if(char_count){ + queue_task(&tty->flip.tqueue, &tq_timer); } } - free_all_interrupts(irq_lines); - restore_flags(flags); - return wild_interrupts; -} /* check_wild_interrupts */ + for (port = 0; port < cy_readl(&board_ctrl->n_channel); port++){ + info = &cy_port[ port + cinfo->first_line ]; + tty = info->tty; + ch_ctrl = &(zfw_ctrl->ch_ctrl[port]); + buf_ctrl = &(zfw_ctrl->buf_ctrl[port]); + +/* Removed due to compilation problems in Alpha systems */ +// if ((char_count = CHARS_IN_BUF(buf_ctrl))){ + + rx_get = cy_readl(&buf_ctrl->rx_get); + rx_put = cy_readl(&buf_ctrl->rx_put); + rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize); + if (rx_put >= rx_get) + char_count = rx_put - rx_get; + else + char_count = rx_put - rx_get + rx_bufsize; + + if ( char_count ){ + + info->last_active = jiffies; + info->jiffies[1] = jiffies; + +#ifdef CY_ENABLE_MONITORING + info->mon.int_count++; + info->mon.char_count += char_count; + if (char_count > info->mon.char_max) + info->mon.char_max = char_count; + info->mon.char_last = char_count; +#endif + if( tty == 0){ + /* flush received characters */ + rx_get = (rx_get + char_count) & (rx_bufsize - 1); + /* SP("-"); */ + info->rflush_count++; + }else{ +#ifdef BLOCKMOVE + /* we'd like to use memcpy(t, f, n) and memset(s, c, count) + for performance, but because of buffer boundaries, there + may be several steps to the operation */ + while(0 < (small_count + = cy_min((rx_bufsize - rx_get), + cy_min((TTY_FLIPBUF_SIZE - tty->flip.count), + char_count)))){ + + memcpy_fromio(tty->flip.char_buf_ptr, + (char *)(cinfo->base_addr + + cy_readl(&buf_ctrl->rx_bufaddr) + + rx_get), + small_count); + + tty->flip.char_buf_ptr += small_count; + memset(tty->flip.flag_buf_ptr, + TTY_NORMAL, + small_count); + tty->flip.flag_buf_ptr += small_count; + rx_get = (rx_get + small_count) & (rx_bufsize - 1); + char_count -= small_count; + tty->flip.count += small_count; + } +#else + while(char_count--){ + if (tty->flip.count >= TTY_FLIPBUF_SIZE){ + break; + } + data = cy_readb(cinfo->base_addr + + cy_readl(&buf_ctrl->rx_bufaddr) + rx_get); + rx_get = (rx_get + 1) & (rx_bufsize - 1); + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = data; + } +#endif + queue_task(&tty->flip.tqueue, &tq_timer); + } + /* Update rx_get */ + cy_writel(&buf_ctrl->rx_get, rx_get); + } -/* - * This routine is called by do_auto_irq(); it attempts to determine - * which interrupt a serial port is configured to use. It is not - * fool-proof, but it works a large part of the time. - */ -static int -get_auto_irq(unsigned char *address) -{ - unsigned long timeout; - unsigned char *base_addr; - int index; +/* Removed due to compilation problems in Alpha systems */ +// if ((char_count = SPACE_IN_BUF(buf_ctrl))){ - index = 0; /* IRQ probing is only for ISA */ - base_addr = address; - intr_base_addr = address; - - /* - * Enable interrupts and see who answers - */ - cy_irq_triggered = 0; - cli(); - base_addr[CyCAR<= jiffies) { - if (cy_irq_triggered) - break; - } - probe_ready = 0; - return(cy_irq_triggered); -} /* get_auto_irq */ + tx_get = cy_readl(&buf_ctrl->tx_get); + tx_put = cy_readl(&buf_ctrl->tx_put); + tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize); + if (tx_put >= tx_get) + char_count = tx_get - tx_put - 1 + tx_bufsize; + else + char_count = tx_get - tx_put - 1; -/* - * Calls get_auto_irq() multiple times, to make sure we don't get - * faked out by random interrupts - */ -static int -do_auto_irq(unsigned char *address) -{ - int irq_lines = 0; - int irq_try_1 = 0, irq_try_2 = 0; - int retries; - unsigned long flags; + if ( char_count ){ - /* Turn on interrupts (they may be off) */ - save_flags(flags); sti(); + if( tty == 0 ){ + goto ztxdone; + } - probe_ready = 0; + if(info->x_char) { /* send special char */ + data = info->x_char; - cy_wild_int_mask = check_wild_interrupts(); + cy_writeb((cinfo->base_addr + + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data); + tx_put = (tx_put + 1) & (tx_bufsize - 1); + info->x_char = 0; + char_count--; + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } + if (info->x_break){ + printk("cyc cyz_poll shouldn't see x_break\n"); + info->x_break = 0; + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } +#ifdef BLOCKMOVE + while(0 < (small_count + = cy_min((tx_bufsize - tx_put), + cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail), + cy_min(info->xmit_cnt, char_count))))){ + + memcpy_toio((char *)(cinfo->base_addr + + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), + &info->xmit_buf[info->xmit_tail], + small_count); + + tx_put = (tx_put + small_count) & (tx_bufsize - 1); + char_count -= small_count; + info->xmit_cnt -= small_count; + info->xmit_tail = + (info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1); + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } +#else + while (info->xmit_cnt && char_count){ + data = info->xmit_buf[info->xmit_tail]; + info->xmit_cnt--; + info->xmit_tail = + (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); + + cy_writeb(cinfo->base_addr + + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put, + data); + tx_put = (tx_put + 1) & (tx_bufsize - 1); + char_count--; + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } - irq_lines = grab_all_interrupts(cy_wild_int_mask); - - for (retries = 0; retries < 5; retries++) { - if (!irq_try_1) - irq_try_1 = get_auto_irq(address); - if (!irq_try_2) - irq_try_2 = get_auto_irq(address); - if (irq_try_1 && irq_try_2) { - if (irq_try_1 == irq_try_2) - break; - irq_try_1 = irq_try_2 = 0; +#endif + ztxdone: + if (info->xmit_cnt < WAKEUP_CHARS) { + cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); + } + /* Update tx_put */ + cy_writel(&buf_ctrl->tx_put, tx_put); } } - restore_flags(flags); - free_all_interrupts(irq_lines); - return (irq_try_1 == irq_try_2) ? irq_try_1 : 0; -} /* do_auto_irq */ + /* poll every 40 ms */ + cyz_timerlist.expires = jiffies + cyz_polling_cycle; + + /* refresh inactivity counter */ + if (cinfo->inact_ctrl) { + cy_writel(&board_ctrl->inactivity, (uclong) ZF_TINACT); + } + } + add_timer(&cyz_timerlist); + + return; +} /* cyz_poll */ + + +/********** End of block of Cyclades-Z specific code *********/ +/***********************************************************/ /* This is called whenever a port becomes active; @@ -1216,72 +1934,137 @@ startup(struct cyclades_port * info) int card,chip,channel,index; if (info->flags & ASYNC_INITIALIZED){ - return 0; + return 0; } if (!info->type){ - if (info->tty){ - set_bit(TTY_IO_ERROR, &info->tty->flags); - } - return 0; + if (info->tty){ + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + return 0; } if (!info->xmit_buf){ - info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL); - if (!info->xmit_buf){ - return -ENOMEM; - } + info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL); + if (!info->xmit_buf){ + return -ENOMEM; + } } - config_setup(info); + set_line_char(info); card = info->card; channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) (cy_card[card].base_addr + (cy_chip_offset[chip]<default_timeout - ? info->default_timeout - : 0x02); /* 10ms rx timeout */ + cy_writeb((ulong)base_addr+(CyRTPR<default_timeout + ? info->default_timeout + : 0x02)); /* 10ms rx timeout */ - write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index); + cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index); - base_addr[CyCAR<flags |= ASYNC_INITIALIZED; + cy_writeb((u_long)base_addr+(CySRER<flags |= ASYNC_INITIALIZED; + + if (info->tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + restore_flags(flags); + } else { + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + int retval; + + base_addr = (unsigned char*) (cy_card[card].base_addr); + + firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); + if (!ISZLOADED(cy_card[card])){ + return -ENODEV; + } + + zfw_ctrl = + (struct ZFW_CTRL *) + (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + +#ifdef SERIAL_DEBUG_OPEN + printk("cyc startup Z card %d, channel %d, base_addr %lx\n", + card, channel, (long)base_addr);/**/ +#endif + cy_writel(&ch_ctrl[channel].op_mode, C_CH_ENABLE); +#ifdef Z_WAKE + cy_writel(&ch_ctrl[channel].intr_enable, + C_IN_MDCD|C_IN_MCTS|C_IN_IOCTLW); +#else + cy_writel(&ch_ctrl[channel].intr_enable, + C_IN_MDCD|C_IN_MCTS); +#endif + retval = cyz_issue_cmd( &cy_card[card], + channel, C_CM_IOCTL, 0L); /* was C_CM_RESET */ + if (retval != 0){ + printk("cyc:startup(1) retval was %x\n", retval); + } + + /* set timeout !!! */ + /* set RTS and DTR !!! */ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR) ; + retval = cyz_issue_cmd(&cy_card[info->card], + channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:startup(2) retval was %x\n", retval); + } +#ifdef SERIAL_DEBUG_DTR + printk("cyc:startup raising Z DTR\n"); +#endif + + /* enable send, recv, modem !!! */ + + info->flags |= ASYNC_INITIALIZED; if (info->tty){ clear_bit(TTY_IO_ERROR, &info->tty->flags); } info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - restore_flags(flags); + } #ifdef SERIAL_DEBUG_OPEN - printk(" done\n"); + printk(" cyc startup done\n"); #endif - return 0; + return 0; } /* startup */ -void + +static void start_xmit( struct cyclades_port *info ) { unsigned long flags; @@ -1290,18 +2073,25 @@ start_xmit( struct cyclades_port *info ) card = info->card; channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<flags & ASYNC_INITIALIZED)){ - return; + return; } card = info->card; channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<xmit_buf){ - unsigned char * temp; - temp = info->xmit_buf; - info->xmit_buf = 0; - free_page((unsigned long) temp); - } + save_flags(flags); cli(); - base_addr[CyCAR<tty || (info->tty->termios->c_cflag & HUPCL)) { - base_addr[CyMSVR1<xmit_buf){ + unsigned char * temp; + temp = info->xmit_buf; + info->xmit_buf = 0; + free_page((unsigned long) temp); + } + cy_writeb((u_long)base_addr+(CyCAR<tty || (info->tty->termios->c_cflag & HUPCL)) { + cy_writeb((u_long)base_addr+(CyMSVR1<tty){ - set_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->tty){ + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); + } else { + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + int retval; + + base_addr = (unsigned char*) (cy_card[card].base_addr); +#ifdef SERIAL_DEBUG_OPEN + printk("cyc shutdown Z card %d, channel %d, base_addr %lx\n", + card, channel, (long)base_addr); +#endif + + firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); + if (!ISZLOADED(cy_card[card])) { + return; } - info->flags &= ~ASYNC_INITIALIZED; - restore_flags(flags); + + zfw_ctrl = + (struct ZFW_CTRL *) + (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &(zfw_ctrl->board_ctrl); + ch_ctrl = zfw_ctrl->ch_ctrl; + + save_flags(flags); cli(); + + if (info->xmit_buf){ + unsigned char * temp; + temp = info->xmit_buf; + info->xmit_buf = 0; + free_page((unsigned long) temp); + } + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + cy_writel((u_long)&ch_ctrl[channel].rs_control, + (uclong)(cy_readl(&ch_ctrl[channel].rs_control) & + ~(C_RS_RTS | C_RS_DTR))); + retval = cyz_issue_cmd(&cy_card[info->card], + channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:shutdown retval (2) was %x\n", retval); + } +#ifdef SERIAL_DEBUG_DTR + printk("cyc:shutdown dropping Z DTR\n"); +#endif + } + + if (info->tty){ + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->flags &= ~ASYNC_INITIALIZED; + + restore_flags(flags); + } #ifdef SERIAL_DEBUG_OPEN - printk(" done\n"); + printk(" cyc shutdown done\n"); #endif return; } /* shutdown */ + /* - * This routine finds or computes the various line characteristics. + * ------------------------------------------------------------ + * cy_open() and friends + * ------------------------------------------------------------ */ -static void -config_setup(struct cyclades_port * info) + +static int +block_til_ready(struct tty_struct *tty, struct file * filp, + struct cyclades_port *info) { + struct wait_queue wait = { current, NULL }; + struct cyclades_card *cinfo; unsigned long flags; - unsigned char *base_addr; - int card,chip,channel,index; - unsigned cflag; - int i; + int chip, channel,index; + int retval; + char *base_addr; - if (!info->tty || !info->tty->termios){ - return; - } - if (info->line == -1){ - return; + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) { + interruptible_sleep_on(&info->close_wait); + } + if (info->flags & ASYNC_HUP_NOTIFY){ + return -EAGAIN; + }else{ + return -ERESTARTSYS; + } } - cflag = info->tty->termios->c_cflag; - /* baud rate */ - i = cflag & CBAUD; -#ifdef CBAUDEX -/* Starting with kernel 1.1.65, there is direct support for - higher baud rates. The following code supports those - changes. The conditional aspect allows this driver to be - used for earlier as well as later kernel versions. (The - mapping is slightly different from serial.c because there - is still the possibility of supporting 75 kbit/sec with - the Cyclades board.) - */ - if (i & CBAUDEX) { - if (i == B57600) - i = 16; - else if(i == B115200) - i = 18; -#ifdef B78600 - else if(i == B78600) - i = 17; -#endif - else - info->tty->termios->c_cflag &= ~CBAUDEX; - } -#endif - if (i == 15) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - i += 1; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - i += 3; - } - info->tbpr = baud_bpr[i]; /* Tx BPR */ - info->tco = baud_co[i]; /* Tx CO */ - info->rbpr = baud_bpr[i]; /* Rx BPR */ - info->rco = baud_co[i]; /* Rx CO */ - if (baud_table[i] == 134) { - info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; - /* get it right for 134.5 baud */ - } else if (baud_table[i]) { - info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2; - /* this needs to be propagated into the card info */ - } else { - info->timeout = 0; - } - /* By tradition (is it a standard?) a baud rate of zero - implies the line should be/has been closed. A bit - later in this routine such a test is performed. */ - - /* byte size and parity */ - info->cor5 = 0; - info->cor4 = 0; - info->cor3 = (info->default_threshold - ? info->default_threshold - : baud_cor3[i]); /* receive threshold */ - info->cor2 = CyETC; - switch(cflag & CSIZE){ - case CS5: - info->cor1 = Cy_5_BITS; - break; - case CS6: - info->cor1 = Cy_6_BITS; - break; - case CS7: - info->cor1 = Cy_7_BITS; - break; - case CS8: - info->cor1 = Cy_8_BITS; - break; - } - if(cflag & CSTOPB){ - info->cor1 |= Cy_2_STOP; - } - if (cflag & PARENB){ - if (cflag & PARODD){ - info->cor1 |= CyPARITY_O; - }else{ - info->cor1 |= CyPARITY_E; + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ASYNC_NORMAL_ACTIVE){ + return -EBUSY; } - }else{ - info->cor1 |= CyPARITY_NONE; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)){ + return -EBUSY; + } + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)){ + return -EBUSY; + } + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; } - - /* CTS flow control flag */ - if (cflag & CRTSCTS){ - info->flags |= ASYNC_CTS_FLOW; - info->cor2 |= CyCtsAE; - }else{ - info->flags &= ~ASYNC_CTS_FLOW; - info->cor2 &= ~CyCtsAE; - } - if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; - else - info->flags |= ASYNC_CHECK_CD; - - /*********************************************** - The hardware option, CyRtsAO, presents RTS when - the chip has characters to send. Since most modems - use RTS as reverse (inbound) flow control, this - option is not used. If inbound flow control is - necessary, DTR can be programmed to provide the - appropriate signals for use with a non-standard - cable. Contact Marcio Saito for details. - ***********************************************/ - card = info->card; - channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (info->flags & ASYNC_CALLOUT_ACTIVE){ + return -EBUSY; + } + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * cy_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("cyc block_til_ready before block: ttyC%d, count = %d\n", + info->line, info->count);/**/ +#endif save_flags(flags); cli(); - base_addr[CyCAR<count--; + restore_flags(flags); +#ifdef SERIAL_DEBUG_COUNT + printk("cyc block_til_ready: (%d): decrementing count to %d\n", + current->pid, info->count); +#endif + info->blocked_open++; - /* tx and rx baud rate */ - - base_addr[CyTCOR<tco; - base_addr[CyTBPR<tbpr; - base_addr[CyRCOR<rco; - base_addr[CyRBPR<rbpr; - - /* set line characteristics according configuration */ + cinfo = &cy_card[info->card]; + channel = info->line - cinfo->first_line; + if (!IS_CYC_Z(*cinfo)) { + chip = channel>>2; + channel &= 0x03; + index = cinfo->bus_index; + base_addr = (char *)(cinfo->base_addr + + (cy_chip_offset[chip]<flags & ASYNC_CALLOUT_ACTIVE)){ + cy_writeb((u_long)base_addr+(CyCAR<state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) + || !(info->flags & ASYNC_INITIALIZED) ){ + if (info->flags & ASYNC_HUP_NOTIFY) { + retval = -EAGAIN; + }else{ + retval = -ERESTARTSYS; + } + break; + } + save_flags(flags); cli(); + cy_writeb((u_long)base_addr+(CyCAR<flags & ASYNC_CALLOUT_ACTIVE) + && !(info->flags & ASYNC_CLOSING) + && (C_CLOCAL(tty) + || (cy_readb(base_addr+(CyMSVR1<signal & ~current->blocked) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cyc block_til_ready blocking: ttyC%d, count = %d\n", + info->line, info->count);/**/ +#endif + schedule(); + } + } else { + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + int retval; + + base_addr = (char *)(cinfo->base_addr); + firm_id = (struct FIRM_ID *) + (base_addr + ID_ADDRESS); + if (!ISZLOADED(*cinfo)){ + return -EINVAL; + } - base_addr[CySCHR1<tty); - base_addr[CySCHR2<tty); - base_addr[CyCOR1<cor1; - base_addr[CyCOR2<cor2; - base_addr[CyCOR3<cor3; - base_addr[CyCOR4<cor4; - base_addr[CyCOR5<cor5; + zfw_ctrl = + (struct ZFW_CTRL *) + (base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + + while (1) { + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR); + retval = cyz_issue_cmd(&cy_card[info->card], + channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:block_til_ready retval was %x\n", retval); + } +#ifdef SERIAL_DEBUG_DTR + printk("cyc:block_til_ready raising Z DTR\n"); +#endif - write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) + || !(info->flags & ASYNC_INITIALIZED) ){ + if (info->flags & ASYNC_HUP_NOTIFY) { + retval = -EAGAIN; + }else{ + retval = -ERESTARTSYS; + } + break; + } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) + && !(info->flags & ASYNC_CLOSING) + && (C_CLOCAL(tty) + || (cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD))) { + break; + } + if (current->signal & ~current->blocked) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cyc block_til_ready blocking: ttyC%d, count = %d\n", + info->line, info->count);/**/ +#endif + schedule(); + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + if (!tty_hung_up_p(filp)){ + info->count++; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc:block_til_ready (%d): incrementing count to %d\n", + current->pid, info->count); +#endif + } + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("cyc:block_til_ready after blocking: ttyC%d, count = %d\n", + info->line, info->count);/**/ +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} /* block_til_ready */ - base_addr[CyCAR<default_timeout - ? info->default_timeout - : 0x02); /* 10ms rx timeout */ +/* + * This routine is called whenever a serial port is opened. It + * performs the serial-specific initialization for the tty structure. + */ +int +cy_open(struct tty_struct *tty, struct file * filp) +{ + struct cyclades_port *info; + int retval, line; - if (C_CLOCAL(info->tty)) { - base_addr[CySRER<0 modem transitions */ - base_addr[CyMCOR1<1 modem transitions */ - base_addr[CyMCOR2<0 modem transitions */ - base_addr[CyMCOR1<1 modem transitions */ - base_addr[CyMCOR2<device) - tty->driver.minor_start; + if ((line < 0) || (NR_PORTS <= line)){ + return -ENODEV; + } + info = &cy_port[line]; + if (info->line < 0){ + return -ENODEV; + } + + /* If the card's firmware hasn't been loaded, + treat it as absent from the system. This + will make the user pay attention. + */ + if (IS_CYC_Z(cy_card[info->card])) { + if (!ISZLOADED(cy_card[info->card])) { + if (((ZE_V1 ==cy_readl(&((struct RUNTIME_9060 *) + ((cy_card[info->card]).ctl_addr))->mail_box_0)) && + Z_FPGA_CHECK(cy_card[info->card])) && + (ZFIRM_HLT==cy_readl(&((struct FIRM_ID *) + ((cy_card[info->card]).base_addr+ID_ADDRESS))->signature))) + { + printk ("Cyclades-Z Error: you need an external power supply for this number of ports.\n\rFirmware halted.\r\n"); + } else { + printk("Cyclades-Z firmware not yet loaded\n"); + } + return -ENODEV; } - - if(i == 0){ /* baud rate is zero, turn off line */ - base_addr[CyMSVR2<line); /* */ #endif - }else{ - base_addr[CyMSVR2<device, "cy_open")){ + return -ENODEV; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cyc:cy_open ttyC%d, count = %d\n", + info->line, info->count);/**/ #endif - } + info->count++; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc:cy_open (%d): incrementing count to %d\n", + current->pid, info->count); +#endif + tty->driver_data = info; + info->tty = tty; - if (info->tty){ - clear_bit(TTY_IO_ERROR, &info->tty->flags); - } + /* Some drivers have (incorrect/incomplete) code to test + against a race condition. Should add good code here!!! */ + if (!tmp_buf) { + tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL); + if (!tmp_buf){ + return -ENOMEM; + } + } - restore_flags(flags); + if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + } + /* + * Start up serial port + */ + retval = startup(info); + if (retval){ + return retval; + } -} /* config_setup */ + MOD_INC_USE_COUNT; + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("cyc:cy_open returning after block_til_ready with %d\n", + retval); +#endif + return retval; + } -static void -cy_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - unsigned long flags; + info->session = current->session; + info->pgrp = current->pgrp; -#ifdef SERIAL_DEBUG_IO - printk("cy_put_char ttyC%d\n", info->line); +#ifdef SERIAL_DEBUG_OPEN + printk(" cyc:cy_open done\n");/**/ #endif - if (serial_paranoia_check(info, tty->device, "cy_put_char")) - return; + return 0; +} /* cy_open */ - if (!tty || !info->xmit_buf) +/* + * cy_wait_until_sent() --- wait until the transmitter is empty + */ +static void cy_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + unsigned char *base_addr; + int card,chip,channel,index; + unsigned long orig_jiffies, char_time; + + if (serial_paranoia_check(info, tty->device, "cy_wait_until_sent")) return; - save_flags(flags); cli(); - if (info->xmit_cnt >= PAGE_SIZE - 1) { - restore_flags(flags); - return; + orig_jiffies = jiffies; + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check + * interval should also be less than the timeout. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout < 0) + timeout = 0; + if (timeout) + char_time = MIN(char_time, timeout); +#ifdef CY_DEBUG_WAIT_UNTIL_SENT + printk("In cy_wait_until_sent(%d) check=%lu...", timeout, char_time); + printk("jiff=%lu...", jiffies); +#endif + card = info->card; + channel = (info->line) - (cy_card[card].first_line); + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char *) + (cy_card[card].base_addr + (cy_chip_offset[chip]<state = TASK_INTERRUPTIBLE; + current->counter = 0; /* make us low-priority */ + current->timeout = jiffies + char_time; + schedule(); + if (current->signal & ~current->blocked) + break; + if (timeout && ((orig_jiffies + timeout) < jiffies)) + break; } + current->state = TASK_RUNNING; + } else { + // Nothing to do! + } + /* Run one more char cycle */ + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; /* make us low-priority */ + current->timeout = jiffies + (char_time * 5); + schedule(); + current->state = TASK_RUNNING; +#ifdef CY_DEBUG_WAIT_UNTIL_SENT + printk("Clean (jiff=%lu)...done\n", jiffies); +#endif +} - info->xmit_buf[info->xmit_head++] = ch; - info->xmit_head &= PAGE_SIZE - 1; - info->xmit_cnt++; - restore_flags(flags); -} /* cy_put_char */ - - +/* + * This routine is called when a particular tty device is closed. + */ static void -cy_flush_chars(struct tty_struct *tty) +cy_close(struct tty_struct * tty, struct file * filp) { - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; unsigned long flags; - unsigned char *base_addr; - int card,chip,channel,index; - -#ifdef SERIAL_DEBUG_IO - printk("cy_flush_chars ttyC%d\n", info->line); /* */ + +#ifdef SERIAL_DEBUG_OTHER + printk("cyc:cy_close ttyC%d\n", info->line); #endif - if (serial_paranoia_check(info, tty->device, "cy_flush_chars")) - return; + if (!info + || serial_paranoia_check(info, tty->device, "cy_close")){ + return; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cyc:cy_close ttyC%d, count = %d\n", info->line, info->count); +#endif - if (info->xmit_cnt <= 0 || tty->stopped - || tty->hw_stopped || !info->xmit_buf) - return; + save_flags(flags); cli(); - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. Info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("cyc:cy_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } +#ifdef SERIAL_DEBUG_COUNT + printk("cyc:cy_close at (%d): decrementing count to %d\n", + current->pid, info->count - 1); +#endif + if (--info->count < 0) { +#ifdef SERIAL_DEBUG_COUNT + printk("cyc:cyc_close setting count to 0\n"); +#endif + info->count = 0; + } + if (info->count) { + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; - save_flags(flags); cli(); - base_addr[CyCAR<closing = 1; + if (info->closing_wait != CY_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + + if (!IS_CYC_Z(cy_card[info->card])) { + unsigned char *base_addr = (unsigned char *) + cy_card[info->card].base_addr; + int index = cy_card[info->card].bus_index; + /* Stop accepting input */ + cy_writeb((u_long)base_addr+(CySRER<flags & ASYNC_INITIALIZED) { + /* Waiting for on-board buffers to be empty before closing + the port */ + cy_wait_until_sent(tty, info->timeout); + } + } else { +#ifdef Z_WAKE + /* Waiting for on-board buffers to be empty before closing the port */ + unsigned char *base_addr = (unsigned char *) + cy_card[info->card].base_addr; + struct FIRM_ID *firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); + struct ZFW_CTRL *zfw_ctrl = + (struct ZFW_CTRL *) (base_addr + cy_readl(&firm_id->zfwctrl_addr)); + struct CH_CTRL *ch_ctrl = zfw_ctrl->ch_ctrl; + int channel = info->line - cy_card[info->card].first_line; + int retval; + + if (cy_readl(&ch_ctrl[channel].flow_status) != C_FS_TXIDLE) { + retval = cyz_issue_cmd(&cy_card[info->card], channel, + C_CM_IOCTLW, 0L); + if (retval != 0){ + printk("cyc:shutdown retval (1) was %x\n", retval); + } + interruptible_sleep_on(&info->shutdown_wait); + } +#endif + } + + shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + info->event = 0; + info->tty = 0; + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + info->close_delay; + schedule(); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + +#ifdef SERIAL_DEBUG_OTHER + printk(" cyc:cy_close done\n"); +#endif + + MOD_DEC_USE_COUNT; restore_flags(flags); -} /* cy_flush_chars */ + return; +} /* cy_close */ /* This routine gets called when tty_write has put something into - the write_queue. If the port is not already transmitting stuff, - start it off by enabling interrupts. The interrupt service - routine will then ensure that the characters are sent. If the - port is already active, there is no need to kick it. + * the write_queue. The characters may come from user space or + * kernel space. + * + * This routine will return the number of characters actually + * accepted for writing. + * + * If the port is not already transmitting stuff, start it off by + * enabling interrupts. The interrupt service routine will then + * ensure that the characters are sent. + * If the port is already active, there is no need to kick it. + * */ static int cy_write(struct tty_struct * tty, int from_user, @@ -1643,69 +2765,162 @@ cy_write(struct tty_struct * tty, int from_user, unsigned long flags; int c, total = 0; -#ifdef SERIAL_DEBUG_IO - printk("cy_write ttyC%d\n", info->line); /* */ +#ifdef CY_DEBUG_IO + printk("cyc:cy_write ttyC%d\n", info->line); /* */ #endif if (serial_paranoia_check(info, tty->device, "cy_write")){ - return 0; + return 0; } - + if (!tty || !info->xmit_buf || !tmp_buf){ return 0; } + if (from_user) + down(&tmp_buf_sem); + save_flags(flags); while (1) { - save_flags(flags); cli(); - c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - if (c <= 0){ - restore_flags(flags); - break; - } + cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) + break; - if (from_user) { - down(&tmp_buf_sem); - memcpy_fromfs(tmp_buf, buf, c); - c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); - up(&tmp_buf_sem); - } else - memcpy(info->xmit_buf + info->xmit_head, buf, c); - info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt += c; - restore_flags(flags); - buf += c; - count -= c; - total += c; + if (from_user) { + copy_from_user(tmp_buf, buf, c); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); + } else + memcpy(info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; +#if 0 + SP("CW"); + CP16(c); + SP(" "); +#endif } - - - if (info->xmit_cnt - && !tty->stopped - && !tty->hw_stopped ) { + if (from_user) + up(&tmp_buf_sem); + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { start_xmit(info); } + restore_flags(flags); return total; } /* cy_write */ -static int -cy_write_room(struct tty_struct *tty) +/* + * This routine is called by the kernel to write a single + * character to the tty device. If the kernel uses this routine, + * it must call the flush_chars() routine (if defined) when it is + * done stuffing characters into the driver. If there is no room + * in the queue, the character is ignored. + */ +static void +cy_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + +#ifdef CY_DEBUG_IO + printk("cyc:cy_put_char ttyC%d\n", info->line); +#endif + + if (serial_paranoia_check(info, tty->device, "cy_put_char")) + return; + + if (!tty || !info->xmit_buf) + return; + + save_flags(flags); cli(); + if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { + restore_flags(flags); + return; + } + + info->xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= SERIAL_XMIT_SIZE - 1; + info->xmit_cnt++; + restore_flags(flags); +#if 0 + SP("+"); +#endif +} /* cy_put_char */ + + +/* + * This routine is called by the kernel after it has written a + * series of characters to the tty device using put_char(). + */ +static void +cy_flush_chars(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + unsigned char *base_addr; + int card,chip,channel,index; + +#ifdef CY_DEBUG_IO + printk("cyc:cy_flush_chars ttyC%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_flush_chars")) + return; + + if (info->xmit_cnt <= 0 || tty->stopped + || tty->hw_stopped || !info->xmit_buf) + return; + + card = info->card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<driver_data; - int ret; - -#ifdef SERIAL_DEBUG_IO - printk("cy_write_room ttyC%d\n", info->line); /* */ + int ret; + +#ifdef CY_DEBUG_IO + printk("cyc:cy_write_room ttyC%d\n", info->line); /* */ #endif if (serial_paranoia_check(info, tty->device, "cy_write_room")) - return 0; - ret = PAGE_SIZE - info->xmit_cnt - 1; + return 0; + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; if (ret < 0) - ret = 0; + ret = 0; return ret; } /* cy_write_room */ @@ -1714,126 +2929,464 @@ static int cy_chars_in_buffer(struct tty_struct *tty) { struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - -#ifdef SERIAL_DEBUG_IO - printk("cy_chars_in_buffer ttyC%d %d\n", info->line, info->xmit_cnt); /* */ -#endif - + int card, channel; + if (serial_paranoia_check(info, tty->device, "cy_chars_in_buffer")) - return 0; - - return info->xmit_cnt; -} /* cy_chars_in_buffer */ + return 0; + card = info->card; + channel = (info->line) - (cy_card[card].first_line); -static void -cy_flush_buffer(struct tty_struct *tty) -{ - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - unsigned long flags; - -#ifdef SERIAL_DEBUG_IO - printk("cy_flush_buffer ttyC%d\n", info->line); /* */ + if (!IS_CYC_Z(cy_card[card])) { +#ifdef CY_DEBUG_IO + printk("cyc:cy_chars_in_buffer ttyC%d %d\n", + info->line, info->xmit_cnt); /* */ #endif + return info->xmit_cnt; + } else { + static volatile struct FIRM_ID *firm_id; + static volatile struct ZFW_CTRL *zfw_ctrl; + static volatile struct CH_CTRL *ch_ctrl; + static volatile struct BUF_CTRL *buf_ctrl; + int char_count; + volatile uclong tx_put, tx_get, tx_bufsize; + + firm_id = (struct FIRM_ID *)(cy_card[card].base_addr + ID_ADDRESS); + zfw_ctrl = (struct ZFW_CTRL *) (cy_card[card].base_addr + + cy_readl(&firm_id->zfwctrl_addr)); + ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); + buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); + + tx_get = cy_readl(&buf_ctrl->tx_get); + tx_put = cy_readl(&buf_ctrl->tx_put); + tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize); + if (tx_put >= tx_get) + char_count = tx_put - tx_get; + else + char_count = tx_put - tx_get + tx_bufsize; +#ifdef CY_DEBUG_IO + printk("cyc:cy_chars_in_buffer ttyC%d %d\n", + info->line, info->xmit_cnt + char_count); /* */ +#endif + return (info->xmit_cnt + char_count); + } +} /* cy_chars_in_buffer */ - if (serial_paranoia_check(info, tty->device, "cy_flush_buffer")) - return; - save_flags(flags); cli(); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - restore_flags(flags); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); -} /* cy_flush_buffer */ + +/* + * ------------------------------------------------------------ + * cy_ioctl() and friends + * ------------------------------------------------------------ + */ -/* This routine is called by the upper-layer tty layer to signal - that incoming characters should be throttled or that the - throttle should be released. +/* + * This routine finds or computes the various line characteristics. + * It used to be called config_setup */ static void -cy_throttle(struct tty_struct * tty) +set_line_char(struct cyclades_port * info) { - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; unsigned long flags; unsigned char *base_addr; int card,chip,channel,index; + unsigned cflag, iflag; + unsigned short chip_number; + int i; -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %d....\n", _tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); - printk("cy_throttle ttyC%d\n", info->line); -#endif - if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ - return; + if (!info->tty || !info->tty->termios){ + return; } - - if (I_IXOFF(tty)) { - info->x_char = STOP_CHAR(tty); - /* Should use the "Send Special Character" feature!!! */ + if (info->line == -1){ + return; } + cflag = info->tty->termios->c_cflag; + iflag = info->tty->termios->c_iflag; card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<line) - (cy_card[card].first_line); + chip_number = channel / 4; - save_flags(flags); cli(); - base_addr[CyCAR<chip_rev >= CD1400_REV_J)) { + /* It is a CD1400 rev. J or later */ + i = 20; + } + else + info->tty->termios->c_cflag &= ~CBAUDEX; + } -static void -cy_unthrottle(struct tty_struct * tty) -{ - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - unsigned long flags; - unsigned char *base_addr; - int card,chip,channel,index; + if (i == 15) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + i += 1; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + i += 3; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){ + switch(info->baud) { + case 57600: + i += 1; break; +#ifdef B76800 + case 76800: + i += 2; break; +#endif + case 115200: + i += 3; break; + case 230400: + i += 5; break; + default: + break; + } + } + } + if(info->chip_rev >= CD1400_REV_J) { + /* It is a CD1400 rev. J or later */ + info->tbpr = baud_bpr_60[i]; /* Tx BPR */ + info->tco = baud_co_60[i]; /* Tx CO */ + info->rbpr = baud_bpr_60[i]; /* Rx BPR */ + info->rco = baud_co_60[i]; /* Rx CO */ + } else { + info->tbpr = baud_bpr_25[i]; /* Tx BPR */ + info->tco = baud_co_25[i]; /* Tx CO */ + info->rbpr = baud_bpr_25[i]; /* Rx BPR */ + info->rco = baud_co_25[i]; /* Rx CO */ + } + if (baud_table[i] == 134) { + info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; + /* get it right for 134.5 baud */ + } else if (baud_table[i]) { + info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2; + /* this needs to be propagated into the card info */ + } else { + info->timeout = 0; + } + /* By tradition (is it a standard?) a baud rate of zero + implies the line should be/has been closed. A bit + later in this routine such a test is performed. */ + + /* byte size and parity */ + info->cor5 = 0; + info->cor4 = 0; + info->cor3 = (info->default_threshold + ? info->default_threshold + : baud_cor3[i]); /* receive threshold */ + info->cor2 = CyETC; + switch(cflag & CSIZE){ + case CS5: + info->cor1 = Cy_5_BITS; + break; + case CS6: + info->cor1 = Cy_6_BITS; + break; + case CS7: + info->cor1 = Cy_7_BITS; + break; + case CS8: + info->cor1 = Cy_8_BITS; + break; + } + if(cflag & CSTOPB){ + info->cor1 |= Cy_2_STOP; + } + if (cflag & PARENB){ + if (cflag & PARODD){ + info->cor1 |= CyPARITY_O; + }else{ + info->cor1 |= CyPARITY_E; + } + }else{ + info->cor1 |= CyPARITY_NONE; + } + + /* CTS flow control flag */ + if (cflag & CRTSCTS){ + info->flags |= ASYNC_CTS_FLOW; + info->cor2 |= CyCtsAE; + }else{ + info->flags &= ~ASYNC_CTS_FLOW; + info->cor2 &= ~CyCtsAE; + } + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else + info->flags |= ASYNC_CHECK_CD; + + /*********************************************** + The hardware option, CyRtsAO, presents RTS when + the chip has characters to send. Since most modems + use RTS as reverse (inbound) flow control, this + option is not used. If inbound flow control is + necessary, DTR can be programmed to provide the + appropriate signals for use with a non-standard + cable. Contact Marcio Saito for details. + ***********************************************/ + + chip = channel>>2; + channel &= 0x03; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<ldisc.chars_in_buffer(tty)); - printk("cy_unthrottle ttyC%d\n", info->line); + save_flags(flags); cli(); + cy_writeb((u_long)base_addr+(CyCAR<tco); + cy_writeb((u_long)base_addr+(CyTBPR<tbpr); + cy_writeb((u_long)base_addr+(CyRCOR<rco); + cy_writeb((u_long)base_addr+(CyRBPR<rbpr); + + /* set line characteristics according configuration */ + + cy_writeb((u_long)base_addr+(CySCHR1<tty)); + cy_writeb((u_long)base_addr+(CySCHR2<tty)); + cy_writeb((u_long)base_addr+(CyCOR1<cor1); + cy_writeb((u_long)base_addr+(CyCOR2<cor2); + cy_writeb((u_long)base_addr+(CyCOR3<cor3); + cy_writeb((u_long)base_addr+(CyCOR4<cor4); + cy_writeb((u_long)base_addr+(CyCOR5<cor5); + + cyy_issue_cmd(base_addr, + CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index); + + cy_writeb((u_long)base_addr+(CyCAR<default_timeout + ? info->default_timeout + : 0x02)); /* 10ms rx timeout */ + + if (C_CLOCAL(info->tty)) { + /* without modem intr */ + cy_writeb((u_long)base_addr+(CySRER<0 modem transitions */ + if ((cflag & CRTSCTS) && info->rflow) { + cy_writeb((u_long)base_addr+(CyMCOR1<1 modem transitions */ + cy_writeb((u_long)base_addr+(CyMCOR2<0 modem transitions */ + if ((cflag & CRTSCTS) && info->rflow) { + cy_writeb((u_long)base_addr+(CyMCOR1<1 modem transitions */ + cy_writeb((u_long)base_addr+(CyMCOR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<device, "cy_nthrottle")){ + if (info->tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + + restore_flags(flags); + } else { + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + struct BUF_CTRL *buf_ctrl; + int retval; + + firm_id = (struct FIRM_ID *) + (cy_card[card].base_addr + ID_ADDRESS); + if (!ISZLOADED(cy_card[card])) { return; - } + } - if (I_IXOFF(tty)) { - info->x_char = START_CHAR(tty); - /* Should use the "Send Special Character" feature!!! */ - } + zfw_ctrl = (struct ZFW_CTRL *) + (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); + buf_ctrl = &zfw_ctrl->buf_ctrl[channel]; - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<comm_baud , 0); break; + */ + case B50: cy_writel(&ch_ctrl->comm_baud , 50); break; + case B75: cy_writel(&ch_ctrl->comm_baud , 75); break; + case B110: cy_writel(&ch_ctrl->comm_baud , 110); break; + case B134: cy_writel(&ch_ctrl->comm_baud , 134); break; + case B150: cy_writel(&ch_ctrl->comm_baud , 150); break; + case B200: cy_writel(&ch_ctrl->comm_baud , 200); break; + case B300: cy_writel(&ch_ctrl->comm_baud , 300); break; + case B600: cy_writel(&ch_ctrl->comm_baud , 600); break; + case B1200: cy_writel(&ch_ctrl->comm_baud , 1200); break; + case B1800: cy_writel(&ch_ctrl->comm_baud , 1800); break; + case B2400: cy_writel(&ch_ctrl->comm_baud , 2400); break; + case B4800: cy_writel(&ch_ctrl->comm_baud , 4800); break; + case B9600: cy_writel(&ch_ctrl->comm_baud , 9600); break; + case B19200: cy_writel(&ch_ctrl->comm_baud , 19200); break; + case B38400: + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI){ + cy_writel(&ch_ctrl->comm_baud , 57600); + }else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI){ + cy_writel(&ch_ctrl->comm_baud , 115200); + }else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){ + cy_writel(&ch_ctrl->comm_baud , info->baud); + }else{ + cy_writel(&ch_ctrl->comm_baud , 38400); + } + break; + case B57600: cy_writel(&ch_ctrl->comm_baud , 57600); break; +#ifdef B76800 + case B76800: cy_writel(&ch_ctrl->comm_baud , 76800); break; +#endif + case B115200: cy_writel(&ch_ctrl->comm_baud , 115200); break; + case B230400: cy_writel(&ch_ctrl->comm_baud , 230400); break; + case B460800: cy_writel(&ch_ctrl->comm_baud , 460800); break; + } - save_flags(flags); cli(); - base_addr[CyCAR<comm_baud)) == 134) { + info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; + /* get it right for 134.5 baud */ + } else if (i) { + info->timeout = (info->xmit_fifo_size*HZ*15/i) + 2; + /* this needs to be propagated into the card info */ + } else { + info->timeout = 0; + } + + /* byte size and parity */ + switch(cflag & CSIZE){ + case CS5: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS5); break; + case CS6: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS6); break; + case CS7: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS7); break; + case CS8: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS8); break; + } + if(cflag & CSTOPB){ + cy_writel(&ch_ctrl->comm_data_l, + cy_readl(&ch_ctrl->comm_data_l) | C_DL_2STOP); + }else{ + cy_writel(&ch_ctrl->comm_data_l, + cy_readl(&ch_ctrl->comm_data_l) | C_DL_1STOP); + } + if (cflag & PARENB){ + if (cflag & PARODD){ + cy_writel(&ch_ctrl->comm_parity , C_PR_ODD); + }else{ + cy_writel(&ch_ctrl->comm_parity , C_PR_EVEN); + } + }else{ + cy_writel(&ch_ctrl->comm_parity , C_PR_NONE); + } + + /* CTS flow control flag */ + if (cflag & CRTSCTS){ + info->flags |= ASYNC_CTS_FLOW; + cy_writel(&ch_ctrl->hw_flow, + cy_readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS); + }else{ + info->flags &= ~ASYNC_CTS_FLOW; + cy_writel(&ch_ctrl->hw_flow, + cy_readl(&ch_ctrl->hw_flow) & ~(C_RS_CTS | C_RS_RTS)); + } + + retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_IOCTL, 0L); + if (retval != 0){ + printk("cyc:set_line_char retval at %d was %x\n", + __LINE__, retval); + } + + /* CD sensitivity */ + if (cflag & CLOCAL){ + info->flags &= ~ASYNC_CHECK_CD; + }else{ + info->flags |= ASYNC_CHECK_CD; + } + + if (iflag & IXON){ + cy_writel(&ch_ctrl->sw_flow, + cy_readl(&ch_ctrl->sw_flow) | C_FL_OXX); + } else { + cy_writel(&ch_ctrl->sw_flow, + cy_readl(&ch_ctrl->sw_flow) & ~C_FL_OXX); + } + + if(i == 0){ /* baud rate is zero, turn off line */ + cy_writel(&ch_ctrl->rs_control, + cy_readl(&ch_ctrl->rs_control) & ~C_RS_DTR); +#ifdef CY_DEBUG_DTR + printk("cyc:set_line_char dropping Z DTR\n"); +#endif + }else{ + cy_writel(&ch_ctrl->rs_control, + cy_readl(&ch_ctrl->rs_control) | C_RS_DTR); +#ifdef CY_DEBUG_DTR + printk("cyc:set_line_char raising Z DTR\n"); +#endif + } + + retval = cyz_issue_cmd( &cy_card[card], channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:set_line_char retval at %d was %x\n", + __LINE__, retval); + } + cy_readl(&ch_ctrl->comm_baud); + + if (info->tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + } + +} /* set_line_char */ - return; -} /* cy_unthrottle */ static int get_serial_info(struct cyclades_port * info, @@ -1850,14 +3403,15 @@ get_serial_info(struct cyclades_port * info, tmp.port = info->card * 0x100 + info->line - cinfo->first_line; tmp.irq = cinfo->irq; tmp.flags = info->flags; - tmp.baud_base = 0; /*!!!*/ tmp.close_delay = info->close_delay; + tmp.baud_base = info->baud; tmp.custom_divisor = 0; /*!!!*/ tmp.hub6 = 0; /*!!!*/ - memcpy_tofs(retinfo,&tmp,sizeof(*retinfo)); + copy_to_user(retinfo,&tmp,sizeof(*retinfo)); return 0; } /* get_serial_info */ + static int set_serial_info(struct cyclades_port * info, struct serial_struct * new_info) @@ -1866,18 +3420,20 @@ set_serial_info(struct cyclades_port * info, struct cyclades_port old_info; if (!new_info) - return -EFAULT; - memcpy_fromfs(&new_serial,new_info,sizeof(new_serial)); + return -EFAULT; + copy_from_user(&new_serial,new_info,sizeof(new_serial)); old_info = *info; if (!suser()) { - if ((new_serial.close_delay != info->close_delay) || + if ((new_serial.close_delay != info->close_delay) || + (new_serial.baud_base != info->baud) || ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))) - return -EPERM; - info->flags = ((info->flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - goto check_and_exit; + return -EPERM; + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + info->baud = new_serial.baud_base; + goto check_and_exit; } @@ -1886,20 +3442,23 @@ set_serial_info(struct cyclades_port * info, * At this point, we start making changes..... */ + info->baud = new_serial.baud_base; info->flags = ((info->flags & ~ASYNC_FLAGS) | - (new_serial.flags & ASYNC_FLAGS)); - info->close_delay = new_serial.close_delay; - + (new_serial.flags & ASYNC_FLAGS)); + info->close_delay = new_serial.close_delay * HZ/100; + info->closing_wait = new_serial.closing_wait * HZ/100; + check_and_exit: if (info->flags & ASYNC_INITIALIZED){ - config_setup(info); - return 0; + set_line_char(info); + return 0; }else{ return startup(info); } } /* set_serial_info */ + static int get_modem_info(struct cyclades_port * info, unsigned int *value) { @@ -1907,32 +3466,73 @@ get_modem_info(struct cyclades_port * info, unsigned int *value) unsigned char *base_addr; unsigned long flags; unsigned char status; + unsigned long lstatus; unsigned int result; + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; card = info->card; channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<rtsdtr_inv) { + result = ((status & CyRTS) ? TIOCM_DTR : 0) + | ((status & CyDTR) ? TIOCM_RTS : 0); + } else { + result = ((status & CyRTS) ? TIOCM_RTS : 0) + | ((status & CyDTR) ? TIOCM_DTR : 0); + } + result |= ((status & CyDCD) ? TIOCM_CAR : 0) + | ((status & CyRI) ? TIOCM_RNG : 0) + | ((status & CyDSR) ? TIOCM_DSR : 0) + | ((status & CyCTS) ? TIOCM_CTS : 0); + } else { + base_addr = (unsigned char*) (cy_card[card].base_addr); + + if (cy_card[card].num_chips != 1){ + return -EINVAL; + } - result = ((status & CyRTS) ? TIOCM_RTS : 0) - | ((status & CyDTR) ? TIOCM_DTR : 0) - | ((status & CyDCD) ? TIOCM_CAR : 0) - | ((status & CyRI) ? TIOCM_RNG : 0) - | ((status & CyDSR) ? TIOCM_DSR : 0) - | ((status & CyCTS) ? TIOCM_CTS : 0); - put_fs_long(result,(unsigned long *) value); + firm_id = (struct FIRM_ID *) + (cy_card[card].base_addr + ID_ADDRESS); + if (ISZLOADED(cy_card[card])) { + zfw_ctrl = (struct ZFW_CTRL *) + (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + lstatus = cy_readl(&ch_ctrl[channel].rs_status); + result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) + | ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) + | ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) + | ((lstatus & C_RS_RI) ? TIOCM_RNG : 0) + | ((lstatus & C_RS_DSR) ? TIOCM_DSR : 0) + | ((lstatus & C_RS_CTS) ? TIOCM_CTS : 0); + }else{ + result = 0; + return -ENODEV; + } + + } + cy_put_user(result,(unsigned long *) value); return 0; } /* get_modem_info */ + static int set_modem_info(struct cyclades_port * info, unsigned int cmd, unsigned int *value) @@ -1940,221 +3540,398 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, int card,chip,channel,index; unsigned char *base_addr; unsigned long flags; - unsigned int arg = get_fs_long((unsigned long *) value); + unsigned int arg = cy_get_user((unsigned long *) value); + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + int retval; card = info->card; channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<zfwctrl_addr)); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS){ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS); + } + if (arg & TIOCM_DTR){ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR); +#ifdef CY_DEBUG_DTR + printk("cyc:set_modem_info raising Z DTR\n"); +#endif + } + break; + case TIOCMBIC: + if (arg & TIOCM_RTS){ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS); + } + if (arg & TIOCM_DTR){ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR); +#ifdef CY_DEBUG_DTR + printk("cyc:set_modem_info clearing Z DTR\n"); +#endif + } + break; + case TIOCMSET: + if (arg & TIOCM_RTS){ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS); + }else{ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS); + } + if (arg & TIOCM_DTR){ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR); +#ifdef CY_DEBUG_DTR + printk("cyc:set_modem_info raising Z DTR\n"); +#endif + }else{ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR); +#ifdef CY_DEBUG_DTR + printk("cyc:set_modem_info clearing Z DTR\n"); +#endif + } + break; + default: return -EINVAL; - } + } + }else{ + return -ENODEV; + } + retval = cyz_issue_cmd(&cy_card[info->card], + channel, C_CM_IOCTLM,0L); + if (retval != 0){ + printk("cyc:set_modem_info retval at %d was %x\n", + __LINE__, retval); + } + } return 0; } /* set_modem_info */ + static void send_break( struct cyclades_port * info, int duration) -{ /* Let the transmit ISR take care of this (since it - requires stuffing characters into the output stream). - */ - info->x_break = duration; - if (!info->xmit_cnt ) { - start_xmit(info); +{ + + if (!IS_CYC_Z(cy_card[info->card])) { + /* Let the transmit ISR take care of this (since it + requires stuffing characters into the output stream). + */ + info->x_break = duration; + if (!info->xmit_cnt ) { + start_xmit(info); + } + } else { + /* For the moment we ignore the duration parameter!!! + A better implementation will use C_CM_SET_BREAK + and C_CM_CLR_BREAK with the appropriate delay. + */ +#if 1 +// this appears to wedge the output data stream +int retval; + retval = cyz_issue_cmd(&cy_card[info->card], + (info->line) - (cy_card[info->card].first_line), + C_CM_SENDBRK, 0L); + if (retval != 0){ + printk("cyc:send_break retval at %d was %x\n", + __LINE__, retval); + } +#endif } } /* send_break */ + static int get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon) { - memcpy_tofs(mon, &info->mon, sizeof(struct cyclades_monitor)); - info->mon.int_count = 0; - info->mon.char_count = 0; - info->mon.char_max = 0; - info->mon.char_last = 0; - return 0; -} + copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor)); + info->mon.int_count = 0; + info->mon.char_count = 0; + info->mon.char_max = 0; + info->mon.char_last = 0; + return 0; +}/* get_mon_info */ + static int set_threshold(struct cyclades_port * info, unsigned long value) { - unsigned char *base_addr; - int card,channel,chip,index; + unsigned char *base_addr; + int card,channel,chip,index; - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<cor3 &= ~CyREC_FIFO; + info->cor3 |= value & CyREC_FIFO; + cy_writeb((u_long)base_addr+(CyCOR3<cor3); + cyy_issue_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index); + } else { + // Nothing to do! + } + return 0; +}/* set_threshold */ - info->cor3 &= ~CyREC_FIFO; - info->cor3 |= value & CyREC_FIFO; - base_addr[CyCOR3<cor3; - write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index); - return 0; -} static int get_threshold(struct cyclades_port * info, unsigned long *value) { - unsigned char *base_addr; - int card,channel,chip,index; - unsigned long tmp; + unsigned char *base_addr; + int card,channel,chip,index; + unsigned long tmp; - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<default_threshold = value & 0x0f; - return 0; -} + info->default_threshold = value & 0x0f; + return 0; +}/* set_default_threshold */ + static int get_default_threshold(struct cyclades_port * info, unsigned long *value) { - put_fs_long(info->default_threshold,value); - return 0; -} + cy_put_user(info->default_threshold,value); + return 0; +}/* get_default_threshold */ + static int set_timeout(struct cyclades_port * info, unsigned long value) { - unsigned char *base_addr; - int card,channel,chip,index; + unsigned char *base_addr; + int card,channel,chip,index; - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<default_timeout = value & 0xff; - return 0; -} + info->default_timeout = value & 0xff; + return 0; +}/* set_default_timeout */ + static int get_default_timeout(struct cyclades_port * info, unsigned long *value) { - put_fs_long(info->default_timeout,value); - return 0; -} + cy_put_user(info->default_timeout,value); + return 0; +}/* get_default_timeout */ +/* + * This routine allows the tty driver to implement device- + * specific ioctl's. If the ioctl number passed in cmd is + * not recognized by the driver, it should return ENOIOCTLCMD. + */ static int cy_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) @@ -2163,8 +3940,9 @@ cy_ioctl(struct tty_struct *tty, struct file * file, struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; int ret_val = 0; -#ifdef SERIAL_DEBUG_OTHER - printk("cy_ioctl ttyC%d, cmd = %x arg = %lx\n", info->line, cmd, arg); /* */ +#ifdef CY_DEBUG_OTHER + printk("cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n", + info->line, cmd, arg); /* */ #endif switch (cmd) { @@ -2176,7 +3954,7 @@ cy_ioctl(struct tty_struct *tty, struct file * file, break; } ret_val = get_mon_info(info, (struct cyclades_monitor *)arg); - break; + break; case CYGETTHRESH: error = verify_area(VERIFY_WRITE, (void *) arg ,sizeof(unsigned long)); @@ -2184,11 +3962,11 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = error; break; } - ret_val = get_threshold(info, (unsigned long *)arg); - break; + ret_val = get_threshold(info, (unsigned long *)arg); + break; case CYSETTHRESH: ret_val = set_threshold(info, (unsigned long)arg); - break; + break; case CYGETDEFTHRESH: error = verify_area(VERIFY_WRITE, (void *) arg ,sizeof(unsigned long)); @@ -2196,11 +3974,11 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = error; break; } - ret_val = get_default_threshold(info, (unsigned long *)arg); - break; + ret_val = get_default_threshold(info, (unsigned long *)arg); + break; case CYSETDEFTHRESH: ret_val = set_default_threshold(info, (unsigned long)arg); - break; + break; case CYGETTIMEOUT: error = verify_area(VERIFY_WRITE, (void *) arg ,sizeof(unsigned long)); @@ -2208,11 +3986,11 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = error; break; } - ret_val = get_timeout(info, (unsigned long *)arg); - break; + ret_val = get_timeout(info, (unsigned long *)arg); + break; case CYSETTIMEOUT: ret_val = set_timeout(info, (unsigned long)arg); - break; + break; case CYGETDEFTIMEOUT: error = verify_area(VERIFY_WRITE, (void *) arg ,sizeof(unsigned long)); @@ -2220,23 +3998,62 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = error; break; } - ret_val = get_default_timeout(info, (unsigned long *)arg); - break; + ret_val = get_default_timeout(info, (unsigned long *)arg); + break; case CYSETDEFTIMEOUT: ret_val = set_default_timeout(info, (unsigned long)arg); + break; + case CYSETRFLOW: + info->rflow = (int)arg; + ret_val = 0; + break; + case CYGETRFLOW: + ret_val = info->rflow; + break; + case CYSETRTSDTR_INV: + info->rtsdtr_inv = (int)arg; + ret_val = 0; + break; + case CYGETRTSDTR_INV: + ret_val = info->rtsdtr_inv; + break; + case CYGETCARDINFO: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(struct cyclades_card)); + if (error){ + ret_val = error; + break; + } + copy_to_user((void *)arg, (void *)&cy_card[info->card], + sizeof (struct cyclades_card)); + ret_val = 0; + break; + case CYGETCD1400VER: + ret_val = info->chip_rev; + break; + case CYZPOLLCYCLE: + cyz_polling_cycle = (HZ * arg) / 1000; + ret_val = 0; + break; + case CYSETWAIT: + info->closing_wait = (unsigned short)arg * HZ/100; + ret_val = 0; + break; + case CYGETWAIT: + ret_val = info->closing_wait / (HZ/100); break; case TCSBRK: /* SVID version: non-zero arg --> no break */ - ret_val = tty_check_change(tty); - if (ret_val) - return ret_val; + ret_val = tty_check_change(tty); + if (ret_val) + return ret_val; tty_wait_until_sent(tty,0); if (!arg) send_break(info, HZ/4); /* 1/4 second */ break; case TCSBRKP: /* support for POSIX tcsendbreak() */ - ret_val = tty_check_change(tty); - if (ret_val) - return ret_val; + ret_val = tty_check_change(tty); + if (ret_val) + return ret_val; tty_wait_until_sent(tty,0); send_break(info, arg ? arg*(HZ/10) : HZ/4); break; @@ -2254,18 +4071,18 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = error; break; } - put_fs_long(C_CLOCAL(tty) ? 1 : 0, + cy_put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); break; case TIOCSSOFTCAR: - error = verify_area(VERIFY_READ, (void *) arg - ,sizeof(unsigned long *)); - if (error) { - ret_val = error; - break; - } + error = verify_area(VERIFY_READ, (void *) arg + ,sizeof(unsigned long *)); + if (error) { + ret_val = error; + break; + } - arg = get_fs_long((unsigned long *) arg); + arg = cy_get_user((unsigned long *) arg); tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); @@ -2300,31 +4117,35 @@ cy_ioctl(struct tty_struct *tty, struct file * file, (struct serial_struct *) arg); break; default: - ret_val = -ENOIOCTLCMD; + ret_val = -ENOIOCTLCMD; } -#ifdef SERIAL_DEBUG_OTHER - printk("cy_ioctl done\n"); +#ifdef CY_DEBUG_OTHER + printk(" cyc:cy_ioctl done\n"); #endif return ret_val; } /* cy_ioctl */ - - +/* + * This routine allows the tty driver to be notified when + * device's termios settings have changed. Note that a + * well-designed tty driver should be prepared to accept the case + * where old == NULL, and try to do something rational. + */ static void cy_set_termios(struct tty_struct *tty, struct termios * old_termios) { struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; -#ifdef SERIAL_DEBUG_OTHER - printk("cy_set_termios ttyC%d\n", info->line); +#ifdef CY_DEBUG_OTHER + printk("cyc:cy_set_termios ttyC%d\n", info->line); #endif if (tty->termios->c_cflag == old_termios->c_cflag) return; - config_setup(info); + set_line_char(info); if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { @@ -2341,353 +4162,281 @@ cy_set_termios(struct tty_struct *tty, struct termios * old_termios) } /* cy_set_termios */ +/* + * void (*set_ldisc)(struct tty_struct *tty); + * + * This routine allows the tty driver to be notified when the + * device's termios settings have changed. + * + */ + + +/* This routine is called by the upper-layer tty layer to signal + that incoming characters should be throttled because the input + buffers are close to full. + */ static void -cy_close(struct tty_struct * tty, struct file * filp) +cy_throttle(struct tty_struct * tty) { - struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; unsigned long flags; + unsigned char *base_addr; + int card,chip,channel,index; -#ifdef SERIAL_DEBUG_OTHER - printk("cy_close ttyC%d\n", info->line); -#endif +#ifdef CY_DEBUG_THROTTLE + char buf[64]; - if (!info - || serial_paranoia_check(info, tty->device, "cy_close")){ - return; - } -#ifdef SERIAL_DEBUG_OPEN - printk("cy_close ttyC%d, count = %d\n", info->line, info->count); + printk("cyc:throttle %s: %d....ttyC%d\n", + _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty), info->line); #endif - save_flags(flags); cli(); - - /* If the TTY is being hung up, nothing to do */ - if (tty_hung_up_p(filp)) { - MOD_DEC_USE_COUNT; - restore_flags(flags); - return; + if (serial_paranoia_check(info, tty->device, "cy_throttle")){ + return; } - - if ((tty->count == 1) && (info->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. Info->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk("cy_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; - } -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): decrementing count to %d\n", __LINE__, current->pid, info->count - 1); -#endif - if (--info->count < 0) { -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d: setting count to 0\n", __LINE__); -#endif - info->count = 0; - } - if (info->count) - { - MOD_DEC_USE_COUNT; - restore_flags(flags); - return; + + if (I_IXOFF(tty)) { + info->x_char = STOP_CHAR(tty); + /* Should use the "Send Special Character" feature!!! */ } - info->flags |= ASYNC_CLOSING; - /* - * Save the termios structure, since this port may have - * separate termios for callout and dialin. - */ - if (info->flags & ASYNC_NORMAL_ACTIVE) - info->normal_termios = *tty->termios; - if (info->flags & ASYNC_CALLOUT_ACTIVE) - info->callout_termios = *tty->termios; - if (info->flags & ASYNC_INITIALIZED) - tty_wait_until_sent(tty, 30*HZ); /* 30 seconds timeout */ - shutdown(info); - if (tty->driver.flush_buffer) - tty->driver.flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); - info->event = 0; - info->tty = 0; - if (info->blocked_open) { - if (info->close_delay) { - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + info->close_delay; - schedule(); + + card = info->card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<open_wait); + restore_flags(flags); + } else { + // Nothing to do! } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| - ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_close done\n"); -#endif - MOD_DEC_USE_COUNT; - restore_flags(flags); return; -} /* cy_close */ +} /* cy_throttle */ + /* - * cy_hangup() --- called by tty_hangup() when a hangup is signaled. + * This routine notifies the tty driver that it should signal + * that characters can now be sent to the tty without fear of + * overrunning the input buffers of the line disciplines. */ -void -cy_hangup(struct tty_struct *tty) +static void +cy_unthrottle(struct tty_struct * tty) { - struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_hangup ttyC%d\n", info->line); /* */ -#endif + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + unsigned char *base_addr; + int card,chip,channel,index; - if (serial_paranoia_check(info, tty->device, "cy_hangup")) - return; - - shutdown(info); - info->event = 0; - info->count = 0; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): setting count to 0\n", __LINE__, current->pid); +#ifdef CY_DEBUG_THROTTLE + char buf[64]; + + printk("cyc:unthrottle %s: %d....ttyC%d\n", + _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty), info->line); #endif - info->tty = 0; - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); - wake_up_interruptible(&info->open_wait); -} /* cy_hangup */ + if (serial_paranoia_check(info, tty->device, "cy_unthrottle")){ + return; + } + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + info->x_char = START_CHAR(tty); + /* Should use the "Send Special Character" feature!!! */ + } -/* - * ------------------------------------------------------------ - * cy_open() and friends - * ------------------------------------------------------------ - */ + card = info->card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<driver_data; + unsigned char *base_addr; + int chip,channel,index; unsigned long flags; - int chip, channel,index; - int retval; - char *base_addr; - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (info->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&info->close_wait); - if (info->flags & ASYNC_HUP_NOTIFY){ - return -EAGAIN; - }else{ - return -ERESTARTSYS; - } - } +#ifdef CY_DEBUG_OTHER + printk("cyc:cy_stop ttyC%d\n", info->line); /* */ +#endif - /* - * If this is a callout device, then just make sure the normal - * device isn't being used. - */ - if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { - if (info->flags & ASYNC_NORMAL_ACTIVE){ - return -EBUSY; - } - if ((info->flags & ASYNC_CALLOUT_ACTIVE) && - (info->flags & ASYNC_SESSION_LOCKOUT) && - (info->session != current->session)){ - return -EBUSY; - } - if ((info->flags & ASYNC_CALLOUT_ACTIVE) && - (info->flags & ASYNC_PGRP_LOCKOUT) && - (info->pgrp != current->pgrp)){ - return -EBUSY; - } - info->flags |= ASYNC_CALLOUT_ACTIVE; - return 0; + if (serial_paranoia_check(info, tty->device, "cy_stop")) + return; + + cinfo = &cy_card[info->card]; + channel = info->line - cinfo->first_line; + if (!IS_CYC_Z(*cinfo)) { + index = cinfo->bus_index; + chip = channel>>2; + channel &= 0x03; + base_addr = (unsigned char*) + (cy_card[info->card].base_addr + + (cy_chip_offset[chip]<f_flags & O_NONBLOCK) { - if (info->flags & ASYNC_CALLOUT_ACTIVE){ - return -EBUSY; - } - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } + return; +} /* cy_stop */ - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that - * cy_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->open_wait, &wait); -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready before block: ttyC%d, count = %d\n", - info->line, info->count);/**/ -#endif - info->count--; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): decrementing count to %d\n", __LINE__, current->pid, info->count); + +static void +cy_start(struct tty_struct *tty) +{ + struct cyclades_card *cinfo; + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned char *base_addr; + int chip,channel,index; + unsigned long flags; + +#ifdef CY_DEBUG_OTHER + printk("cyc:cy_start ttyC%d\n", info->line); /* */ #endif - info->blocked_open++; + if (serial_paranoia_check(info, tty->device, "cy_start")) + return; + cinfo = &cy_card[info->card]; channel = info->line - cinfo->first_line; - chip = channel>>2; - channel &= 0x03; index = cinfo->bus_index; - base_addr = (char *) (cinfo->base_addr + (cy_chip_offset[chip]<flags & ASYNC_CALLOUT_ACTIVE)){ - base_addr[CyCAR<state = TASK_INTERRUPTIBLE; - if (tty_hung_up_p(filp) - || !(info->flags & ASYNC_INITIALIZED) ){ - if (info->flags & ASYNC_HUP_NOTIFY) { - retval = -EAGAIN; - }else{ - retval = -ERESTARTSYS; - } - break; - } - save_flags(flags); cli(); - base_addr[CyCAR<flags & ASYNC_CALLOUT_ACTIVE) - && !(info->flags & ASYNC_CLOSING) - && (C_CLOCAL(tty) - || (base_addr[CyMSVR1<signal & ~current->blocked) { - retval = -ERESTARTSYS; - break; - } -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready blocking: ttyC%d, count = %d\n", - info->line, info->count);/**/ -#endif - schedule(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&info->open_wait, &wait); - if (!tty_hung_up_p(filp)){ - info->count++; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): incrementing count to %d\n", __LINE__, current->pid, info->count); -#endif + if (!IS_CYC_Z(*cinfo)) { + chip = channel>>2; + channel &= 0x03; + base_addr = (unsigned char*) + (cy_card[info->card].base_addr + + (cy_chip_offset[chip]<blocked_open--; -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready after blocking: ttyC%d, count = %d\n", - info->line, info->count);/**/ -#endif - if (retval) - return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} /* block_til_ready */ -/* - * This routine is called whenever a serial port is opened. It - * performs the serial-specific initialization for the tty structure. - */ -int -cy_open(struct tty_struct *tty, struct file * filp) -{ - struct cyclades_port *info; - int retval, line; + return; +} /* cy_start */ - line = MINOR(tty->device) - tty->driver.minor_start; - if ((line < 0) || (NR_PORTS <= line)){ - return -ENODEV; - } - info = &cy_port[line]; - if (info->line < 0){ - return -ENODEV; - } -#ifdef SERIAL_DEBUG_OTHER - printk("cy_open ttyC%d\n", info->line); /* */ -#endif - if (serial_paranoia_check(info, tty->device, "cy_open")){ - return -ENODEV; - } -#ifdef SERIAL_DEBUG_OPEN - printk("cy_open ttyC%d, count = %d\n", info->line, info->count);/**/ -#endif - info->count++; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): incrementing count to %d\n", __LINE__, current->pid, info->count); + +static void +cy_flush_buffer(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + int card, channel; + unsigned long flags; + +#ifdef CY_DEBUG_IO + printk("cyc:cy_flush_buffer ttyC%d\n", info->line); /* */ #endif - tty->driver_data = info; - info->tty = tty; - if (!tmp_buf) { - tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL); - if (!tmp_buf){ - return -ENOMEM; - } - } + if (serial_paranoia_check(info, tty->device, "cy_flush_buffer")) + return; + save_flags(flags); cli(); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + restore_flags(flags); + + card = info->card; + channel = (info->line) - (cy_card[card].first_line); - if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { - if (tty->driver.subtype == SERIAL_TYPE_NORMAL) - *tty->termios = info->normal_termios; - else - *tty->termios = info->callout_termios; - } - /* - * Start up serial port - */ - retval = startup(info); - if (retval){ - return retval; + if (IS_CYC_Z(cy_card[card])) { /* If it is a Z card, flush the on-board + buffers as well */ + static volatile struct FIRM_ID *firm_id; + static volatile struct ZFW_CTRL *zfw_ctrl; + static volatile struct CH_CTRL *ch_ctrl; + static volatile struct BUF_CTRL *buf_ctrl; + + firm_id = (struct FIRM_ID *)(cy_card[card].base_addr + ID_ADDRESS); + zfw_ctrl = (struct ZFW_CTRL *) (cy_card[card].base_addr + + cy_readl(&firm_id->zfwctrl_addr)); + ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); + buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); + + while (cy_readl(&buf_ctrl->tx_get) != cy_readl(&buf_ctrl->tx_put)) + cy_writel(&buf_ctrl->tx_put, cy_readl(&buf_ctrl->tx_get)); } + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} /* cy_flush_buffer */ - MOD_INC_USE_COUNT; - retval = block_til_ready(tty, filp, info); - if (retval) { -#ifdef SERIAL_DEBUG_OPEN - printk("cy_open returning after block_til_ready with %d\n", - retval); +/* + * cy_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void +cy_hangup(struct tty_struct *tty) +{ + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + +#ifdef CY_DEBUG_OTHER + printk("cyc:cy_hangup ttyC%d\n", info->line); /* */ #endif - return retval; - } - info->session = current->session; - info->pgrp = current->pgrp; + if (serial_paranoia_check(info, tty->device, "cy_hangup")) + return; -#ifdef SERIAL_DEBUG_OPEN - printk("cy_open done\n");/**/ + cy_flush_buffer(tty); + shutdown(info); + info->event = 0; + info->count = 0; +#ifdef CY_DEBUG_COUNT + printk("cyc:cy_hangup (%d): setting count to 0\n", current->pid); #endif - return 0; -} /* cy_open */ - + info->tty = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + wake_up_interruptible(&info->open_wait); +} /* cy_hangup */ /* @@ -2698,33 +4447,25 @@ cy_open(struct tty_struct *tty, struct file * filp) * --------------------------------------------------------------------- */ -/* - * This routine prints out the appropriate serial driver version - * number, and identifies which options were configured into this - * driver. - */ -static void -show_version(void) -{ - printk("Cyclom driver %s\n",rcsid); -} /* show_version */ - -/* initialize chips on card -- return number of valid +/* initialize chips on Cyclom-Y card -- return number of valid chips (which is number of ports/4) */ -int -cy_init_card(unsigned char *true_base_addr,int index) +__initfunc(static unsigned short +cyy_init_card(volatile ucchar *true_base_addr,int index)) { unsigned int chip_number; - unsigned char* base_addr; + volatile ucchar* base_addr; - true_base_addr[Cy_HwReset<= CD1400_REV_J){ + /* It is a CD1400 rev. J or later */ + /* Impossible to reach 5ms with this chip. + Changed to 2ms instead (f = 500 Hz). */ + cy_writeb((u_long)base_addr+(CyPPR< NR_PORTS) { + printk("Cyclom-Y/ISA found at 0x%lx ", + (unsigned long) cy_isa_address); + printk("but no more channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); + return(nboard); + } + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclom-Y/ISA found at 0x%lx ", + (unsigned long) cy_isa_address); + printk("but no more cards can be used .\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + return(nboard); + } + + /* allocate IRQ */ + if(request_irq(cy_isa_irq, cyy_interrupt, + SA_INTERRUPT, "cyclomY", NULL)) + { + printk("Cyclom-Y/ISA found at 0x%lx ", + (unsigned long) cy_isa_address); + printk("but could not allocate IRQ#%d.\n", + cy_isa_irq); + return(nboard); + } + + /* set cy_card */ + cy_card[j].base_addr = (u_long) cy_isa_address; + cy_card[j].ctl_addr = 0; + cy_card[j].irq = (int) cy_isa_irq; + cy_card[j].bus_index = 0; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = cy_isa_nchan/4; + IRQ_cards[cy_isa_irq] = &cy_card[j]; + nboard++; + + /* print message */ + printk("Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d, ", + j+1, (unsigned long) cy_isa_address, + (unsigned long)(cy_isa_address + (CyISA_Ywin - 1)), + cy_isa_irq); + printk("%d channels starting from port %d.\n", + cy_isa_nchan, cy_next_channel); + cy_next_channel += cy_isa_nchan; + } + return(nboard); + +} /* cy_detect_isa */ + +/* + * --------------------------------------------------------------------- + * cy_detect_pci() - Test PCI bus presence and Cyclom-Ye/PCI. + * sets global variables and return the number of PCI boards found. + * --------------------------------------------------------------------- + */ +__initfunc(static int +cy_detect_pci(void)) +{ +#ifdef CONFIG_PCI + unsigned char cyy_bus, cyy_dev_fn, cyy_rev_id; + unsigned long pci_intr_ctrl; + unsigned char cy_pci_irq; + uclong cy_pci_addr0, cy_pci_addr1, cy_pci_addr2; + unsigned short i,j,cy_pci_nchan; + unsigned short device_id,dev_index = 0,board_index = 0; + uclong mailbox; + uclong Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0; + + if(pcibios_present() == 0) { /* PCI bus not present */ + return(0); + } + for (i = 0; i < NR_CARDS; i++) { + /* look for a Cyclades card by vendor and device id */ + while((device_id = cy_pci_dev_id[dev_index]) != 0) { + if(pcibios_find_device(PCI_VENDOR_ID_CYCLADES, + device_id,board_index, + &cyy_bus, &cyy_dev_fn) != 0) + { + dev_index++; /* try next device id */ + board_index = 0; + } else { + board_index++; + break; /* found a board */ + } + } + + if (device_id == 0) + break; + + /* read PCI configuration area */ + pcibios_read_config_byte(cyy_bus, cyy_dev_fn, + PCI_INTERRUPT_LINE, &cy_pci_irq); + pcibios_read_config_dword(cyy_bus, cyy_dev_fn, + PCI_BASE_ADDRESS_0, + (unsigned int *) &cy_pci_addr0); + pcibios_read_config_dword(cyy_bus, cyy_dev_fn, + PCI_BASE_ADDRESS_1, + (unsigned int *) &cy_pci_addr1); + pcibios_read_config_dword(cyy_bus, cyy_dev_fn, + PCI_BASE_ADDRESS_2, + (unsigned int *) &cy_pci_addr2); + pcibios_read_config_byte(cyy_bus, cyy_dev_fn, + PCI_REVISION_ID, &cyy_rev_id); + + if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) + || (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){ +#ifdef CY_PCI_DEBUG + printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ", + cyy_bus, cyy_dev_fn); + printk("rev_id=%d) IRQ%d\n", + cyy_rev_id, (int)cy_pci_irq); + printk("Cyclom-Y/PCI:found winaddr=0x%lx ioaddr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr1); +#endif + cy_pci_addr1 &= PCI_BASE_ADDRESS_IO_MASK; + cy_pci_addr2 &= PCI_BASE_ADDRESS_MEM_MASK; + +#if defined(__alpha__) + if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */ + printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ", + cyy_bus, cyy_dev_fn); + printk("rev_id=%d) IRQ%d\n", + cyy_rev_id, (int)cy_pci_irq); + printk("Cyclom-Y/PCI:found winaddr=0x%lx ioaddr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr1); + printk("Cyclom-Y/PCI not supported for low addresses in " + "Alpha systems.\n"); + i--; + continue; + } +#else + if ((ulong)cy_pci_addr2 >= 0x100000) /* above 1M? */ + cy_pci_addr2 = (ulong) ioremap(cy_pci_addr2, CyPCI_Ywin); +#endif + +#ifdef CY_PCI_DEBUG + printk("Cyclom-Y/PCI: relocate winaddr=0x%lx ioaddr=0x%lx\n", + (u_long)cy_pci_addr2, (u_long)cy_pci_addr1); +#endif + cy_pci_nchan = (unsigned short)(CyPORTS_PER_CHIP * + cyy_init_card((volatile ucchar *)cy_pci_addr2, 1)); + if(cy_pci_nchan == 0) { + printk("Cyclom-Y PCI host card with "); + printk("no Serial-Modules at 0x%lx.\n", + (ulong) cy_pci_addr2); + i--; + continue; + } + if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { + printk("Cyclom-Y/PCI found at 0x%lx ", + (ulong) cy_pci_addr2); + printk("but no channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); + return(i); + } + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclom-Y/PCI found at 0x%lx ", + (ulong) cy_pci_addr2); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + return(i); + } + + /* allocate IRQ */ + if(request_irq(cy_pci_irq, cyy_interrupt, + SA_INTERRUPT, "cyclomY", NULL)) + { + printk("Cyclom-Y/PCI found at 0x%lx ", + (ulong) cy_pci_addr2); + printk("but could not allocate IRQ%d.\n", + cy_pci_irq); + return(i); + } + + /* set cy_card */ + cy_card[j].base_addr = (ulong)cy_pci_addr2; + cy_card[j].ctl_addr = 0; + cy_card[j].irq = (int) cy_pci_irq; + cy_card[j].bus_index = 1; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = cy_pci_nchan/4; + IRQ_cards[cy_pci_irq] = &cy_card[j]; + + /* enable interrupts in the PCI interface */ + outw(inw(cy_pci_addr1+0x68)|0x0900,cy_pci_addr1+0x68); + pci_intr_ctrl = (unsigned long) + (inw(cy_pci_addr1+0x68) + | inw(cy_pci_addr1+0x6a)<<16); + + /* print message */ + printk("Cyclom-Y/PCI #%d: 0x%lx-0x%lx, IRQ%d, ", + j+1, + (ulong)cy_pci_addr2, + (ulong)(cy_pci_addr2 + CyPCI_Ywin - 1), + (int)cy_pci_irq); + printk("%d channels starting from port %d.\n", + cy_pci_nchan, cy_next_channel); + + cy_next_channel += cy_pci_nchan; + }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){ + /* print message */ + printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ", + cyy_bus, cyy_dev_fn); + printk("rev_id=%d) IRQ%d\n", + cyy_rev_id, (int)cy_pci_irq); + printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr0); + printk("Cyclades-Z/PCI not supported for low addresses\n"); + break; + }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){ +#ifdef CY_PCI_DEBUG + printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ", + cyy_bus, cyy_dev_fn); + printk("rev_id=%d) IRQ%d\n", + cyy_rev_id, (int)cy_pci_irq); + printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr0); +#endif + cy_pci_addr0 &= PCI_BASE_ADDRESS_MEM_MASK; +#if !defined(__alpha__) + cy_pci_addr0 = (unsigned int) ioremap( + cy_pci_addr0 & PAGE_MASK, + PAGE_ALIGN(CyPCI_Zctl)) + + (cy_pci_addr0 & (PAGE_SIZE-1)); +#endif + mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) + cy_pci_addr0)->mail_box_0); + cy_pci_addr2 &= PCI_BASE_ADDRESS_MEM_MASK; + if (mailbox == ZE_V1) { +#if !defined(__alpha__) + cy_pci_addr2 = (unsigned int) ioremap( + cy_pci_addr2 & PAGE_MASK, + PAGE_ALIGN(CyPCI_Ze_win)) + + (cy_pci_addr2 & (PAGE_SIZE-1)); +#endif + if (ZeIndex == NR_CARDS) { + printk("Cyclades-Ze/PCI found at 0x%lx ", + (ulong)cy_pci_addr2); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + } else { + Ze_addr0[ZeIndex] = cy_pci_addr0; + Ze_addr2[ZeIndex] = cy_pci_addr2; + ZeIndex++; + } + i--; + continue; + } else { +#if !defined(__alpha__) + cy_pci_addr2 = (unsigned int) ioremap( + cy_pci_addr2 & PAGE_MASK, + PAGE_ALIGN(CyPCI_Zwin)) + + (cy_pci_addr2 & (PAGE_SIZE-1)); +#endif + } + +#ifdef CY_PCI_DEBUG + printk("Cyclades-Z/PCI: relocate winaddr=0x%lx ctladdr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr0); + if (mailbox == ZO_V1) { + cy_writel(&((struct RUNTIME_9060 *) + (cy_pci_addr0))->loc_addr_base, WIN_CREG); + PAUSE + printk("Cyclades-8Zo/PCI: FPGA id %lx, ver %lx\n", + (ulong)(0xff & cy_readl(&((struct CUSTOM_REG *) + (cy_pci_addr2))->fpga_id)), + (ulong)(0xff & cy_readl(&((struct CUSTOM_REG *) + (cy_pci_addr2))->fpga_version))); + cy_writel(&((struct RUNTIME_9060 *) + (cy_pci_addr0))->loc_addr_base, WIN_RAM); + } else { + printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n"); + } +#endif + /* The following clears the firmware id word. This ensures + that the driver will not attempt to talk to the board + until it has been properly initialized. + */ + PAUSE + if ((mailbox == ZO_V1) || (mailbox == ZO_V2)) + cy_writel((ulong)(cy_pci_addr2+ID_ADDRESS), 0L); + + /* This must be a Cyclades-8Zo/PCI. The extendable + version will have a different device_id and will + be allocated its maximum number of ports. */ + cy_pci_nchan = 8; + + if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { + printk("Cyclades-8Zo/PCI found at 0x%lx ", + (ulong)cy_pci_addr2); + printk("but no channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); + return(i); + } + + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclades-8Zo/PCI found at 0x%lx ", + (ulong)cy_pci_addr2); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + return(i); + } + + /* allocate IRQ only if board has an IRQ */ + if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) { + if(request_irq(cy_pci_irq,cyz_interrupt, + SA_INTERRUPT,"cyclomZ",NULL)) + { + printk("Could not allocate IRQ%d ", + cy_pci_irq); + printk("for Cyclades-8Zo/PCI at 0x%lx.\n", + (ulong)cy_pci_addr2); + return(i); + } + } + + + /* set cy_card */ + cy_card[j].base_addr = cy_pci_addr2; + cy_card[j].ctl_addr = cy_pci_addr0; + cy_card[j].irq = (int) cy_pci_irq; + cy_card[j].bus_index = 1; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = 1; + IRQ_cards[cy_pci_irq] = &cy_card[j]; + + /* print message */ + /* don't report IRQ if board is no IRQ */ + if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) { + printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, IRQ%d, ", + j+1,(ulong)cy_pci_addr2, + (ulong)(cy_pci_addr2 + CyPCI_Zwin - 1), + (int)cy_pci_irq); + }else{ + printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, ", + j+1,(ulong)cy_pci_addr2, + (ulong)(cy_pci_addr2 + CyPCI_Zwin - 1)); + } + printk("%d channels starting from port %d.\n", + cy_pci_nchan,cy_next_channel); + cy_next_channel += cy_pci_nchan; + } + } + + for (; ZeIndex != 0 && i < NR_CARDS; i++) { + cy_pci_addr0 = Ze_addr0[0]; + cy_pci_addr2 = Ze_addr2[0]; + for (j = 0 ; j < ZeIndex-1 ; j++) { + Ze_addr0[j] = Ze_addr0[j+1]; + Ze_addr2[j] = Ze_addr2[j+1]; + } + ZeIndex--; + mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) + cy_pci_addr0)->mail_box_0); +#ifdef CY_PCI_DEBUG + printk("Cyclades-Z/PCI: relocate winaddr=0x%lx ctladdr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr0); + printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n"); +#endif + /* The following clears the firmware id word. This ensures + that the driver will not attempt to talk to the board + until it has been properly initialized. + */ + PAUSE + /* This must be the new Cyclades-Ze/PCI. */ + cy_pci_nchan = ZE_V1_NPORTS; + + if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { + printk("Cyclades-Ze/PCI found at 0x%lx ", + (ulong)cy_pci_addr2); + printk("but no channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); + return(i); + } + + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclades-Ze/PCI found at 0x%lx ", + (ulong)cy_pci_addr2); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + return(i); + } + + /* allocate IRQ only if board has an IRQ */ + if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) { + if(request_irq(cy_pci_irq,cyz_interrupt, + SA_INTERRUPT,"cyclomZ",NULL)) + { + printk("Could not allocate IRQ%d ", + cy_pci_irq); + printk("for Cyclades-Ze/PCI at 0x%lx.\n", + (ulong) cy_pci_addr2); + return(i); + } + } + + /* set cy_card */ + cy_card[j].base_addr = cy_pci_addr2; + cy_card[j].ctl_addr = cy_pci_addr0; + cy_card[j].irq = (int) cy_pci_irq; + cy_card[j].bus_index = 1; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = 1; + IRQ_cards[cy_pci_irq] = &cy_card[j]; + + /* print message */ + /* don't report IRQ if board is no IRQ */ + if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) { + printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, IRQ%d, ", + j+1,(ulong)cy_pci_addr2, + (ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1), + (int)cy_pci_irq); + }else{ + printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, ", + j+1,(ulong)cy_pci_addr2, + (ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1)); + } + printk("%d channels starting from port %d.\n", + cy_pci_nchan,cy_next_channel); + cy_next_channel += cy_pci_nchan; + } + if (ZeIndex != 0) { + printk("Cyclades-Ze/PCI found at 0x%x ", + (unsigned int) Ze_addr2[0]); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + } + return(i); +#else + return(0); +#endif /* ifdef CONFIG_PCI */ +} /* cy_detect_pci */ + + +/* + * This routine prints out the appropriate serial driver version number + * and identifies which options were configured into this driver. + */ +static inline void +show_version(void) +{ + char *rcsvers, *rcsdate, *tmp; + rcsvers = strchr(rcsid, ' '); rcsvers++; + tmp = strchr(rcsvers, ' '); *tmp++ = '\0'; + rcsdate = strchr(tmp, ' '); rcsdate++; + tmp = strrchr(rcsdate, ' '); *tmp = '\0'; + printk("Cyclades driver %s %s\n", + rcsvers, rcsdate); + printk(" built %s %s\n", + __DATE__, __TIME__); +} /* show_version */ + /* The serial driver boot-time initialization code! Hardware I/O ports are mapped to character special devices on a @@ -2789,15 +5049,21 @@ cy_init_card(unsigned char *true_base_addr,int index) device driver because the Cyclom is more properly a multiplexer, not just an aggregation of serial ports on one card. - If there are more cards with more ports than have been statically - allocated above, a warning is printed and the extra ports are ignored. + If there are more cards with more ports than have been + statically allocated above, a warning is printed and the + extra ports are ignored. */ -int -cy_init(void) + +__initfunc(int +cy_init(void)) { - struct cyclades_port *info; + struct cyclades_port *info; struct cyclades_card *cinfo; - int board,port,i; + int number_z_boards = 0; + int board,port,i,index; + unsigned long mailbox; + unsigned short chip_number; + int nports; show_version(); @@ -2813,7 +5079,7 @@ cy_init(void) cy_serial_driver.subtype = SERIAL_TYPE_NORMAL; cy_serial_driver.init_termios = tty_std_termios; cy_serial_driver.init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; + B9600 | CS8 | CREAD | HUPCL | CLOCAL; cy_serial_driver.flags = TTY_DRIVER_REAL_RAW; cy_serial_driver.refcount = &serial_refcount; cy_serial_driver.table = serial_table; @@ -2845,31 +5111,31 @@ cy_init(void) cy_callout_driver.subtype = SERIAL_TYPE_CALLOUT; if (tty_register_driver(&cy_serial_driver)) - panic("Couldn't register Cyclom serial driver\n"); + panic("Couldn't register Cyclades serial driver\n"); if (tty_register_driver(&cy_callout_driver)) - panic("Couldn't register Cyclom callout driver\n"); + panic("Couldn't register Cyclades callout driver\n"); init_bh(CYCLADES_BH, do_cyclades_bh); for (i = 0; i < 16; i++) { - IRQ_cards[i] = 0; + IRQ_cards[i] = 0; } for (i = 0; i < NR_CARDS; i++) { - /* base_addr=0 indicates board not found */ - cy_card[i].base_addr = 0; + /* base_addr=0 indicates board not found */ + cy_card[i].base_addr = 0; } /* the code below is responsible to find the boards. Each different type of board has its own detection routine. If a board is found, the next cy_card structure available is set by the detection - routine. These functions are responsible for checking the availability - of cy_card and cy_port data structures and updating the - cy_next_channel. */ + routine. These functions are responsible for checking the + availability of cy_card and cy_port data structures and updating + the cy_next_channel. */ /* look for isa boards */ cy_isa_nboard = cy_detect_isa(); - + /* look for pci boards */ cy_pci_nboard = cy_detect_pci(); @@ -2877,288 +5143,225 @@ cy_init(void) /* invalidate remaining cy_card structures */ for (i = 0 ; i < NR_CARDS ; i++) { - if (cy_card[i].base_addr == 0) { - cy_card[i].first_line = -1; - } + if (cy_card[i].base_addr == 0) { + cy_card[i].first_line = -1; + cy_card[i].ctl_addr = 0; + cy_card[i].irq = 0; + cy_card[i].bus_index = 0; + cy_card[i].first_line = 0; + cy_card[i].num_chips = 0; + } } /* invalidate remaining cy_port structures */ for (i = cy_next_channel ; i < NR_PORTS ; i++) { - cy_port[i].line = -1; - cy_port[i].magic = -1; + cy_port[i].line = -1; + cy_port[i].magic = -1; } /* initialize per-port data structures for each valid board found */ for (board = 0 ; board < cy_nboard ; board++) { - cinfo = &cy_card[board]; - for (port = cinfo->first_line ; - port < cinfo->first_line + 4*cinfo->num_chips ; - port++) - { - info = &cy_port[port]; - info->magic = CYCLADES_MAGIC; - info->type = PORT_CIRRUS; - info->card = board; - info->line = port; - info->flags = STD_COM_FLAGS; - info->tty = 0; - info->xmit_fifo_size = 12; - info->cor1 = CyPARITY_NONE|Cy_1_STOP|Cy_8_BITS; - info->cor2 = CyETC; - info->cor3 = 0x08; /* _very_ small receive threshold */ - info->cor4 = 0; - info->cor5 = 0; - info->tbpr = baud_bpr[13]; /* Tx BPR */ - info->tco = baud_co[13]; /* Tx CO */ - info->rbpr = baud_bpr[13]; /* Rx BPR */ - info->rco = baud_co[13]; /* Rx CO */ - info->close_delay = 0; - info->x_char = 0; - info->event = 0; - info->count = 0; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d: setting count to 0\n", __LINE__); -#endif - info->blocked_open = 0; - info->default_threshold = 0; - info->default_timeout = 0; - info->tqueue.routine = do_softint; - info->tqueue.data = info; - info->callout_termios =cy_callout_driver.init_termios; - info->normal_termios = cy_serial_driver.init_termios; - info->open_wait = 0; - info->close_wait = 0; - /* info->session */ - /* info->pgrp */ - info->read_status_mask = CyTIMEOUT| CySPECHAR| CyBREAK - | CyPARITY| CyFRAME| CyOVERRUN; - /* info->timeout */ - } + cinfo = &cy_card[board]; + if (cinfo->num_chips == 1){ /* Cyclades-Z */ + number_z_boards++; + mailbox = cy_readl(&((struct RUNTIME_9060 *) + cy_card[board].ctl_addr)->mail_box_0); + nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8; + for (port = cinfo->first_line ; + port < cinfo->first_line + nports; + port++) + { + info = &cy_port[port]; + info->magic = CYCLADES_MAGIC; + info->type = PORT_STARTECH; + info->card = board; + info->line = port; + info->chip_rev = 0; + info->flags = STD_COM_FLAGS; + info->tty = 0; + if (mailbox == ZO_V1) + info->xmit_fifo_size = CYZ_FIFO_SIZE; + else + info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE; + info->cor1 = 0; + info->cor2 = 0; + info->cor3 = 0; + info->cor4 = 0; + info->cor5 = 0; + info->tbpr = 0; + info->tco = 0; + info->rbpr = 0; + info->rco = 0; + info->close_delay = 5*HZ/10; + info->closing_wait = CLOSING_WAIT_DELAY; + info->x_char = 0; + info->event = 0; + info->count = 0; +#ifdef CY_DEBUG_COUNT +// printk("cyc:cy_init(1) setting Z count to 0\n"); +#endif + info->blocked_open = 0; + info->default_threshold = 0; + info->default_timeout = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->callout_termios = + cy_callout_driver.init_termios; + info->normal_termios = + cy_serial_driver.init_termios; + info->open_wait = 0; + info->close_wait = 0; + info->shutdown_wait = 0; + /* info->session */ + /* info->pgrp */ + info->read_status_mask = 0; + /* info->timeout */ + /* Bentson's vars */ + info->jiffies[0] = 0; + info->jiffies[1] = 0; + info->jiffies[2] = 0; + info->rflush_count = 0; + } + continue; + }else{ /* Cyclom-Y of some kind*/ + index = cinfo->bus_index; + for (port = cinfo->first_line ; + port < cinfo->first_line + 4*cinfo->num_chips ; + port++) + { + info = &cy_port[port]; + info->magic = CYCLADES_MAGIC; + info->type = PORT_CIRRUS; + info->card = board; + info->line = port; + info->flags = STD_COM_FLAGS; + info->tty = 0; + info->xmit_fifo_size = CyMAX_CHAR_FIFO; + info->cor1 = CyPARITY_NONE|Cy_1_STOP|Cy_8_BITS; + info->cor2 = CyETC; + info->cor3 = 0x08; /* _very_ small rcv threshold */ + info->cor4 = 0; + info->cor5 = 0; + info->close_delay = 5*HZ/10; + info->closing_wait = CLOSING_WAIT_DELAY; + chip_number = (port - cinfo->first_line) / 4; + if ((info->chip_rev = cy_readb(cinfo->base_addr + + (cy_chip_offset[chip_number]<= CD1400_REV_J) { + /* It is a CD1400 rev. J or later */ + info->tbpr = baud_bpr_60[13]; /* Tx BPR */ + info->tco = baud_co_60[13]; /* Tx CO */ + info->rbpr = baud_bpr_60[13]; /* Rx BPR */ + info->rco = baud_co_60[13]; /* Rx CO */ + info->rflow = 0; + info->rtsdtr_inv = 1; + } else { + info->tbpr = baud_bpr_25[13]; /* Tx BPR */ + info->tco = baud_co_25[13]; /* Tx CO */ + info->rbpr = baud_bpr_25[13]; /* Rx BPR */ + info->rco = baud_co_25[13]; /* Rx CO */ + info->rflow = 0; + info->rtsdtr_inv = 0; + } + info->x_char = 0; + info->event = 0; + info->count = 0; +#ifdef CY_DEBUG_COUNT +// printk("cyc:cy_init(2) setting Y count to 0\n"); +#endif + info->blocked_open = 0; + info->default_threshold = 0; + info->default_timeout = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->callout_termios = + cy_callout_driver.init_termios; + info->normal_termios = + cy_serial_driver.init_termios; + info->open_wait = 0; + info->close_wait = 0; + info->shutdown_wait = 0; + /* info->session */ + /* info->pgrp */ + info->read_status_mask = + CyTIMEOUT| CySPECHAR| CyBREAK + | CyPARITY| CyFRAME| CyOVERRUN; + /* info->timeout */ + } + } + } + + if ( number_z_boards && !cyz_timeron){ + cyz_timeron++; + cyz_timerlist.expires = jiffies + 1; + add_timer(&cyz_timerlist); +#ifdef CY_PCI_DEBUG + printk("Cyclades-Z polling initialized\n"); +#endif } + return 0; } /* cy_init */ #ifdef MODULE +/* See linux/drivers/char/riscom.c for ideas on how to + pass additional base addresses to the driver!!! */ int init_module(void) { return(cy_init()); -} +} /* init_module */ void cleanup_module(void) { int i; + unsigned long flags; + + if (cyz_timeron){ + cyz_timeron = 0; + del_timer(&cyz_timerlist); + } + save_flags(flags); cli(); + free_page((unsigned long)tmp_buf); if (tty_unregister_driver(&cy_callout_driver)) - printk("Couldn't unregister Cyclom callout driver\n"); + printk("Couldn't unregister Cyclades callout driver\n"); if (tty_unregister_driver(&cy_serial_driver)) - printk("Couldn't unregister Cyclom serial driver\n"); + printk("Couldn't unregister Cyclades serial driver\n"); + + restore_flags(flags); for (i = 0; i < NR_CARDS; i++) { - if (cy_card[i].base_addr != 0) - { - free_irq(cy_card[i].irq,NULL); - } + if (cy_card[i].base_addr != 0 + && cy_card[i].irq) + { + free_irq(cy_card[i].irq,NULL); + } } -} -#endif - -/* - * --------------------------------------------------------------------- - * cy_detect_isa() - Probe for Cyclom-Y/ISA boards. - * sets global variables and return the number of ISA boards found. - * --------------------------------------------------------------------- - */ -int -cy_detect_isa() -{ - unsigned int cy_isa_irq,nboard; - unsigned char *cy_isa_address; - unsigned short i,j,cy_isa_nchan; - - nboard = 0; - - /* scan the address table probing for Cyclom-Y/ISA boards */ - for (i = 0 ; i < NR_ISA_ADDRESSES ; i++) { - cy_isa_address = cy_isa_addresses[i]; - if (cy_isa_address == 0x0000) { - return(nboard); - } - - /* probe for CD1400... */ - cy_isa_nchan = 4 * cy_init_card(cy_isa_address,0); - if (cy_isa_nchan == 0) { - continue; - } - - /* find out the board's irq by probing */ - cy_isa_irq = do_auto_irq(cy_isa_address); - if (cy_isa_irq == 0) { - printk("Cyclom-Y/ISA found at 0x%x but the IRQ could not be detected.\n", - (unsigned int) cy_isa_address); - continue; - } - - if((cy_next_channel+cy_isa_nchan) > NR_PORTS) { - printk("Cyclom-Y/ISA found at 0x%x but no more channel structures are available.\n", - (unsigned int) cy_isa_address); - return(nboard); - } - /* fill the next cy_card structure available */ - for (j = 0 ; j < NR_CARDS ; j++) { - if (cy_card[j].base_addr == 0) break; - } - if (j == NR_CARDS) { /* no more cy_cards available */ - printk("Cyclom-Y/ISA found at 0x%x but no more card structures are available.\n", - (unsigned int) cy_isa_address); - return(nboard); - } - - /* allocate IRQ */ - if(request_irq(cy_isa_irq,cy_interrupt,SA_INTERRUPT,"cyclades",NULL)) - { - printk("Cyclom-Y/ISA found at 0x%x but could not allocate interrupt IRQ#%d.\n", - (unsigned int) cy_isa_address,cy_isa_irq); - return(nboard); - } - - /* set cy_card */ - cy_card[j].base_addr = (int) cy_isa_address; - cy_card[j].irq = (int) cy_isa_irq; - cy_card[j].bus_index = 0; - cy_card[j].first_line = cy_next_channel; - cy_card[j].num_chips = cy_isa_nchan/4; - IRQ_cards[cy_isa_irq] = &cy_card[j]; - nboard++; - - /* print message */ - printk("Cyclom-Y/ISA #%d: 0x%x-0x%x, IRQ%d, %d channels starting from port %d.\n", - j+1,(unsigned int) cy_isa_address, - (unsigned int)(cy_isa_address + 0x1fff), - cy_isa_irq,cy_isa_nchan,cy_next_channel); - cy_next_channel += cy_isa_nchan; - } - return(nboard); - -} - -/* - * --------------------------------------------------------------------- - * cy_detect_pci() - Test PCI bus presence and Cyclom-Ye/PCI. - * sets global variables and return the number of PCI boards found. - * --------------------------------------------------------------------- - */ -int -cy_detect_pci() +} /* cleanup_module */ +#else +/* called by linux/init/main.c to parse command line options */ +void +cy_setup(char *str, int *ints) { -#ifdef CONFIG_PCI - unsigned char cyy_bus, cyy_dev_fn, cyy_rev_id; - unsigned long pci_intr_ctrl; - unsigned char cy_pci_irq; - unsigned int cy_pci_address, cy_pci_io; - unsigned short i,j,cy_pci_nchan; - unsigned short device_id,dev_index = 0,board_index = 0; - - if(pcibios_present() == 0) { /* PCI bus not present */ - return(0); - } - for (i = 0; i < NR_CARDS; i++) { - /* look for a Cyclom-Y card by vendor and device id */ - while((device_id = cy_pci_dev_id[dev_index]) != 0) { - if(pcibios_find_device(PCI_VENDOR_ID_CYCLADES, - device_id,board_index, - &cyy_bus, &cyy_dev_fn) != 0) - { - dev_index++; /* try next device id */ - board_index = 0; - } else { - board_index++; - break; /* found a board */ - } - } - if (device_id == 0) break; - - /* read PCI configuration area */ - pcibios_read_config_byte(cyy_bus, cyy_dev_fn, - PCI_INTERRUPT_LINE, &cy_pci_irq); - pcibios_read_config_dword(cyy_bus, cyy_dev_fn, - PCI_BASE_ADDRESS_1, &cy_pci_io); - pcibios_read_config_dword(cyy_bus, cyy_dev_fn, - PCI_BASE_ADDRESS_2, &cy_pci_address); - pcibios_read_config_byte(cyy_bus, cyy_dev_fn, - PCI_REVISION_ID, &cyy_rev_id); - cy_pci_address &= 0xfffffff0; - if ((ulong)cy_pci_address >= 0x100000) { /* above 1M? */ - cy_pci_address = - (unsigned int) vremap(cy_pci_address,0x4000); - } - cy_pci_io &= 0xfffffffc; - cy_pci_nchan = 4 * cy_init_card((unsigned char *) - cy_pci_address,1); - if(cy_pci_nchan == 0) { - printk("Cyclom-Y PCI host card with no Serial-Modules at 0x%x.\n", - (unsigned int) cy_pci_address); - continue; - } - if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { - printk("Cyclom-Y/PCI found at 0x%x but no more channel structures are available.\n", - (unsigned int) cy_pci_address); - return(i); - } -#ifdef CY_PCI_DEBUG - printk("Cyclom-Ye/PCI #%d (bus=0x0%x, pci_id=0x%x, rev_id=%d).\n", - i+1,cyy_bus,cyy_dev_fn,cyy_rev_id); - printk("Cyclom-Ye/PCI: found at 0x%x, IRQ%d, ioaddr = 0x%lx.\n", - cy_pci_address,(int)cy_pci_irq,cy_pci_io); -#endif - /* fill the next cy_card structure available */ - for (j = 0 ; j < NR_CARDS ; j++) { - if (cy_card[j].base_addr == 0) break; - } - if (j == NR_CARDS) { /* no more cy_cards available */ - printk("Cyclom-Y/PCI found at 0x%x but no more card structures are available.\n", - (unsigned int) cy_pci_address); - return(i); - } + int i, j; - /* allocate IRQ */ - if(request_irq(cy_pci_irq,cy_interrupt,SA_INTERRUPT,"cyclades",NULL)) - { - printk("Cyclom-Y/PCI found at 0x%x but could not allocate interrupt IRQ%d.\n", - (unsigned int) cy_pci_address,cy_pci_irq); - return(i); - } + for (i = 0 ; i < NR_ISA_ADDRS ; i++) { + if (cy_isa_addresses[i] == 0) break; + } + for (j = 1; j <= ints[0]; j++){ + if ( i < NR_ISA_ADDRS ){ + cy_isa_addresses[i++] = (unsigned char *)(ints[j]); + } + } - /* set cy_card */ - cy_card[j].base_addr = (int) cy_pci_address; - cy_card[j].irq = (int) cy_pci_irq; - cy_card[j].bus_index = 1; - cy_card[j].first_line = cy_next_channel; - cy_card[j].num_chips = cy_pci_nchan/4; - IRQ_cards[cy_pci_irq] = &cy_card[j]; - - /* enable interrupts in the PCI interface */ - outw(inw(cy_pci_io+0x68)|0x0900,cy_pci_io+0x68); - pci_intr_ctrl = (unsigned long)(inw(cy_pci_io+0x68) | inw(cy_pci_io+0x6a)<<16); - - /* print message */ - printk("Cyclom-Y/PCI #%d: 0x%x-0x%x, IRQ%d, %d channels starting from port %d.\n", - j+1,(unsigned int) cy_pci_address, - (unsigned int)(cy_pci_address + 0x3fff), - (int)cy_pci_irq,cy_pci_nchan,cy_next_channel); - - cy_next_channel += cy_pci_nchan; - } - return(i); -#else - return(0); -#endif /* ifdef CONFIG_PCI */ -} +} /* cy_setup */ +#endif -#ifdef CYCLOM_SHOW_STATUS +#ifdef CY_SHOW_STATUS static void show_status(int line_num) { @@ -3183,7 +5386,8 @@ show_status(int line_num) printk(" cy_port\n"); printk(" card line flags = %d %d %x\n", info->card, info->line, info->flags); - printk(" *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x\n", + printk(" *tty read_status_mask timeout xmit_fifo_size ", + printk("= %lx %x %x %x\n", (long)info->tty, info->read_status_mask, info->timeout, info->xmit_fifo_size); printk(" cor1,cor2,cor3,cor4,cor5 = %x %x %x %x %x\n", @@ -3200,59 +5404,60 @@ show_status(int line_num) save_flags(flags); cli(); - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<"); -MODULE_PARM(portbase, "i"); -MODULE_PARM(irq, "2-15i"); -MODULE_PARM(showcapimsgs, "0-3i"); +MODULE_PARM(showcapimsgs, "0-5i"); MODULE_PARM(loaddebug, "0-1i"); #endif @@ -99,7 +155,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"; @@ -111,7 +167,7 @@ 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) (((cp)-cards)+1) @@ -128,6 +184,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) { @@ -271,7 +338,7 @@ void avmb1_handle_free_ncci(avmb1_card * card, } } APPL(appl)->releasing--; - if (APPL(appl)->releasing == 0) { + if (APPL(appl)->releasing <= 0) { APPL(appl)->signal = 0; APPL_MARK_FREE(appl); printk(KERN_INFO "b1capi: appl %d down\n", appl); @@ -404,25 +471,36 @@ static void notify_handler(void *dummy) /* -------- card ready callback ------------------------------- */ + void avmb1_card_ready(avmb1_card * card) { + struct capi_profile *profp = + (struct capi_profile *)card->version[VER_PROFILE]; + char *dversion = card->version[VER_DRIVER]; __u16 appl; + char *cardname, cname[20]; + __u32 flag; + int nbchan = profp->nbchannel; card->cversion.majorversion = 2; card->cversion.minorversion = 0; - card->cversion.majormanuversion = (card->version[VER_DRIVER][0] - '0') << 4; - card->cversion.majormanuversion |= (card->version[VER_DRIVER][2] - '0'); - card->cversion.minormanuversion = (card->version[VER_DRIVER][3] - '0') << 4; - card->cversion.minormanuversion |= (card->version[VER_DRIVER][5] - '0') * 10; - card->cversion.minormanuversion |= (card->version[VER_DRIVER][6] - '0'); + card->cversion.majormanuversion = (((dversion[0] - '0') & 0xf) << 4); + card->cversion.majormanuversion |= ((dversion[2] - '0') & 0xf); + card->cversion.minormanuversion = (dversion[3] - '0') << 4; + card->cversion.minormanuversion |= + (dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf); card->cardstate = CARD_RUNNING; - for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { if (VALID_APPLID(appl) && !APPL(appl)->releasing) { + int nconn, want = APPL(appl)->rparam.level3cnt; + + if (want > 0) nconn = want; + else nconn = nbchan * -want; + if (nconn == 0) nconn = nbchan; + B1_send_register(card->port, appl, - 1024 * (APPL(appl)->rparam.level3cnt+1), - APPL(appl)->rparam.level3cnt, + 1024 * (nconn+1), nconn, APPL(appl)->rparam.datablkcnt, APPL(appl)->rparam.datablklen); } @@ -430,10 +508,45 @@ void avmb1_card_ready(avmb1_card * card) set_bit(CARDNR(card), ¬ify_up_set); queue_task(&tq_state_notify, &tq_scheduler); - printk(KERN_NOTICE "b1capi: card %d ready.\n", CARDNR(card)); + + flag = ((__u8 *)(profp->manu))[1]; + switch (flag) { + case 0: cardname = cardtype2str(card->cardtype); break; + case 3: cardname = "PCMCIA B"; break; + case 4: cardname = "PCMCIA M1"; break; + case 5: cardname = "PCMCIA M2"; break; + case 6: cardname = "B1 V3.0"; break; + case 7: cardname = "B1 PCI"; break; + default: cardname = cname; break; + sprintf(cname, "AVM?%u", (unsigned int)flag); + break; + } + printk(KERN_NOTICE "b1capi: card %d \"%s\" ready.\n", + CARDNR(card), cardname); + flag = ((__u8 *)(profp->manu))[3]; + if (flag) + printk(KERN_NOTICE "b1capi: card %d Protocol:%s%s%s%s%s%s%s\n", + CARDNR(card), + (flag & 0x01) ? " DSS1" : "", + (flag & 0x02) ? " CT1" : "", + (flag & 0x04) ? " VN3" : "", + (flag & 0x08) ? " NI1" : "", + (flag & 0x10) ? " AUSTEL" : "", + (flag & 0x20) ? " ESS" : "", + (flag & 0x40) ? " 1TR6" : "" + ); + flag = ((__u8 *)(profp->manu))[5]; + if (flag) + printk(KERN_NOTICE "b1capi: card %d Linetype:%s%s%s%s\n", + CARDNR(card), + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); } -static void avmb1_card_down(avmb1_card * card) +static void avmb1_card_down(avmb1_card * card, int notify) { __u16 appl; @@ -460,52 +573,135 @@ static void avmb1_card_down(avmb1_card * 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; - card = &cards[ncards]; + for (i=0; i < CAPI_MAXCONTR && cards[i].cardstate != CARD_FREE; i++) ; + + if (i == CAPI_MAXCONTR) { + printk(KERN_ERR "b1capi: out of controller slots\n"); + return -ENFILE; + } + + card = &cards[i]; memset(card, 0, sizeof(avmb1_card)); - sprintf(card->name, "avmb1-%d", ncards + 1); + sprintf(card->name, "avmb1-%d", CARDNR(card)); - request_region(port, AVMB1_PORTLEN, card->name); + if (allocio) + request_region(port, AVMB1_PORTLEN, card->name); - if ((irqval = request_irq(irq, avmb1_interrupt, SA_SHIRQ, card->name, card)) != 0) { + if ((irqval = request_irq(irq, avmb1_interrupt, + SA_SHIRQ, card->name, card)) != 0) { printk(KERN_ERR "b1capi: unable to get IRQ %d (irqval=%d).\n", irq, irqval); release_region((unsigned short) port, AVMB1_PORTLEN); - return -EIO; + return -EBUSY; } + + card->cardstate = CARD_DETECTED; ncards++; - card->cnr = ncards; + card->cnr = CARDNR(card); card->port = port; card->irq = irq; - card->cardstate = CARD_DETECTED; - return 0; + card->cardtype = cardtype; + return card->cnr; } -int avmb1_probecard(int port, int irq) +int avmb1_addcard(int port, int irq, int cardtype) +{ + return avmb1_registercard(port, irq, cardtype, 1); +} + +int avmb1_detectcard(int port, int irq, int cardtype) { int rc; - if (check_region((unsigned short) port, AVMB1_PORTLEN)) { - printk(KERN_WARNING - "b1capi: ports 0x%03x-0x%03x in use.\n", - portbase, portbase + AVMB1_PORTLEN); - return -EIO; + if (!B1_valid_irq(irq, cardtype)) { + printk(KERN_WARNING "b1capi: irq %d not valid for %s-card.\n", + irq, cardtype2str(cardtype)); + return -EINVAL; } - if (!B1_valid_irq(irq)) { - printk(KERN_WARNING "b1capi: irq %d not valid.\n", irq); - return -EIO; + if (!B1_valid_port(port, cardtype)) { + printk(KERN_WARNING "b1capi: port 0x%x not valid for %s-card.\n", + port, cardtype2str(cardtype)); + return -EINVAL; } - if ((rc = B1_detect(port)) != 0) { - printk(KERN_NOTICE "b1capi: NO card at 0x%x (%d)\n", port, rc); + B1_reset(port); + if ((rc = B1_detect(port, cardtype)) != 0) { + printk(KERN_NOTICE "b1capi: NO %s-card at 0x%x (%d)\n", + cardtype2str(cardtype), port, rc); return -EIO; } B1_reset(port); - printk(KERN_NOTICE "b1capi: AVM-B1-Controller detected at 0x%x\n", port); + switch (cardtype) { + default: + case AVM_CARDTYPE_M1: + case AVM_CARDTYPE_M2: + case AVM_CARDTYPE_B1: + printk(KERN_NOTICE "b1capi: AVM-%s-Controller detected at 0x%x\n", cardtype2str(cardtype), port); + break; + case AVM_CARDTYPE_T1: + break; + } + + return 0; +} + +int avmb1_probecard(int port, int irq, int cardtype) +{ + if (check_region((unsigned short) port, AVMB1_PORTLEN)) { + printk(KERN_WARNING + "b1capi: ports 0x%03x-0x%03x in use.\n", + port, port + AVMB1_PORTLEN); + return -EBUSY; + } + return avmb1_detectcard(port, irq, cardtype); +} + +int avmb1_unregistercard(int cnr, int freeio) +{ + avmb1_card * card; + if (!VALID_CARD(cnr)) + return -ESRCH; + card = CARD(cnr); + + if (card->cardstate == CARD_FREE) + return -ESRCH; + if (card->cardstate == CARD_RUNNING) + avmb1_card_down(card, freeio); + + if (card->cardstate != CARD_FREE) + if (card->cardtype == AVM_CARDTYPE_T1) + T1_reset(card->port); + + free_irq(card->irq, card); + if (freeio) + release_region(card->port, AVMB1_PORTLEN); + card->cardstate = CARD_FREE; + return 0; +} + +int avmb1_resetcard(int cnr) +{ + avmb1_card * card; + + if (!VALID_CARD(cnr)) + return -ESRCH; + card = CARD(cnr); + if (card->cardstate == CARD_FREE) + return -ESRCH; + + if (card->cardstate == CARD_RUNNING) + avmb1_card_down(card, 0); + + B1_reset(card->port); + B1_reset(card->port); + + card->cardstate = CARD_DETECTED; return 0; } @@ -517,7 +713,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; } @@ -526,6 +722,7 @@ static int capi_installed(void) static __u16 capi_register(capi_register_params * rparam, __u16 * applidp) { + int nconn, want = rparam->level3cnt; int i; int appl; @@ -544,14 +741,21 @@ 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++) { + struct capi_profile *profp = + (struct capi_profile *)cards[i].version[VER_PROFILE]; + if (cards[i].cardstate != CARD_RUNNING) continue; + + if (want > 0) nconn = want; + else nconn = profp->nbchannel * -want; + if (nconn == 0) nconn = profp->nbchannel; + B1_send_register(cards[i].port, appl, - 1024 * (APPL(appl)->rparam.level3cnt + 1), - APPL(appl)->rparam.level3cnt, - APPL(appl)->rparam.datablkcnt, - APPL(appl)->rparam.datablklen); + 1024 * (nconn+1), nconn, + APPL(appl)->rparam.datablkcnt, + APPL(appl)->rparam.datablklen); } *applidp = appl; printk(KERN_INFO "b1capi: appl %d up\n", appl); @@ -564,20 +768,18 @@ static __u16 capi_release(__u16 applid) struct sk_buff *skb; int i; - if (ncards == 0) - return CAPI_REGNOTINSTALLED; if (!VALID_APPLID(applid) || APPL(applid)->releasing) return CAPI_ILLAPPNR; while ((skb = skb_dequeue(&APPL(applid)->recv_queue)) != 0) kfree_skb(skb, FREE_READ); - for (i = 0; i < ncards; i++) { + for (i = 0; i < CAPI_MAXCONTR; i++) { if (cards[i].cardstate != CARD_RUNNING) { continue; } APPL(applid)->releasing++; B1_send_release(cards[i].port, applid); } - if (APPL(applid)->releasing == 0) { + if (APPL(applid)->releasing <= 0) { APPL(applid)->signal = 0; APPL_MARK_FREE(applid); printk(KERN_INFO "b1capi: appl %d down\n", applid); @@ -661,7 +863,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; } @@ -698,33 +900,87 @@ 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); + if (cdef.cardtype == AVM_CARDTYPE_T1) { + int i; + for (i=0; i < CAPI_MAXCONTR; i++) { + if ( cards[i].cardstate != CARD_FREE + && cards[i].cardtype == AVM_CARDTYPE_T1 + && cards[i].cardnr == cdef.cardnr) { + printk(KERN_ERR + "b1capi: T1-HEMA-card-%d already at 0x%x\n", + cdef.cardnr, cards[i].port); + return -EBUSY; + } + } + rc = T1_detectandinit(cdef.port,cdef.irq,cdef.cardnr); + if (rc) { + printk(KERN_NOTICE "b1capi: NO T1-HEMA-card-%d at 0x%x (%d)\n", + cdef.cardnr, cdef.port, rc); + return -EIO; + } + printk(KERN_NOTICE "b1capi: T1-HEMA-card-%d at 0x%x\n", + cdef.cardnr, cdef.port); + } + + rc = avmb1_addcard(cdef.port, cdef.irq, cdef.cardtype); + if (rc < 0) + return rc; + /* don't want to change interface t + addcard/probecard/registercard */ + if (cdef.cardtype == AVM_CARDTYPE_T1) { + int i; + for (i=0; i < CAPI_MAXCONTR; i++) { + if (cards[i].cnr == rc) { + cards[i].cardnr = cdef.cardnr; + break; + } + } + } + return rc; case AVMB1_LOAD: + case AVMB1_LOAD_AND_CONFIG: + + if (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 (loaddebug) - printk(KERN_DEBUG "b1capi: load: invalid parameter contr=%d len=%d\n", ldef.contr, ldef.t4file.len); + if (ldef.t4file.len <= 0) { + printk(KERN_DEBUG "b1capi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len); return -EINVAL; } @@ -746,13 +1002,34 @@ static int capi_manufacturer(unsigned int cmd, void *data) } B1_reset(card->port); + + if (loaddebug) { + printk(KERN_DEBUG "b1capi: loading contr %d\n", + ldef.contr); + } + if ((rc = B1_load_t4file(card->port, &ldef.t4file))) { B1_reset(card->port); printk(KERN_ERR "b1capi: failed to load t4file!!\n"); card->cardstate = CARD_DETECTED; return rc; } + B1_disable_irq(card->port); + + if (ldef.t4config.len > 0) { /* load config */ + if (loaddebug) { + printk(KERN_DEBUG "b1capi: loading config to contr %d\n", + ldef.contr); + } + if ((rc = B1_load_config(card->port, &ldef.t4config))) { + B1_reset(card->port); + printk(KERN_ERR "b1capi: failed to load config!!\n"); + card->cardstate = CARD_DETECTED; + return rc; + } + } + if (loaddebug) { printk(KERN_DEBUG "b1capi: load: ready contr %d: checking\n", ldef.contr); @@ -770,8 +1047,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_enable_irq(card->port); + B1_setinterrupt(card->port, card->irq, card->cardtype); restore_flags(flags); if (loaddebug) { @@ -782,7 +1058,14 @@ static int capi_manufacturer(unsigned int cmd, void *data) /* * init card */ - B1_send_init(card->port, AVM_NAPPS, AVM_NNCCI, card->cnr - 1); + if (card->cardtype == AVM_CARDTYPE_T1) + B1_send_init(card->port, AVM_NAPPS, + AVM_NNCCI_PER_CHANNEL*30, + card->cnr - 1); + else + B1_send_init(card->port, AVM_NAPPS, + AVM_NNCCI_PER_CHANNEL*2, + card->cnr - 1); if (loaddebug) { printk(KERN_DEBUG "b1capi: load: waiting for init reply contr %d\n", @@ -799,25 +1082,45 @@ 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); - card = CARD(rdef.contr); + case AVMB1_GET_CARDINFO: + if ((rc = copy_from_user((void *) &gdef, data, + sizeof(avmb1_getdef)))) + return rc; - if (card->cardstate == CARD_RUNNING) - avmb1_card_down(card); + if (!VALID_CARD(gdef.contr)) + return -ESRCH; - card->cardstate = CARD_DETECTED; + card = CARD(gdef.contr); - B1_reset(card->port); - B1_reset(card->port); + gdef.cardstate = card->cardstate; + gdef.cardtype = card->cardtype; + + if ((rc = copy_to_user(data, (void *) &gdef, + sizeof(avmb1_getdef)))) + return rc; return 0; + case AVMB1_REMOVECARD: + if ((rc = copy_from_user((void *) &rdef, data, + sizeof(avmb1_resetdef)))) + return rc; + if (!VALID_CARD(rdef.contr)) + return -ESRCH; + + card = CARD(rdef.contr); + + if (card->cardstate != CARD_DETECTED) + return -EBUSY; + + return avmb1_unregistercard(rdef.contr, 1); } return -EINVAL; } @@ -855,6 +1158,7 @@ struct capi_interface *attach_capi_interface(struct capi_interface_user *userp) userp->next = capi_users; capi_users = userp; MOD_INC_USE_COUNT; + printk(KERN_NOTICE "b1capi: %s attached\n", userp->name); return &avmb1_interface; } @@ -868,6 +1172,7 @@ int detach_capi_interface(struct capi_interface_user *userp) *pp = userp->next; userp->next = 0; MOD_DEC_USE_COUNT; + printk(KERN_NOTICE "b1capi: %s detached\n", userp->name); return 0; } } @@ -884,6 +1189,10 @@ EXPORT_SYMBOL(attach_capi_interface); EXPORT_SYMBOL(detach_capi_interface); EXPORT_SYMBOL(avmb1_addcard); EXPORT_SYMBOL(avmb1_probecard); +EXPORT_SYMBOL(avmb1_registercard); +EXPORT_SYMBOL(avmb1_unregistercard); +EXPORT_SYMBOL(avmb1_resetcard); +EXPORT_SYMBOL(avmb1_detectcard); #else static struct symbol_table capidev_syms = { @@ -892,6 +1201,10 @@ static struct symbol_table capidev_syms = X(detach_capi_interface), X(avmb1_addcard), X(avmb1_probecard), + X(avmb1_registercard), + X(avmb1_unregistercard), + X(avmb1_resetcard), + X(avmb1_detectcard), #include }; #endif @@ -933,15 +1246,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 @@ -963,20 +1268,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 7028b5d39977..ae09ed9e0d54 100644 --- a/drivers/isdn/avmb1/b1lli.c +++ b/drivers/isdn/avmb1/b1lli.c @@ -1,11 +1,45 @@ /* - * $Id: b1lli.c,v 1.1.2.1 1997/07/13 12:16:46 calle Exp $ + * $Id: b1lli.c,v 1.1.2.10 1998/03/20 20:34:41 calle Exp $ * * ISDN lowlevel-module for AVM B1-card. * * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1lli.c,v $ + * Revision 1.1.2.10 1998/03/20 20:34:41 calle + * port valid check now only for T1, because of the PCI and PCMCIA cards. + * + * Revision 1.1.2.9 1998/03/20 14:38:20 calle + * capidrv: prepared state machines for suspend/resume/hold + * capidrv: fix bug in state machine if B1/T1 is out of nccis + * b1capi: changed some errno returns. + * b1capi: detect if you try to add same T1 to different io address. + * b1capi: change number of nccis depending on number of channels. + * b1lli: cosmetics + * + * Revision 1.1.2.8 1998/03/18 17:43:29 calle + * T1 with fastlink, bugfix for multicontroller support in capidrv.c + * + * Revision 1.1.2.7 1998/03/04 17:33:50 calle + * Changes for T1. + * + * Revision 1.1.2.6 1998/02/27 15:40:44 calle + * T1 running with slow link. bugfix in capi_release. + * + * Revision 1.1.2.5 1998/02/13 16:28:28 calle + * first step for T1 + * + * Revision 1.1.2.4 1998/01/27 16:12:51 calle + * Support for PCMCIA B1/M1/M2 ready. + * + * Revision 1.1.2.3 1998/01/15 15:33:37 calle + * print cardtype, d2 protocol and linetype after load. + * + * Revision 1.1.2.2 1997/11/26 10:46:55 calle + * prepared for M1 (Mobile) and T1 (PMX) cards. + * prepared to set configuration after load to support other D-channel + * protocols, point-to-point and leased lines. + * * Revision 1.1.2.1 1997/07/13 12:16:46 calle * bug fix for more than one controller in connect_req. * @@ -20,6 +54,7 @@ * * */ +/* #define FASTLINK_DEBUG */ #include #include @@ -34,6 +69,8 @@ #include "capicmd.h" #include "capiutil.h" +extern int showcapimsgs; + /* * LLI Messages to the ISDN-ControllerISDN Controller */ @@ -69,6 +106,11 @@ * B3Length data .... */ +#define SEND_CONFIG 0x21 /* + */ + +#define SEND_POLLACK 0x73 /* T1 Watchdog */ + /* * LLI Messages from the ISDN-ControllerISDN Controller */ @@ -112,6 +154,13 @@ #define RECEIVE_RELEASE 0x26 /* * int32 AppllID int32 0xffffffff */ +#define RECEIVE_TASK_READY 0x31 /* + * int32 tasknr + * int32 Length Taskname ... + */ + +#define WRITE_REGISTER 0x00 +#define READ_REGISTER 0x01 /* * port offsets @@ -124,6 +173,44 @@ #define B1_RESET 0x10 #define B1_ANALYSE 0x04 +/* Hema card T1 */ + +#define T1_FASTLINK 0x00 +#define T1_SLOWLINK 0x08 + +#define T1_READ B1_READ +#define T1_WRITE B1_WRITE +#define T1_INSTAT B1_INSTAT +#define T1_OUTSTAT B1_OUTSTAT +#define T1_IRQENABLE 0x05 +#define T1_FIFOSTAT 0x06 +#define T1_RESETLINK 0x10 +#define T1_ANALYSE 0x11 +#define T1_IRQMASTER 0x12 +#define T1_IDENT 0x17 +#define T1_RESETBOARD 0x1f + +#define T1F_IREADY 0x01 +#define T1F_IHALF 0x02 +#define T1F_IFULL 0x04 +#define T1F_IEMPTY 0x08 +#define T1F_IFLAGS 0xF0 + +#define T1F_OREADY 0x10 +#define T1F_OHALF 0x20 +#define T1F_OEMPTY 0x40 +#define T1F_OFULL 0x80 +#define T1F_OFLAGS 0xF0 + +/* there are HEMA cards with 1k and 4k FIFO out */ +#define FIFO_OUTBSIZE 256 +#define FIFO_INPBSIZE 512 + +#define HEMA_VERSION_ID 0 +#define HEMA_PAL_ID 0 + +#define B1_STAT0(cardtype) ((cardtype) == AVM_CARDTYPE_M1 ? 0x81200000l : 0x80A00000l) +#define B1_STAT1(cardtype) (0x80E00000l) static inline unsigned char b1outp(unsigned short base, @@ -134,6 +221,191 @@ static inline unsigned char b1outp(unsigned short base, return inb(base + B1_ANALYSE); } +static inline void t1outp(unsigned short base, + unsigned short offset, + unsigned char value) +{ + outb(value, base + offset); +} + +static inline unsigned char t1inp(unsigned short base, + unsigned short offset) +{ + return inb(base + offset); +} + +static inline int B1_isfastlink(unsigned short base) +{ + return (inb(base + T1_IDENT) & ~0x82) == 1; +} +static inline unsigned char B1_fifostatus(unsigned short base) +{ + return inb(base + T1_FIFOSTAT); +} + +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 + 1 * HZ; /* maximum wait time 1 sec */ + while (!B1_rx_full(base) && i > jiffies); + if (B1_rx_full(base)) + return inb(base + B1_READ); + printk(KERN_CRIT "b1lli(0x%x): rx not full after 1 second\n", base); + return 0; +} + +static inline unsigned int B1_get_word(unsigned 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; +#ifdef FASTLINK_DEBUG + unsigned wcnt = 0, bcnt = 0; +#endif + + len = i = B1_get_word(base); + if (B1_isfastlink(base)) { + int status; + while (i > 0) { + status = B1_fifostatus(base) & (T1F_IREADY|T1F_IHALF); + if (i >= FIFO_INPBSIZE) status |= T1F_IFULL; + + switch (status) { + case T1F_IREADY|T1F_IHALF|T1F_IFULL: + insb(base+B1_READ, dp, FIFO_INPBSIZE); + dp += FIFO_INPBSIZE; + i -= FIFO_INPBSIZE; +#ifdef FASTLINK_DEBUG + wcnt += FIFO_INPBSIZE; +#endif + break; + case T1F_IREADY|T1F_IHALF: + insb(base+B1_READ,dp, i); +#ifdef FASTLINK_DEBUG + wcnt += i; +#endif + dp += i; + i = 0; + if (i == 0) + break; + /* fall through */ + default: + *dp++ = B1_get_byte(base); + i--; +#ifdef FASTLINK_DEBUG + bcnt++; +#endif + break; + } + } +#ifdef FASTLINK_DEBUG + if (wcnt) + printk(KERN_DEBUG "b1lli(0x%x): get_slice l=%d w=%d b=%d\n", + base, len, wcnt, bcnt); +#endif + } else { + while (i-- > 0) + *dp++ = B1_get_byte(base); + } + return len; +} + +static inline void B1_put_slice(unsigned short base, + unsigned char *dp, unsigned int len) +{ + unsigned i = len; + B1_put_word(base, i); + if (B1_isfastlink(base)) { + int status; + while (i > 0) { + status = B1_fifostatus(base) & (T1F_OREADY|T1F_OHALF); + if (i >= FIFO_OUTBSIZE) status |= T1F_OEMPTY; + switch (status) { + case T1F_OREADY|T1F_OHALF|T1F_OEMPTY: + outsb(base+B1_WRITE, dp, FIFO_OUTBSIZE); + dp += FIFO_OUTBSIZE; + i -= FIFO_OUTBSIZE; + break; + case T1F_OREADY|T1F_OHALF: + outsb(base+B1_WRITE, dp, i); + dp += i; + i = 0; + break; + default: + B1_put_byte(base, *dp++); + i--; + break; + } + } + } else { + while (i-- > 0) + B1_put_byte(base, *dp++); + } +} + +static void b1_wr_reg(unsigned 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, @@ -153,19 +425,79 @@ static int irq_table[16] = 112, /* irq 15 */ }; -int B1_valid_irq(unsigned irq) +static int hema_irq_table[16] = +{0, + 0, + 0, + 0x80, /* irq 3 */ + 0, + 0x90, /* irq 5 */ + 0, + 0xA0, /* irq 7 */ + 0, + 0xB0, /* irq 9 */ + 0xC0, /* irq 10 */ + 0xD0, /* irq 11 */ + 0xE0, /* irq 12 */ + 0, + 0, + 0xF0, /* irq 15 */ +}; + + +int B1_valid_irq(unsigned irq, int cardtype) { - return irq_table[irq] != 0; + switch (cardtype) { + default: + case AVM_CARDTYPE_M1: + case AVM_CARDTYPE_M2: + case AVM_CARDTYPE_B1: + return irq_table[irq & 0xf] != 0; + case AVM_CARDTYPE_T1: + return hema_irq_table[irq & 0xf] != 0; + } } -unsigned char B1_assign_irq(unsigned short base, unsigned irq) +int B1_valid_port(unsigned port, int cardtype) { - return b1outp(base, B1_RESET, irq_table[irq]); + switch (cardtype) { + default: + case AVM_CARDTYPE_M1: + case AVM_CARDTYPE_M2: + case AVM_CARDTYPE_B1: +#if 0 /* problem with PCMCIA and PCI cards */ + switch (port) { + case 0x150: + case 0x250: + case 0x300: + case 0x340: + return 1; + } + return 0; +#else + return 1; +#endif + case AVM_CARDTYPE_T1: + return ((port & 0x7) == 0) && ((port & 0x30) != 0x30); + } } -unsigned char B1_enable_irq(unsigned short base) +void B1_setinterrupt(unsigned short base, + unsigned irq, int cardtype) { - return b1outp(base, B1_INSTAT, 0x02); + switch (cardtype) { + case AVM_CARDTYPE_T1: + t1outp(base, B1_INSTAT, 0x00); + t1outp(base, B1_INSTAT, 0x02); + t1outp(base, T1_IRQMASTER, 0x08); + default: + case AVM_CARDTYPE_M1: + case AVM_CARDTYPE_M2: + case AVM_CARDTYPE_B1: + b1outp(base, B1_INSTAT, 0x00); + b1outp(base, B1_RESET, irq_table[irq]); + b1outp(base, B1_INSTAT, 0x02); + } } unsigned char B1_disable_irq(unsigned short base) @@ -173,6 +505,11 @@ unsigned char B1_disable_irq(unsigned short base) return b1outp(base, B1_INSTAT, 0x00); } +void T1_disable_irq(unsigned short base) +{ + t1outp(base, T1_IRQMASTER, 0x00); +} + void B1_reset(unsigned short base) { b1outp(base, B1_RESET, 0); @@ -185,24 +522,39 @@ void B1_reset(unsigned short base) udelay(55 * 2 * 1000); /* 2 TIC's */ } -int B1_detect(unsigned short base) +void T1_reset(unsigned short base) +{ + /* reset T1 Controller */ + B1_reset(base); + /* disable irq on HEMA */ + t1outp(base, B1_INSTAT, 0x00); + t1outp(base, B1_OUTSTAT, 0x00); + t1outp(base, T1_IRQMASTER, 0x00); + /* reset HEMA board configuration */ + t1outp(base, T1_RESETBOARD, 0xf); +} + +int B1_detect(unsigned 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 */ @@ -211,71 +563,91 @@ 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) +int T1_detectandinit(unsigned short base, unsigned irq, int cardnr) { - 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); -} + unsigned char cregs[8]; + unsigned char reverse_cardnr; + unsigned long flags; + unsigned char dummy; + int i; -static inline unsigned int B1_get_slice(unsigned short base, - unsigned char *dp) -{ - unsigned int len, i; + reverse_cardnr = ((cardnr & 0x01) << 3) | ((cardnr & 0x02) << 1) + | ((cardnr & 0x04) >> 1) | ((cardnr & 0x08) >> 3); + cregs[0] = (HEMA_VERSION_ID << 4) | (reverse_cardnr & 0xf); + cregs[1] = 0x00; /* fast & slow link connected to CON1 */ + cregs[2] = 0x05; /* fast link 20MBit, slow link 20 MBit */ + cregs[3] = 0; + cregs[4] = 0x11; /* zero wait state */ + cregs[5] = hema_irq_table[irq & 0xf]; + cregs[6] = 0; + cregs[7] = 0; - len = i = B1_get_word(base); - while (i-- > 0) - *dp++ = B1_get_byte(base); - return len; -} + save_flags(flags); + cli(); + /* board reset */ + t1outp(base, T1_RESETBOARD, 0xf); + udelay(100 * 1000); + dummy = t1inp(base, T1_FASTLINK+T1_OUTSTAT); /* first read */ + + /* write config */ + dummy = (base >> 4) & 0xff; + for (i=1;i<=0xf;i++) t1outp(base, i, dummy); + t1outp(base, HEMA_PAL_ID & 0xf, dummy); + t1outp(base, HEMA_PAL_ID >> 4, cregs[0]); + for(i=1;i<7;i++) t1outp(base, 0, cregs[i]); + t1outp(base, ((base >> 4)) & 0x3, cregs[7]); + restore_flags(flags); -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++); + udelay(100 * 1000); + t1outp(base, T1_FASTLINK+T1_RESETLINK, 0); + t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0); + udelay(10 * 1000); + t1outp(base, T1_FASTLINK+T1_RESETLINK, 1); + t1outp(base, T1_SLOWLINK+T1_RESETLINK, 1); + udelay(100 * 1000); + t1outp(base, T1_FASTLINK+T1_RESETLINK, 0); + t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0); + udelay(10 * 1000); + t1outp(base, T1_FASTLINK+T1_ANALYSE, 0); + udelay(5 * 1000); + t1outp(base, T1_SLOWLINK+T1_ANALYSE, 0); + + if (t1inp(base, T1_FASTLINK+T1_OUTSTAT) != 0x1) /* tx empty */ + return 1; + if (t1inp(base, T1_FASTLINK+T1_INSTAT) != 0x0) /* rx empty */ + return 2; + if (t1inp(base, T1_FASTLINK+T1_IRQENABLE) != 0x0) + return 3; + if ((t1inp(base, T1_FASTLINK+T1_FIFOSTAT) & 0xf0) != 0x70) + return 4; + if ((t1inp(base, T1_FASTLINK+T1_IRQMASTER) & 0x0e) != 0) + return 5; + if ((t1inp(base, T1_FASTLINK+T1_IDENT) & 0x7d) != 1) + return 6; + if (t1inp(base, T1_SLOWLINK+T1_OUTSTAT) != 0x1) /* tx empty */ + return 7; + if ((t1inp(base, T1_SLOWLINK+T1_IRQMASTER) & 0x0e) != 0) + return 8; + if ((t1inp(base, T1_SLOWLINK+T1_IDENT) & 0x7d) != 0) + return 9; + return 0; } extern int loaddebug; @@ -319,6 +691,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; @@ -331,7 +759,7 @@ int B1_loaded(unsigned short base) break; } if (!B1_tx_empty(base)) { - printk(KERN_ERR "b1lli: B1_loaded: timeout tx\n"); + printk(KERN_ERR "b1lli(0x%x): B1_loaded: timeout tx\n", base); return 0; } B1_put_byte(base, SEND_POLL); @@ -343,11 +771,12 @@ int B1_loaded(unsigned short base) printk(KERN_DEBUG "b1capi: loaded: ok\n"); return 1; } - printk(KERN_ERR "b1lli: B1_loaded: got 0x%x ???\n", ans); + printk(KERN_ERR "b1lli(0x%x): B1_loaded: got 0x%x ???\n", + base, ans); return 0; } } - printk(KERN_ERR "b1lli: B1_loaded: timeout rx\n"); + printk(KERN_ERR "b1lli(0x%x): B1_loaded: timeout rx\n", base); return 0; } @@ -411,8 +840,6 @@ void B1_send_release(unsigned short port, restore_flags(flags); } -extern int showcapimsgs; - void B1_send_message(unsigned short port, struct sk_buff *skb) { unsigned long flags; @@ -479,6 +906,7 @@ void B1_handle_interrupt(avmb1_card * card) unsigned NCCI; unsigned WindowSize; +t1retry: if (!B1_rx_full(card->port)) return; @@ -555,7 +983,7 @@ void B1_handle_interrupt(avmb1_card * card) WindowSize = B1_get_word(card->port); if (showcapimsgs) - printk(KERN_DEBUG "b1lli: NEW_NCCI app %u ncci 0x%x\n", ApplId, NCCI); + printk(KERN_DEBUG "b1lli(0x%x): NEW_NCCI app %u ncci 0x%x\n", card->port, ApplId, NCCI); avmb1_handle_new_ncci(card, ApplId, NCCI, WindowSize); @@ -567,19 +995,23 @@ void B1_handle_interrupt(avmb1_card * card) NCCI = B1_get_word(card->port); if (showcapimsgs) - printk(KERN_DEBUG "b1lli: FREE_NCCI app %u ncci 0x%x\n", ApplId, NCCI); + printk(KERN_DEBUG "b1lli(0x%x): FREE_NCCI app %u ncci 0x%x\n", card->port, ApplId, NCCI); avmb1_handle_free_ncci(card, ApplId, NCCI); break; case RECEIVE_START: + if (card->cardtype == AVM_CARDTYPE_T1) { + B1_put_byte(card->port, SEND_POLLACK); + /* printk(KERN_DEBUG "b1lli: T1 watchdog\n"); */ + } if (card->blocked) - printk(KERN_DEBUG "b1lli: RESTART\n"); + printk(KERN_DEBUG "b1lli(0x%x): RESTART\n", card->port); card->blocked = 0; break; case RECEIVE_STOP: - printk(KERN_DEBUG "b1lli: STOP\n"); + printk(KERN_DEBUG "b1lli(0x%x): STOP\n", card->port); card->blocked = 1; break; @@ -588,14 +1020,24 @@ 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(0x%x): %s-card (%s) now active\n", + card->port, card->version[VER_CARDTYPE], - card->version[VER_DRIVER], - card->version[VER_PROTO]); + card->version[VER_DRIVER]); avmb1_card_ready(card); break; + case RECEIVE_TASK_READY: + ApplId = (unsigned) B1_get_word(card->port); + MsgLen = B1_get_slice(card->port, card->msgbuf); + card->msgbuf[MsgLen] = 0; + printk(KERN_INFO "b1lli(0x%x): Task %d \"%s\" ready.\n", + card->port, ApplId, card->msgbuf); + break; default: - printk(KERN_ERR "b1lli: B1_handle_interrupt: 0x%x ???\n", b1cmd); + printk(KERN_ERR "b1lli(0x%x): B1_handle_interrupt: 0x%x ???\n", + card->port, b1cmd); break; } + if (card->cardtype == AVM_CARDTYPE_T1) + goto t1retry; } diff --git a/drivers/isdn/avmb1/b1pci.c b/drivers/isdn/avmb1/b1pci.c index af7b58407795..585ad29030d4 100644 --- a/drivers/isdn/avmb1/b1pci.c +++ b/drivers/isdn/avmb1/b1pci.c @@ -1,11 +1,20 @@ /* - * $Id: b1pci.c,v 1.2 1997/05/18 09:24:13 calle Exp $ + * $Id: b1pci.c,v 1.2.2.2 1998/01/23 16:49:30 calle Exp $ * * Module for AVM B1 PCI-card. * * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1pci.c,v $ + * Revision 1.2.2.2 1998/01/23 16:49:30 calle + * added functions for pcmcia cards, + * avmb1_addcard returns now the controller number. + * + * Revision 1.2.2.1 1997/11/26 10:46:57 calle + * prepared for M1 (Mobile) and T1 (PMX) cards. + * prepared to set configuration after load to support other D-channel + * protocols, point-to-point and leased lines. + * * Revision 1.2 1997/05/18 09:24:13 calle * added verbose disconnect reason reporting to avmb1. * some fixes in capi20 interface. @@ -34,7 +43,7 @@ #define PCI_DEVICE_ID_AVM_B1 0x700 #endif -static char *revision = "$Revision: 1.2 $"; +static char *revision = "$Revision: 1.2.2.2 $"; /* ------------------------------------------------------------- */ @@ -98,13 +107,13 @@ int b1pci_init(void) 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/capidrv.c b/drivers/isdn/avmb1/capidrv.c index b818a85a4e09..268a3826da85 100644 --- a/drivers/isdn/avmb1/capidrv.c +++ b/drivers/isdn/avmb1/capidrv.c @@ -1,11 +1,46 @@ /* - * $Id: capidrv.c,v 1.3.2.1 1997/07/13 12:16:48 calle Exp $ + * $Id: capidrv.c,v 1.3.2.11 1998/04/02 10:27:59 calle Exp $ * * ISDN4Linux Driver, using capi20 interface (kernelcapi) * * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capidrv.c,v $ + * Revision 1.3.2.11 1998/04/02 10:27:59 calle + * version check for D2 trace was wrong :-( + * + * Revision 1.3.2.10 1998/03/20 14:38:24 calle + * capidrv: prepared state machines for suspend/resume/hold + * capidrv: fix bug in state machine if B1/T1 is out of nccis + * b1capi: changed some errno returns. + * b1capi: detect if you try to add same T1 to different io address. + * b1capi: change number of nccis depending on number of channels. + * b1lli: cosmetics + * + * Revision 1.3.2.9 1998/03/20 09:01:12 calle + * Changes capi_register handling to get full support for 30 bchannels. + * + * Revision 1.3.2.8 1998/03/18 17:51:28 calle + * added controller number to error messages + * + * Revision 1.3.2.7 1998/02/27 15:40:47 calle + * T1 running with slow link. bugfix in capi_release. + * + * Revision 1.3.2.6 1998/02/02 19:51:13 calle + * Fixed vbox (audio) acceptb. + * + * Revision 1.3.2.5 1997/10/29 09:35:29 calle + * correct byteorder problem with new isdnlog interface. + * + * Revision 1.3.2.4 1997/10/26 15:04:24 calle + * prepared isdnlog interface for d2-trace in newer firmware. + * + * Revision 1.3.2.3 1997/10/24 06:37:00 calle + * changed LISTEN cipmask, now we can distinguish voice, fax und data calls. + * + * Revision 1.3.2.2 1997/10/08 05:42:25 calle + * Added isdnlog support. patch to isdnlog needed. + * * Revision 1.3.2.1 1997/07/13 12:16:48 calle * bug fix for more than one controller in connect_req. * @@ -51,7 +86,7 @@ #include "capicmd.h" #include "capidrv.h" -static char *revision = "$Revision: 1.3.2.1 $"; +static char *revision = "$Revision: 1.3.2.11 $"; int debugmode = 0; #ifdef HAS_NEW_SYMTAB @@ -125,8 +160,15 @@ struct capidrv_contr { } *bchans; struct capidrv_plci *plci_list; + + /* for q931 data */ + __u8 q931_buf[4096]; + __u8 *q931_read; + __u8 *q931_write; + __u8 *q931_end; }; + struct capidrv_data { __u16 appid; int ncontr; @@ -144,6 +186,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) @@ -170,9 +215,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; } } @@ -338,8 +382,8 @@ static void free_plci(capidrv_contr * card, capidrv_plci * plcip) return; } } - printk(KERN_ERR "capidrv: free_plci %p (0x%x) not found, Huh?\n", - plcip, plcip->plci); + printk(KERN_ERR "capidrv-%d: free_plci %p (0x%x) not found, Huh?\n", + card->contrnr, plcip, plcip->plci); } /* -------- ncci management ------------------------------------------ */ @@ -437,15 +481,15 @@ struct listenstatechange { static struct listenstatechange listentable[] = { - {ST_LISTEN_NONE, ST_LISTEN_WAIT_CONF, EV_LISTEN_REQ}, - {ST_LISTEN_ACTIVE, ST_LISTEN_ACTIVE_WAIT_CONF, EV_LISTEN_REQ}, - {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_ERROR}, - {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_ERROR}, - {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY}, - {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY}, - {ST_LISTEN_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK}, - {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK}, - {}, + {ST_LISTEN_NONE, ST_LISTEN_WAIT_CONF, EV_LISTEN_REQ}, + {ST_LISTEN_ACTIVE, ST_LISTEN_ACTIVE_WAIT_CONF, EV_LISTEN_REQ}, + {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_ERROR}, + {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_ERROR}, + {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY}, + {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY}, + {ST_LISTEN_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK}, + {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK}, + {}, }; static void listen_change_state(capidrv_contr * card, int event) @@ -454,15 +498,15 @@ static void listen_change_state(capidrv_contr * card, int event) while (p->event) { if (card->state == p->actstate && p->event == event) { if (debugmode) - printk(KERN_DEBUG "capidrv: listen_change_state %d -> %d\n", - card->state, p->nextstate); + printk(KERN_DEBUG "capidrv-%d: listen_change_state %d -> %d\n", + card->contrnr, card->state, p->nextstate); card->state = p->nextstate; return; } p++; } - printk(KERN_ERR "capidrv: listen_change_state state=%d event=%d ????\n", - card->state, event); + printk(KERN_ERR "capidrv-%d: listen_change_state state=%d event=%d ????\n", + card->contrnr, card->state, event); } @@ -492,46 +536,57 @@ struct plcistatechange { static struct plcistatechange plcitable[] = { /* P-0 */ - {ST_PLCI_NONE, ST_PLCI_OUTGOING, EV_PLCI_CONNECT_REQ, 0}, - {ST_PLCI_NONE, ST_PLCI_ALLOCATED, EV_PLCI_FACILITY_IND_UP, 0}, - {ST_PLCI_NONE, ST_PLCI_INCOMING, EV_PLCI_CONNECT_IND, 0}, + {ST_PLCI_NONE, ST_PLCI_OUTGOING, EV_PLCI_CONNECT_REQ, 0}, + {ST_PLCI_NONE, ST_PLCI_ALLOCATED, EV_PLCI_FACILITY_IND_UP, 0}, + {ST_PLCI_NONE, ST_PLCI_INCOMING, EV_PLCI_CONNECT_IND, 0}, + {ST_PLCI_NONE, ST_PLCI_RESUMEING, EV_PLCI_RESUME_REQ, 0}, /* P-0.1 */ - {ST_PLCI_OUTGOING, ST_PLCI_NONE, EV_PLCI_CONNECT_CONF_ERROR, p0}, - {ST_PLCI_OUTGOING, ST_PLCI_ALLOCATED, EV_PLCI_CONNECT_CONF_OK, 0}, - {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, - {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_OUTGOING, ST_PLCI_NONE, EV_PLCI_CONNECT_CONF_ERROR, p0}, + {ST_PLCI_OUTGOING, ST_PLCI_ALLOCATED, EV_PLCI_CONNECT_CONF_OK, 0}, + {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, + {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, /* P-1 */ - {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0}, - {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, -{ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, - {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0}, + {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, + {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, /* P-ACT */ - {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, - {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, - {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, + {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + {ST_PLCI_ACTIVE, ST_PLCI_HELD, EV_PLCI_HOLD_IND, 0}, + {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_SUSPEND_IND, 0}, /* P-2 */ - {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0}, - {ST_PLCI_INCOMING, ST_PLCI_FACILITY_IND, EV_PLCI_FACILITY_IND_UP, 0}, - {ST_PLCI_INCOMING, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_RESP, 0}, - {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, - {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, - {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0}, + {ST_PLCI_INCOMING, ST_PLCI_FACILITY_IND, EV_PLCI_FACILITY_IND_UP, 0}, + {ST_PLCI_INCOMING, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_RESP, 0}, + {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, + {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CD_IND, 0}, /* P-3 */ -{ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0}, -{ST_PLCI_FACILITY_IND, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_ACTIVE_IND, 0}, -{ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, - {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, - {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0}, + {ST_PLCI_FACILITY_IND, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_ACTIVE_IND, 0}, + {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, + {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, /* P-4 */ - {ST_PLCI_ACCEPTING, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0}, - {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, -{ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, - {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + {ST_PLCI_ACCEPTING, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0}, + {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, + {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, /* P-5 */ -{ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + {ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, /* P-6 */ - {ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0}, - {}, + {ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0}, + /* P-0.Res */ + {ST_PLCI_RESUMEING, ST_PLCI_NONE, EV_PLCI_RESUME_CONF_ERROR, p0}, + {ST_PLCI_RESUMEING, ST_PLCI_RESUME, EV_PLCI_RESUME_CONF_OK, 0}, + /* P-RES */ + {ST_PLCI_RESUME, ST_PLCI_ACTIVE, EV_PLCI_RESUME_IND, 0}, + /* P-HELD */ + {ST_PLCI_HELD, ST_PLCI_ACTIVE, EV_PLCI_RETRIEVE_IND, 0}, + {}, }; static void plci_change_state(capidrv_contr * card, capidrv_plci * plci, int event) @@ -540,8 +595,8 @@ static void plci_change_state(capidrv_contr * card, capidrv_plci * plci, int eve while (p->event) { if (plci->state == p->actstate && p->event == event) { if (debugmode) - printk(KERN_DEBUG "capidrv: plci_change_state:0x%x %d -> %d\n", - plci->plci, plci->state, p->nextstate); + printk(KERN_DEBUG "capidrv-%d: plci_change_state:0x%x %d -> %d\n", + card->contrnr, plci->plci, plci->state, p->nextstate); plci->state = p->nextstate; if (p->changefunc) p->changefunc(card, plci); @@ -549,8 +604,8 @@ static void plci_change_state(capidrv_contr * card, capidrv_plci * plci, int eve } p++; } - printk(KERN_ERR "capidrv: plci_change_state:0x%x state=%d event=%d ????\n", - plci->plci, plci->state, event); + printk(KERN_ERR "capidrv-%d: plci_change_state:0x%x state=%d event=%d ????\n", + card->contrnr, plci->plci, plci->state, event); } /* ------------------------------------------------------------------ */ @@ -567,7 +622,7 @@ static void n0(capidrv_contr * card, capidrv_ncci * ncci) ncci->plcip->plci, 0, /* BChannelinformation */ 0, /* Keypadfacility */ - 0, /* Useruserdata */ + 0, /* Useruserdata */ /* $$$$ */ 0 /* Facilitydataarray */ ); send_message(card, &cmsg); @@ -592,34 +647,35 @@ struct nccistatechange { static struct nccistatechange nccitable[] = { /* N-0 */ - {ST_NCCI_NONE, ST_NCCI_OUTGOING, EV_NCCI_CONNECT_B3_REQ, 0}, - {ST_NCCI_NONE, ST_NCCI_INCOMING, EV_NCCI_CONNECT_B3_IND, 0}, + {ST_NCCI_NONE, ST_NCCI_OUTGOING, EV_NCCI_CONNECT_B3_REQ, 0}, + {ST_NCCI_NONE, ST_NCCI_INCOMING, EV_NCCI_CONNECT_B3_IND, 0}, /* N-0.1 */ - {ST_NCCI_OUTGOING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_CONF_OK, 0}, - {ST_NCCI_OUTGOING, ST_NCCI_NONE, EV_NCCI_CONNECT_B3_CONF_ERROR, 0}, + {ST_NCCI_OUTGOING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_CONF_OK, 0}, + {ST_NCCI_OUTGOING, ST_NCCI_NONE, EV_NCCI_CONNECT_B3_CONF_ERROR, n0}, /* N-1 */ - {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_CONNECT_B3_REJECT, 0}, - {ST_NCCI_INCOMING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_RESP, 0}, + {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_CONNECT_B3_REJECT, 0}, + {ST_NCCI_INCOMING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_RESP, 0}, {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, - {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, + {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, /* N-2 */ - {ST_NCCI_ALLOCATED, ST_NCCI_ACTIVE, EV_NCCI_CONNECT_B3_ACTIVE_IND, 0}, - {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, -{ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, + {ST_NCCI_ALLOCATED, ST_NCCI_ACTIVE, EV_NCCI_CONNECT_B3_ACTIVE_IND, 0}, + {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, + {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, /* N-ACT */ - {ST_NCCI_ACTIVE, ST_NCCI_RESETING, EV_NCCI_RESET_B3_REQ, 0}, - {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, - {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, + {ST_NCCI_ACTIVE, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0}, + {ST_NCCI_ACTIVE, ST_NCCI_RESETING, EV_NCCI_RESET_B3_REQ, 0}, + {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, + {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, /* N-3 */ - {ST_NCCI_RESETING, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0}, + {ST_NCCI_RESETING, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0}, {ST_NCCI_RESETING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, - {ST_NCCI_RESETING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, + {ST_NCCI_RESETING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, /* N-4 */ - {ST_NCCI_DISCONNECTING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, - {ST_NCCI_DISCONNECTING, ST_NCCI_PREVIOUS, EV_NCCI_DISCONNECT_B3_CONF_ERROR, 0}, + {ST_NCCI_DISCONNECTING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, + {ST_NCCI_DISCONNECTING, ST_NCCI_PREVIOUS, EV_NCCI_DISCONNECT_B3_CONF_ERROR,0}, /* N-5 */ - {ST_NCCI_DISCONNECTED, ST_NCCI_NONE, EV_NCCI_DISCONNECT_B3_RESP, n0}, - {}, + {ST_NCCI_DISCONNECTED, ST_NCCI_NONE, EV_NCCI_DISCONNECT_B3_RESP, n0}, + {}, }; static void ncci_change_state(capidrv_contr * card, capidrv_ncci * ncci, int event) @@ -628,8 +684,8 @@ static void ncci_change_state(capidrv_contr * card, capidrv_ncci * ncci, int eve while (p->event) { if (ncci->state == p->actstate && p->event == event) { if (debugmode) - printk(KERN_DEBUG "capidrv: ncci_change_state:0x%x %d -> %d\n", - ncci->ncci, ncci->state, p->nextstate); + printk(KERN_DEBUG "capidrv-%d: ncci_change_state:0x%x %d -> %d\n", + card->contrnr, ncci->ncci, ncci->state, p->nextstate); if (p->nextstate == ST_NCCI_PREVIOUS) { ncci->state = ncci->oldstate; ncci->oldstate = p->actstate; @@ -643,8 +699,8 @@ static void ncci_change_state(capidrv_contr * card, capidrv_ncci * ncci, int eve } p++; } - printk(KERN_ERR "capidrv: ncci_change_state:0x%x state=%d event=%d ????\n", - ncci->ncci, ncci->state, event); + printk(KERN_ERR "capidrv-%d: ncci_change_state:0x%x state=%d event=%d ????\n", + card->contrnr, ncci->ncci, ncci->state, event); } /* ------------------------------------------------------------------- */ @@ -677,8 +733,8 @@ static void handle_controller(_cmsg * cmsg) case CAPI_LISTEN_CONF: /* Controller */ if (debugmode) - printk(KERN_DEBUG "capidrv: listenconf Info=0x%4x (%s) cipmask=0x%x\n", - cmsg->Info, capi_info2str(cmsg->Info), card->cipmask); + printk(KERN_DEBUG "capidrv-%d: listenconf Info=0x%4x (%s) cipmask=0x%x\n", + card->contrnr, cmsg->Info, capi_info2str(cmsg->Info), card->cipmask); if (cmsg->Info) { listen_change_state(card, EV_LISTEN_CONF_ERROR); } else if (card->cipmask == 0) { @@ -689,8 +745,55 @@ 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-%d: %s from controller 0x%x layer 0x%x, ignored\n", + card->contrnr, + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrController, layer); + break; + } goto ignored; case CAPI_MANUFACTURER_CONF: /* Controller */ + if (cmsg->ManuID == 0x214D5641) { + char *s = 0; + switch (cmsg->Class) { + case 0: break; + case 1: s = "unknown class"; break; + case 2: s = "unknown function"; break; + default: s = "unkown error"; break; + } + if (s) + printk(KERN_INFO "capidrv-%d: %s from controller 0x%x function %d: %s\n", + card->contrnr, + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrController, + cmsg->Function, s); + break; + } goto ignored; case CAPI_FACILITY_IND: /* Controller/plci/ncci */ goto ignored; @@ -702,14 +805,16 @@ static void handle_controller(_cmsg * cmsg) goto ignored; default: - printk(KERN_ERR "capidrv: got %s from controller 0x%x ???", + printk(KERN_ERR "capidrv-%d: got %s from controller 0x%x ???", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrController); } return; ignored: - printk(KERN_INFO "capidrv: %s from controller 0x%x ignored\n", + printk(KERN_INFO "capidrv-%d: %s from controller 0x%x ignored\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrController); } @@ -722,12 +827,12 @@ static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg) int chan; if ((chan = new_bchan(card)) == -1) { - printk(KERN_ERR "capidrv: incoming call on not existing bchan ?\n"); + printk(KERN_ERR "capidrv-%d: incoming call on not existing bchan ?\n", card->contrnr); return; } bchan = &card->bchans[chan]; if ((plcip = new_plci(card, chan)) == 0) { - printk(KERN_ERR "capidrv: incoming call: no memory, sorry.\n"); + printk(KERN_ERR "capidrv-%d: incoming call: no memory, sorry.\n", card->contrnr); return; } bchan->incoming = 1; @@ -749,7 +854,8 @@ static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg) cmd.parm.setup.plan = cmsg->CallingPartyNumber[1]; cmd.parm.setup.screen = cmsg->CallingPartyNumber[2]; - printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s\n", + printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s\n", + card->contrnr, cmd.parm.setup.phone, cmd.parm.setup.si1, cmd.parm.setup.si2, @@ -766,7 +872,8 @@ static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg) cmsg->Reject = 1; /* ignore */ send_message(card, cmsg); plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT); - printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s ignored\n", + printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s ignored\n", + card->contrnr, cmd.parm.setup.phone, cmd.parm.setup.si1, cmd.parm.setup.si2, @@ -783,7 +890,8 @@ static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg) * and CONNECT_RESP already sent. */ if (plcip->state == ST_PLCI_INCOMING) { - printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s tty alerting\n", + printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s tty alerting\n", + card->contrnr, cmd.parm.setup.phone, cmd.parm.setup.si1, cmd.parm.setup.si2, @@ -800,7 +908,8 @@ static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg) plcip->msgid = cmsg->Messagenumber; send_message(card, cmsg); } else { - printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s on netdev\n", + printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s on netdev\n", + card->contrnr, cmd.parm.setup.phone, cmd.parm.setup.si1, cmd.parm.setup.si2, @@ -843,7 +952,8 @@ static void handle_plci(_cmsg * cmsg) case CAPI_DISCONNECT_IND: /* plci */ if (cmsg->Reason) { - printk(KERN_INFO "capidrv: %s reason 0x%x (%s) for plci 0x%x\n", + printk(KERN_INFO "capidrv-%d: %s reason 0x%x (%s) for plci 0x%x\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Reason, capi_info2str(cmsg->Reason), cmsg->adr.adrPLCI); } @@ -861,7 +971,8 @@ static void handle_plci(_cmsg * cmsg) case CAPI_DISCONNECT_CONF: /* plci */ if (cmsg->Info) { - printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n", + printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Info, capi_info2str(cmsg->Info), cmsg->adr.adrPLCI); @@ -874,7 +985,8 @@ static void handle_plci(_cmsg * cmsg) case CAPI_ALERT_CONF: /* plci */ if (cmsg->Info) { - printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n", + printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Info, capi_info2str(cmsg->Info), cmsg->adr.adrPLCI); @@ -887,7 +999,8 @@ static void handle_plci(_cmsg * cmsg) case CAPI_CONNECT_CONF: /* plci */ if (cmsg->Info) { - printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n", + printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Info, capi_info2str(cmsg->Info), cmsg->adr.adrPLCI); @@ -920,7 +1033,7 @@ static void handle_plci(_cmsg * cmsg) nccip = new_ncci(card, plcip, cmsg->adr.adrPLCI); if (!nccip) { - printk(KERN_ERR "capidrv: no mem for ncci, sorry\n"); + printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr); break; /* $$$$ */ } capi_fill_CONNECT_B3_REQ(cmsg, @@ -960,7 +1073,8 @@ static void handle_plci(_cmsg * cmsg) break; } } - printk(KERN_ERR "capidrv: %s\n", capi_cmsg2str(cmsg)); + printk(KERN_ERR "capidrv-%d: %s\n", + card->contrnr, capi_cmsg2str(cmsg)); break; case CAPI_CONNECT_ACTIVE_CONF: /* plci */ @@ -976,18 +1090,21 @@ static void handle_plci(_cmsg * cmsg) goto ignored; default: - printk(KERN_ERR "capidrv: got %s for plci 0x%x ???", + printk(KERN_ERR "capidrv-%d: got %s for plci 0x%x ???", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrPLCI); } return; ignored: - printk(KERN_INFO "capidrv: %s for plci 0x%x ignored\n", + printk(KERN_INFO "capidrv-%d: %s for plci 0x%x ignored\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrPLCI); return; notfound: - printk(KERN_ERR "capidrv: %s: plci 0x%x not found\n", + printk(KERN_ERR "capidrv-%d: %s: plci 0x%x not found\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrPLCI); return; @@ -1021,8 +1138,8 @@ static void handle_ncci(_cmsg * cmsg) cmd.arg = nccip->chan; card->interface.statcallb(&cmd); - printk(KERN_INFO "capidrv: chan %d up with ncci 0x%x\n", - nccip->chan, nccip->ncci); + printk(KERN_INFO "capidrv-%d: chan %d up with ncci 0x%x\n", + card->contrnr, nccip->chan, nccip->ncci); break; case CAPI_CONNECT_B3_ACTIVE_CONF: /* ncci */ @@ -1046,9 +1163,10 @@ static void handle_ncci(_cmsg * cmsg) ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_RESP); break; } - printk(KERN_ERR "capidrv: no mem for ncci, sorry\n"); + printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr); } else { - printk(KERN_ERR "capidrv: %s: plci for ncci 0x%x not found\n", + printk(KERN_ERR "capidrv-%d: %s: plci for ncci 0x%x not found\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrNCCI); } @@ -1071,7 +1189,8 @@ static void handle_ncci(_cmsg * cmsg) nccip->ncci = cmsg->adr.adrNCCI; if (cmsg->Info) { - printk(KERN_INFO "capidrv: %s info 0x%x (%s) for ncci 0x%x\n", + printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Info, capi_info2str(cmsg->Info), cmsg->adr.adrNCCI); @@ -1118,7 +1237,8 @@ static void handle_ncci(_cmsg * cmsg) if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) goto notfound; if (cmsg->Info) { - printk(KERN_INFO "capidrv: %s info 0x%x (%s) for ncci 0x%x\n", + printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Info, capi_info2str(cmsg->Info), cmsg->adr.adrNCCI); @@ -1127,6 +1247,9 @@ static void handle_ncci(_cmsg * cmsg) break; case CAPI_RESET_B3_IND: /* ncci */ + if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) + goto notfound; + ncci_change_state(card, nccip, EV_NCCI_RESET_B3_IND); capi_cmsg_answer(cmsg); send_message(card, cmsg); break; @@ -1140,18 +1263,21 @@ static void handle_ncci(_cmsg * cmsg) goto ignored; default: - printk(KERN_ERR "capidrv: got %s for ncci 0x%x ???", + printk(KERN_ERR "capidrv-%d: got %s for ncci 0x%x ???", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrNCCI); } return; ignored: - printk(KERN_INFO "capidrv: %s for ncci 0x%x ignored\n", + printk(KERN_INFO "capidrv-%d: %s for ncci 0x%x ignored\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrNCCI); return; notfound: - printk(KERN_ERR "capidrv: %s: ncci 0x%x not found\n", + printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrNCCI); } @@ -1169,7 +1295,8 @@ static void handle_data(_cmsg * cmsg, struct sk_buff *skb) return; } if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) { - printk(KERN_ERR "capidrv: %s: ncci 0x%x not found\n", + printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrNCCI); kfree_skb(skb, FREE_READ); @@ -1190,7 +1317,8 @@ static void capidrv_signal(__u16 applid, __u32 dummy) while ((*capifuncs->capi_get_message) (global.appid, &skb) == CAPI_NOERROR) { capi_message2cmsg(&s_cmsg, skb->data); if (debugmode > 1) - printk(KERN_DEBUG "capidrv_signal: %s\n", capi_cmsg2str(&s_cmsg)); + printk(KERN_DEBUG "capidrv_signal: applid=%d %s\n", + applid, capi_cmsg2str(&s_cmsg)); if (s_cmsg.Command == CAPI_DATA_B3 && s_cmsg.Subcommand == CAPI_IND) { @@ -1209,13 +1337,69 @@ 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 "capidrv-%d: avmb1_q931_data: len == %d\n", + card->contrnr, len); + return; + } + + save_flags(flags); + cli(); + + if (level2) { + PUTBYTE_TO_STATUS(card, 'D'); + PUTBYTE_TO_STATUS(card, '2'); + PUTBYTE_TO_STATUS(card, send ? '>' : '<'); + PUTBYTE_TO_STATUS(card, ':'); + } else { + PUTBYTE_TO_STATUS(card, 'D'); + PUTBYTE_TO_STATUS(card, '3'); + PUTBYTE_TO_STATUS(card, send ? '>' : '<'); + PUTBYTE_TO_STATUS(card, ':'); + } + + for (p = data, end = data+len; p < end; p++) { + __u8 w; + PUTBYTE_TO_STATUS(card, ' '); + w = (*p >> 4) & 0xf; + PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w); + w = *p & 0xf; + PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w); + } + PUTBYTE_TO_STATUS(card, '\n'); + + restore_flags(flags); + + cmd.command = ISDN_STAT_STAVAIL; + cmd.driver = card->myid; + cmd.arg = len*3+5; + card->interface.statcallb(&cmd); +} + +/* ------------------------------------------------------------------- */ + static _cmsg cmdcmsg; static int capidrv_ioctl(isdn_ctrl * c, capidrv_contr * card) { switch (c->arg) { default: - printk(KERN_DEBUG "capidrv: capidrv_ioctl(%ld) called ??\n", c->arg); + printk(KERN_DEBUG "capidrv-%d: capidrv_ioctl(%ld) called ??\n", + card->contrnr, c->arg); return -EINVAL; } return -EINVAL; @@ -1236,7 +1420,8 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) __u8 called[ISDN_MSNLEN + 2]; if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n", + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n", + card->contrnr, c->arg, c->parm.setup.phone, c->parm.setup.si1, @@ -1246,7 +1431,8 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) bchan = &card->bchans[c->arg % card->nbchan]; if (bchan->plcip) { - printk(KERN_ERR "capidrv: dail ch=%ld,\"%s,%d,%d,%s\" in use (plci=0x%x)\n", + printk(KERN_ERR "capidrv-%d: dail ch=%ld,\"%s,%d,%d,%s\" in use (plci=0x%x)\n", + card->contrnr, c->arg, c->parm.setup.phone, c->parm.setup.si1, @@ -1308,10 +1494,11 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) case ISDN_CMD_ACCEPTD: - if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_ACCEPTD(ch=%ld)\n", - c->arg); bchan = &card->bchans[c->arg % card->nbchan]; + if (debugmode) + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTD(ch=%ld) l2=%d l3=%d\n", + card->contrnr, + c->arg, bchan->l2, bchan->l3); capi_fill_CONNECT_RESP(&cmdcmsg, global.appid, @@ -1339,19 +1526,22 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) case ISDN_CMD_ACCEPTB: if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_ACCEPTB(ch=%ld)\n", + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTB(ch=%ld)\n", + card->contrnr, c->arg); return -ENOSYS; case ISDN_CMD_HANGUP: if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_HANGUP(ch=%ld)\n", + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_HANGUP(ch=%ld)\n", + card->contrnr, c->arg); bchan = &card->bchans[c->arg % card->nbchan]; if (bchan->disconnecting) { if (debugmode) - printk(KERN_DEBUG "capidrv: chan %ld already disconnecting ...\n", + printk(KERN_DEBUG "capidrv-%d: chan %ld already disconnecting ...\n", + card->contrnr, c->arg); return 0; } @@ -1390,23 +1580,26 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) case ISDN_CMD_SETL2: if (debugmode) - printk(KERN_DEBUG "capidrv: set L2 on chan %ld to %ld\n", + printk(KERN_DEBUG "capidrv-%d: set L2 on chan %ld to %ld\n", + card->contrnr, (c->arg & 0xff), (c->arg >> 8)); - bchan = &card->bchans[c->arg % card->nbchan]; + bchan = &card->bchans[(c->arg & 0xff) % card->nbchan]; bchan->l2 = (c->arg >> 8); return 0; case ISDN_CMD_SETL3: if (debugmode) - printk(KERN_DEBUG "capidrv: set L3 on chan %ld to %ld\n", + printk(KERN_DEBUG "capidrv-%d: set L3 on chan %ld to %ld\n", + card->contrnr, (c->arg & 0xff), (c->arg >> 8)); - bchan = &card->bchans[c->arg % card->nbchan]; + bchan = &card->bchans[(c->arg & 0xff) % card->nbchan]; bchan->l3 = (c->arg >> 8); return 0; case ISDN_CMD_SETEAZ: if (debugmode) - printk(KERN_DEBUG "capidrv: set EAZ \"%s\" on chan %ld\n", + printk(KERN_DEBUG "capidrv-%d: set EAZ \"%s\" on chan %ld\n", + card->contrnr, c->parm.num, c->arg); bchan = &card->bchans[c->arg % card->nbchan]; strncpy(bchan->msn, c->parm.num, ISDN_MSNLEN); @@ -1414,46 +1607,54 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) case ISDN_CMD_CLREAZ: if (debugmode) - printk(KERN_DEBUG "capidrv: clearing EAZ on chan %ld\n", c->arg); + printk(KERN_DEBUG "capidrv-%d: clearing EAZ on chan %ld\n", + card->contrnr, c->arg); bchan = &card->bchans[c->arg % card->nbchan]; bchan->msn[0] = 0; return 0; case ISDN_CMD_LOCK: if (debugmode > 1) - printk(KERN_DEBUG "capidrv: ISDN_CMD_LOCK (%ld)\n", c->arg); + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_LOCK (%ld)\n", card->contrnr, c->arg); MOD_INC_USE_COUNT; break; case ISDN_CMD_UNLOCK: if (debugmode > 1) - printk(KERN_DEBUG "capidrv: ISDN_CMD_UNLOCK (%ld)\n", c->arg); + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_UNLOCK (%ld)\n", + card->contrnr, c->arg); MOD_DEC_USE_COUNT; break; /* never called */ case ISDN_CMD_GETL2: if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_GETL2\n"); + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETL2\n", + card->contrnr); return -ENODEV; case ISDN_CMD_GETL3: if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_GETL3\n"); + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETL3\n", + card->contrnr); return -ENODEV; case ISDN_CMD_GETEAZ: if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_GETEAZ\n"); + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETEAZ\n", + card->contrnr); return -ENODEV; case ISDN_CMD_SETSIL: if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_SETSIL\n"); + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_SETSIL\n", + card->contrnr); return -ENODEV; case ISDN_CMD_GETSIL: if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_GETSIL\n"); + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETSIL\n", + card->contrnr); return -ENODEV; default: - printk(KERN_ERR "capidrv: ISDN_CMD_%d, Huh?\n", c->command); + printk(KERN_ERR "capidrv-%d: ISDN_CMD_%d, Huh?\n", + card->contrnr, c->command); return -EINVAL; } return 0; @@ -1467,8 +1668,8 @@ static int if_command(isdn_ctrl * c) return capidrv_command(c, card); printk(KERN_ERR - "capidrv: if_command %d called with invalid driverId %d!\n", - c->command, c->driver); + "capidrv-%d: if_command %d called with invalid driverId %d!\n", + card->contrnr, c->command, c->driver); return -ENODEV; } @@ -1484,15 +1685,15 @@ static int if_sendbuf(int id, int channel, struct sk_buff *skb) __u16 errcode; if (!card) { - printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n", - id); + printk(KERN_ERR "capidrv-%d: if_sendbuf called with invalid driverId %d!\n", + card->contrnr, id); return 0; } bchan = &card->bchans[channel % card->nbchan]; nccip = bchan->nccip; if (!nccip || nccip->state != ST_NCCI_ACTIVE) { - printk(KERN_ERR "capidrv: if_sendbuf: %s:%d: chan not up!\n", - card->name, channel); + printk(KERN_ERR "capidrv-%d: if_sendbuf: %s:%d: chan not up!\n", + card->contrnr, card->name, channel); return 0; } capi_fill_DATA_B3_REQ(&sendcmsg, global.appid, card->msgid++, @@ -1507,12 +1708,13 @@ static int if_sendbuf(int id, int channel, struct sk_buff *skb) if (skb_headroom(skb) < msglen) { struct sk_buff *nskb = dev_alloc_skb(msglen + skb->len); if (!nskb) { - printk(KERN_ERR "capidrv: if_sendbuf: no memory\n"); + printk(KERN_ERR "capidrv-%d: if_sendbuf: no memory\n", + card->contrnr); return 0; } #if 0 - printk(KERN_DEBUG "capidrv: only %d bytes headroom\n", - skb_headroom(skb)); + printk(KERN_DEBUG "capidrv-%d: only %d bytes headroom\n", + card->contrnr, skb_headroom(skb)); #endif SET_SKB_FREE(nskb); memcpy(skb_put(nskb, msglen), sendcmsg.buf, msglen); @@ -1542,6 +1744,82 @@ static int if_sendbuf(int id, int channel, struct sk_buff *skb) } } +static int if_readstat(__u8 *buf, int len, int user, int id, int channel) +{ + capidrv_contr *card = findcontrbydriverid(id); + int count; + __u8 *p; + + if (!card) { + printk(KERN_ERR "capidrv-%d: if_readstat called with invalid driverId %d!\n", + card->contrnr, id); + return -ENODEV; + } + + for (p=buf, count=0; count < len; p++, count++) { + if (user) + put_user(*card->q931_read++, p); + else + *p = *card->q931_read++; + if (card->q931_read > card->q931_end) + card->q931_read = card->q931_buf; + } + return count; + +} + +static void enable_dchannel_trace(capidrv_contr *card) +{ + __u8 manufacturer[CAPI_MANUFACTURER_LEN]; + capi_version version; + __u16 contr = card->contrnr; + __u16 errcode; + __u16 avmversion[3]; + + errcode = (*capifuncs->capi_get_manufacturer)(contr, manufacturer); + if (errcode != CAPI_NOERROR) { + printk(KERN_ERR "%s: can't get manufacturer (0x%x)\n", + card->name, errcode); + return; + } + if (strstr(manufacturer, "AVM") == 0) { + printk(KERN_ERR "%s: not from AVM, no d-channel trace possible\n", + card->name); + return; + } + errcode = (*capifuncs->capi_get_version)(contr, &version); + if (errcode != CAPI_NOERROR) { + printk(KERN_ERR "%s: can't get version (0x%x)\n", + card->name, errcode); + return; + } + avmversion[0] = (version.majormanuversion >> 4) & 0x0f; + avmversion[1] = (version.majormanuversion << 4) & 0xf0; + avmversion[1] |= (version.minormanuversion >> 4) & 0x0f; + avmversion[2] |= version.minormanuversion & 0x0f; + + if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) { + printk(KERN_INFO "%s: D2 trace enabled\n", card->name); + capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid, + card->msgid++, + contr, + 0x214D5641, /* ManuID */ + 0, /* Class */ + 1, /* Function */ + (_cstruct)"\004\200\014\000\000"); + } else { + printk(KERN_INFO "%s: D3 trace enabled\n", card->name); + capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid, + card->msgid++, + contr, + 0x214D5641, /* ManuID */ + 0, /* Class */ + 1, /* Function */ + (_cstruct)"\004\002\003\000\000"); + } + send_message(card, &cmdcmsg); +} + static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) { capidrv_contr *card; @@ -1571,7 +1849,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 | @@ -1584,6 +1862,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; @@ -1603,7 +1884,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, @@ -1619,6 +1900,9 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) printk(KERN_INFO "%s: now up (%d B channels)\n", card->name, card->nbchan); + if (card->nbchan == 2) /* no T1 */ + enable_dchannel_trace(card); + return 0; } @@ -1709,8 +1993,8 @@ int capidrv_init(void) } else strcpy(rev, " ??? "); - rparam.level3cnt = 2; - rparam.datablkcnt = 8; + rparam.level3cnt = -2; /* number of bchannels twice */ + rparam.datablkcnt = 16; rparam.datablklen = 2048; errcode = (*capifuncs->capi_register) (&rparam, &global.appid); if (errcode) { diff --git a/drivers/isdn/avmb1/capidrv.h b/drivers/isdn/avmb1/capidrv.h index f30c3f4dd1de..614010224b3a 100644 --- a/drivers/isdn/avmb1/capidrv.h +++ b/drivers/isdn/avmb1/capidrv.h @@ -1,11 +1,19 @@ /* - * $Id: capidrv.h,v 1.1 1997/03/04 21:50:33 calle Exp $ + * $Id: capidrv.h,v 1.1.2.1 1998/03/20 14:38:28 calle Exp $ * * ISDN4Linux Driver, using capi20 interface (kernelcapi) * * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capidrv.h,v $ + * Revision 1.1.2.1 1998/03/20 14:38:28 calle + * capidrv: prepared state machines for suspend/resume/hold + * capidrv: fix bug in state machine if B1/T1 is out of nccis + * b1capi: changed some errno returns. + * b1capi: detect if you try to add same T1 to different io address. + * b1capi: change number of nccis depending on number of channels. + * b1lli: cosmetics + * * Revision 1.1 1997/03/04 21:50:33 calle * Frirst version in isdn4linux * @@ -49,34 +57,70 @@ #define ST_PLCI_ACCEPTING 6 /* P-4 */ #define ST_PLCI_DISCONNECTING 7 /* P-5 */ #define ST_PLCI_DISCONNECTED 8 /* P-6 */ +#define ST_PLCI_RESUMEING 9 /* P-0.Res */ +#define ST_PLCI_RESUME 10 /* P-Res */ +#define ST_PLCI_HELD 11 /* P-HELD */ -#define EV_PLCI_CONNECT_REQ 1 /* P-0 -> P-0.1 */ -#define EV_PLCI_CONNECT_CONF_ERROR 2 /* P-0.1 -> P-0 */ -#define EV_PLCI_CONNECT_CONF_OK 3 /* P-0.1 -> P-1 */ -#define EV_PLCI_FACILITY_IND_UP 4 /* P-0 -> P-1 */ -#define EV_PLCI_CONNECT_IND 5 /* P-0 -> P-2 */ -#define EV_PLCI_CONNECT_ACTIVE_IND 6 /* P-1 -> P-ACT */ +#define EV_PLCI_CONNECT_REQ 1 /* P-0 -> P-0.1 + */ +#define EV_PLCI_CONNECT_CONF_ERROR 2 /* P-0.1 -> P-0 + */ +#define EV_PLCI_CONNECT_CONF_OK 3 /* P-0.1 -> P-1 + */ +#define EV_PLCI_FACILITY_IND_UP 4 /* P-0 -> P-1 + */ +#define EV_PLCI_CONNECT_IND 5 /* P-0 -> P-2 + */ +#define EV_PLCI_CONNECT_ACTIVE_IND 6 /* P-1 -> P-ACT + */ #define EV_PLCI_CONNECT_REJECT 7 /* P-2 -> P-5 - P-3 -> P-5 */ + P-3 -> P-5 + */ #define EV_PLCI_DISCONNECT_REQ 8 /* P-1 -> P-5 P-2 -> P-5 P-3 -> P-5 P-4 -> P-5 - P-ACT -> P-5 */ + P-ACT -> P-5 + P-Res -> P-5 (*) + P-HELD -> P-5 (*) + */ #define EV_PLCI_DISCONNECT_IND 9 /* P-1 -> P-6 P-2 -> P-6 P-3 -> P-6 P-4 -> P-6 P-5 -> P-6 - P-ACT -> P-6 */ + P-ACT -> P-6 + P-Res -> P-6 (*) + P-HELD -> P-6 (*) + */ #define EV_PLCI_FACILITY_IND_DOWN 10 /* P-0.1 -> P-5 P-1 -> P-5 P-ACT -> P-5 P-2 -> P-5 P-3 -> P-5 - P-4 -> P-5 */ -#define EV_PLCI_DISCONNECT_RESP 11 /* P-6 -> P-0 */ -#define EV_PLCI_CONNECT_RESP 12 /* P-6 -> P-0 */ + P-4 -> P-5 + */ +#define EV_PLCI_DISCONNECT_RESP 11 /* P-6 -> P-0 + */ +#define EV_PLCI_CONNECT_RESP 12 /* P-6 -> P-0 + */ + +#define EV_PLCI_RESUME_REQ 13 /* P-0 -> P-0.Res + */ +#define EV_PLCI_RESUME_CONF_OK 14 /* P-0.Res -> P-Res + */ +#define EV_PLCI_RESUME_CONF_ERROR 15 /* P-0.Res -> P-0 + */ +#define EV_PLCI_RESUME_IND 16 /* P-Res -> P-ACT + */ +#define EV_PLCI_HOLD_IND 17 /* P-ACT -> P-HELD + */ +#define EV_PLCI_RETRIEVE_IND 18 /* P-HELD -> P-ACT + */ +#define EV_PLCI_SUSPEND_IND 19 /* P-ACT -> P-5 + */ +#define EV_PLCI_CD_IND 20 /* P-2 -> P-5 + */ /* * per ncci state machine diff --git a/drivers/isdn/hisax/Makefile b/drivers/isdn/hisax/Makefile index 2cc325b1f246..65e1341ee187 100644 --- a/drivers/isdn/hisax/Makefile +++ b/drivers/isdn/hisax/Makefile @@ -1,7 +1,7 @@ L_OBJS := M_OBJS := -O_OBJS := isdnl1.o config.o tei.o isdnl2.o isdnl3.o \ - q931.o callc.o fsm.o +O_OBJS := isdnl1.o tei.o isdnl2.o isdnl3.o \ + lmgr.o q931.o callc.o fsm.o # EXTRA_CFLAGS += -S @@ -17,31 +17,112 @@ ifeq ($(CONFIG_HISAX_1TR6),y) O_OBJS += l3_1tr6.o endif +ISAC_OBJ := +ARCOFI_OBJ := +HSCX_OBJ := +HFC_OBJ := +HFC_2BDS0 := + ifeq ($(CONFIG_HISAX_16_0),y) O_OBJS += teles0.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o endif ifeq ($(CONFIG_HISAX_16_3),y) O_OBJS += teles3.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o endif -ifeq ($(CONFIG_HISAX_AVM_A1),y) - O_OBJS += avm_a1.o +ifeq ($(CONFIG_HISAX_TELESPCI),y) + O_OBJS += telespci.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o endif -ifeq ($(CONFIG_HISAX_ELSA_PCC),y) - O_OBJS += elsa.o +ifeq ($(CONFIG_HISAX_S0BOX),y) + O_OBJS += s0box.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_AVM_A1),y) + O_OBJS += avm_a1.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o endif -ifeq ($(CONFIG_HISAX_ELSA_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_NICCY),y) + O_OBJS += niccy.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +O_OBJS += $(ISAC_OBJ) $(HSCX_OBJ) $(HFC_OBJ) $(HFC_2BDS0) $(ARCOFI_OBJ) +OX_OBJS += config.o + O_TARGET := + ifeq ($(CONFIG_ISDN_DRV_HISAX),y) O_TARGET += hisax.o else diff --git a/drivers/isdn/hisax/arcofi.c b/drivers/isdn/hisax/arcofi.c new file mode 100644 index 000000000000..29c43ddcb892 --- /dev/null +++ b/drivers/isdn/hisax/arcofi.c @@ -0,0 +1,75 @@ +/* $Id: arcofi.c,v 1.1.2.3 1998/05/27 18:04:48 keil Exp $ + + * arcofi.h Ansteuerung ARCOFI 2165 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * + * $Log: arcofi.c,v $ + * Revision 1.1.2.3 1998/05/27 18:04:48 keil + * HiSax 3.0 + * + * Revision 1.1.2.2 1998/04/11 18:45:13 keil + * New interface + * + * Revision 1.1.2.1 1997/11/15 18:57:37 keil + * ARCOFI 2165 support + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isdnl1.h" +#include "isac.h" + +int +send_arcofi(struct IsdnCardState *cs, const u_char *msg, int bc, int receive) { + u_char val; + char tmp[32]; + long flags; + int cnt=50; + + cs->mon_txp = 0; + cs->mon_txc = msg[0]; + memcpy(cs->mon_tx, &msg[1], cs->mon_txc); + switch(bc) { + case 0: break; + case 1: cs->mon_tx[1] |= 0x40; + break; + default: break; + } + cs->mocr &= 0x0f; + cs->mocr |= 0xa0; + test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags); + if (receive) + test_and_clear_bit(HW_MON1_RX_END, &cs->HW_Flags); + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + val = cs->readisac(cs, ISAC_MOSR); + cs->writeisac(cs, ISAC_MOX1, cs->mon_tx[cs->mon_txp++]); + cs->mocr |= 0x10; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + save_flags(flags); + sti(); + while (cnt && !test_bit(HW_MON1_TX_END, &cs->HW_Flags)) { + cnt--; + udelay(500); +#if 0 + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); +#endif + } + if (receive) { + while (cnt && !test_bit(HW_MON1_RX_END, &cs->HW_Flags)) { + cnt--; + udelay(500); + } + } + 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..2ad5ff3095c7 --- /dev/null +++ b/drivers/isdn/hisax/arcofi.h @@ -0,0 +1,23 @@ +/* $Id: arcofi.h,v 1.1.2.3 1998/05/27 18:04:50 keil Exp $ + + * arcofi.h Ansteuerung ARCOFI 2165 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: arcofi.h,v $ + * Revision 1.1.2.3 1998/05/27 18:04:50 keil + * HiSax 3.0 + * + * Revision 1.1.2.2 1998/04/11 18:45:14 keil + * New interface + * + * Revision 1.1.2.1 1997/11/15 18:57:38 keil + * ARCOFI 2165 support + * + * + */ + +#define ARCOFI_USE 1 + +extern int send_arcofi(struct IsdnCardState *cs, const u_char *msg, int bc, int receive); diff --git a/drivers/isdn/hisax/asuscom.c b/drivers/isdn/hisax/asuscom.c new file mode 100644 index 000000000000..d667cf7af9b4 --- /dev/null +++ b/drivers/isdn/hisax/asuscom.c @@ -0,0 +1,406 @@ +/* $Id: asuscom.c,v 1.1.2.3 1998/06/18 23:10:26 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.1.2.3 1998/06/18 23:10:26 keil + * Support for new IPAC card + * + * Revision 1.1.2.2 1998/04/08 21:58:37 keil + * New init code + * + * Revision 1.1.2.1 1998/01/27 22:34:02 keil + * dynalink ----> asuscom + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "ipac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *Asuscom_revision = "$Revision: 1.1.2.3 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ASUS_ISAC 0 +#define ASUS_HSCX 1 +#define ASUS_ADR 2 +#define ASUS_CTRL_U7 3 +#define ASUS_CTRL_POTS 5 + +#define ASUS_IPAC_ALE 0 +#define ASUS_IPAC_DATA 1 + +#define ASUS_ISACHSCX 1 +#define ASUS_IPAC 2 + +/* CARD_ADR (Write) */ +#define ASUS_RESET 0x80 /* Bit 7 Reset-Leitung */ + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size); +} + +static u_char +ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80)); +} + +static void +WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80, value); +} + +static void +ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size); +} + +static void +WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.asus.adr, + cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.asus.adr, + cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.asus.adr, \ + cs->hw.asus.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.asus.adr, \ + cs->hw.asus.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.asus.adr, \ + cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.asus.adr, \ + cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +asuscom_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 1) { + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0x0); + } + if (stat & 2) { + writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0x0); + } +} + +static void +asuscom_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char ista, val, icnt = 20; + char tmp[64]; + + if (!cs) { + printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n"); + return; + } + ista = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA); +Start_IPAC: + if (cs->debug & L1_DEB_IPAC) { + sprintf(tmp, "IPAC ISTA %02X", ista); + debugl1(cs, tmp); + } + if (ista & 0x0f) { + val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40); + if (ista & 0x01) + val |= 0x01; + if (ista & 0x04) + val |= 0x02; + if (ista & 0x08) + val |= 0x04; + if (val) + hscx_int_main(cs, val); + } + if (ista & 0x20) { + val = 0xfe & readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA | 0x80); + if (val) { + isac_interrupt(cs, val); + } + } + if (ista & 0x10) { + val = 0x01; + isac_interrupt(cs, val); + } + ista = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA); + if ((ista & 0x3f) && icnt) { + icnt--; + goto Start_IPAC; + } + if (!icnt) + printk(KERN_WARNING "ASUS IRQ LOOP\n"); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xC0); +} + +void +release_io_asuscom(struct IsdnCardState *cs) +{ + int bytecnt = 8; + + if (cs->hw.asus.cfg_reg) + release_region(cs->hw.asus.cfg_reg, bytecnt); +} + +static void +reset_asuscom(struct IsdnCardState *cs) +{ + long flags; + + if (cs->subtyp == ASUS_IPAC) + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x20); + else + byteout(cs->hw.asus.adr, ASUS_RESET); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + if (cs->subtyp == ASUS_IPAC) + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x0); + else + byteout(cs->hw.asus.adr, 0); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + if (cs->subtyp == ASUS_IPAC) { + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_CONF, 0x0); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ACFG, 0xff); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_AOE, 0x0); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xc0); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_PCFG, 0x12); + } + restore_flags(flags); +} + +static int +Asus_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_asuscom(cs); + return(0); + case CARD_RELEASE: + release_io_asuscom(cs); + return(0); + case CARD_SETIRQ: + if (cs->subtyp == ASUS_IPAC) + return(request_irq(cs->irq, &asuscom_interrupt_ipac, + I4L_IRQ_FLAG, "HiSax", cs)); + else + return(request_irq(cs->irq, &asuscom_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + cs->debug |= L1_DEB_IPAC; + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_asuscom(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + u_char val; + char tmp[64]; + + strcpy(tmp, Asuscom_revision); + printk(KERN_INFO "HiSax: Asuscom ISDNLink driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_ASUSCOM) + return (0); + + bytecnt = 8; + cs->hw.asus.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + if (check_region((cs->hw.asus.cfg_reg), bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.asus.cfg_reg, + cs->hw.asus.cfg_reg + bytecnt); + return (0); + } else { + request_region(cs->hw.asus.cfg_reg, bytecnt, "asuscom isdn"); + } + printk(KERN_INFO "ISDNLink: defined at 0x%x IRQ %d\n", + cs->hw.asus.cfg_reg, cs->irq); + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Asus_card_msg; + val = readreg(cs->hw.asus.cfg_reg + ASUS_IPAC_ALE, + cs->hw.asus.cfg_reg + ASUS_IPAC_DATA, IPAC_ID); + if (val == 1) { + cs->subtyp = ASUS_IPAC; + cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_IPAC_ALE; + cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA; + cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + cs->readisac = &ReadISAC_IPAC; + cs->writeisac = &WriteISAC_IPAC; + cs->readisacfifo = &ReadISACfifo_IPAC; + cs->writeisacfifo = &WriteISACfifo_IPAC; + printk(KERN_INFO "Asus: IPAC version %x\n", val); + } else { + cs->subtyp = ASUS_ISACHSCX; + cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_ADR; + cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_ISAC; + cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_HSCX; + cs->hw.asus.u7 = cs->hw.asus.cfg_reg + ASUS_CTRL_U7; + cs->hw.asus.pots = cs->hw.asus.cfg_reg + ASUS_CTRL_POTS; + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + ISACVersion(cs, "ISDNLink:"); + if (HscxVersion(cs, "ISDNLink:")) { + printk(KERN_WARNING + "ISDNLink: wrong HSCX versions check IO address\n"); + release_io_asuscom(cs); + return (0); + } + } + printk(KERN_INFO "ISDNLink: resetting card\n"); + reset_asuscom(cs); + return (1); +} diff --git a/drivers/isdn/hisax/avm_a1.c b/drivers/isdn/hisax/avm_a1.c index 6f80ae8bcd85..42434c789308 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 1.6.2.10 1998/05/27 18:04:50 keil Exp $ * avm_a1.c low level stuff for AVM A1 (Fritz) isdn cards * @@ -6,6 +6,33 @@ * * * $Log: avm_a1.c,v $ + * Revision 1.6.2.10 1998/05/27 18:04:50 keil + * HiSax 3.0 + * + * Revision 1.6.2.9 1998/04/08 21:58:39 keil + * New init code + * + * Revision 1.6.2.8 1998/01/27 22:37:49 keil + * fast io + * + * Revision 1.6.2.7 1998/01/13 23:06:11 keil + * really disable internal timer + * + * Revision 1.6.2.6 1998/01/02 06:49:01 calle + * Perodic timer of A1 now disabled, no need for linux driver. + * + * Revision 1.6.2.5 1997/11/15 18:50:41 keil + * new common init function + * + * Revision 1.6.2.4 1997/10/17 22:13:29 keil + * update to last hisax version + * + * Revision 2.1 1997/07/27 21:47:13 keil + * new interface structures + * + * Revision 2.0 1997/06/26 11:02:48 keil + * New Layer and card interface + * * Revision 1.6 1997/04/13 19:54:07 keil * Change in IRQ check delay for SMP * @@ -27,17 +54,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: 1.6.2.10 $"; -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define AVM_A1_STAT_ISAC 0x01 +#define AVM_A1_STAT_HSCX 0x02 +#define AVM_A1_STAT_TIMER 0x04 + +#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 +85,302 @@ 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) -{ - printk(KERN_DEBUG "HSCX %d\n", hscx); - printk(KERN_DEBUG "ISTA %x\n", readreg(sp->hscx[hscx], HSCX_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readreg(sp->hscx[hscx], HSCX_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readreg(sp->hscx[hscx], HSCX_EXIR)); -} - -void -avm_a1_report(struct IsdnCardState *sp) +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - printk(KERN_DEBUG "ISAC\n"); - printk(KERN_DEBUG "ISTA %x\n", readreg(sp->isac, ISAC_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readreg(sp->isac, ISAC_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readreg(sp->isac, ISAC_EXIR)); - hscxreport(sp, 0); - hscxreport(sp, 1); + writereg(cs->hw.avm.isac, offset, value); } -/* - * HSCX stuff goes here - */ - static void -hscx_empty_fifo(struct HscxState *hsp, int count) +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo(sp->hscx[hsp->hscx], ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + read_fifo(cs->hw.avm.isacfifo, data, size); } static void -hscx_fill_fifo(struct HscxState *hsp) -{ - 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) +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = readreg(sp->hscx[hsp->hscx], HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!(r & 0x80)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX invalid frame"); - if ((r & 0x40) && hsp->mode) - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX RDO mode=%d", - hsp->mode); - debugl1(sp, tmp); - } - if (!(r & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX CRC error"); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - } else { - count = readreg(sp->hscx[hsp->hscx], HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - if ((count = hsp->rcvidx - 1) > 0) { - if (!(skb = dev_alloc_skb(count))) - printk(KERN_WARNING "AVM: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "AVM: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - dev_kfree_skb(hsp->tx_skb, FREE_WRITE); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } -} - -/* - * ISAC stuff goes here - */ - -static void -isac_empty_fifo(struct IsdnCardState *sp, int count) -{ - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo(sp->isac, ptr, count); - writereg(sp->isac, ISAC_CMDR, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + write_fifo(cs->hw.avm.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.avm.hscx[hscx], offset)); } 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) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - u_char exval; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; - - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = readreg(sp->isac, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!(exval & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - writereg(sp->isac, ISAC_CMDR, 0x80); - } else { - count = readreg(sp->isac, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "AVM: D receive out of memory\n"); - else { - SET_SKB_FREE(skb); - memcpy(skb_put(skb, count), sp->rcvbuf, count); - skb_queue_tail(&sp->rq, skb); - } - } - } - sp->rcvidx = 0; - isac_sched_event(sp, ISAC_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - isac_empty_fifo(sp, 32); - } - if (val & 0x20) { /* RSC */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RSC interrupt"); - } - if (val & 0x10) { /* XPR */ - if (sp->tx_skb) - if (sp->tx_skb->len) { - isac_fill_fifo(sp); - goto afterXPR; - } else { - dev_kfree_skb(sp->tx_skb, FREE_WRITE); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (readreg(sp->isac, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = readreg(sp->isac, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - } + writereg(cs->hw.avm.hscx[hscx], offset, value); } -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ +/* + * fast interrupt HSCX stuff goes here + */ - u_char exval; - struct HscxState *hsp; - char tmp[32]; +#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 *) irq2dev_map[intno]; - - if (!sp) { + if (!cs) { printk(KERN_WARNING "AVM A1: Spurious interrupt!\n"); return; } - while (((sval = bytein(sp->cfg_reg)) & 0xf) != 0x7) { + while (((sval = bytein(cs->hw.avm.cfg_reg)) & 0xf) != 0x7) { if (!(sval & AVM_A1_STAT_TIMER)) { - byteout(sp->cfg_reg, 0x14); - byteout(sp->cfg_reg, 0x18); - sval = bytein(sp->cfg_reg); - } else if (sp->debug & L1_DEB_INTSTAT) { + 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: + inithscxisac(cs, 1); + byteout(cs->hw.avm.cfg_reg, 0x16); + byteout(cs->hw.avm.cfg_reg, 0x1E); + inithscxisac(cs, 2); + return(0); + case CARD_TEST: + return(0); + } + return(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.interrupts[sp->irq]; - sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter); - debugl1(sp, tmp); - clear_pending_ints(sp); - ret = get_irq(sp->cardnr, &avm_a1_interrupt); - if (ret) { - initisac(sp); - sp->modehscx(sp->hs, 0, 0); - sp->modehscx(sp->hs + 1, 0, 0); - while (loop++ < 10) { - /* At least 1-3 irqs must happen - * (one from HSCX A, one from HSCX B, 3rd from ISAC) - */ - if (kstat.interrupts[sp->irq] > sp->counter) - break; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 1; - schedule(); - } - sprintf(tmp, "IRQ %d count %d", sp->irq, - kstat.interrupts[sp->irq]); - debugl1(sp, tmp); - if (kstat.interrupts[sp->irq] == sp->counter) { - printk(KERN_WARNING - "AVM A1: IRQ(%d) getting no interrupts during init\n", - sp->irq); - irq2dev_map[sp->irq] = NULL; - free_irq(sp->irq, NULL); - return (0); - } - } - return (ret); -} - -int -setup_avm_a1(struct IsdnCard *card) -{ - u_char val, verA, verB; - struct IsdnCardState *sp = card->sp; + u_char val; + struct IsdnCardState *cs = card->cs; long flags; char tmp[64]; strcpy(tmp, avm_revision); - printk(KERN_NOTICE "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp)); - if (sp->typ != ISDN_CTYPE_A1) + printk(KERN_INFO "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_A1) return (0); - sp->cfg_reg = card->para[1] + 0x1800; - sp->isac = card->para[1] + 0x1400; - sp->hscx[0] = card->para[1] + 0x400; - sp->hscx[1] = card->para[1] + 0xc00; - sp->irq = card->para[0]; - if (check_region((sp->cfg_reg), 8)) { + cs->hw.avm.cfg_reg = card->para[1] + 0x1800; + cs->hw.avm.isac = card->para[1] + 0x1400 - 0x20; + cs->hw.avm.hscx[0] = card->para[1] + 0x400 - 0x20; + cs->hw.avm.hscx[1] = card->para[1] + 0xc00 - 0x20; + cs->hw.avm.isacfifo = card->para[1] + 0x1000; + cs->hw.avm.hscxfifo[0] = card->para[1]; + cs->hw.avm.hscxfifo[1] = card->para[1] + 0x800; + cs->irq = card->para[0]; + if (check_region((cs->hw.avm.cfg_reg), 8)) { printk(KERN_WARNING "HiSax: %s config port %x-%x already in use\n", CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + 8); + cs->hw.avm.cfg_reg, + cs->hw.avm.cfg_reg + 8); return (0); } else { - request_region(sp->cfg_reg, 8, "avm cfg"); + request_region(cs->hw.avm.cfg_reg, 8, "avm cfg"); } - if (check_region((sp->isac), 32)) { + if (check_region((cs->hw.avm.isac + 32), 32)) { printk(KERN_WARNING "HiSax: %s isac ports %x-%x already in use\n", - CardType[sp->typ], - sp->isac, - sp->isac + 32); - release_ioregs(card, 0); + CardType[cs->typ], + cs->hw.avm.isac + 32, + cs->hw.avm.isac + 64); + release_ioregs(cs, 0); return (0); } else { - request_region(sp->isac, 32, "HiSax isac"); + request_region(cs->hw.avm.isac + 32, 32, "HiSax isac"); } - if (check_region((sp->isac - 0x400), 1)) { + if (check_region((cs->hw.avm.isacfifo), 1)) { printk(KERN_WARNING "HiSax: %s isac fifo port %x already in use\n", - CardType[sp->typ], - sp->isac - 0x400); - release_ioregs(card, 1); + CardType[cs->typ], + cs->hw.avm.isacfifo); + release_ioregs(cs, 1); return (0); } else { - request_region(sp->isac - 0x400, 1, "HiSax isac fifo"); + request_region(cs->hw.avm.isacfifo, 1, "HiSax isac fifo"); } - if (check_region((sp->hscx[0]), 32)) { + if (check_region((cs->hw.avm.hscx[0]) + 32, 32)) { printk(KERN_WARNING "HiSax: %s hscx A ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[0], - sp->hscx[0] + 32); - release_ioregs(card, 3); + CardType[cs->typ], + cs->hw.avm.hscx[0] + 32, + cs->hw.avm.hscx[0] + 64); + release_ioregs(cs, 3); return (0); } else { - request_region(sp->hscx[0], 32, "HiSax hscx A"); + request_region(cs->hw.avm.hscx[0] + 32, 32, "HiSax hscx A"); } - if (check_region((sp->hscx[0] - 0x400), 1)) { + if (check_region(cs->hw.avm.hscxfifo[0], 1)) { printk(KERN_WARNING "HiSax: %s hscx A fifo port %x already in use\n", - CardType[sp->typ], - sp->hscx[0] - 0x400); - release_ioregs(card, 7); + CardType[cs->typ], + cs->hw.avm.hscxfifo[0]); + release_ioregs(cs, 7); return (0); } else { - request_region(sp->hscx[0] - 0x400, 1, "HiSax hscx A fifo"); + request_region(cs->hw.avm.hscxfifo[0], 1, "HiSax hscx A fifo"); } - if (check_region((sp->hscx[1]), 32)) { + if (check_region(cs->hw.avm.hscx[1] + 32, 32)) { printk(KERN_WARNING "HiSax: %s hscx B ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[1], - sp->hscx[1] + 32); - release_ioregs(card, 0xf); + CardType[cs->typ], + cs->hw.avm.hscx[1] + 32, + cs->hw.avm.hscx[1] + 64); + release_ioregs(cs, 0xf); return (0); } else { - request_region(sp->hscx[1], 32, "HiSax hscx B"); + request_region(cs->hw.avm.hscx[1] + 32, 32, "HiSax hscx B"); } - if (check_region((sp->hscx[1] - 0x400), 1)) { + if (check_region(cs->hw.avm.hscxfifo[1], 1)) { printk(KERN_WARNING "HiSax: %s hscx B fifo port %x already in use\n", - CardType[sp->typ], - sp->hscx[1] - 0x400); - release_ioregs(card, 0x1f); + CardType[cs->typ], + cs->hw.avm.hscxfifo[1]); + release_ioregs(cs, 0x1f); return (0); } else { - request_region(sp->hscx[1] - 0x400, 1, "HiSax hscx B fifo"); + request_region(cs->hw.avm.hscxfifo[1], 1, "HiSax hscx B fifo"); } save_flags(flags); - byteout(sp->cfg_reg, 0x0); + byteout(cs->hw.avm.cfg_reg, 0x0); sti(); HZDELAY(HZ / 5 + 1); - byteout(sp->cfg_reg, 0x1); + byteout(cs->hw.avm.cfg_reg, 0x1); HZDELAY(HZ / 5 + 1); - byteout(sp->cfg_reg, 0x0); + byteout(cs->hw.avm.cfg_reg, 0x0); HZDELAY(HZ / 5 + 1); - val = sp->irq; + val = cs->irq; if (val == 9) val = 2; - byteout(sp->cfg_reg + 1, val); + byteout(cs->hw.avm.cfg_reg + 1, val); HZDELAY(HZ / 5 + 1); - byteout(sp->cfg_reg, 0x0); + byteout(cs->hw.avm.cfg_reg, 0x0); HZDELAY(HZ / 5 + 1); restore_flags(flags); - val = bytein(sp->cfg_reg); + val = bytein(cs->hw.avm.cfg_reg); printk(KERN_INFO "AVM A1: Byte at %x is %x\n", - sp->cfg_reg, val); - val = bytein(sp->cfg_reg + 3); + cs->hw.avm.cfg_reg, val); + val = bytein(cs->hw.avm.cfg_reg + 3); printk(KERN_INFO "AVM A1: Byte at %x is %x\n", - sp->cfg_reg + 3, val); - val = bytein(sp->cfg_reg + 2); + cs->hw.avm.cfg_reg + 3, val); + val = bytein(cs->hw.avm.cfg_reg + 2); printk(KERN_INFO "AVM A1: Byte at %x is %x\n", - sp->cfg_reg + 2, val); - byteout(sp->cfg_reg, 0x14); - byteout(sp->cfg_reg, 0x18); - val = bytein(sp->cfg_reg); + cs->hw.avm.cfg_reg + 2, val); + val = bytein(cs->hw.avm.cfg_reg); printk(KERN_INFO "AVM A1: Byte at %x is %x\n", - sp->cfg_reg, val); - - 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/callc.c b/drivers/isdn/hisax/callc.c index 91c868942082..3cb5f9ce7c4f 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 1.30.2.9 1998/05/27 18:04:53 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,150 +7,123 @@ * Fritz Elfert * * $Log: callc.c,v $ - * Revision 1.30 1997/05/29 10:40:43 keil - * chanp->impair was uninitialised + * Revision 1.30.2.9 1998/05/27 18:04:53 keil + * HiSax 3.0 * - * Revision 1.29 1997/04/23 20:09:49 fritz - * Removed tmp, used by removed debugging code. + * Revision 1.30.2.8 1998/04/11 18:48:26 keil + * remove debug * - * Revision 1.28 1997/04/21 13:42:25 keil - * Remove unneeded debug + * Revision 1.30.2.7 1998/04/08 21:51:50 keil + * new debug * - * Revision 1.27 1997/04/16 14:21:01 keil - * remove unused variable + * Revision 1.30.2.6 1998/03/07 23:15:02 tsbogend + * made HiSax working on Linux/Alpha * - * Revision 1.26 1997/04/13 19:55:21 keil - * Changes in debugging code + * Revision 1.30.2.5 1998/02/09 11:24:17 keil + * New leased line support (Read README.HiSax!) * - * Revision 1.25 1997/04/06 22:54:08 keil - * Using SKB's + * Revision 1.30.2.4 1998/01/27 22:46:00 keil + * B-channel send delay now configurable * - * Revision 1.24 1997/03/05 11:28:03 keil - * fixed undefined l2tei procedure - * a layer1 release delete now the drel timer + * Revision 1.30.2.3 1998/01/11 23:21:03 keil + * Missing callc state * - * Revision 1.23 1997/03/04 23:07:42 keil - * bugfix dial parameter + * Revision 1.30.2.2 1997/11/15 18:54:31 keil + * cosmetics * - * Revision 1.22 1997/02/27 13:51:55 keil - * Reset B-channel (dlc) statemachine in every release + * Revision 1.30.2.1 1997/10/17 22:13:32 keil + * update to last hisax version * - * Revision 1.21 1997/02/19 09:24:27 keil - * Bugfix: Hangup to LL if a ttyI rings + * Revision 2.6 1997/09/11 17:26:58 keil + * Open B-channel if here are incomming packets * - * Revision 1.20 1997/02/17 00:32:47 keil - * Bugfix: No Busy reported to LL + * Revision 2.5 1997/08/07 17:46:05 keil + * Fix Incomming Call without broadcast * - * Revision 1.19 1997/02/14 12:23:10 fritz - * Added support for new insmod parameter handling. + * Revision 2.4 1997/08/03 14:37:58 keil + * Activate Layer2 in PtP mode * - * Revision 1.18 1997/02/11 01:36:58 keil - * Changed setup-interface (incoming and outgoing), cause reporting + * Revision 2.3 1997/07/31 19:23:40 keil + * LAYER2_WATCHING for PtP * - * 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.2 1997/07/31 11:48:18 keil + * experimental REJECT after ALERTING * - * Revision 1.16 1997/01/27 23:17:03 keil - * delete timers while unloading + * Revision 2.1 1997/07/30 17:12:59 keil + * more changes for 'One TEI per card' * - * Revision 1.15 1997/01/27 16:00:38 keil - * D-channel shutdown delay; improved callback + * Revision 2.0 1997/07/27 21:12:21 keil + * CRef based L3; new channel handling; many other stuff * - * Revision 1.14 1997/01/21 22:16:39 keil - * new statemachine; leased line support; cleanup for 2.0 + * Revision 1.31 1997/06/26 11:09:23 keil + * New managment and minor changes * - * 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 * */ #define __NO_VERSION__ #include "hisax.h" +#include "../avmb1/capicmd.h" /* this should be moved in a common place */ #ifdef MODULE -#if (LINUX_VERSION_CODE < 0x020111) extern long mod_use_count_; #define MOD_USE_COUNT mod_use_count_ -#else -#define MOD_USE_COUNT ((&__this_module)->usecount) -#endif #endif /* MODULE */ -const char *l4_revision = "$Revision: 1.30 $"; +const char *lli_revision = "$Revision: 1.30.2.9 $"; extern struct IsdnCard cards[]; extern int nrcards; extern void HiSax_mod_dec_use_count(void); extern void HiSax_mod_inc_use_count(void); -static int init_ds(struct Channel *chanp, int incoming); -static void release_ds(struct Channel *chanp); +static int init_b_st(struct Channel *chanp, int incoming); +static void release_b_st(struct Channel *chanp); static struct Fsm callcfsm = -{NULL, 0, 0}; -static struct Fsm lcfsm = -{NULL, 0, 0}; +{NULL, 0, 0, NULL, NULL}; static int chancount = 0; -/* Flags for remembering action done in l4 */ - -#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 +#define FLG_RESUME 16 /* * Because of callback it's a good idea to delay the shutdown of the d-channel */ -#define DREL_TIMER_VALUE 30000 +#define DREL_TIMER_VALUE 40000 /* * Find card with given driverId @@ -162,12 +135,25 @@ 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; } +int +discard_queue(struct sk_buff_head *q) +{ + struct sk_buff *skb; + int ret=0; + + while ((skb = skb_dequeue(q))) { + dev_kfree_skb(skb, FREE_READ); + ret++; + } + return(ret); +} + static void link_debug(struct Channel *chanp, char *s, int direction) { @@ -176,7 +162,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 +219,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 +249,7 @@ static char *strEvent[] = "EV_SETUP_CMPL_IND", "EV_BC_EST", "EV_WRITEBUF", - "EV_DATAIN", + "EV_ESTABLISH", "EV_HANGUP", "EV_BC_REL", "EV_CINF", @@ -276,1105 +262,1045 @@ static char *strEvent[] = "EV_RELEASE_ERR", }; -enum { - ST_LC_NULL, - ST_LC_ACTIVATE_WAIT, - ST_LC_DELAY, - ST_LC_ESTABLISH_WAIT, - ST_LC_CONNECTED, - ST_LC_FLUSH_WAIT, - ST_LC_FLUSH_DELAY, - ST_LC_RELEASE_WAIT, -}; - -#define LC_STATE_COUNT (ST_LC_RELEASE_WAIT+1) - -static char *strLcState[] = -{ - "ST_LC_NULL", - "ST_LC_ACTIVATE_WAIT", - "ST_LC_DELAY", - "ST_LC_ESTABLISH_WAIT", - "ST_LC_CONNECTED", - "ST_LC_FLUSH_WAIT", - "ST_LC_FLUSH_DELAY", - "ST_LC_RELEASE_WAIT", -}; - -enum { - EV_LC_ESTABLISH, - EV_LC_PH_ACTIVATE, - EV_LC_PH_DEACTIVATE, - EV_LC_DL_ESTABLISH, - EV_LC_TIMER, - EV_LC_DL_FLUSH, - EV_LC_DL_RELEASE, - EV_LC_FLUSH, - EV_LC_RELEASE, -}; - -#define LC_EVENT_COUNT (EV_LC_RELEASE+1) - -static char *strLcEvent[] = -{ - "EV_LC_ESTABLISH", - "EV_LC_PH_ACTIVATE", - "EV_LC_PH_DEACTIVATE", - "EV_LC_DL_ESTABLISH", - "EV_LC_TIMER", - "EV_LC_DL_FLUSH", - "EV_LC_DL_RELEASE", - "EV_LC_FLUSH", - "EV_LC_RELEASE", -}; - -#define LC_D 0 -#define LC_B 1 - static inline void -l4_deliver_cause(struct Channel *chanp) +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 *) (long)chanp->chan); + FsmChangeState(fi, ST_IN_WAIT_LL); + test_and_set_bit(FLG_CALL_REC, &chanp->Flags); + if (chanp->debug & 1) + link_debug(chanp, "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 *) (long)chanp->chan); + FsmChangeState(fi, ST_NULL); + } + } else if (fi->state == ST_WAIT_DSHUTDOWN) + FsmChangeState(fi, ST_NULL); +} + +static void +lli_d_released(struct FsmInst *fi, int event, void *arg) +{ + 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; - switch (chanp->l2_active_protocol) { - case (ISDN_PROTO_L2_X75I): - chanp->lc_b.l2_establish = !0; - break; - case (ISDN_PROTO_L2_HDLC): - case (ISDN_PROTO_L2_TRANS): - chanp->lc_b.l2_establish = 0; - break; - default: - printk(KERN_WARNING "l4_prep_dialout unknown protocol\n"); - break; - } - if (chanp->Flags & FLG_ESTAB_D) { + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) { FsmEvent(fi, EV_DLEST, NULL); } else { - chanp->Flags = FLG_START_D; - if (chanp->leased) { - chanp->lc_d.l2_establish = 0; - } - FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL); + chanp->Flags = 0; + if (EV_RESUME == event) + test_and_set_bit(FLG_RESUME, &chanp->Flags); + test_and_set_bit(FLG_START_D, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL); } } static void -l4_do_dialout(struct FsmInst *fi, int event, void *arg) +lli_do_dialout(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; + int ev; FsmChangeState(fi, ST_OUT_DIAL); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); + if (test_and_clear_bit(FLG_RESUME, &chanp->Flags)) + ev = CC_RESUME | REQUEST; + else + ev = CC_SETUP | REQUEST; if (chanp->leased) { - chanp->para.bchannel = (chanp->chan & 1) + 1; FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL); } else { - SETBIT(chanp->Flags, FLG_ESTAB_D); - chanp->para.callref = chanp->outcallref; - chanp->outcallref++; - if (chanp->outcallref == 128) - chanp->outcallref = 64; - chanp->is.l4.l4l3(&chanp->is, CC_SETUP_REQ, NULL); - SETBIT(chanp->Flags, FLG_CALL_SEND); + test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, ev, chanp); + test_and_set_bit(FLG_CALL_SEND, &chanp->Flags); } } static void -l4_init_bchan_out(struct FsmInst *fi, int event, void *arg) +lli_init_bchan_out(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_BCONN); - SETBIT(chanp->Flags, FLG_LL_DCONN); + test_and_set_bit(FLG_LL_DCONN, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, "STAT_DCONN", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DCONN; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - init_ds(chanp, 0); - SETBIT(chanp->Flags, FLG_START_B); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL); + chanp->cs->iif.statcallb(&ic); + init_b_st(chanp, 0); + test_and_set_bit(FLG_START_B, &chanp->Flags); + chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL); } static void -l4_go_active(struct FsmInst *fi, int event, void *arg) +lli_go_active(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_ACTIVE); chanp->data_open = !0; - SETBIT(chanp->Flags, FLG_CONNECT_B); + test_and_set_bit(FLG_CONNECT_B, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, "STAT_BCONN", 0); - SETBIT(chanp->Flags, FLG_LL_BCONN); - ic.driver = chanp->sp->myid; + test_and_set_bit(FLG_LL_BCONN, &chanp->Flags); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BCONN; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_CONN, (void *) (long)chanp->chan); } +/* + * RESUME + */ + /* incomming call */ static void -l4_start_dchan(struct FsmInst *fi, int event, void *arg) +lli_start_dchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_IN_WAIT_D); FsmDelTimer(&chanp->drel_timer, 61); - if (chanp->Flags & FLG_ESTAB_D) { + 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)) + chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL); } static void -l4_deliver_call(struct FsmInst *fi, int event, void *arg) +lli_deliver_call(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; int ret; char txt[32]; + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); /* * Report incoming calls only once to linklevel, use CallFlags * which is set to 3 with each broadcast message in isdnl1.c * and resetted if a interface answered the STAT_ICALL. */ - if ((chanp->sp) && (chanp->sp->CallFlags == 3)) { + if (1) { /* for only one TEI */ FsmChangeState(fi, ST_IN_WAIT_LL); - SETBIT(chanp->Flags, FLG_ESTAB_D); - SETBIT(chanp->Flags, FLG_CALL_REC); + test_and_set_bit(FLG_CALL_REC, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, "STAT_ICALL", 0); - ic.driver = chanp->sp->myid; + 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 | REQUEST, chanp->proc); + } else { + test_and_set_bit(FLG_DO_ALERT, &chanp->Flags); + FsmChangeState(fi, ST_IN_WAIT_D); + test_and_set_bit(FLG_START_D, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, + DL_ESTABLISH | REQUEST, NULL); + } break; case 2: /* Rejecting Call */ - RESBIT(chanp->Flags, FLG_CALL_REC); + test_and_clear_bit(FLG_CALL_REC, &chanp->Flags); break; case 0: /* OK, nobody likes this call */ default: /* statcallb problems */ - chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); FsmChangeState(fi, ST_NULL); - chanp->Flags = FLG_ESTAB_D; - FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61); + if (test_bit(FLG_ESTAB_D, &chanp->Flags) && + !test_bit(FLG_PTP, &chanp->d_st->l2.flag)) + FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61); break; } } else { - chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); FsmChangeState(fi, ST_NULL); - chanp->Flags = FLG_ESTAB_D; - FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62); + if (test_bit(FLG_ESTAB_D, &chanp->Flags) && + !test_bit(FLG_PTP, &chanp->d_st->l2.flag)) + FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62); + } +} + +static void +lli_establish_d(struct FsmInst *fi, int event, void *arg) +{ + /* This establish the D-channel for pending L3 messages + * without blocking the channel + */ + struct Channel *chanp = fi->userdata; + + test_and_set_bit(FLG_DO_ESTAB, &chanp->Flags); + FsmChangeState(fi, ST_IN_WAIT_D); + test_and_set_bit(FLG_START_D, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL); +} + +static void +lli_do_action(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); + if (chanp->leased) { + FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); + test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags); + test_and_clear_bit(FLG_DO_CONNECT, &chanp->Flags); + FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL); + } else if (test_and_clear_bit(FLG_DO_CONNECT, &chanp->Flags) && + !test_bit(FLG_DO_HANGUP, &chanp->Flags)) { + FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); + test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); + } else if (test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags)) { + if (test_bit(FLG_DO_HANGUP, &chanp->Flags)) + FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63); + FsmChangeState(fi, ST_IN_ALERT_SEND); + test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); + } else if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags)) { + FsmChangeState(fi, ST_WAIT_DRELEASE); + chanp->proc->para.cause = 0x15; /* Call Rejected */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc); + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); } } static void -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 | RESPONSE, chanp->proc); } static void -l4_init_bchan_in(struct FsmInst *fi, int event, void *arg) +lli_init_bchan_in(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_BCONN); - SETBIT(chanp->Flags, FLG_LL_DCONN); + test_and_set_bit(FLG_LL_DCONN, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, "STAT_DCONN", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DCONN; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); chanp->l2_active_protocol = chanp->l2_protocol; chanp->incoming = !0; - chanp->lc_b.l2_start = 0; - switch (chanp->l2_active_protocol) { - case (ISDN_PROTO_L2_X75I): - chanp->lc_b.l2_establish = !0; - break; - case (ISDN_PROTO_L2_HDLC): - case (ISDN_PROTO_L2_TRANS): - chanp->lc_b.l2_establish = 0; - break; - default: - printk(KERN_WARNING "r9 unknown protocol\n"); - break; - } - init_ds(chanp, !0); - SETBIT(chanp->Flags, FLG_START_B); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL); + init_b_st(chanp, !0); + test_and_set_bit(FLG_START_B, &chanp->Flags); + chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL); } -/* Call clearing */ +/* Call suspend */ static void -l4_reject_call(struct FsmInst *fi, int event, void *arg) +lli_suspend(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_WAIT_DRELEASE); - chanp->para.cause = 0x15; /* Call Rejected */ - chanp->is.l4.l4l3(&chanp->is, CC_REJECT_REQ, NULL); - SETBIT(chanp->Flags, FLG_DISC_SEND); + chanp->d_st->lli.l4l3(chanp->d_st, CC_SUSPEND | REQUEST, chanp->proc); } +/* Call clearing */ + static void -l4_cancel_call(struct FsmInst *fi, int event, void *arg) +lli_cancel_call(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_DRELEASE); - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - RESBIT(chanp->Flags, FLG_LL_BCONN); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); - } - chanp->para.cause = 0x10; /* Normal Call Clearing */ - chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL); - SETBIT(chanp->Flags, FLG_DISC_SEND); + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc); + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); } static void -l4_shutdown_d(struct FsmInst *fi, int event, void *arg) +lli_shutdown_d(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_WAIT_DSHUTDOWN); FsmDelTimer(&chanp->drel_timer, 62); - RESBIT(chanp->Flags, FLG_ESTAB_D); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); + if (test_bit(FLG_PTP, &chanp->d_st->l2.flag)) { + FsmChangeState(fi, ST_NULL); + } else { + if (!test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags)) { + if (chanp->chan) { + if (chanp->cs->channel[0].fi.state != ST_NULL) + return; + } else { + if (chanp->cs->channel[1].fi.state != ST_NULL) + return; + } + } + FsmChangeState(fi, ST_WAIT_DSHUTDOWN); + test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); + } } static void -l4_timeout_d(struct FsmInst *fi, int event, void *arg) +lli_timeout_d(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - if (chanp->Flags & FLG_LL_DCONN) { + if (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); + if (!test_bit(FLG_PTP, &chanp->d_st->l2.flag)) FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); } static void -l4_go_null(struct FsmInst *fi, int event, void *arg) +lli_go_null(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_NULL); chanp->Flags = 0; FsmDelTimer(&chanp->drel_timer, 63); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); } static void -l4_disconn_bchan(struct FsmInst *fi, int event, void *arg) +lli_disconn_bchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; chanp->data_open = 0; FsmChangeState(fi, ST_WAIT_BRELEASE); - RESBIT(chanp->Flags, FLG_CONNECT_B); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); + test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags); + chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); } static void -l4_send_d_disc(struct FsmInst *fi, int event, void *arg) +lli_send_d_disc(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - - if (chanp->Flags & (FLG_DISC_REC | FLG_REL_REC)) + if (test_bit(FLG_DISC_REC, &chanp->Flags) || + test_bit(FLG_REL_REC, &chanp->Flags)) return; FsmChangeState(fi, ST_WAIT_DRELEASE); - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - RESBIT(chanp->Flags, FLG_LL_BCONN); - ic.driver = chanp->sp->myid; + 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); + chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); + } else { + if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags)) + chanp->proc->para.cause = 0x15; /* Call Reject */ + else + chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc); + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); } - chanp->para.cause = 0x10; /* Normal Call Clearing */ - chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL); - SETBIT(chanp->Flags, FLG_DISC_SEND); } static void -l4_released_bchan(struct FsmInst *fi, int event, void *arg) +lli_released_bchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_DCOMMAND); chanp->data_open = 0; - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - RESBIT(chanp->Flags, FLG_LL_BCONN); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); + release_b_st(chanp); + test_and_clear_bit(FLG_START_B, &chanp->Flags); } static void -l4_release_bchan(struct FsmInst *fi, int event, void *arg) +lli_release_bchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; chanp->data_open = 0; - SETBIT(chanp->Flags, FLG_DISC_REC); + test_and_set_bit(FLG_DISC_REC, &chanp->Flags); FsmChangeState(fi, ST_WAIT_BREL_DISC); - RESBIT(chanp->Flags, FLG_CONNECT_B); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); + test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags); + chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); } static void -l4_received_d_rel(struct FsmInst *fi, int event, void *arg) +lli_received_d_rel(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; chanp->data_open = 0; - FsmChangeState(fi, ST_WAIT_DSHUTDOWN); - SETBIT(chanp->Flags, FLG_REL_REC); - if (chanp->Flags & FLG_CONNECT_B) { - chanp->lc_b.l2_establish = 0; /* direct reset in lc_b.lcfi */ - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); - RESBIT(chanp->Flags, FLG_CONNECT_B); + FsmChangeState(fi, ST_NULL); + test_and_set_bit(FLG_REL_REC, &chanp->Flags); + if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) { + chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); } - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - ic.driver = chanp->sp->myid; + 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->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); } - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - ic.driver = chanp->sp->myid; + 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_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 | REQUEST, chanp->proc); } /* processing charge info */ static void -l4_charge_info(struct FsmInst *fi, int event, void *arg) +lli_charge_info(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_CINF; ic.arg = chanp->chan; - sprintf(ic.parm.num, "%d", chanp->para.chargeinfo); - chanp->sp->iif.statcallb(&ic); + sprintf(ic.parm.num, "%d", chanp->proc->para.chargeinfo); + chanp->cs->iif.statcallb(&ic); } /* error procedures */ static void -l4_no_dchan(struct FsmInst *fi, int event, void *arg) +lli_no_dchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; if (chanp->debug & 1) link_debug(chanp, "STAT_NODCH", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_NODCH; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); chanp->Flags = 0; FsmChangeState(fi, ST_NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); + chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); } static void -l4_no_dchan_ready(struct FsmInst *fi, int event, void *arg) +lli_no_dchan_ready(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - ic.driver = chanp->sp->myid; + 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 | REQUEST, chanp->proc); chanp->Flags = 0; FsmChangeState(fi, ST_NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); + chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); } static void -l4_no_setup_rsp(struct FsmInst *fi, int event, void *arg) +lli_no_setup_rsp(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - chanp->Flags = 0; FsmChangeState(fi, ST_NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); + test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - ic.driver = chanp->sp->myid; + 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->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); } - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - ic.driver = chanp->sp->myid; + 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->Flags = 0; - chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); -} -/* *INDENT-OFF* */ -static struct FsmNode fnlist[] = -{ - {ST_NULL, EV_DIAL, l4_prep_dialout}, - {ST_NULL, EV_SETUP_IND, l4_start_dchan}, - {ST_NULL, EV_SHUTDOWN_D, l4_shutdown_d}, - {ST_NULL, EV_DLRL, l4_go_null}, - {ST_OUT_WAIT_D, EV_DLEST, l4_do_dialout}, - {ST_OUT_WAIT_D, EV_DLRL, l4_no_dchan}, - {ST_OUT_WAIT_D, EV_HANGUP, l4_no_dchan}, - {ST_IN_WAIT_D, EV_DLEST, l4_deliver_call}, - {ST_IN_WAIT_D, EV_DLRL, l4_no_dchan_in}, - {ST_IN_WAIT_D, EV_HANGUP, l4_no_dchan_in}, - {ST_OUT_DIAL, EV_SETUP_CNF, l4_init_bchan_out}, - {ST_OUT_DIAL, EV_HANGUP, l4_cancel_call}, - {ST_OUT_DIAL, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_OUT_DIAL, EV_RELEASE_IND, l4_received_d_rel}, - {ST_OUT_DIAL, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_OUT_DIAL, EV_NOSETUP_RSP, l4_no_setup_rsp}, - {ST_OUT_DIAL, EV_SETUP_ERR, l4_setup_err}, - {ST_IN_WAIT_LL, EV_SETUP_CMPL_IND, l4_init_bchan_in}, - {ST_IN_WAIT_LL, EV_ACCEPTD, l4_send_dconnect}, - {ST_IN_WAIT_LL, EV_HANGUP, l4_reject_call}, - {ST_IN_WAIT_LL, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_IN_WAIT_LL, EV_RELEASE_IND, l4_received_d_rel}, - {ST_IN_WAIT_LL, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_IN_ALERT_SEND, EV_SETUP_CMPL_IND, l4_init_bchan_in}, - {ST_IN_ALERT_SEND, EV_ACCEPTD, l4_send_dconnect}, - {ST_IN_ALERT_SEND, EV_HANGUP, l4_send_d_disc}, - {ST_IN_ALERT_SEND, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_IN_ALERT_SEND, EV_RELEASE_IND, l4_received_d_rel}, - {ST_IN_ALERT_SEND, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_IN_WAIT_CONN_ACK, EV_SETUP_CMPL_IND, l4_init_bchan_in}, - {ST_IN_WAIT_CONN_ACK, EV_HANGUP, l4_send_d_disc}, - {ST_IN_WAIT_CONN_ACK, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_IN_WAIT_CONN_ACK, EV_RELEASE_IND, l4_received_d_rel}, - {ST_IN_WAIT_CONN_ACK, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_IN_WAIT_CONN_ACK, EV_CONNECT_ERR, l4_connect_err}, - {ST_WAIT_BCONN, EV_BC_EST, l4_go_active}, - {ST_WAIT_BCONN, EV_BC_REL, l4_send_d_disc}, - {ST_WAIT_BCONN, EV_HANGUP, l4_send_d_disc}, - {ST_WAIT_BCONN, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_WAIT_BCONN, EV_RELEASE_IND, l4_received_d_rel}, - {ST_WAIT_BCONN, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_ACTIVE, EV_CINF, l4_charge_info}, - {ST_ACTIVE, EV_BC_REL, l4_released_bchan}, - {ST_ACTIVE, EV_HANGUP, l4_disconn_bchan}, - {ST_ACTIVE, EV_DISCONNECT_IND, l4_release_bchan}, - {ST_ACTIVE, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_ACTIVE, EV_RELEASE_IND, l4_received_d_rel}, - {ST_ACTIVE, EV_DLRL, l4_active_dlrl}, - {ST_WAIT_BRELEASE, EV_BC_REL, l4_send_d_disc}, - {ST_WAIT_BRELEASE, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_WAIT_BRELEASE, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_WAIT_BRELEASE, EV_RELEASE_IND, l4_received_d_rel}, - {ST_WAIT_BREL_DISC, EV_BC_REL, l4_received_d_disc}, - {ST_WAIT_BREL_DISC, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_WAIT_BREL_DISC, EV_RELEASE_IND, l4_received_d_rel}, - {ST_WAIT_DCOMMAND, EV_HANGUP, l4_send_d_disc}, - {ST_WAIT_DCOMMAND, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_WAIT_DCOMMAND, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_WAIT_DCOMMAND, EV_RELEASE_IND, l4_received_d_rel}, - {ST_WAIT_DRELEASE, EV_RELEASE_IND, l4_timeout_d}, - {ST_WAIT_DRELEASE, EV_RELEASE_CNF, l4_timeout_d}, - {ST_WAIT_DRELEASE, EV_RELEASE_ERR, l4_timeout_d}, - {ST_WAIT_DRELEASE, EV_DIAL, l4_no_dchan_ready}, - {ST_WAIT_D_REL_CNF, EV_RELEASE_CNF, l4_timeout_d}, - {ST_WAIT_D_REL_CNF, EV_RELEASE_ERR, l4_timeout_d}, - {ST_WAIT_D_REL_CNF, EV_DIAL, l4_no_dchan_ready}, - {ST_WAIT_DSHUTDOWN, EV_DLRL, l4_go_null}, - {ST_WAIT_DSHUTDOWN, EV_DIAL, l4_prep_dialout}, - {ST_WAIT_DSHUTDOWN, EV_SETUP_IND, l4_start_dchan}, -}; -/* *INDENT-ON* */ - - - - -#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode)) - -static void -lc_r1(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - FsmChangeState(fi, ST_LC_ACTIVATE_WAIT); - FsmAddTimer(&lf->act_timer, 1000, EV_LC_TIMER, NULL, 50); - lf->st->ma.manl1(lf->st, PH_ACTIVATE, NULL); - -} - -static void -lc_r6(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - FsmDelTimer(&lf->act_timer, 50); - FsmChangeState(fi, ST_LC_DELAY); - FsmAddTimer(&lf->act_timer, 40, EV_LC_TIMER, NULL, 51); -} - -static void -lc_r2(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - if (lf->l2_establish) { - FsmChangeState(fi, ST_LC_ESTABLISH_WAIT); - if (lf->l2_start) - lf->st->ma.manl2(lf->st, DL_ESTABLISH, NULL); - } else { - FsmChangeState(fi, ST_LC_CONNECTED); - lf->lccall(lf, LC_ESTABLISH, NULL); - } -} - -static void -lc_r3(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - FsmChangeState(fi, ST_LC_CONNECTED); - lf->lccall(lf, LC_ESTABLISH, NULL); -} - -static void -lc_r7(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - FsmChangeState(fi, ST_LC_FLUSH_WAIT); - lf->st->ma.manl2(lf->st, DL_FLUSH, NULL); -} - -static void -lc_r4(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - if (lf->l2_establish) { - FsmChangeState(fi, ST_LC_RELEASE_WAIT); - lf->st->ma.manl2(lf->st, DL_RELEASE, NULL); - /* This timer is for releasing the channel even - * there is a hang in layer 2 ; 5 sec are a try - */ - FsmAddTimer(&lf->act_timer, 5000, EV_LC_TIMER, NULL, 53); + chanp->cs->iif.statcallb(&ic); + chanp->Flags = 0; } else { - FsmChangeState(fi, ST_LC_NULL); - lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL); - lf->lccall(lf, LC_RELEASE, NULL); + 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 | REQUEST, chanp->proc); + chanp->Flags = 0; + chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); } + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); } -static void -lc_r4_1(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - FsmChangeState(fi, ST_LC_FLUSH_DELAY); - FsmAddTimer(&lf->act_timer, 50, EV_LC_TIMER, NULL, 52); -} - -static void -lc_r5_1(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - FsmChangeState(fi, ST_LC_RELEASE_WAIT); - /* This delay is needed for send out the UA frame before - * PH_DEACTIVATE the interface - */ - FsmAddTimer(&lf->act_timer, 10, EV_LC_TIMER, NULL, 54); -} - -static void -lc_r5(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - FsmDelTimer(&lf->act_timer, 54); - FsmChangeState(fi, ST_LC_NULL); - lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL); - lf->lccall(lf, LC_RELEASE, NULL); -} /* *INDENT-OFF* */ -static struct FsmNode LcFnList[] = -{ - {ST_LC_NULL, EV_LC_ESTABLISH, lc_r1}, - {ST_LC_ACTIVATE_WAIT, EV_LC_PH_ACTIVATE, lc_r6}, - {ST_LC_DELAY, EV_LC_TIMER, lc_r2}, - {ST_LC_DELAY, EV_LC_DL_ESTABLISH, lc_r3}, - {ST_LC_ESTABLISH_WAIT, EV_LC_DL_ESTABLISH, lc_r3}, - {ST_LC_ESTABLISH_WAIT, EV_LC_RELEASE, lc_r5}, - {ST_LC_CONNECTED, EV_LC_FLUSH, lc_r7}, - {ST_LC_CONNECTED, EV_LC_RELEASE, lc_r4}, - {ST_LC_CONNECTED, EV_LC_DL_RELEASE, lc_r5_1}, - {ST_LC_FLUSH_WAIT, EV_LC_DL_FLUSH, lc_r4_1}, - {ST_LC_FLUSH_DELAY, EV_LC_TIMER, lc_r4}, - {ST_LC_RELEASE_WAIT, EV_LC_DL_RELEASE, lc_r5}, - {ST_LC_RELEASE_WAIT, EV_LC_TIMER, lc_r5}, - {ST_LC_ACTIVATE_WAIT, EV_LC_TIMER, lc_r5}, - {ST_LC_ESTABLISH_WAIT, EV_LC_DL_RELEASE, lc_r5}, +static struct FsmNode fnlist[] HISAX_INITDATA = +{ + {ST_NULL, EV_DIAL, lli_prep_dialout}, + {ST_NULL, EV_RESUME, lli_prep_dialout}, + {ST_NULL, EV_SETUP_IND, lli_deliver_call}, + {ST_NULL, EV_SHUTDOWN_D, lli_shutdown_d}, + {ST_NULL, EV_DLRL, lli_go_null}, + {ST_NULL, EV_DLEST, lli_d_established}, + {ST_NULL, EV_ESTABLISH, lli_establish_d}, + {ST_OUT_WAIT_D, EV_DLEST, lli_do_dialout}, + {ST_OUT_WAIT_D, EV_DLRL, lli_no_dchan}, + {ST_OUT_WAIT_D, EV_HANGUP, lli_no_dchan}, + {ST_IN_WAIT_D, EV_DLEST, lli_do_action}, + {ST_IN_WAIT_D, EV_DLRL, lli_no_dchan_in}, + {ST_IN_WAIT_D, EV_ACCEPTD, lli_start_dchan}, + {ST_IN_WAIT_D, EV_HANGUP, lli_start_dchan}, + {ST_OUT_DIAL, EV_SETUP_CNF, lli_init_bchan_out}, + {ST_OUT_DIAL, EV_HANGUP, lli_cancel_call}, + {ST_OUT_DIAL, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_OUT_DIAL, EV_RELEASE_IND, lli_received_d_rel}, + {ST_OUT_DIAL, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_OUT_DIAL, EV_NOSETUP_RSP, lli_no_setup_rsp}, + {ST_OUT_DIAL, EV_SETUP_ERR, lli_setup_err}, + {ST_OUT_DIAL, EV_DLRL, lli_got_dlrl}, + {ST_IN_WAIT_LL, EV_DLEST, lli_d_established}, + {ST_IN_WAIT_LL, EV_DLRL, lli_d_released}, + {ST_IN_WAIT_LL, EV_ACCEPTD, lli_start_dchan}, + {ST_IN_WAIT_LL, EV_HANGUP, lli_start_dchan}, + {ST_IN_WAIT_LL, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_IN_WAIT_LL, EV_RELEASE_IND, lli_received_d_rel}, + {ST_IN_WAIT_LL, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_IN_ALERT_SEND, EV_SETUP_CMPL_IND, lli_init_bchan_in}, + {ST_IN_ALERT_SEND, EV_ACCEPTD, lli_send_dconnect}, + {ST_IN_ALERT_SEND, EV_HANGUP, lli_send_d_disc}, + {ST_IN_ALERT_SEND, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_IN_ALERT_SEND, EV_RELEASE_IND, lli_received_d_rel}, + {ST_IN_ALERT_SEND, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_IN_ALERT_SEND, EV_DLRL, lli_got_dlrl}, + {ST_IN_WAIT_CONN_ACK, EV_SETUP_CMPL_IND, lli_init_bchan_in}, + {ST_IN_WAIT_CONN_ACK, EV_HANGUP, lli_send_d_disc}, + {ST_IN_WAIT_CONN_ACK, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_IN_WAIT_CONN_ACK, EV_RELEASE_IND, lli_received_d_rel}, + {ST_IN_WAIT_CONN_ACK, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_IN_WAIT_CONN_ACK, EV_CONNECT_ERR, lli_connect_err}, + {ST_IN_WAIT_CONN_ACK, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_BCONN, EV_BC_EST, lli_go_active}, + {ST_WAIT_BCONN, EV_BC_REL, lli_send_d_disc}, + {ST_WAIT_BCONN, EV_HANGUP, lli_send_d_disc}, + {ST_WAIT_BCONN, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_WAIT_BCONN, EV_RELEASE_IND, lli_received_d_rel}, + {ST_WAIT_BCONN, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_WAIT_BCONN, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_BCONN, EV_CINF, lli_charge_info}, + {ST_ACTIVE, EV_CINF, lli_charge_info}, + {ST_ACTIVE, EV_BC_REL, lli_released_bchan}, + {ST_ACTIVE, EV_SUSPEND, lli_suspend}, + {ST_ACTIVE, EV_HANGUP, lli_disconn_bchan}, + {ST_ACTIVE, EV_DISCONNECT_IND, lli_release_bchan}, + {ST_ACTIVE, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_ACTIVE, EV_RELEASE_IND, lli_received_d_rel}, + {ST_ACTIVE, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_BRELEASE, EV_BC_REL, lli_send_d_disc}, + {ST_WAIT_BRELEASE, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_WAIT_BRELEASE, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_WAIT_BRELEASE, EV_RELEASE_IND, lli_received_d_rel}, + {ST_WAIT_BRELEASE, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_BREL_DISC, EV_BC_REL, lli_received_d_disc}, + {ST_WAIT_BREL_DISC, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_WAIT_BREL_DISC, EV_RELEASE_IND, lli_received_d_rel}, + {ST_WAIT_BREL_DISC, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_DCOMMAND, EV_HANGUP, lli_send_d_disc}, + {ST_WAIT_DCOMMAND, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_WAIT_DCOMMAND, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_WAIT_DCOMMAND, EV_RELEASE_IND, lli_received_d_rel}, + {ST_WAIT_DCOMMAND, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_DRELEASE, EV_RELEASE_IND, lli_timeout_d}, + {ST_WAIT_DRELEASE, EV_RELEASE_CNF, lli_timeout_d}, + {ST_WAIT_DRELEASE, EV_RELEASE_ERR, lli_timeout_d}, + {ST_WAIT_DRELEASE, EV_DIAL, lli_no_dchan_ready}, + {ST_WAIT_DRELEASE, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_D_REL_CNF, EV_RELEASE_CNF, lli_timeout_d}, + {ST_WAIT_D_REL_CNF, EV_RELEASE_ERR, lli_timeout_d}, +/* ETS 300-104 16.1 */ + {ST_WAIT_D_REL_CNF, EV_RELEASE_IND, lli_timeout_d}, + {ST_WAIT_D_REL_CNF, EV_DIAL, lli_no_dchan_ready}, + {ST_WAIT_D_REL_CNF, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_DSHUTDOWN, EV_DLRL, lli_go_null}, + {ST_WAIT_DSHUTDOWN, EV_DLEST, lli_d_established}, + {ST_WAIT_DSHUTDOWN, EV_DIAL, lli_prep_dialout}, + {ST_WAIT_DSHUTDOWN, EV_RESUME, lli_prep_dialout}, + {ST_WAIT_DSHUTDOWN, EV_SETUP_IND, lli_deliver_call}, }; /* *INDENT-ON* */ +#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode)) - - - - - - - -#define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode)) - -void -CallcNew(void) +HISAX_INITFUNC(void +CallcNew(void)) { callcfsm.state_count = STATE_COUNT; callcfsm.event_count = EVENT_COUNT; callcfsm.strEvent = strEvent; callcfsm.strState = strState; FsmNew(&callcfsm, fnlist, FNCOUNT); - - lcfsm.state_count = LC_STATE_COUNT; - lcfsm.event_count = LC_EVENT_COUNT; - lcfsm.strEvent = strLcEvent; - lcfsm.strState = strLcState; - FsmNew(&lcfsm, LcFnList, LC_FN_COUNT); } void CallcFree(void) { - FsmFree(&lcfsm); FsmFree(&callcfsm); } static void -release_ds(struct Channel *chanp) +release_b_st(struct Channel *chanp) { - struct PStack *st = &chanp->ds; - struct IsdnCardState *sp; - struct HscxState *hsp; - - sp = st->l1.hardware; - hsp = sp->hs + chanp->hscx; - - close_hscxstate(hsp); + struct PStack *st = chanp->b_st; + chanp->bcs->BC_Close(chanp->bcs); switch (chanp->l2_active_protocol) { case (ISDN_PROTO_L2_X75I): releasestack_isdnl2(st); break; case (ISDN_PROTO_L2_HDLC): case (ISDN_PROTO_L2_TRANS): +// case (ISDN_PROTO_L2_MODEM): releasestack_transl2(st); break; } - /* Reset B-Channel Statemachine */ - FsmDelTimer(&chanp->lc_b.act_timer, 79); - FsmChangeState(&chanp->lc_b.lcfi, ST_LC_NULL); } -static void -cc_l1man(struct PStack *st, int pr, void *arg) +struct Channel +*selectfreechannel(struct PStack *st) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct IsdnCardState *cs = st->l1.hardware; + struct Channel *chanp = st->lli.userdata; + int i; - switch (pr) { - case (PH_ACTIVATE): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_ACTIVATE, NULL); - break; - case (PH_DEACTIVATE): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_DEACTIVATE, NULL); - break; - } + if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) + i=1; + else + i=0; + while (i<2) { + if (chanp->fi.state == ST_NULL) + return (chanp); + chanp++; + i++; + } + return (NULL); } -static void -cc_l2man(struct PStack *st, int pr, void *arg) +int +is_activ(struct PStack *st) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct IsdnCardState *cs = st->l1.hardware; + struct Channel *chanp = st->lli.userdata; + int i; - switch (pr) { - case (DL_ESTABLISH): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_ESTABLISH, NULL); - break; - case (DL_RELEASE): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_RELEASE, NULL); - break; - case (DL_FLUSH): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_FLUSH, NULL); - break; - } + if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) + i=1; + else + i=0; + while (i<2) { + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) + return (1); + chanp++; + i++; + } + return (0); } static void -dcc_l1man(struct PStack *st, int pr, void *arg) +dchan_l3l4(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; - - switch (pr) { - case (PH_ACTIVATE): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_ACTIVATE, NULL); - break; - case (PH_DEACTIVATE): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_DEACTIVATE, NULL); - break; - } -} + struct l3_process *pc = arg; + struct IsdnCardState *cs = st->l1.hardware; + struct Channel *chanp; + int event; + char tmp[64], tm[32]; -static void -dcc_l2man(struct PStack *st, int pr, void *arg) -{ - struct Channel *chanp = (struct Channel *) st->l4.userdata; switch (pr) { - case (DL_ESTABLISH): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_ESTABLISH, NULL); + case (DL_ESTABLISH | INDICATION): + event = EV_DLEST; break; - case (DL_RELEASE): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_RELEASE, NULL); + case (DL_RELEASE | INDICATION): + event = EV_DLRL; + break; + default: + event = -1; break; } -} - -static void -l2tei_dummy(struct PStack *st, int pr, void *arg) -{ - struct Channel *chanp = (struct Channel *) st->l4.userdata; - char tmp[64], tm[32]; - - jiftime(tm, jiffies); - sprintf(tmp, "%s Channel %d Warning! Dummy l2tei called pr=%d\n", tm, chanp->chan, pr); - HiSax_putstatus(chanp->sp, tmp); -} - -static void -ll_handler(struct PStack *st, int pr, void *arg) -{ - struct Channel *chanp = (struct Channel *) st->l4.userdata; - char tmp[64], tm[32]; - + if (event >= 0) { + int i; + + chanp = st->lli.userdata; + if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) + i = 1; + else + i = 0; + while (i < 2) { + FsmEvent(&chanp->fi, event, NULL); + chanp++; + i++; + } + return; + } else if (pr == (CC_SETUP | INDICATION)) { + if (!(chanp = selectfreechannel(pc->st))) { + pc->st->lli.l4l3(pc->st, CC_DLRL | REQUEST, pc); + } else { + chanp->proc = pc; + pc->chan = chanp; + FsmEvent(&chanp->fi, EV_SETUP_IND, NULL); + } + return; + } + chanp = pc->chan; switch (pr) { - case (CC_DISCONNECT_IND): + case (CC_DISCONNECT | INDICATION): FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL); break; - case (CC_RELEASE_CNF): + case (CC_RELEASE | CONFIRM): FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL); break; - case (CC_SETUP_IND): - FsmEvent(&chanp->fi, EV_SETUP_IND, NULL); + case (CC_SUSPEND | CONFIRM): + FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL); + break; + case (CC_RESUME | CONFIRM): + FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL); + break; + case (CC_RESUME_ERR): + FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL); break; - case (CC_RELEASE_IND): + case (CC_RELEASE | INDICATION): FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL); break; - case (CC_SETUP_COMPLETE_IND): + case (CC_SETUP_COMPL | INDICATION): FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL); break; - case (CC_SETUP_CNF): + case (CC_SETUP | CONFIRM): FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL); break; - case (CC_INFO_CHARGE): + case (CC_CHARGE | INDICATION): FsmEvent(&chanp->fi, EV_CINF, NULL); break; - case (CC_NOSETUP_RSP_ERR): + case (CC_NOSETUP_RSP): FsmEvent(&chanp->fi, EV_NOSETUP_RSP, NULL); break; case (CC_SETUP_ERR): @@ -1386,49 +1312,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 | INDICATION): + case (CC_ALERTING | INDICATION): + 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 */ - } - sprintf(tmp, "Channel %d q.921", chanp->chan); + 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 */ + if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) + sprintf(tmp, "DCh%d Q.921", chanp->chan); + else + sprintf(tmp, "DCh Q.921"); setstack_isdnl2(st, tmp); - setstack_isdnl3(st, chanp); - st->l4.userdata = chanp; - st->l4.l2writewakeup = NULL; - st->l3.l3l4 = ll_handler; - st->l1.l1man = cc_l1man; - st->l2.l2man = cc_l2man; - st->pa = &chanp->para; - HiSax_addlist(sp, st); + setstack_l3dc(st, chanp); + st->lli.userdata = chanp; + st->lli.l2writewakeup = NULL; + st->l3.l3l4 = dchan_l3l4; } static void @@ -1439,77 +1367,44 @@ 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); -} - -static void -lc_debug(struct FsmInst *fi, char *s) -{ - char str[256], tm[32]; - struct LcFsm *lf = fi->userdata; - - jiftime(tm, jiffies); - sprintf(str, "%s Channel %d lc %s\n", tm, lf->ch->chan, s); - HiSax_putstatus(lf->ch->sp, str); -} - -static void -dlc_debug(struct FsmInst *fi, char *s) -{ - char str[256], tm[32]; - struct LcFsm *lf = fi->userdata; - - jiftime(tm, jiffies); - sprintf(str, "%s Channel %d dlc %s\n", tm, lf->ch->chan, s); - HiSax_putstatus(lf->ch->sp, str); + HiSax_putstatus(chanp->cs, str); } static void -lccall_d(struct LcFsm *lf, int pr, void *arg) -{ - struct Channel *chanp = lf->ch; - - switch (pr) { - case (LC_ESTABLISH): - FsmEvent(&chanp->fi, EV_DLEST, NULL); - break; - case (LC_RELEASE): - FsmEvent(&chanp->fi, EV_DLRL, NULL); - break; - } +dummy_pstack(struct PStack *st, int pr, void *arg) { + printk(KERN_WARNING"call to dummy_pstack pr=%04x arg %lx\n", pr, (long)arg); } static void -lccall_b(struct LcFsm *lf, int pr, void *arg) -{ - struct Channel *chanp = lf->ch; - - switch (pr) { - case (LC_ESTABLISH): - FsmEvent(&chanp->fi, EV_BC_EST, NULL); - break; - case (LC_RELEASE): - FsmEvent(&chanp->fi, EV_BC_REL, NULL); - break; - } +init_PStack(struct PStack **stp) { + *stp = kmalloc(sizeof(struct PStack), GFP_ATOMIC); + (*stp)->next = NULL; + (*stp)->l1.l1l2 = dummy_pstack; + (*stp)->l1.l1hw = dummy_pstack; + (*stp)->l1.l1tei = dummy_pstack; + (*stp)->l2.l2tei = dummy_pstack; + (*stp)->l2.l2l1 = dummy_pstack; + (*stp)->l2.l2l3 = dummy_pstack; + (*stp)->l3.l3l2 = dummy_pstack; + (*stp)->l3.l3l4 = dummy_pstack; + (*stp)->lli.l4l3 = dummy_pstack; + (*stp)->ma.layer = dummy_pstack; } static void -init_chan(int chan, struct IsdnCardState *csta, int hscx, - unsigned int ces) +init_chan(int chan, struct IsdnCardState *csta) { struct Channel *chanp = csta->channel + chan; - chanp->sp = csta; - chanp->hscx = hscx; + chanp->cs = csta; + chanp->bcs = csta->bcs + chan; chanp->chan = chan; chanp->incoming = 0; chanp->debug = 0; chanp->Flags = 0; chanp->leased = 0; - chanp->impair = 0; - init_is(chanp, ces); - + init_PStack(&chanp->b_st); + chanp->b_st->l1.delay = DEFAULT_B_DELAY; chanp->fi.fsm = &callcfsm; chanp->fi.state = ST_NULL; chanp->fi.debug = 0; @@ -1517,58 +1412,46 @@ 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)) { + init_PStack(&chanp->d_st); + if (chan) + csta->channel->d_st->next = chanp->d_st; + chanp->d_st->next = NULL; + init_d_st(chanp); + } else { + chanp->d_st = csta->channel->d_st; + } chanp->data_open = 0; } int CallcNewChan(struct IsdnCardState *csta) { - int ces; - chancount += 2; - ces = randomces(); - init_chan(0, csta, 1, ces++); - ces %= 0xffff; - init_chan(1, csta, 0, ces++); + init_chan(0, csta); + init_chan(1, csta); printk(KERN_INFO "HiSax: 2 channels added\n"); + if (test_bit(FLG_PTP, &csta->channel->d_st->l2.flag)) { + printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n"); + test_and_set_bit(FLG_START_D, &csta->channel->Flags); + csta->channel->d_st->lli.l4l3(csta->channel->d_st, + DL_ESTABLISH | REQUEST, NULL); + } return (2); } static void -release_is(struct Channel *chanp) +release_d_st(struct Channel *chanp) { - struct PStack *st = &chanp->is; + struct PStack *st = chanp->d_st; + if (!st) + return; releasestack_isdnl2(st); releasestack_isdnl3(st); HiSax_rmlist(st->l1.hardware, st); + kfree(st); + chanp->d_st = NULL; } void @@ -1579,30 +1462,44 @@ 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); - } - release_is(csta->channel + i); + if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) + release_d_st(csta->channel + i); + if (csta->channel[i].b_st) { + if (test_and_clear_bit(FLG_START_B, &csta->channel[i].Flags)) + release_b_st(csta->channel + i); + kfree(csta->channel[i].b_st); + csta->channel[i].b_st = NULL; + } else + printk(KERN_WARNING "CallcFreeChan b_st ch%d allready freed\n", i); + if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) { + release_d_st(csta->channel + i); + } else + csta->channel[i].d_st = NULL; } } static void lldata_handler(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp = (struct Channel *) st->lli.userdata; struct sk_buff *skb = arg; switch (pr) { - case (DL_DATA): + case (DL_DATA | INDICATION): if (chanp->data_open) - chanp->sp->iif.rcvcallb_skb(chanp->sp->myid, chanp->chan, skb); + chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb); else { - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); } break; + case (DL_ESTABLISH | INDICATION): + case (DL_ESTABLISH | CONFIRM): + FsmEvent(&chanp->fi, EV_BC_EST, NULL); + break; + case (DL_RELEASE | INDICATION): + case (DL_RELEASE | CONFIRM): + FsmEvent(&chanp->fi, EV_BC_REL, NULL); + break; default: printk(KERN_WARNING "lldata_handler unknown primitive %d\n", pr); @@ -1613,18 +1510,26 @@ 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 | INDICATION): if (chanp->data_open) - chanp->sp->iif.rcvcallb_skb(chanp->sp->myid, chanp->chan, skb); + chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb); else { - SET_SKB_FREE(skb); + link_debug(chanp, "channel not open", 0); dev_kfree_skb(skb, FREE_READ); } break; + case (PH_ACTIVATE | INDICATION): + case (PH_ACTIVATE | CONFIRM): + FsmEvent(&chanp->fi, EV_BC_EST, NULL); + break; + case (PH_DEACTIVATE | INDICATION): + case (PH_DEACTIVATE | CONFIRM): + FsmEvent(&chanp->fi, EV_BC_REL, NULL); + break; default: printk(KERN_WARNING "lltrans_handler unknown primitive %d\n", pr); @@ -1633,78 +1538,139 @@ 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; + switch (chanp->l2_active_protocol) { + case (ISDN_PROTO_L2_X75I): + case (ISDN_PROTO_L2_HDLC): + st->l1.mode = L1_MODE_HDLC; + break; + case (ISDN_PROTO_L2_TRANS): + st->l1.mode = L1_MODE_TRANS; + break; +#if 0 + case (ISDN_PROTO_L2_MODEM): + st->l1.mode = L1_MODE_MODEM; + break; +#endif + } + if (chanp->bcs->BC_SetStack(st, chanp->bcs)) return (-1); - - st->l2.extended = 0; - st->l2.laptype = LAPB; - st->l2.orig = !incoming; - st->l2.t200 = 1000; /* 1000 milliseconds */ + st->l2.flag = 0; + test_and_set_bit(FLG_LAPB, &st->l2.flag); + st->l2.maxlen = MAX_DATA_SIZE; + if (!incoming) + test_and_set_bit(FLG_ORIG, &st->l2.flag); + st->l2.T200 = 1000; /* 1000 milliseconds */ st->l2.window = 7; - st->l2.n200 = 4; /* try 4 times */ - st->l2.t203 = 5000; /* 5000 milliseconds */ - + st->l2.N200 = 4; /* try 4 times */ + st->l2.T203 = 5000; /* 5000 milliseconds */ st->l3.debug = 0; switch (chanp->l2_active_protocol) { case (ISDN_PROTO_L2_X75I): - sprintf(tmp, "Channel %d x.75", chanp->chan); + sprintf(tmp, "Ch%d X.75", chanp->chan); setstack_isdnl2(st, tmp); + setstack_l3bc(st, chanp); st->l2.l2l3 = lldata_handler; - st->l1.l1man = dcc_l1man; - st->l2.l2man = dcc_l2man; - st->l2.l2tei = l2tei_dummy; - st->l4.userdata = chanp; - st->l4.l1writewakeup = NULL; - st->l4.l2writewakeup = ll_writewakeup; + st->lli.userdata = chanp; + st->lli.l1writewakeup = NULL; + st->lli.l2writewakeup = ll_writewakeup; st->l2.l2m.debug = chanp->debug & 16; st->l2.debug = chanp->debug & 64; - st->ma.manl2(st, MDL_NOTEIPROC, NULL); - st->l1.hscxmode = 2; /* Packet-Mode ? */ - st->l1.hscxchannel = chanp->para.bchannel - 1; break; case (ISDN_PROTO_L2_HDLC): - st->l1.l1l2 = lltrans_handler; - st->l1.l1man = dcc_l1man; - st->l4.userdata = chanp; - st->l4.l1writewakeup = ll_writewakeup; - st->l1.hscxmode = 2; - st->l1.hscxchannel = chanp->para.bchannel - 1; - break; case (ISDN_PROTO_L2_TRANS): +// case (ISDN_PROTO_L2_MODEM): st->l1.l1l2 = lltrans_handler; - st->l1.l1man = dcc_l1man; - st->l4.userdata = chanp; - st->l4.l1writewakeup = ll_writewakeup; - st->l1.hscxmode = 1; - st->l1.hscxchannel = chanp->para.bchannel - 1; + st->lli.userdata = chanp; + st->lli.l1writewakeup = ll_writewakeup; + setstack_transl2(st); + setstack_l3bc(st, chanp); break; } + if (chanp->leased) + st->l1.bc = chanp->chan & 1; + else + st->l1.bc = chanp->proc->para.bchannel - 1; return (0); } +static void +leased_l4l3(struct PStack *st, int pr, void *arg) +{ + struct Channel *chanp = (struct Channel *) st->lli.userdata; + struct sk_buff *skb = arg; + + switch (pr) { + case (DL_DATA | REQUEST): + link_debug(chanp, "leased line d-channel DATA", 0); + dev_kfree_skb(skb, FREE_READ); + break; + case (DL_ESTABLISH | REQUEST): + st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL); + break; + case (DL_RELEASE | REQUEST): + break; + default: + printk(KERN_WARNING "transd_l4l3 unknown primitive %d\n", + pr); + break; + } +} + +static void +leased_l1l2(struct PStack *st, int pr, void *arg) +{ + struct Channel *chanp = (struct Channel *) st->lli.userdata; + struct sk_buff *skb = arg; + int i,event = EV_DLRL; + + switch (pr) { + case (PH_DATA | INDICATION): + link_debug(chanp, "leased line d-channel DATA", 0); + dev_kfree_skb(skb, FREE_READ); + break; + case (PH_ACTIVATE | INDICATION): + case (PH_ACTIVATE | CONFIRM): + event = EV_DLEST; + case (PH_DEACTIVATE | INDICATION): + case (PH_DEACTIVATE | CONFIRM): + if (test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags)) + i = 1; + else + i = 0; + while (i < 2) { + FsmEvent(&chanp->fi, event, NULL); + chanp++; + i++; + } + break; + default: + printk(KERN_WARNING + "transd_l1l2 unknown primitive %d\n", pr); + break; + } +} + static void channel_report(struct Channel *chanp) { @@ -1719,17 +1685,75 @@ 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].d_st->l3.l3m.debug = debugflags & 0x80; + chanp[i].b_st->l3.l3m.debug = debugflags & 0x100; + chanp[i].b_st->ma.tei_m.debug = debugflags & 0x200; + chanp[i].b_st->ma.debug = debugflags & 0x200; + chanp[i].d_st->l1.l1m.debug = debugflags & 0x1000; } csta->dlogflag = debugflags & 4; - csta->teistack->l2.l2m.debug = debugflags & 512; } +#if 0 +static void +capi_debug(struct Channel *chanp, capi_msg *cm) +{ + char tmp[256], tm[32]; + char *t = tmp; + + jiftime(tm, jiffies); + t += sprintf(tmp, "%s Channel %d CAPIMSG", tm, chanp->chan); + t += QuickHex(t, (u_char *)cm, (cm->Length>50)? 50: cm->Length); + t--; + *t++ ='\n'; + *t++ = 0; + HiSax_putstatus(chanp->cs, tmp); +} + +void +lli_got_fac_req(struct Channel *chanp, capi_msg *cm) { + if ((cm->para[0] != 3) || (cm->para[1] != 0)) + return; + if (cm->para[2]<3) + return; + if (cm->para[4] != 0) + return; + switch(cm->para[3]) { + case 4: /* Suspend */ + if (cm->para[5]) { + strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1); + FsmEvent(&chanp->fi, EV_SUSPEND, cm); + } + break; + case 5: /* Resume */ + if (cm->para[5]) { + strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1); + if (chanp->fi.state == ST_NULL) { + FsmEvent(&chanp->fi, EV_RESUME, cm); + } else { + FsmDelTimer(&chanp->dial_timer, 72); + FsmAddTimer(&chanp->dial_timer, 80, EV_RESUME, cm, 73); + } + } + break; + } +} + +void +lli_got_manufacturer(struct Channel *chanp, struct IsdnCardState *cs, capi_msg *cm) { + if ((cs->typ == ISDN_CTYPE_ELSA) || (cs->typ == ISDN_CTYPE_ELSA_PNP) || + (cs->typ == ISDN_CTYPE_ELSA_PCI)) { + if (cs->hw.elsa.MFlag) { + cs->cardmsg(cs, CARD_AUX_IND, cm->para); + } + } +} +#endif + int HiSax_command(isdn_ctrl * ic) { @@ -1745,12 +1769,12 @@ HiSax_command(isdn_ctrl * ic) ic->command, ic->driver); return -ENODEV; } + switch (ic->command) { case (ISDN_CMD_SETEAZ): chanp = csta->channel + ic->arg; - if (chanp->debug & 1) - link_debug(chanp, "SETEAZ", 1); break; + case (ISDN_CMD_SETL2): chanp = csta->channel + (ic->arg & 0xff); if (chanp->debug & 1) { @@ -1768,9 +1792,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) { @@ -1798,26 +1822,31 @@ HiSax_command(isdn_ctrl * ic) link_debug(chanp, "HANGUP", 1); FsmEvent(&chanp->fi, EV_HANGUP, NULL); break; - case (ISDN_CMD_SUSPEND): +#if 0 + case (CAPI_PUT_MESSAGE): chanp = csta->channel + ic->arg; - if (chanp->debug & 1) { - sprintf(tmp, "SUSPEND %s", ic->parm.num); - link_debug(chanp, tmp, 1); - } - FsmEvent(&chanp->fi, EV_SUSPEND, ic); + if (chanp->debug & 1) + capi_debug(chanp, &ic->parm.cmsg); + if (ic->parm.cmsg.Length < 8) + break; + switch(ic->parm.cmsg.Command) { + case CAPI_FACILITY: + if (ic->parm.cmsg.Subcommand == CAPI_REQ) + lli_got_fac_req(chanp, &ic->parm.cmsg); + break; + case CAPI_MANUFACTURER: + if (ic->parm.cmsg.Subcommand == CAPI_REQ) + lli_got_manufacturer(chanp, csta, &ic->parm.cmsg); + break; + default: break; - case (ISDN_CMD_RESUME): - chanp = csta->channel + ic->arg; - if (chanp->debug & 1) { - sprintf(tmp, "RESUME %s", ic->parm.num); - link_debug(chanp, tmp, 1); } - FsmEvent(&chanp->fi, EV_RESUME, ic); break; +#endif 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 +1857,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 +1881,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].b_st->l1.delay = num; + csta->channel[1].b_st->l1.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 +1898,72 @@ 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); + 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--; + chanp = csta->channel +num; + chanp->leased = 1; + sprintf(tmp, "card %d channel %d set leased mode\n", + csta->cardnr + 1, num + 1); + HiSax_putstatus(csta, tmp); + chanp->d_st->l1.l1l2 = leased_l1l2; + chanp->d_st->lli.l4l3 = leased_l4l3; + chanp->d_st->lli.l4l3(chanp->d_st, + DL_ESTABLISH | REQUEST, NULL); + } + break; + case (6): /* set B-channel test loop */ + num = *(unsigned int *) ic->parm.num; + if (csta->stlist) + csta->stlist->l2.l2l1(csta->stlist, + PH_TESTLOOP | REQUEST, (void *) (long)num); + break; + case (7): /* set card in PTP mode */ + if (test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) { + printk(KERN_ERR "HiSax PTP mode only with one TEI possible\n"); + } else { + test_and_set_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag); + test_and_set_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag); + csta->channel[0].d_st->l2.tei = 0; + sprintf(tmp, "set card in PTP mode\n"); + HiSax_putstatus(csta, tmp); + printk(KERN_DEBUG "HiSax: %s", tmp); + } + break; + case (8): /* set card in FIXED TEI mode */ + num = *(unsigned int *) ic->parm.num; + chanp = csta->channel + (num & 1); + num = num >>1; + test_and_set_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag); + chanp->d_st->l2.tei = num; + sprintf(tmp, "set card in FIXED TEI (%d) mode\n", num); HiSax_putstatus(csta, tmp); + printk(KERN_DEBUG "HiSax: %s", tmp); break; #ifdef MODULE case (55): -#if (LINUX_VERSION_CODE < 0x020111) - MOD_USE_COUNT = MOD_VISITED; -#else MOD_USE_COUNT = 0; -#endif HiSax_mod_inc_use_count(); break; #endif /* MODULE */ case (11): csta->debug = *(unsigned int *) ic->parm.num; sprintf(tmp, "l1 debugging flags card %d set to %x\n", - csta->cardnr + 1, csta->debug); - HiSax_putstatus(cards[0].sp, tmp); + 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: @@ -1933,7 +1996,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 +2008,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 +2022,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->l2_active_protocol == ISDN_PROTO_L2_X75I) + st->l3.l3l2(st, DL_DATA | REQUEST, nskb); + else { + chanp->bcs->tx_cnt += len; + st->l2.l2l1(st, PH_DATA | REQUEST, nskb); } dev_kfree_skb(skb, FREE_WRITE); } else diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c index 595fb713e56a..f23309cc404e 100644 --- a/drivers/isdn/hisax/config.c +++ b/drivers/isdn/hisax/config.c @@ -1,58 +1,54 @@ -/* $Id: config.c,v 1.15 1997/04/06 22:57:24 keil Exp $ +/* $Id: config.c,v 1.15.2.11 1998/05/27 18:05:07 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden * * * $Log: config.c,v $ - * Revision 1.15 1997/04/06 22:57:24 keil - * Hisax version 2.1 + * Revision 1.15.2.11 1998/05/27 18:05:07 keil + * HiSax 3.0 * - * Revision 1.14 1997/03/25 23:11:22 keil - * US NI-1 protocol + * Revision 1.15.2.10 1998/04/11 18:43:13 keil + * New cards * - * Revision 1.13 1997/03/23 21:45:49 keil - * Add support for ELSA PCMCIA + * Revision 1.15.2.9 1998/03/07 23:15:12 tsbogend + * made HiSax working on Linux/Alpha * - * Revision 1.12 1997/03/11 21:01:43 keil - * nzproto is only used with modules + * Revision 1.15.2.8 1998/02/11 19:21:37 keil + * fix typo * - * Revision 1.11 1997/02/14 12:23:12 fritz - * Added support for new insmod parameter handling. + * Revision 1.15.2.7 1998/02/11 14:23:08 keil + * support for Dr Neuhaus Niccy PnP and PCI * - * Revision 1.10 1997/02/14 09:22:09 keil - * Final 2.0 version + * Revision 1.15.2.6 1998/02/09 11:21:19 keil + * Sedlbauer PCMCIA support from Marcus Niemann * - * Revision 1.9 1997/02/10 11:45:09 fritz - * More changes for Kernel 2.1.X compatibility. + * Revision 1.15.2.5 1998/01/27 23:28:48 keil + * v2.8 * - * Revision 1.8 1997/02/09 00:28:05 keil - * new interface handling, one interface per card - * default protocol now works again + * Revision 1.15.2.4 1998/01/27 22:33:53 keil + * dynalink ----> asuscom * - * Revision 1.7 1997/01/27 15:56:57 keil - * Teles PCMCIA ITK ix1 micro added + * Revision 1.15.2.3 1998/01/11 22:55:15 keil + * 16.3c support * - * Revision 1.6 1997/01/21 22:17:56 keil - * new module load syntax + * Revision 1.15.2.2 1997/11/15 18:55:46 keil + * New init, new cards * - * Revision 1.5 1997/01/09 18:28:20 keil - * cosmetic cleanups + * Revision 1.15.2.1 1997/10/17 22:13:40 keil + * update to last hisax version * - * Revision 1.4 1996/11/05 19:35:17 keil - * using config.h; some spelling fixes + * Revision 2.2 1997/09/11 17:24:46 keil + * Add new cards * - * 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 +64,31 @@ * { 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 TELES PCI no parameter + * 22 Sedlbauer Speed Star p0=irq p1=iobase + * 23 reserved + * 24 Dr Neuhaus Niccy PnP/PCI card p0=irq p1=IO0 p2=IO1 (PnP only) + * 25 Teles S0Box p0=irq p1=iobase (from isapnp setup) * * * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1 @@ -85,38 +96,139 @@ * */ -#ifdef CONFIG_HISAX_ELSA_PCC +#ifdef CONFIG_HISAX_ELSA #define DEFAULT_CARD ISDN_CTYPE_ELSA -#define DEFAULT_CFG {0,0,0} +#define DEFAULT_CFG {0,0,0,0} +int elsa_init_pcmcia(void*, int, int*, int); +#ifdef MODULE +static struct symbol_table hisax_syms_elsa = { +#include + X(elsa_init_pcmcia), +#include +}; +void register_elsa_symbols(void) { + register_symtab(&hisax_syms_elsa); +} #endif -#ifdef CONFIG_HISAX_ELSA_PCMCIA -#define DEFAULT_CARD ISDN_CTYPE_ELSA_QS1000 -#define DEFAULT_CFG {3,0x2f8,0} #endif #ifdef CONFIG_HISAX_AVM_A1 #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_A1 -#define DEFAULT_CFG {10,0x340,0} +#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_S0BOX +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_S0BOX +#define DEFAULT_CFG {7,0x378,0,0} #endif #ifdef CONFIG_HISAX_16_0 #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_16_0 -#define DEFAULT_CFG {15,0xd0000,0xd80} +#define DEFAULT_CFG {15,0xd0000,0xd80,0} +#endif + +#ifdef CONFIG_HISAX_TELESPCI +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_TELESPCI +#define DEFAULT_CFG {0,0,0,0} #endif #ifdef CONFIG_HISAX_IX1MICROR2 #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_IX1MICROR2 -#define DEFAULT_CFG {5,0x390,0} +#define DEFAULT_CFG {5,0x390,0,0} +#endif + +#ifdef CONFIG_HISAX_DIEHLDIVA +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_DIEHLDIVA +#define DEFAULT_CFG {0,0x0,0,0} +#endif + +#ifdef CONFIG_HISAX_ASUSCOM +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_ASUSCOM +#define DEFAULT_CFG {5,0x200,0,0} +#endif + +#ifdef CONFIG_HISAX_TELEINT +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_TELEINT +#define DEFAULT_CFG {5,0x300,0,0} +#endif + +#ifdef CONFIG_HISAX_SEDLBAUER +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_SEDLBAUER +#define DEFAULT_CFG {11,0x270,0,0} +int sedl_init_pcmcia(void*, int, int*, int); +#ifdef MODULE +static struct symbol_table hisax_syms_sedl= { +#include + X(sedl_init_pcmcia), +#include +}; +void register_sedl_symbols(void) { + register_symtab(&hisax_syms_sedl); +} +#endif +#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 +262,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[] = { @@ -162,65 +274,65 @@ struct IsdnCard cards[] = EMPTY_CARD, EMPTY_CARD, EMPTY_CARD, - EMPTY_CARD, - EMPTY_CARD, - EMPTY_CARD, - EMPTY_CARD, - EMPTY_CARD, - EMPTY_CARD, - EMPTY_CARD, - EMPTY_CARD, }; -static char HiSaxID[96] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ +static char HiSaxID[64] HISAX_INITDATA = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ -"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ -"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; -char *HiSax_id = HiSaxID; +"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; +char *HiSax_id HISAX_INITDATA = HiSaxID; #ifdef MODULE /* Variables for insmod */ -int type[] = -{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -int protocol[] = -{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -int io[] = -{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -#ifdef CONFIG_HISAX_16_3 /* For Creatix/Teles PnP */ -int io0[] = -{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -int io1[] = -{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static int type[] HISAX_INITDATA = +{0, 0, 0, 0, 0, 0, 0, 0}; +static int protocol[] HISAX_INITDATA = +{0, 0, 0, 0, 0, 0, 0, 0}; +static int io[] HISAX_INITDATA = +{0, 0, 0, 0, 0, 0, 0, 0}; +#undef IO0_IO1 +#ifdef CONFIG_HISAX_16_3 +#define IO0_IO1 #endif -int irq[] = -{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -int mem[] = -{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -char *id = HiSaxID; +#ifdef CONFIG_HISAX_NICCY +#undef IO0_IO1 +#define IO0_IO1 +#endif +#ifdef IO0_IO1 +static int io0[] HISAX_INITDATA = +{0, 0, 0, 0, 0, 0, 0, 0}; +static int io1[] HISAX_INITDATA = +{0, 0, 0, 0, 0, 0, 0, 0}; +#endif +static int irq[] HISAX_INITDATA = +{0, 0, 0, 0, 0, 0, 0, 0}; +static int mem[] HISAX_INITDATA = +{0, 0, 0, 0, 0, 0, 0, 0}; +static char *id HISAX_INITDATA = HiSaxID; #if (LINUX_VERSION_CODE > 0x020111) MODULE_AUTHOR("Karsten Keil"); -MODULE_PARM(type, "1-16i"); -MODULE_PARM(protocol, "1-16i"); -MODULE_PARM(io, "1-16i"); -MODULE_PARM(irq, "1-16i"); -MODULE_PARM(mem, "1-16i"); +MODULE_PARM(type, "1-8i"); +MODULE_PARM(protocol, "1-8i"); +MODULE_PARM(io, "1-8i"); +MODULE_PARM(irq, "1-8i"); +MODULE_PARM(mem, "1-8i"); MODULE_PARM(id, "s"); #ifdef CONFIG_HISAX_16_3 /* For Creatix/Teles PnP */ -MODULE_PARM(io0, "1-16i"); -MODULE_PARM(io1, "1-16i"); -#endif -#endif +MODULE_PARM(io0, "1-8i"); +MODULE_PARM(io1, "1-8i"); +#endif /* CONFIG_HISAX_16_3 */ +#endif /* (LINUX_VERSION_CODE > 0x020111) */ +#endif /* MODULE */ -#endif +int nrcards; extern char *l1_revision; extern char *l2_revision; extern char *l3_revision; -extern char *l4_revision; +extern char *lli_revision; extern char *tei_revision; -char * -HiSax_getrev(const char *revision) +HISAX_INITFUNC(char * +HiSax_getrev(const char *revision)) { char *rev; char *p; @@ -234,7 +346,24 @@ HiSax_getrev(const char *revision) return rev; } -int nrcards; +HISAX_INITFUNC(void +HiSaxVersion(void)) +{ + char tmp[64]; + + printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n"); + printk(KERN_INFO "HiSax: Version 3.0\n"); + strcpy(tmp, l1_revision); + printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp)); + strcpy(tmp, l2_revision); + printk(KERN_INFO "HiSax: Layer2 Revision %s\n", HiSax_getrev(tmp)); + strcpy(tmp, tei_revision); + printk(KERN_INFO "HiSax: TeiMgr Revision %s\n", HiSax_getrev(tmp)); + strcpy(tmp, l3_revision); + printk(KERN_INFO "HiSax: Layer3 Revision %s\n", HiSax_getrev(tmp)); + strcpy(tmp, lli_revision); + printk(KERN_INFO "HiSax: LinkLayer Revision %s\n", HiSax_getrev(tmp)); +} void HiSax_mod_dec_use_count(void) @@ -251,15 +380,15 @@ 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; argc = ints[0]; i = 0; j = 1; - while (argc && (i < 16)) { + while (argc && (i < HISAX_MAX_CARDS)) { if (argc) { cards[i].typ = ints[j]; j++; @@ -297,35 +426,32 @@ 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 nrcards = 0; - strcpy(tmp, l1_revision); - r += sprintf(r, "%s/", HiSax_getrev(tmp)); - strcpy(tmp, l2_revision); - r += sprintf(r, "%s/", HiSax_getrev(tmp)); - strcpy(tmp, l3_revision); - r += sprintf(r, "%s/", HiSax_getrev(tmp)); - strcpy(tmp, l4_revision); - r += sprintf(r, "%s/", HiSax_getrev(tmp)); - strcpy(tmp, tei_revision); - r += sprintf(r, "%s", HiSax_getrev(tmp)); - - printk(KERN_NOTICE "HiSax: Driver for Siemens chip set ISDN cards\n"); - printk(KERN_NOTICE "HiSax: Version 2.1\n"); - printk(KERN_NOTICE "HiSax: Revisions %s\n", rev); - + HiSaxVersion(); #ifdef MODULE if (id) /* If id= string used */ HiSax_id = id; - for (i = 0; i < 16; i++) { + for (i = 0; i < HISAX_MAX_CARDS; i++) { cards[i].typ = type[i]; if (protocol[i]) { cards[i].protocol = protocol[i]; @@ -343,37 +469,46 @@ 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: + case ISDN_CTYPE_S0BOX: cards[i].para[0] = irq[i]; cards[i].para[1] = io[i]; break; - + case ISDN_CTYPE_ELSA_PCI: + case ISDN_CTYPE_NETJET: + case ISDN_CTYPE_AMD7930: + case ISDN_CTYPE_TELESPCI: + break; } } if (!nzproto) { @@ -386,29 +521,31 @@ HiSax_init(void) HiSax_id = HiSaxID; if (!HiSaxID[0]) strcpy(HiSaxID, "HiSax"); - for (i = 0; i < 16; i++) + for (i = 0; i < HISAX_MAX_CARDS; i++) if (cards[i].typ > 0) nrcards++; printk(KERN_DEBUG "HiSax: Total %d card%s defined\n", nrcards, (nrcards > 1) ? "s" : ""); CallcNew(); + Isdnl3New(); Isdnl2New(); - if (HiSax_inithardware()) { + TeiNew(); + Isdnl1New(); + if (HiSax_inithardware(NULL)) { /* Install only, if at least one card found */ /* No symbols to export, hide all symbols */ #ifdef MODULE -#if (LINUX_VERSION_CODE < 0x020111) register_symtab(NULL); -#else - EXPORT_NO_SYMBOLS; -#endif - printk(KERN_NOTICE "HiSax: module installed\n"); + printk(KERN_INFO "HiSax: module installed\n"); #endif return (0); } else { + Isdnl1Free(); + TeiFree(); Isdnl2Free(); + Isdnl3Free(); CallcFree(); return -EIO; } @@ -418,8 +555,116 @@ HiSax_init(void) void cleanup_module(void) { - HiSax_closehardware(); - printk(KERN_NOTICE "HiSax module removed\n"); + int cardnr = nrcards -1; + long flags; + + save_flags(flags); + cli(); + while(cardnr>=0) + HiSax_closecard(cardnr--); + Isdnl1Free(); + TeiFree(); + Isdnl2Free(); + Isdnl3Free(); + CallcFree(); + restore_flags(flags); + printk(KERN_INFO "HiSax module removed\n"); } +#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 8 structs, even though we only accept + two pcmcia cards + */ + for (i = 0; i < HISAX_MAX_CARDS; i++) { + cards[i].para[0] = irq[i]; + cards[i].para[1] = io[i]; + cards[i].typ = type[i]; + if (protocol[i]) { + cards[i].protocol = protocol[i]; + nzproto++; + } + } + cards[0].para[0] = pcm_irq; + cards[0].para[1] = (int)pcm_iob; + cards[0].protocol = prot; + cards[0].typ = 10; + nzproto = 1; + + if (!HiSax_id) + HiSax_id = HiSaxID; + if (!HiSaxID[0]) + strcpy(HiSaxID, "HiSax"); + for (i = 0; i < HISAX_MAX_CARDS; i++) + if (cards[i].typ > 0) + nrcards++; + printk(KERN_DEBUG "HiSax: Total %d card%s defined\n", + nrcards, (nrcards > 1) ? "s" : ""); + + Isdnl1New(); + CallcNew(); + Isdnl3New(); + Isdnl2New(); + TeiNew(); + HiSax_inithardware(busy_flag); + printk(KERN_NOTICE "HiSax: module installed\n"); + 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 8 structs, even though we only accept + two pcmcia cards + */ + for (i = 0; i < HISAX_MAX_CARDS; i++) { + cards[i].para[0] = irq[i]; + cards[i].para[1] = io[i]; + cards[i].typ = type[i]; + if (protocol[i]) { + cards[i].protocol = protocol[i]; + nzproto++; + } + } + cards[0].para[0] = pcm_irq; + cards[0].para[1] = (int)pcm_iob; + cards[0].protocol = prot; + cards[0].typ = ISDN_CTYPE_SEDLBAUER_PCMCIA; + nzproto = 1; + + if (!HiSax_id) + HiSax_id = HiSaxID; + if (!HiSaxID[0]) + strcpy(HiSaxID, "HiSax"); + for (i = 0; i < HISAX_MAX_CARDS; i++) + if (cards[i].typ > 0) + nrcards++; + printk(KERN_DEBUG "HiSax: Total %d card%s defined\n", + nrcards, (nrcards > 1) ? "s" : ""); + + Isdnl1New(); + CallcNew(); + Isdnl3New(); + 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..5b909ef55602 --- /dev/null +++ b/drivers/isdn/hisax/diva.c @@ -0,0 +1,605 @@ +/* $Id: diva.c,v 1.1.2.9 1998/06/27 21:58:34 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.1.2.9 1998/06/27 21:58:34 keil + * fix release of diva 2.01 + * + * Revision 1.1.2.8 1998/05/27 18:05:11 keil + * HiSax 3.0 + * + * Revision 1.1.2.7 1998/04/24 13:37:13 keil + * Support for DIVA 2.01 IPAC + * + * Revision 1.1.2.6 1998/04/08 22:05:21 keil + * Forgot PCI fix + * + * Revision 1.1.2.5 1998/04/08 21:49:27 keil + * New init; fix PCI for more as one card + * + * Revision 1.1.2.4 1998/03/07 23:15:14 tsbogend + * made HiSax working on Linux/Alpha + * + * Revision 1.1.2.3 1998/01/27 22:37:35 keil + * fast io + * + * Revision 1.1.2.2 1997/11/15 18:50:44 keil + * new common init function + * + * Revision 1.1.2.1 1997/10/17 22:10:36 keil + * new files on 2.0 + * + * Revision 1.1 1997/09/18 17:11:20 keil + * first version + * + * + */ + +#define __NO_VERSION__ +#include +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "ipac.h" +#include "isdnl1.h" +#include +#include + +extern const char *CardType[]; + +const char *Diva_revision = "$Revision: 1.1.2.9 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define DIVA_HSCX_DATA 0 +#define DIVA_HSCX_ADR 4 +#define DIVA_ISA_ISAC_DATA 2 +#define DIVA_ISA_ISAC_ADR 6 +#define DIVA_ISA_CTRL 7 +#define DIVA_IPAC_ADR 0 +#define DIVA_IPAC_DATA 1 + +#define DIVA_PCI_ISAC_DATA 8 +#define DIVA_PCI_ISAC_ADR 0xc +#define DIVA_PCI_CTRL 0x10 + +/* SUB Types */ +#define DIVA_ISA 1 +#define DIVA_PCI 2 +#define DIVA_IPAC_ISA 3 + +/* PCI stuff */ +#define PCI_VENDOR_EICON_DIEHL 0x1133 +#define PCI_DIVA20PRO_ID 0xe001 +#define PCI_DIVA20_ID 0xe002 +#define PCI_DIVA20PRO_U_ID 0xe003 +#define PCI_DIVA20_U_ID 0xe004 + +/* CTRL (Read) */ +#define DIVA_IRQ_STAT 0x01 +#define DIVA_EEPROM_SDA 0x02 +/* CTRL (Write) */ +#define DIVA_IRQ_REQ 0x01 +#define DIVA_RESET 0x08 +#define DIVA_EEPROM_CLK 0x40 +#define DIVA_PCI_LED_A 0x10 +#define DIVA_PCI_LED_B 0x20 +#define DIVA_ISA_LED_A 0x20 +#define DIVA_ISA_LED_B 0x40 +#define DIVA_IRQ_CLR 0x80 + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return(readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size); +} + +static u_char +ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset+0x80)); +} + +static void +WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset|0x80, value); +} + +static void +ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size); +} + +static void +WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return(readreg(cs->hw.diva.hscx_adr, + cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.diva.hscx_adr, + cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.diva.hscx_adr, \ + cs->hw.diva.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.diva.hscx_adr, \ + cs->hw.diva.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.diva.hscx_adr, \ + cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.diva.hscx_adr, \ + cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +diva_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, sval, stat = 0; + int cnt=8; + + if (!cs) { + printk(KERN_WARNING "Diva: Spurious interrupt!\n"); + return; + } + while (((sval = bytein(cs->hw.diva.ctrl)) & DIVA_IRQ_REQ) && cnt) { + val = readreg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_ISTA + 0x40); + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA); + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + cnt--; + } + if (!cnt) + printk(KERN_WARNING "Diva: IRQ LOOP\n"); + if (stat & 1) { + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0x0); + } + if (stat & 2) { + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0x0); + } +} + +static void +diva_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char ista,val; + char tmp[64]; + int icnt=20; + + if (!cs) { + printk(KERN_WARNING "Diva: Spurious interrupt!\n"); + return; + } + ista = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA); +Start_IPAC: + if (cs->debug & L1_DEB_IPAC) { + sprintf(tmp, "IPAC ISTA %02X", ista); + debugl1(cs, tmp); + } + if (ista & 0x0f) { + val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, HSCX_ISTA + 0x40); + if (ista & 0x01) + val |= 0x01; + if (ista & 0x04) + val |= 0x02; + if (ista & 0x08) + val |= 0x04; + if (val) + hscx_int_main(cs, val); + } + if (ista & 0x20) { + val = 0xfe & readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA + 0x80); + if (val) { + isac_interrupt(cs, val); + } + } + if (ista & 0x10) { + val = 0x01; + isac_interrupt(cs, val); + } + ista = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA); + if ((ista & 0x3f) && icnt) { + icnt--; + goto Start_IPAC; + } + if (!icnt) + printk(KERN_WARNING "DIVA IPAC IRQ LOOP\n"); + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xFF); + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xC0); +} + + +void +release_io_diva(struct IsdnCardState *cs) +{ + int bytecnt; + + if (cs->subtyp != DIVA_IPAC_ISA) { + del_timer(&cs->hw.diva.tl); + if (cs->hw.diva.cfg_reg) + byteout(cs->hw.diva.ctrl, 0); /* LED off, Reset */ + } + if ((cs->subtyp == DIVA_ISA) || (cs->subtyp == DIVA_IPAC_ISA)) + bytecnt = 8; + else + bytecnt = 32; + if (cs->hw.diva.cfg_reg) { + release_region(cs->hw.diva.cfg_reg, bytecnt); + } +} + +static void +reset_diva(struct IsdnCardState *cs) +{ + long flags; + + save_flags(flags); + sti(); + if (cs->subtyp == DIVA_IPAC_ISA) { + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x20); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x00); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xc0); + schedule(); + } else { + cs->hw.diva.ctrl_reg = 0; /* Reset On */ + byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + cs->hw.diva.ctrl_reg |= DIVA_RESET; /* Reset Off */ + byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + if (cs->subtyp == DIVA_ISA) + cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A; + else + cs->hw.diva.ctrl_reg |= DIVA_PCI_LED_A; + byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); + } + restore_flags(flags); +} + +#define DIVA_ASSIGN 1 + +static void +diva_led_handler(struct IsdnCardState *cs) +{ + int blink = 0; + + if (cs->subtyp == DIVA_IPAC_ISA) + return; + del_timer(&cs->hw.diva.tl); + if (cs->hw.diva.status & DIVA_ASSIGN) + cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_A : DIVA_PCI_LED_A; + else { + cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_A : DIVA_PCI_LED_A; + blink = 250; + } + if (cs->hw.diva.status & 0xf000) + cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_B : DIVA_PCI_LED_B; + else if (cs->hw.diva.status & 0x0f00) { + cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_B : DIVA_PCI_LED_B; + blink = 500; + } else + cs->hw.diva.ctrl_reg &= ~((DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_B : DIVA_PCI_LED_B); + + byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); + if (blink) { + init_timer(&cs->hw.diva.tl); + cs->hw.diva.tl.expires = jiffies + ((blink * HZ) / 1000); + add_timer(&cs->hw.diva.tl); + } +} + +static int +Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_diva(cs); + return(0); + case CARD_RELEASE: + release_io_diva(cs); + return(0); + case CARD_SETIRQ: + if (cs->subtyp == DIVA_IPAC_ISA) { + return(request_irq(cs->irq, &diva_interrupt_ipac, + I4L_IRQ_FLAG, "HiSax", cs)); + } else { + return(request_irq(cs->irq, &diva_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + } + case CARD_INIT: + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); + case (MDL_REMOVE | REQUEST): + cs->hw.diva.status = 0; + break; + case (MDL_ASSIGN | REQUEST): + cs->hw.diva.status |= DIVA_ASSIGN; + break; + case MDL_INFO_SETUP: + if ((long)arg) + cs->hw.diva.status |= 0x0200; + else + cs->hw.diva.status |= 0x0100; + break; + case MDL_INFO_CONN: + if ((long)arg) + cs->hw.diva.status |= 0x2000; + else + cs->hw.diva.status |= 0x1000; + break; + case MDL_INFO_REL: + if ((long)arg) { + cs->hw.diva.status &= ~0x2000; + cs->hw.diva.status &= ~0x0200; + } else { + cs->hw.diva.status &= ~0x1000; + cs->hw.diva.status &= ~0x0100; + } + break; + } + if (cs->subtyp != DIVA_IPAC_ISA) + diva_led_handler(cs); + return(0); +} + + + +static int pci_index __initdata = 0; + +__initfunc(int +setup_diva(struct IsdnCard *card)) +{ + int bytecnt; + u_char val; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, Diva_revision); + printk(KERN_INFO "HiSax: Eicon.Diehl Diva driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_DIEHLDIVA) + return(0); + cs->hw.diva.status = 0; + if (card->para[1]) { + cs->hw.diva.ctrl_reg = 0; + cs->hw.diva.cfg_reg = card->para[1]; + val = readreg(cs->hw.diva.cfg_reg + DIVA_IPAC_ADR, + cs->hw.diva.cfg_reg + DIVA_IPAC_DATA, IPAC_ID); + printk(KERN_INFO "Diva: IPAC version %x\n", val); + if (val == 1) { + cs->subtyp = DIVA_IPAC_ISA; + cs->hw.diva.ctrl = 0; + cs->hw.diva.isac = card->para[1] + DIVA_IPAC_DATA; + cs->hw.diva.hscx = card->para[1] + DIVA_IPAC_DATA; + cs->hw.diva.isac_adr = card->para[1] + DIVA_IPAC_ADR; + cs->hw.diva.hscx_adr = card->para[1] + DIVA_IPAC_ADR; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + } else { + cs->subtyp = DIVA_ISA; + cs->hw.diva.ctrl = card->para[1] + DIVA_ISA_CTRL; + cs->hw.diva.isac = card->para[1] + DIVA_ISA_ISAC_DATA; + cs->hw.diva.hscx = card->para[1] + DIVA_HSCX_DATA; + cs->hw.diva.isac_adr = card->para[1] + DIVA_ISA_ISAC_ADR; + cs->hw.diva.hscx_adr = card->para[1] + DIVA_HSCX_ADR; + } + cs->irq = card->para[0]; + bytecnt = 8; + } else { +#if CONFIG_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr; + + cs->subtyp = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL, + PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = DIVA_PCI; + else if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL, + PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = DIVA_PCI; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_2, &pci_ioaddr); + if (cs->subtyp) + break; + } + if (!cs->subtyp) { + printk(KERN_WARNING "Diva: No PCI card found\n"); + return(0); + } + pci_index++; + if (!pci_irq) { + printk(KERN_WARNING "Diva: No IRQ for PCI card found\n"); + return(0); + } + + if (!pci_ioaddr) { + printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.diva.cfg_reg = pci_ioaddr; + cs->hw.diva.ctrl = pci_ioaddr + DIVA_PCI_CTRL; + cs->hw.diva.isac = pci_ioaddr + DIVA_PCI_ISAC_DATA; + cs->hw.diva.hscx = pci_ioaddr + DIVA_HSCX_DATA; + cs->hw.diva.isac_adr = pci_ioaddr + DIVA_PCI_ISAC_ADR; + cs->hw.diva.hscx_adr = pci_ioaddr + DIVA_HSCX_ADR; + cs->irq = pci_irq; + bytecnt = 32; +#else + printk(KERN_WARNING "Diva: cfgreg 0 and NO_PCI_BIOS\n"); + printk(KERN_WARNING "Diva: unable to config DIVA PCI\n"); + return (0); +#endif /* CONFIG_PCI */ + } + + printk(KERN_INFO + "Diva: %s card configured at 0x%x IRQ %d\n", + (cs->subtyp == DIVA_PCI) ? "PCI" : + (cs->subtyp == DIVA_ISA) ? "ISA" : "IPAC", + cs->hw.diva.cfg_reg, cs->irq); + if (check_region(cs->hw.diva.cfg_reg, bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.diva.cfg_reg, + cs->hw.diva.cfg_reg + bytecnt); + return (0); + } else { + request_region(cs->hw.diva.cfg_reg, bytecnt, "diva isdn"); + } + + reset_diva(cs); + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Diva_card_msg; + if (cs->subtyp == DIVA_IPAC_ISA) { + cs->readisac = &ReadISAC_IPAC; + cs->writeisac = &WriteISAC_IPAC; + cs->readisacfifo = &ReadISACfifo_IPAC; + cs->writeisacfifo = &WriteISACfifo_IPAC; + val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ID); + printk(KERN_INFO "Diva: IPAC version %x\n", val); + } else { + cs->hw.diva.tl.function = (void *) diva_led_handler; + cs->hw.diva.tl.data = (long) cs; + init_timer(&cs->hw.diva.tl); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + ISACVersion(cs, "Diva:"); + if (HscxVersion(cs, "Diva:")) { + printk(KERN_WARNING + "Diva: wrong HSCX versions check IO address\n"); + release_io_diva(cs); + return (0); + } + } + return (1); +} diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c index 825625042ed5..92972565c5fd 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 1.14.2.11 1998/05/27 18:05:14 keil Exp $ * elsa.c low level stuff for Elsa isdn cards * @@ -6,1243 +6,848 @@ * * Thanks to Elsa GmbH for documents and informations * + * Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE) + * for ELSA PCMCIA support + * * * $Log: elsa.c,v $ - * Revision 1.14 1997/04/13 19:53:25 keil - * Fixed QS1000 init, change in IRQ check delay for SMP + * Revision 1.14.2.11 1998/05/27 18:05:14 keil + * HiSax 3.0 * - * Revision 1.13 1997/04/07 22:58:07 keil - * need include config.h + * Revision 1.14.2.10 1998/04/11 18:46:03 keil + * QS3000PCI support, changes for arcofi * - * Revision 1.12 1997/04/06 22:54:14 keil - * Using SKB's + * Revision 1.14.2.9 1998/04/08 21:44:36 keil + * new init; fix PCI for more as one card * - * Revision 1.11 1997/03/23 21:45:46 keil - * Add support for ELSA PCMCIA + * Revision 1.14.2.8 1998/03/07 23:15:15 tsbogend + * made HiSax working on Linux/Alpha * - * Revision 1.10 1997/03/12 21:42:19 keil - * Bugfix: IRQ hangs with QS1000 + * Revision 1.14.2.7 1998/01/27 22:37:36 keil + * fast io * - * Revision 1.9 1997/03/04 15:57:39 keil - * bugfix IRQ reset Quickstep, ELSA PC changes, some stuff for new cards + * Revision 1.14.2.6 1998/01/11 22:57:10 keil + * add PCMCIA maintainer Klaus * - * Revision 1.8 1997/01/27 15:51:48 keil - * SMP proof,cosmetics + * Revision 1.14.2.5 1997/11/15 18:50:47 keil + * new common init function * - * Revision 1.7 1997/01/21 22:20:48 keil - * Elsa Quickstep support + * Revision 1.14.2.4 1997/10/17 22:13:44 keil + * update to last hisax version * - * Revision 1.6 1997/01/09 18:22:46 keil - * one more PCC-8 fix + * Revision 2.1 1997/07/27 21:47:08 keil + * new interface structures * - * Revision 1.5 1996/12/08 19:46:14 keil - * PCC-8 correct IRQs; starting ARCOFI support + * Revision 2.0 1997/06/26 11:02:40 keil + * New Layer and card interface * - * 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 +#include +#include extern const char *CardType[]; -const char *Elsa_revision = "$Revision: 1.14 $"; +const char *Elsa_revision = "$Revision: 1.14.2.11 $"; const char *Elsa_Types[] = {"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro", - "PCMCIA", "QS 1000", "QS 3000"}; + "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI"}; const char *ITACVer[] = {"?0?", "?1?", "?2?", "?3?", "?4?", "V2.2", "B1", "A1"}; -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ELSA_ISAC 0 +#define ELSA_ISAC_PCM 1 +#define ELSA_ITAC 1 +#define ELSA_HSCX 2 +#define ELSA_ALE 3 +#define ELSA_ALE_PCM 4 +#define ELSA_CONTROL 4 +#define ELSA_CONFIG 5 +#define ELSA_START_TIMER 6 +#define ELSA_TRIG_IRQ 7 + +#define ELSA_PC 1 +#define ELSA_PCC8 2 +#define ELSA_PCC16 3 +#define ELSA_PCF 4 +#define ELSA_PCFPRO 5 +#define ELSA_PCMCIA 6 +#define ELSA_QS1000 7 +#define ELSA_QS3000 8 +#define ELSA_QS1000PCI 9 +#define ELSA_QS3000PCI 10 + +/* PCI stuff */ +#define PCI_VENDOR_ELSA 0x1048 +#define PCI_QS1000_ID 0x1000 +#define PCI_QS3000_ID 0x3000 +#define ELSA_PCI_IRQ_MASK 0x04 + +/* ITAC Registeradressen (only Microlink PC) */ +#define ITAC_SYS 0x34 +#define ITAC_ISEN 0x48 +#define ITAC_RFIE 0x4A +#define ITAC_XFIE 0x4C +#define ITAC_SCIE 0x4E +#define ITAC_STIE 0x46 + +/*** *** + *** Makros als Befehle fuer die Kartenregister *** + *** (mehrere Befehle werden durch Bit-Oderung kombiniert) *** + *** ***/ + +/* Config-Register (Read) */ +#define ELSA_TIMER_RUN 0x02 /* Bit 1 des Config-Reg */ +#define ELSA_TIMER_RUN_PCC8 0x01 /* Bit 0 des Config-Reg bei PCC */ +#define ELSA_IRQ_IDX 0x38 /* Bit 3,4,5 des Config-Reg */ +#define ELSA_IRQ_IDX_PCC8 0x30 /* Bit 4,5 des Config-Reg */ +#define ELSA_IRQ_IDX_PC 0x0c /* Bit 2,3 des Config-Reg */ + +/* Control-Register (Write) */ +#define ELSA_LINE_LED 0x02 /* Bit 1 Gelbe LED */ +#define ELSA_STAT_LED 0x08 /* Bit 3 Gruene LED */ +#define ELSA_ISDN_RESET 0x20 /* Bit 5 Reset-Leitung */ +#define ELSA_ENA_TIMER_INT 0x80 /* Bit 7 Freigabe Timer Interrupt */ + +/* ALE-Register (Read) */ +#define ELSA_HW_RELEASE 0x07 /* Bit 0-2 Hardwarerkennung */ +#define ELSA_S0_POWER_BAD 0x08 /* Bit 3 S0-Bus Spannung fehlt */ + +/* Status Flags */ +#define ELSA_TIMER_AKTIV 1 +#define ELSA_BAD_PWR 2 +#define ELSA_ASSIGN 4 + +#define RS_ISR_PASS_LIMIT 256 +#define _INLINE_ inline +#define FLG_MODEM_ACTIVE 1 +/* IPAC AUX */ +#define ELSA_IPAC_LINE_LED 0x40 /* Bit 6 Gelbe LED */ +#define ELSA_IPAC_STAT_LED 0x80 /* Bit 7 Gruene LED */ + +const u_char ARCOFI_VERSION[] = {2,0xa0,0}; +const u_char ARCOFI_COP_5[] = {4,0xa1,0x25,0xbb,0x4a}; /* GTX */ +const u_char ARCOFI_COP_6[] = {6,0xa1,0x26,0,0,0x82,0x7c}; /* GRL GRH */ +const u_char ARCOFI_COP_7[] = {4,0xa1,0x27,0x80,0x80}; /* GZ */ +const u_char ARCOFI_COP_8[] = {10,0xa1,0x28,0x49,0x31,0x8,0x13,0x6e,0x88,0x2a,0x61}; /* TX */ +const u_char ARCOFI_COP_9[] = {10,0xa1,0x29,0x80,0xcb,0xe9,0x88,0x00,0xc8,0xd8,0x80}; /* RX */ +const u_char ARCOFI_XOP_0[] = {2,0xa1,0x30}; /* PWR Down */ +const u_char ARCOFI_XOP_1[] = {2,0xa1,0x31}; /* PWR UP */ +const u_char ARCOFI_XOP_F[] = {2,0xa1,0x3f}; /* Normal OP */ +const u_char ARCOFI_SOP_F[] = {10,0xa1,0x1f,0x00,0x50,0x10,0x00,0x00,0x80,0x02,0x12}; + +static void set_arcofi(struct IsdnCardState *cs, int bc); + +#if ARCOFI_USE +#include "elsa_ser.c" +#endif 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 */ +/* Interface functions */ - byteout(adr + CARD_ALE, 0); - insb(adr + CARD_ISAC, data, size); -} - - -static inline void -writeisac(unsigned int adr, u_char off, u_char data) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - long flags; - - save_flags(flags); - cli(); - byteout(adr + CARD_ALE, off + 0x20); - byteout(adr + CARD_ISAC, data); - restore_flags(flags); + return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset)); } -static inline void -write_fifo_isac(unsigned int adr, u_char * data, int size) +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - /* fifo write without cli because it's allready done */ - - byteout(adr + CARD_ALE, 0); - outsb(adr + CARD_ISAC, data, size); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset, value); } -#ifdef CONFIG_HISAX_ELSA_PCC -static inline u_char -readitac(unsigned int adr, u_char off) +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - 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); + readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size); } -static inline void -writeitac(unsigned int adr, u_char off, u_char data) +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - long flags; - - save_flags(flags); - cli(); - byteout(adr + CARD_ALE, off); - byteout(adr + CARD_ITAC, data); - restore_flags(flags); + writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size); } -static inline int -TimerRun(struct IsdnCardState *sp) +static u_char +ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset) { - 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); + return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset+0x80)); } -static inline void -elsa_led_handler(struct IsdnCardState *sp) +static void +WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value) { - - 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); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset|0x80, value); } -#endif -static inline void -waitforCEC(int adr, int hscx) +static void +ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) { - int to = 50; - - while ((readhscx(adr, hscx, HSCX_STAR) & 0x04) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Elsa: waitforCEC timeout\n"); + readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size); } - -static inline void -waitforXFW(int adr, int hscx) +static void +WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) { - int to = 50; - - while ((!(readhscx(adr, hscx, HSCX_STAR) & 0x44) == 0x40) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Elsa: waitforXFW timeout\n"); + writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size); } -static inline void -writehscxCMDR(int adr, int hscx, u_char data) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - long flags; - - save_flags(flags); - cli(); - waitforCEC(adr, hscx); - writehscx(adr, hscx, HSCX_CMDR, data); - restore_flags(flags); + return (readreg(cs->hw.elsa.ale, + cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0))); } -/* - * fast interrupt here - */ - - static void -hscxreport(struct IsdnCardState *sp, int hscx) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - 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)); + writereg(cs->hw.elsa.ale, + cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0), value); } -void -elsa_report(struct IsdnCardState *sp) -{ - printk(KERN_DEBUG "ISAC\n"); - printk(KERN_DEBUG "ISTA %x\n", readisac(sp->cfg_reg, ISAC_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readisac(sp->cfg_reg, ISAC_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readisac(sp->cfg_reg, ISAC_EXIR)); - hscxreport(sp, 0); - hscxreport(sp, 1); -} - -/* - * HSCX stuff goes here - */ - -static void -hscx_empty_fifo(struct HscxState *hsp, int count) +static inline u_char +readitac(struct IsdnCardState *cs, u_char off) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; + register u_char ret; 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); + 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_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (ret); } -static void -hscx_fill_fifo(struct HscxState *hsp) +static inline void +writeitac(struct IsdnCardState *cs, u_char off, u_char data) { - struct IsdnCardState *sp = hsp->sp; - int more, count; - u_char *ptr; long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_fill_fifo"); - - if (!hsp->tx_skb) - return; - if (hsp->tx_skb->len <= 0) - return; - - more = (hsp->mode == 1) ? 1 : 0; - if (hsp->tx_skb->len > 32) { - more = !0; - count = 32; - } else - count = hsp->tx_skb->len; - - waitforXFW(sp->cfg_reg, hsp->hscx); save_flags(flags); cli(); - ptr = hsp->tx_skb->data; - skb_pull(hsp->tx_skb, count); - hsp->tx_cnt -= count; - hsp->count += count; - write_fifo_hscx(sp->cfg_reg, hsp->hscx, ptr, count); - writehscxCMDR(sp->cfg_reg, hsp->hscx, more ? 0x8 : 0xa); + byteout(cs->hw.elsa.ale, off); + byteout(cs->hw.elsa.itac, data); 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) +static inline int +TimerRun(struct IsdnCardState *cs) { - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = readhscx(sp->cfg_reg, hsp->hscx, HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!(r & 0x80)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX invalid frame"); - if ((r & 0x40) && hsp->mode) - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX RDO mode=%d", - hsp->mode); - debugl1(sp, tmp); - } - if (!(r & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX CRC error"); - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80); - } else { - count = readhscx(sp->cfg_reg, hsp->hscx, HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - if ((count = hsp->rcvidx - 1) > 0) { - if (sp->debug & L1_DEB_HSCX_FIFO) { - sprintf(tmp, "HX Frame %d", count); - debugl1(sp, tmp); - } - if (!(skb = dev_alloc_skb(count))) - printk(KERN_WARNING "Elsa: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "elsa: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - dev_kfree_skb(hsp->tx_skb, FREE_WRITE); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } + 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); } - /* - * ISAC stuff goes here + * fast interrupt HSCX stuff goes here */ -static void -isac_empty_fifo(struct IsdnCardState *sp, int count) -{ - u_char *ptr; - long flags; +#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) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_empty_fifo"); +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.elsa.ale, \ + cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt) - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - writeisac(sp->cfg_reg, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo_isac(sp->cfg_reg, ptr, count); - writeisac(sp->cfg_reg, ISAC_CMDR, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.elsa.ale, \ + cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt) - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } -} +#include "hscx_irq.c" static void -isac_fill_fifo(struct IsdnCardState *sp) +elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs) { - 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; + struct IsdnCardState *cs = dev_id; + u_char val; + u_char tmp[32]; + int icnt=20; - count = sp->tx_skb->len; - if (count <= 0) + if (!cs) { + printk(KERN_WARNING "Elsa: Spurious interrupt!\n"); 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); + 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; } - writeisac(sp->cfg_reg, ISAC_CIX0, (command << 2) | 3); -} - -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval, v1; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; #if ARCOFI_USE - struct BufHeader *ibh; - u_char *ptr; -#endif - - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = readisac(sp->cfg_reg, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!(exval & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - writeisac(sp->cfg_reg, ISAC_CMDR, 0x80); - } else { - count = readisac(sp->cfg_reg, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - sp->rcvidx = 0; - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "Elsa: D receive out of memory\n"); - else { - SET_SKB_FREE(skb); - memcpy(skb_put(skb, count), sp->rcvbuf, count); - skb_queue_tail(&sp->rq, skb); - } - } + if (cs->hw.elsa.MFlag) { + val = serial_inp(cs, UART_IIR); + if (!(val & UART_IIR_NO_INT)) { + sprintf(tmp,"IIR %02x", val); + debugl1(cs, tmp); + rs_interrupt_elsa(intno, cs); } - sp->rcvidx = 0; - isac_sched_event(sp, ISAC_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - isac_empty_fifo(sp, 32); - } - if (val & 0x20) { /* RSC */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RSC interrupt"); - } - if (val & 0x10) { /* XPR */ - if (sp->tx_skb) - if (sp->tx_skb->len) { - isac_fill_fifo(sp); - goto afterXPR; - } else { - dev_kfree_skb(sp->tx_skb, FREE_WRITE); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (readisac(sp->cfg_reg, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = readisac(sp->cfg_reg, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - if (exval & 0x08) { - v1 = readisac(sp->cfg_reg, ISAC_MOSR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC MOSR %02x", v1); - debugl1(sp, tmp); - } -#if ARCOFI_USE - if (v1 & 0x08) { - if (!sp->mon_rx) - if (BufPoolGet(&(sp->mon_rx), &(sp->rbufpool), - GFP_ATOMIC, (void *) 1, 3)) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON RX out of buffers!"); - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a); - goto afterMONR0; - } else - sp->mon_rxp = 0; - ibh = sp->mon_rx; - ptr = DATAPTR(ibh); - ptr += sp->mon_rxp; - sp->mon_rxp++; - if (sp->mon_rxp >= 3072) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a); - sp->mon_rxp = 0; - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON RX overflow!"); - goto afterMONR0; - } - *ptr = readisac(sp->cfg_reg, ISAC_MOR0); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC MOR0 %02x", *ptr); - debugl1(sp, tmp); - } - } - afterMONR0: - if (v1 & 0x80) { - if (!sp->mon_rx) - if (BufPoolGet(&(sp->mon_rx), &(sp->rbufpool), - GFP_ATOMIC, (void *) 1, 3)) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON RX out of buffers!"); - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - goto afterMONR1; - } else - sp->mon_rxp = 0; - ibh = sp->mon_rx; - ptr = DATAPTR(ibh); - ptr += sp->mon_rxp; - sp->mon_rxp++; - if (sp->mon_rxp >= 3072) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - sp->mon_rxp = 0; - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON RX overflow!"); - goto afterMONR1; - } - *ptr = readisac(sp->cfg_reg, ISAC_MOR1); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC MOR1 %02x", *ptr); - debugl1(sp, tmp); - } - } - afterMONR1: - if (v1 & 0x04) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a); - sp->mon_rx->datasize = sp->mon_rxp; - sp->mon_flg |= MON0_RX; - } - if (v1 & 0x40) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - sp->mon_rx->datasize = sp->mon_rxp; - sp->mon_flg |= MON1_RX; - } - if (v1 == 0x02) { - ibh = sp->mon_tx; - if (!ibh) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a); - goto AfterMOX0; - } - count = ibh->datasize - sp->mon_txp; - if (count <= 0) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0f); - BufPoolRelease(sp->mon_tx); - sp->mon_tx = NULL; - sp->mon_txp = 0; - sp->mon_flg |= MON0_TX; - goto AfterMOX0; - } - ptr = DATAPTR(ibh); - ptr += sp->mon_txp; - sp->mon_txp++; - writeisac(sp->cfg_reg, ISAC_MOX0, *ptr); - } - AfterMOX0: - if (v1 == 0x20) { - ibh = sp->mon_tx; - if (!ibh) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - goto AfterMOX1; - } - count = ibh->datasize - sp->mon_txp; - if (count <= 0) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0xf0); - BufPoolRelease(sp->mon_tx); - sp->mon_tx = NULL; - sp->mon_txp = 0; - sp->mon_flg |= MON1_TX; - goto AfterMOX1; - } - ptr = DATAPTR(ibh); - ptr += sp->mon_txp; - sp->mon_txp++; - writeisac(sp->cfg_reg, ISAC_MOX1, *ptr); - } - AfterMOX1: #endif - } + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); } -} - -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ - - u_char exval; - struct HscxState *hsp; - char tmp[32]; - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = readhscx(sp->cfg_reg, 1, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); + 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; } - 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); - } + 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; } - 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); + 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++; } - hscx_interrupt(sp, exval, 0); } + if (cs->hw.elsa.MFlag) { + val = serial_inp(cs, UART_MCR); + val ^= 0x8; + serial_outp(cs, UART_MCR, val); + val = serial_inp(cs, UART_MCR); + val ^= 0x8; + serial_outp(cs, UART_MCR, val); + } + if (cs->hw.elsa.trig) + byteout(cs->hw.elsa.trig, 0x00); + writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0x0); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0x0); } static void -elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs) +elsa_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) { - struct IsdnCardState *sp; - u_char val; + struct IsdnCardState *cs = dev_id; + u_char ista,val; + char tmp[64]; + int icnt=20; - sp = (struct IsdnCardState *) irq2dev_map[intno]; - - if (!sp) { + if (!cs) { printk(KERN_WARNING "Elsa: Spurious interrupt!\n"); return; } -#ifdef CONFIG_HISAX_ELSA_PCC - INT_RESTART: - if (!TimerRun(sp)) { - /* Timer Restart */ - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - if (!(sp->counter++ & 0x3f)) { - /* Call LEDs all 64 tics */ - elsa_led_handler(sp); - } + val = bytein(cs->hw.elsa.cfg + 0x4c); /* PCI IRQ */ + if (!(val & ELSA_PCI_IRQ_MASK)) + 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); } -#endif - val = readhscx(sp->cfg_reg, 1, HSCX_ISTA); - Start_HSCX: - if (val) { - hscx_int_main(sp, val); + 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); } - val = readisac(sp->cfg_reg, ISAC_ISTA); - Start_ISAC: - if (val) { - isac_interrupt(sp, val); - } -#ifdef CONFIG_HISAX_ELSA_PCC - if (!TimerRun(sp)) - goto INT_RESTART; -#endif - val = readhscx(sp->cfg_reg, 1, HSCX_ISTA); - if (val) { - if (sp->debug & L1_DEB_HSCX) - debugl1(sp, "HSCX IntStat after IntRoutine"); - goto Start_HSCX; + if (ista & 0x20) { + val = 0xfe & readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA + 0x80); + if (val) { + isac_interrupt(cs, val); + } } - val = readisac(sp->cfg_reg, ISAC_ISTA); - if (val) { - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "ISAC IntStat after IntRoutine"); - goto Start_ISAC; + if (ista & 0x10) { + val = 0x01; + isac_interrupt(cs, val); } - 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); + ista = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA); + if ((ista & 0x3f) && icnt) { + icnt--; + goto Start_IPAC; } -#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 (!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); } - -static void -initisac(struct IsdnCardState *sp) +void +release_io_elsa(struct IsdnCardState *cs) { - 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); -} + int bytecnt = 8; -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); + del_timer(&cs->hw.elsa.tl); + if (cs->hw.elsa.ctrl) + byteout(cs->hw.elsa.ctrl, 0); /* LEDs Out */ + if (cs->subtyp == ELSA_QS1000PCI) { + byteout(cs->hw.elsa.cfg + 0x4c, 0x01); /* disable IRQ */ + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff); + bytecnt = 2; + release_region(cs->hw.elsa.cfg, 0x80); } - 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->subtyp == ELSA_QS3000PCI) { + byteout(cs->hw.elsa.cfg + 0x4c, 0x03); /* enable ELSA PCI IRQ */ + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff); + release_region(cs->hw.elsa.cfg, 0x80); + } + if ((cs->subtyp == ELSA_PCFPRO) || + (cs->subtyp == ELSA_QS3000) || + (cs->subtyp == ELSA_PCF) || + (cs->subtyp == ELSA_QS3000PCI)) { + bytecnt = 16; + release_modem(cs); } - writehscx(sp->cfg_reg, hscx, HSCX_ISTA, 0x00); + if (cs->hw.elsa.base) + release_region(cs->hw.elsa.base, bytecnt); } -void -release_io_elsa(struct IsdnCard *card) +static void +reset_elsa(struct IsdnCardState *cs) { - int bytecnt = 8; + long flags; - if (card->sp->subtyp == ELSA_PCFPRO) - bytecnt = 16; - if (card->sp->cfg_reg) - release_region(card->sp->cfg_reg, bytecnt); + 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); + } + if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) { + save_flags(flags); + sti(); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x00); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0); + schedule(); + restore_flags(flags); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x0); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0x3c); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff); + if (cs->subtyp == ELSA_QS1000PCI) + byteout(cs->hw.elsa.cfg + 0x4c, 0x41); /* enable ELSA PCI IRQ */ + else if (cs->subtyp == ELSA_QS3000PCI) + byteout(cs->hw.elsa.cfg + 0x4c, 0x43); /* enable ELSA PCI IRQ */ + } } 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 +init_arcofi(struct IsdnCardState *cs) { + send_arcofi(cs, ARCOFI_XOP_0, 1, 0); +/* send_arcofi(cs, ARCOFI_XOP_F, 1); +*/ } -static void -clear_pending_ints(struct IsdnCardState *sp) -{ -#ifdef CONFIG_HISAX_ELSA_PCMCIA - int val; - char tmp[64]; +#define ARCDEL 500 - val = readhscx(sp->cfg_reg, 1, HSCX_ISTA); - sprintf(tmp, "HSCX B ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readhscx(sp->cfg_reg, 1, HSCX_EXIR); - sprintf(tmp, "HSCX B EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x02) { - val = readhscx(sp->cfg_reg, 0, HSCX_EXIR); - sprintf(tmp, "HSCX A EXIR %x", val); - debugl1(sp, tmp); - } - val = readhscx(sp->cfg_reg, 0, HSCX_ISTA); - sprintf(tmp, "HSCX A ISTA %x", val); - debugl1(sp, tmp); - val = readhscx(sp->cfg_reg, 1, HSCX_STAR); - sprintf(tmp, "HSCX B STAR %x", val); - debugl1(sp, tmp); - val = readhscx(sp->cfg_reg, 0, HSCX_STAR); - sprintf(tmp, "HSCX A STAR %x", val); - debugl1(sp, tmp); - val = readisac(sp->cfg_reg, ISAC_STAR); - sprintf(tmp, "ISAC STAR %x", val); - debugl1(sp, tmp); - val = readisac(sp->cfg_reg, ISAC_MODE); - sprintf(tmp, "ISAC MODE %x", val); - debugl1(sp, tmp); - val = readisac(sp->cfg_reg, ISAC_ADF2); - sprintf(tmp, "ISAC ADF2 %x", val); - debugl1(sp, tmp); - val = readisac(sp->cfg_reg, ISAC_ISTA); - sprintf(tmp, "ISAC ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readisac(sp->cfg_reg, ISAC_EXIR); - sprintf(tmp, "ISAC EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x04) { - val = readisac(sp->cfg_reg, ISAC_CIR0); - sprintf(tmp, "ISAC CIR0 %x", val); - debugl1(sp, tmp); - } -#endif - writehscx(sp->cfg_reg, 0, HSCX_MASK, 0xFF); - writehscx(sp->cfg_reg, 1, HSCX_MASK, 0xFF); - writeisac(sp->cfg_reg, ISAC_MASK, 0xFF); -#ifdef CONFIG_HISAX_ELSA_PCC - if (sp->subtyp == ELSA_QS1000) { - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff); - } -#endif - writehscx(sp->cfg_reg, 0, HSCX_MASK, 0x0); - writehscx(sp->cfg_reg, 1, HSCX_MASK, 0x0); - writeisac(sp->cfg_reg, ISAC_MASK, 0x0); - writeisac(sp->cfg_reg, ISAC_CMDR, 0x41); +static void +set_arcofi(struct IsdnCardState *cs, int bc) { + long flags; + char tmp[32]; + + sprintf(tmp,"set_arcofi bc=%d", bc); + debugl1(cs, tmp); + save_flags(flags); + sti(); + send_arcofi(cs, ARCOFI_XOP_0, bc, 0); + udelay(ARCDEL); + send_arcofi(cs, ARCOFI_COP_5, bc, 0); + udelay(ARCDEL); + send_arcofi(cs, ARCOFI_COP_6, bc, 0); + udelay(ARCDEL); + send_arcofi(cs, ARCOFI_COP_7, bc, 0); + udelay(ARCDEL); + send_arcofi(cs, ARCOFI_COP_8, bc, 0); + udelay(ARCDEL); + send_arcofi(cs, ARCOFI_COP_9, bc, 0); + udelay(ARCDEL); + send_arcofi(cs, ARCOFI_SOP_F, bc, 0); + udelay(ARCDEL); + send_arcofi(cs, ARCOFI_XOP_1, bc, 0); + udelay(ARCDEL); + send_arcofi(cs, ARCOFI_XOP_F, bc, 0); + restore_flags(flags); } -static void -check_arcofi(struct IsdnCardState *sp) +static int +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(0); + } + send_arcofi(cs, ARCOFI_VERSION, 0, 1); + if (test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags)) { + if (test_and_clear_bit(HW_MON1_RX_END, &cs->HW_Flags)) { + 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); + return(1); } - sp->mon_flg = 0; #endif + return(0); } -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.interrupts[sp->irq]; - printk(KERN_INFO "Elsa: IRQ %d count %d\n", sp->irq, irq_cnt); - ret = get_irq(sp->cardnr, &elsa_interrupt); -#ifdef CONFIG_HISAX_ELSA_PCC - byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff); -#endif - while (ret && cnt) { - sp->counter = 0; - clear_pending_ints(sp); - initisac(sp); - sp->modehscx(sp->hs, 0, 0); - sp->modehscx(sp->hs + 1, 0, 0); - save_flags(flags); - sp->counter = 0; - sti(); -#ifdef CONFIG_HISAX_ELSA_PCC - byteout(sp->cfg_reg + CARD_CONTROL, ISDN_RESET | ENABLE_TIM_INT); - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + (110 * HZ) / 1000; /* Timeout 110ms */ - schedule(); - restore_flags(flags); - printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n", - sp->counter); - if (abs(sp->counter - 13) < 3) { - printk(KERN_INFO "Elsa: timer and irq OK\n"); - } else { - printk(KERN_WARNING - "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n", - sp->counter, sp->irq); - } + if (cs->subtyp == ELSA_PCMCIA) + return; + del_timer(&cs->hw.elsa.tl); + if (cs->hw.elsa.status & ELSA_ASSIGN) + cs->hw.elsa.ctrl_reg |= ELSA_STAT_LED; + else if (cs->hw.elsa.status & ELSA_BAD_PWR) + cs->hw.elsa.ctrl_reg &= ~ELSA_STAT_LED; + else { + cs->hw.elsa.ctrl_reg ^= ELSA_STAT_LED; + blink = 250; + } + if (cs->hw.elsa.status & 0xf000) + cs->hw.elsa.ctrl_reg |= ELSA_LINE_LED; + else if (cs->hw.elsa.status & 0x0f00) { + cs->hw.elsa.ctrl_reg ^= ELSA_LINE_LED; + blink = 500; + } else + cs->hw.elsa.ctrl_reg &= ~ELSA_LINE_LED; + + if ((cs->subtyp == ELSA_QS1000PCI) || + (cs->subtyp == ELSA_QS3000PCI)) { + u_char led = 0xff; + if (cs->hw.elsa.ctrl_reg & ELSA_LINE_LED) + led ^= ELSA_IPAC_LINE_LED; + if (cs->hw.elsa.ctrl_reg & ELSA_STAT_LED) + led ^= ELSA_IPAC_STAT_LED; + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, led); + } else + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + if (blink) { + init_timer(&cs->hw.elsa.tl); + cs->hw.elsa.tl.expires = jiffies + ((blink * HZ) / 1000); + add_timer(&cs->hw.elsa.tl); + } +} + +static int +Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + int pwr, len, ret = 0; + u_char *msg; + 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) || + (cs->subtyp == ELSA_QS3000PCI)) + 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: + cs->debug |= L1_DEB_IPAC; + inithscxisac(cs, 1); + if ((cs->subtyp == ELSA_QS1000) || + (cs->subtyp == ELSA_QS3000)) + { + byteout(cs->hw.elsa.timer, 0); + } + if (cs->hw.elsa.trig) + byteout(cs->hw.elsa.trig, 0xff); + inithscxisac(cs, 2); + return(0); + case CARD_TEST: + if ((cs->subtyp == ELSA_PCMCIA) || + (cs->subtyp == ELSA_QS1000PCI)) { + return(0); + } else if (cs->subtyp == ELSA_QS3000PCI) { + ret = 0; + } else { + save_flags(flags); + cs->hw.elsa.counter = 0; + sti(); + cs->hw.elsa.ctrl_reg |= ELSA_ENA_TIMER_INT; + cs->hw.elsa.status |= ELSA_TIMER_AKTIV; + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + byteout(cs->hw.elsa.timer, 0); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (110 * HZ) / 1000; /* Timeout 110ms */ + schedule(); + restore_flags(flags); + cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT; + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + cs->hw.elsa.status &= ~ELSA_TIMER_AKTIV; + printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n", + cs->hw.elsa.counter); + if (abs(cs->hw.elsa.counter - 13) < 3) { + printk(KERN_INFO "Elsa: timer and irq OK\n"); + ret = 0; + } else { + printk(KERN_WARNING + "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n", + cs->hw.elsa.counter, cs->irq); + ret = 1; + } + } +#if ARCOFI_USE + if (check_arcofi(cs)) { + init_modem(cs); + } #endif - printk(KERN_INFO "Elsa: IRQ %d count %d\n", sp->irq, - kstat.interrupts[sp->irq]); - if (kstat.interrupts[sp->irq] == irq_cnt) { - printk(KERN_WARNING - "Elsa: IRQ(%d) getting no interrupts during init %d\n", - sp->irq, 4 - cnt); - if (cnt == 1) { - irq2dev_map[sp->irq] = NULL; - free_irq(sp->irq, NULL); - return (0); + elsa_led_handler(cs); + return(ret); + case (MDL_REMOVE | REQUEST): + cs->hw.elsa.status &= 0; + break; + case (MDL_ASSIGN | REQUEST): + cs->hw.elsa.status |= ELSA_ASSIGN; + break; + case MDL_INFO_SETUP: + if ((long) arg) + cs->hw.elsa.status |= 0x0200; + else + cs->hw.elsa.status |= 0x0100; + break; + case MDL_INFO_CONN: + if ((long) arg) + cs->hw.elsa.status |= 0x2000; + else + cs->hw.elsa.status |= 0x1000; + break; + case MDL_INFO_REL: + if ((long) arg) { + cs->hw.elsa.status &= ~0x2000; + cs->hw.elsa.status &= ~0x0200; } else { - reset_elsa(sp); - cnt--; + cs->hw.elsa.status &= ~0x1000; + cs->hw.elsa.status &= ~0x0100; } - } else { - check_arcofi(sp); - cnt = 0; - } + break; + case CARD_AUX_IND: + if (cs->hw.elsa.MFlag) { + if (!arg) + return(0); + msg = arg; + len = *msg; + msg++; + modem_write_cmd(cs, msg, len); + } + break; } - sp->counter = 0; - return (ret); + 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 +856,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 +888,301 @@ 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; + cs->hw.elsa.MFlag = 0; + if (cs->typ == ISDN_CTYPE_ELSA) { + cs->hw.elsa.base = card->para[0]; printk(KERN_INFO "Elsa: Microlink IO probing\n"); - if (sp->cfg_reg) { - if (!(sp->subtyp = probe_elsa_adr(sp->cfg_reg))) { + if (cs->hw.elsa.base) { + if (!(cs->subtyp = probe_elsa_adr(cs->hw.elsa.base, + cs->typ))) { printk(KERN_WARNING "Elsa: no Elsa Microlink at 0x%x\n", - sp->cfg_reg); + cs->hw.elsa.base); return (0); } } else - sp->cfg_reg = probe_elsa(sp); - if (sp->cfg_reg) { - val = bytein(sp->cfg_reg + CARD_CONFIG); - if (sp->subtyp == ELSA_PC) { + cs->hw.elsa.base = probe_elsa(cs); + if (cs->hw.elsa.base) { + cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG; + cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL; + cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE; + cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC; + cs->hw.elsa.itac = cs->hw.elsa.base + ELSA_ITAC; + cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ; + cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER; + val = bytein(cs->hw.elsa.cfg); + if (cs->subtyp == ELSA_PC) { const u_char CARD_IrqTab[8] = {7, 3, 5, 9, 0, 0, 0, 0}; - sp->irq = CARD_IrqTab[(val & IRQ_INDEX_PC) >> 2]; - } else if (sp->subtyp == ELSA_PCC8) { + cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PC) >> 2]; + } else if (cs->subtyp == ELSA_PCC8) { const u_char CARD_IrqTab[8] = {7, 3, 5, 9, 0, 0, 0, 0}; - sp->irq = CARD_IrqTab[(val & IRQ_INDEX_PCC8) >> 4]; + cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PCC8) >> 4]; } else { const u_char CARD_IrqTab[8] = {15, 10, 15, 3, 11, 5, 11, 9}; - sp->irq = CARD_IrqTab[(val & IRQ_INDEX) >> 3]; + cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX) >> 3]; } - val = bytein(sp->cfg_reg + CARD_ALE) & 0x7; + val = bytein(cs->hw.elsa.ale) & ELSA_HW_RELEASE; if (val < 3) val |= 8; val += 'A' - 3; if (val == 'B' || val == 'C') val ^= 1; - if ((sp->subtyp == ELSA_PCFPRO) && (val = 'G')) + if ((cs->subtyp == ELSA_PCFPRO) && (val = 'G')) val = 'C'; printk(KERN_INFO "Elsa: %s found at 0x%x Rev.:%c IRQ %d\n", - Elsa_Types[sp->subtyp], - sp->cfg_reg, - val, sp->irq); - val = bytein(sp->cfg_reg + CARD_ALE) & 0x08; - if (val) + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + val, cs->irq); + val = bytein(cs->hw.elsa.ale) & ELSA_S0_POWER_BAD; + if (val) { printk(KERN_WARNING "Elsa: Microlink S0 bus power bad\n"); + cs->hw.elsa.status |= ELSA_BAD_PWR; + } } else { printk(KERN_WARNING "No Elsa Microlink found\n"); return (0); } - } else if (sp->typ == ISDN_CTYPE_ELSA_QS1000) { - sp->cfg_reg = card->para[1]; - sp->irq = card->para[0]; - sp->subtyp = ELSA_QS1000; + } else if (cs->typ == ISDN_CTYPE_ELSA_PNP) { + cs->hw.elsa.base = card->para[1]; + cs->irq = card->para[0]; + cs->subtyp = ELSA_QS1000; + cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG; + cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE; + cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC; + cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ; + cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER; + cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL; printk(KERN_INFO - "Elsa: %s found at 0x%x IRQ %d\n", - Elsa_Types[sp->subtyp], - sp->cfg_reg, - sp->irq); - } else - return (0); -#endif -#ifdef CONFIG_HISAX_ELSA_PCMCIA - if (sp->typ == ISDN_CTYPE_ELSA_QS1000) { - sp->cfg_reg = card->para[1]; - sp->irq = card->para[0]; - sp->subtyp = ELSA_PCMCIA; + "Elsa: %s defined at 0x%x IRQ %d\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + cs->irq); + } else if (cs->typ == ISDN_CTYPE_ELSA_PCMCIA) { + cs->hw.elsa.base = card->para[1]; + cs->irq = card->para[0]; + cs->subtyp = ELSA_PCMCIA; + cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE_PCM; + cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC_PCM; + cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + cs->hw.elsa.timer = 0; + cs->hw.elsa.trig = 0; + cs->hw.elsa.ctrl = 0; printk(KERN_INFO - "Elsa: %s 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 if (pcibios_find_device(PCI_VENDOR_ELSA, + PCI_QS3000_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = ELSA_QS3000PCI; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr); + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.elsa.cfg = pci_ioaddr; + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_3, &pci_ioaddr); + if (cs->subtyp) + break; + } + if (!cs->subtyp) { + printk(KERN_WARNING "Elsa: No PCI card found\n"); + return(0); + } + pci_index++; + if (!pci_irq) { + printk(KERN_WARNING "Elsa: No IRQ for PCI card found\n"); + return(0); + } + + if (!pci_ioaddr) { + printk(KERN_WARNING "Elsa: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.elsa.base = pci_ioaddr; + cs->hw.elsa.ale = pci_ioaddr; + cs->hw.elsa.isac = pci_ioaddr +1; + cs->hw.elsa.hscx = pci_ioaddr +1; + cs->irq = pci_irq; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + cs->hw.elsa.timer = 0; + cs->hw.elsa.trig = 0; + printk(KERN_INFO + "Elsa: %s defined at 0x%x/0x%x IRQ %d\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + cs->hw.elsa.cfg, + cs->irq); +#else + printk(KERN_WARNING "Elsa: Elsa PCI and NO_PCI_BIOS\n"); + printk(KERN_WARNING "Elsa: unable to config Elsa PCI\n"); + return (0); +#endif /* CONFIG_PCI */ + } else return (0); -#endif - switch (sp->subtyp) { + switch (cs->subtyp) { case ELSA_PC: - bytecnt = 8; - break; case ELSA_PCC8: - bytecnt = 8; - break; - case ELSA_PCFPRO: - bytecnt = 16; - break; case ELSA_PCC16: + case ELSA_QS1000: + case ELSA_PCMCIA: bytecnt = 8; break; + case ELSA_PCFPRO: case ELSA_PCF: + case ELSA_QS3000PCI: bytecnt = 16; break; - case ELSA_QS1000: - bytecnt = 8; - break; - case ELSA_PCMCIA: - bytecnt = 8; + case ELSA_QS1000PCI: + bytecnt = 2; break; default: printk(KERN_WARNING - "Unknown ELSA subtype %d\n", sp->subtyp); + "Unknown ELSA subtype %d\n", cs->subtyp); return (0); } - - if (check_region((sp->cfg_reg), bytecnt)) { + /* In case of the elsa pcmcia card, this region is in use, + reserved for us by the card manager. So we do not check it + here, it would fail. */ + if (cs->typ != ISDN_CTYPE_ELSA_PCMCIA && check_region(cs->hw.elsa.base, bytecnt)) { printk(KERN_WARNING "HiSax: %s config port %x-%x already in use\n", CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + bytecnt); + cs->hw.elsa.base, + cs->hw.elsa.base + bytecnt); return (0); } else { - request_region(sp->cfg_reg, bytecnt, "elsa isdn"); + request_region(cs->hw.elsa.base, bytecnt, "elsa isdn"); } - - /* Teste Timer */ -#ifdef CONFIG_HISAX_ELSA_PCC - byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff); - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - if (!TimerRun(sp)) { - byteout(sp->cfg_reg + CARD_START_TIMER, 0); /* 2. Versuch */ - if (!TimerRun(sp)) { + if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) { + if (check_region(cs->hw.elsa.cfg, 0x80)) { printk(KERN_WARNING - "Elsa: timer do not start\n"); - release_io_elsa(card); + "HiSax: %s pci port %x-%x already in use\n", + CardType[card->typ], + cs->hw.elsa.cfg, + cs->hw.elsa.cfg + 0x80); + release_region(cs->hw.elsa.base, bytecnt); return (0); + } else { + request_region(cs->hw.elsa.cfg, 0x80, "elsa isdn pci"); } } - save_flags(flags); - sti(); - HZDELAY(1); /* wait >=10 ms */ - restore_flags(flags); - if (TimerRun(sp)) { - printk(KERN_WARNING "Elsa: timer do not run down\n"); - release_io_elsa(card); - return (0); + cs->hw.elsa.tl.function = (void *) elsa_led_handler; + cs->hw.elsa.tl.data = (long) cs; + init_timer(&cs->hw.elsa.tl); + /* Teste Timer */ + if (cs->hw.elsa.timer) { + byteout(cs->hw.elsa.trig, 0xff); + byteout(cs->hw.elsa.timer, 0); + if (!TimerRun(cs)) { + byteout(cs->hw.elsa.timer, 0); /* 2. Versuch */ + if (!TimerRun(cs)) { + printk(KERN_WARNING + "Elsa: timer do not start\n"); + release_io_elsa(cs); + return (0); + } + } + save_flags(flags); + sti(); + HZDELAY(1); /* wait >=10 ms */ + restore_flags(flags); + if (TimerRun(cs)) { + printk(KERN_WARNING "Elsa: timer do not run down\n"); + release_io_elsa(cs); + return (0); + } + printk(KERN_INFO "Elsa: timer OK; resetting card\n"); } - printk(KERN_INFO "Elsa: timer OK; resetting card\n"); - reset_elsa(sp); -#endif - verA = readhscx(sp->cfg_reg, 0, HSCX_VSTR) & 0xf; - verB = readhscx(sp->cfg_reg, 1, HSCX_VSTR) & 0xf; - printk(KERN_INFO "Elsa: HSCX version A: %s B: %s\n", - HscxVersion(verA), HscxVersion(verB)); - val = readisac(sp->cfg_reg, ISAC_RBCH); - printk(KERN_INFO "Elsa: ISAC %s\n", - ISACVersion(val)); - -#ifdef CONFIG_HISAX_ELSA_PCMCIA - if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) { - printk(KERN_WARNING - "Elsa: wrong HSCX versions check IO address\n"); - release_io_elsa(card); - return (0); + 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->subtyp == ELSA_QS3000PCI)) { + cs->readisac = &ReadISAC_IPAC; + cs->writeisac = &WriteISAC_IPAC; + cs->readisacfifo = &ReadISACfifo_IPAC; + cs->writeisacfifo = &WriteISACfifo_IPAC; + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ID); + printk(KERN_INFO "Elsa: IPAC version %x\n", val); + } else { + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + ISACVersion(cs, "Elsa:"); + if (HscxVersion(cs, "Elsa:")) { + printk(KERN_WARNING + "Elsa: wrong HSCX versions check IO address\n"); + release_io_elsa(cs); + return (0); + } } -#endif - -#ifdef CONFIG_HISAX_ELSA_PCC - if (sp->subtyp == ELSA_PC) { - val = readitac(sp->cfg_reg, ITAC_SYS); + if (cs->subtyp == ELSA_PC) { + val = readitac(cs, ITAC_SYS); printk(KERN_INFO "Elsa: ITAC version %s\n", ITACVer[val & 7]); - writeitac(sp->cfg_reg, ITAC_ISEN, 0); - writeitac(sp->cfg_reg, ITAC_RFIE, 0); - writeitac(sp->cfg_reg, ITAC_XFIE, 0); - writeitac(sp->cfg_reg, ITAC_SCIE, 0); - writeitac(sp->cfg_reg, ITAC_STIE, 0); + writeitac(cs, ITAC_ISEN, 0); + writeitac(cs, ITAC_RFIE, 0); + writeitac(cs, ITAC_XFIE, 0); + writeitac(cs, ITAC_SCIE, 0); + writeitac(cs, ITAC_STIE, 0); } -#endif - sp->modehscx = &modehscx; - sp->ph_command = &ph_command; - sp->hscx_fill_fifo = &hscx_fill_fifo; - sp->isac_fill_fifo = &isac_fill_fifo; - return (1); } diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c new file mode 100644 index 000000000000..f5d96ccdeb37 --- /dev/null +++ b/drivers/isdn/hisax/elsa_ser.c @@ -0,0 +1,684 @@ +#include +#include + +#define MAX_MODEM_BUF 256 +#define WAKEUP_CHARS (MAX_MODEM_BUF/2) +#define RS_ISR_PASS_LIMIT 256 +#define BASE_BAUD ( 1843200 / 16 ) + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define SERIAL_DEBUG_OPEN 1 +#define SERIAL_DEBUG_INTR 1 +#define SERIAL_DEBUG_FLOW 1 +#undef SERIAL_DEBUG_REG +//#define SERIAL_DEBUG_REG + +#ifdef SERIAL_DEBUG_REG +static u_char deb[32]; +const char *ModemIn[] = {"RBR","IER","IIR","LCR","MCR","LSR","MSR","SCR"}; +const char *ModemOut[] = {"THR","IER","FCR","LCR","MCR","LSR","MSR","SCR"}; +#endif + +static char *MInit_1 = "AT &F &C1 E0 &D2 L2 M1 S64=13\n\0"; +static char *MInit_2 = "AT+FCLASS=0 V1 S2=128 X1 \\V8\n\0"; +static char *MInit_3 = "AT %G0 %B2400 L0 M0 &G0 %E1 %L1 %M0 %C3 \\N3\n\0"; + + +static inline unsigned int serial_in(struct IsdnCardState *cs, int offset) +{ +#ifdef SERIAL_DEBUG_REG + u_int val = inb(cs->hw.elsa.base + 8 + offset); + sprintf(deb,"in %s %02x",ModemIn[offset], val); + debugl1(cs, deb); + return(val); +#else + return inb(cs->hw.elsa.base + 8 + offset); +#endif +} + +static inline unsigned int serial_inp(struct IsdnCardState *cs, int offset) +{ +#ifdef SERIAL_DEBUG_REG +#ifdef CONFIG_SERIAL_NOPAUSE_IO + u_int val = inb(cs->hw.elsa.base + 8 + offset); + sprintf(deb,"inp %s %02x",ModemIn[offset], val); +#else + u_int val = inb_p(cs->hw.elsa.base + 8 + offset); + sprintf(deb,"inP %s %02x",ModemIn[offset], val); +#endif + debugl1(cs, deb); + return(val); +#else +#ifdef CONFIG_SERIAL_NOPAUSE_IO + return inb(cs->hw.elsa.base + 8 + offset); +#else + return inb_p(cs->hw.elsa.base + 8 + offset); +#endif +#endif +} + +static inline void serial_out(struct IsdnCardState *cs, int offset, int value) +{ +#ifdef SERIAL_DEBUG_REG + sprintf(deb,"out %s %02x",ModemOut[offset], value); + debugl1(cs, deb); +#endif + outb(value, cs->hw.elsa.base + 8 + offset); +} + +static inline void serial_outp(struct IsdnCardState *cs, int offset, + int value) +{ +#ifdef SERIAL_DEBUG_REG +#ifdef CONFIG_SERIAL_NOPAUSE_IO + sprintf(deb,"outp %s %02x",ModemOut[offset], value); +#else + sprintf(deb,"outP %s %02x",ModemOut[offset], value); +#endif + debugl1(cs, deb); +#endif +#ifdef CONFIG_SERIAL_NOPAUSE_IO + outb(value, cs->hw.elsa.base + 8 + offset); +#else + outb_p(value, cs->hw.elsa.base + 8 + offset); +#endif +} + +/* + * This routine is called to set the UART divisor registers to match + * the specified baud rate for a serial port. + */ +static void change_speed(struct IsdnCardState *cs, int baud) +{ + int quot = 0, baud_base; + unsigned cval, fcr = 0; + int bits; + char tmp[32]; + unsigned long flags; + + + /* byte size and parity */ + cval = 0x03; bits = 10; + /* Determine divisor based on baud rate */ + baud_base = BASE_BAUD; + quot = baud_base / baud; + /* If the quotient is ever zero, default to 9600 bps */ + if (!quot) + quot = baud_base / 9600; + + /* Set up FIFO's */ + if ((baud_base / quot) < 2400) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; + serial_outp(cs, UART_FCR, fcr); + /* CTS flow control flag and modem status interrupts */ + cs->hw.elsa.IER &= ~UART_IER_MSI; + cs->hw.elsa.IER |= UART_IER_MSI; + serial_outp(cs, UART_IER, cs->hw.elsa.IER); + + sprintf(tmp,"modem quot=0x%x", quot); + debugl1(cs, tmp); + save_flags(flags); cli(); + serial_outp(cs, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ + serial_outp(cs, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_outp(cs, UART_DLM, quot >> 8); /* MS of divisor */ + serial_outp(cs, UART_LCR, cval); /* reset DLAB */ + restore_flags(flags); +} + +static int mstartup(struct IsdnCardState *cs) +{ + unsigned long flags; + int retval=0; + + + save_flags(flags); cli(); + + /* + * Clear the FIFO buffers and disable them + * (they will be reenabled in change_speed()) + */ + serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); + + /* + * At this point there's no way the LSR could still be 0xFF; + * if it is, then bail out, because there's likely no UART + * here. + */ + if (serial_inp(cs, UART_LSR) == 0xff) { + retval = -ENODEV; + goto errout; + } + + /* + * Clear the interrupt registers. + */ + (void) serial_inp(cs, UART_RX); + (void) serial_inp(cs, UART_IIR); + (void) serial_inp(cs, UART_MSR); + + /* + * Now, initialize the UART + */ + serial_outp(cs, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ + + cs->hw.elsa.MCR = 0; + cs->hw.elsa.MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2; + serial_outp(cs, UART_MCR, cs->hw.elsa.MCR); + + /* + * Finally, enable interrupts + */ + cs->hw.elsa.IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + serial_outp(cs, UART_IER, cs->hw.elsa.IER); /* enable interrupts */ + + /* + * And clear the interrupt registers again for luck. + */ + (void)serial_inp(cs, UART_LSR); + (void)serial_inp(cs, UART_RX); + (void)serial_inp(cs, UART_IIR); + (void)serial_inp(cs, UART_MSR); + + cs->hw.elsa.transcnt = cs->hw.elsa.transp = 0; + cs->hw.elsa.rcvcnt = cs->hw.elsa.rcvp =0; + + /* + * and set the speed of the serial port + */ + change_speed(cs, 57600*2); + cs->hw.elsa.MFlag = 1; +errout: + restore_flags(flags); + return retval; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void mshutdown(struct IsdnCardState *cs) +{ + unsigned long flags; + + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial ...."); +#endif + + save_flags(flags); cli(); /* Disable interrupts */ + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free the irq + * here so the queue might never be waken up + */ + + cs->hw.elsa.IER = 0; + serial_outp(cs, UART_IER, 0x00); /* disable all intrs */ + cs->hw.elsa.MCR &= ~UART_MCR_OUT2; + + /* disable break condition */ + serial_outp(cs, UART_LCR, serial_inp(cs, UART_LCR) & ~UART_LCR_SBC); + + cs->hw.elsa.MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + serial_outp(cs, UART_MCR, cs->hw.elsa.MCR); + + /* disable FIFO's */ + serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); + serial_inp(cs, UART_RX); /* read data port to reset things */ + + restore_flags(flags); +} + +inline int +write_modem(struct BCState *bcs) { + int ret=0; + struct IsdnCardState *cs = bcs->cs; + int count, len, fp, buflen; + long flags; + + if (!bcs->hw.hscx.tx_skb) + return 0; + if (bcs->hw.hscx.tx_skb->len <= 0) + return 0; + save_flags(flags); + cli(); + buflen = MAX_MODEM_BUF - cs->hw.elsa.transcnt; + len = MIN(buflen, bcs->hw.hscx.tx_skb->len); + fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp; + fp &= (MAX_MODEM_BUF -1); + count = MIN(len, MAX_MODEM_BUF - fp); + if (count < len) { + memcpy(cs->hw.elsa.transbuf + fp, skb_pull(bcs->hw.hscx.tx_skb, count), count); + cs->hw.elsa.transcnt += count; + ret = count; + count = len - count; + fp = 0; + } + memcpy(cs->hw.elsa.transbuf + fp, skb_pull(bcs->hw.hscx.tx_skb, count), count); + cs->hw.elsa.transcnt += count; + ret += count; + + if (cs->hw.elsa.transcnt && + !(cs->hw.elsa.IER & UART_IER_THRI)) { + cs->hw.elsa.IER |= UART_IER_THRI; + serial_outp(cs, UART_IER, cs->hw.elsa.IER); + } + restore_flags(flags); + return(ret); +} + +inline void +modem_fill(struct BCState *bcs) { + + if (bcs->hw.hscx.tx_skb) { + if (bcs->hw.hscx.tx_skb->len) { + write_modem(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, FREE_WRITE); + 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); + write_modem(bcs); + } else { + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + hscx_sched_event(bcs, B_XMTBUFREADY); + } +} + +static inline void receive_chars(struct IsdnCardState *cs, + int *status) +{ + unsigned char ch; + struct sk_buff *skb; + + do { + ch = serial_in(cs, UART_RX); + if (cs->hw.elsa.rcvcnt >= MAX_MODEM_BUF) + break; + cs->hw.elsa.rcvbuf[cs->hw.elsa.rcvcnt++] = ch; +#ifdef SERIAL_DEBUG_INTR + printk("DR%02x:%02x...", ch, *status); +#endif + if (*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE)) { + +#ifdef SERIAL_DEBUG_INTR + printk("handling exept...."); +#endif + } + *status = serial_inp(cs, UART_LSR); + } while (*status & UART_LSR_DR); + if (cs->hw.elsa.MFlag == 2) { + if (!(skb = dev_alloc_skb(cs->hw.elsa.rcvcnt))) + printk(KERN_WARNING "ElsaSER: receive out of memory\n"); + else { + memcpy(skb_put(skb, cs->hw.elsa.rcvcnt), cs->hw.elsa.rcvbuf, + cs->hw.elsa.rcvcnt); + skb_queue_tail(& cs->hw.elsa.bcs->rqueue, skb); + } + hscx_sched_event(cs->hw.elsa.bcs, B_RCVBUFREADY); + } else { + char tmp[128]; + char *t = tmp; + + t += sprintf(t, "modem read cnt %d", cs->hw.elsa.rcvcnt); + QuickHex(t, cs->hw.elsa.rcvbuf, cs->hw.elsa.rcvcnt); + debugl1(cs, tmp); + } + cs->hw.elsa.rcvcnt = 0; +} + +static inline void transmit_chars(struct IsdnCardState *cs, int *intr_done) +{ + int count; + char tmp[64]; + + sprintf(tmp, "transmit_chars: p(%x) cnt(%x)", cs->hw.elsa.transp, + cs->hw.elsa.transcnt); + debugl1(cs, tmp); + if (cs->hw.elsa.transcnt <= 0) { + cs->hw.elsa.IER &= ~UART_IER_THRI; + serial_out(cs, UART_IER, cs->hw.elsa.IER); + return; + } + + count = 16; + do { + serial_outp(cs, UART_TX, cs->hw.elsa.transbuf[cs->hw.elsa.transp++]); + if (cs->hw.elsa.transp >= MAX_MODEM_BUF) + cs->hw.elsa.transp=0; + if (--cs->hw.elsa.transcnt <= 0) + break; + } while (--count > 0); + if ((cs->hw.elsa.transcnt < WAKEUP_CHARS) && (cs->hw.elsa.MFlag==2)) + modem_fill(cs->hw.elsa.bcs); + +#ifdef SERIAL_DEBUG_INTR + printk("THRE..."); +#endif + if (intr_done) + *intr_done = 0; + if (cs->hw.elsa.transcnt <= 0) { + cs->hw.elsa.IER &= ~UART_IER_THRI; + serial_outp(cs, UART_IER, cs->hw.elsa.IER); + } +} + +#if 0 +static inline void check_modem_status(struct IsdnCardState *cs) +{ + int status; + struct async_struct *info = cs->hw.elsa.info; + struct async_icount *icount; + + status = serial_inp(info, UART_MSR); + + if (status & UART_MSR_ANY_DELTA) { + icount = &info->state->icount; + /* update input line counters */ + if (status & UART_MSR_TERI) + icount->rng++; + if (status & UART_MSR_DDSR) + icount->dsr++; + if (status & UART_MSR_DDCD) { + icount->dcd++; + } + if (status & UART_MSR_DCTS) + icount->cts++; +// wake_up_interruptible(&info->delta_msr_wait); + } + + if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { +#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) + printk("ttys%d CD now %s...", info->line, + (status & UART_MSR_DCD) ? "on" : "off"); +#endif + if (status & UART_MSR_DCD) +// wake_up_interruptible(&info->open_wait); +; + else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_CALLOUT_NOHUP))) { +#ifdef SERIAL_DEBUG_OPEN + printk("doing serial hangup..."); +#endif + if (info->tty) + tty_hangup(info->tty); + } + } +#if 0 + if (info->flags & ASYNC_CTS_FLOW) { + if (info->tty->hw_stopped) { + if (status & UART_MSR_CTS) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx start..."); +#endif + info->tty->hw_stopped = 0; + info->IER |= UART_IER_THRI; + serial_outp(info, UART_IER, info->IER); +// rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + return; + } + } else { + if (!(status & UART_MSR_CTS)) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx stop..."); +#endif + info->tty->hw_stopped = 1; + info->IER &= ~UART_IER_THRI; + serial_outp(info, UART_IER, info->IER); + } + } + } +#endif 0 +} +#endif + +static void rs_interrupt_elsa(int irq, struct IsdnCardState *cs) +{ + int status, iir, msr; + int pass_counter = 0; + u_char tmp[64]; + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_single(%d)...", irq); +#endif + + do { + status = serial_inp(cs, UART_LSR); + sprintf(tmp,"rs LSR %02x", status); + debugl1(cs, tmp); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(cs, &status); + if (status & UART_LSR_THRE) + transmit_chars(cs, 0); + if (pass_counter++ > RS_ISR_PASS_LIMIT) { + printk("rs_single loop break.\n"); + break; + } + iir = serial_inp(cs, UART_IIR); + sprintf(tmp,"rs IIR %02x", iir); + debugl1(cs, tmp); + if ((iir & 0xf) == 0) { + msr = serial_inp(cs, UART_MSR); + sprintf(tmp,"rs MSR %02x", msr); + debugl1(cs, tmp); + } + } while (!(iir & UART_IIR_NO_INT)); +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} + +extern int open_hscxstate(struct IsdnCardState *cs, int bc); +extern void modehscx(struct BCState *bcs, int mode, int bc); +extern void hscx_l2l1(struct PStack *st, int pr, void *arg); + +void +close_elsastate(struct BCState *bcs) +{ + struct sk_buff *skb; + +// modehscx(bcs, 0, 0); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + if (bcs->hw.hscx.rcvbuf) { + if (bcs->mode != L1_MODE_MODEM) + kfree(bcs->hw.hscx.rcvbuf); + bcs->hw.hscx.rcvbuf = NULL; + } + while ((skb = skb_dequeue(&bcs->rqueue))) { + dev_kfree_skb(skb, FREE_READ); + } + while ((skb = skb_dequeue(&bcs->squeue))) { + dev_kfree_skb(skb, FREE_WRITE); + } + if (bcs->hw.hscx.tx_skb) { + dev_kfree_skb(bcs->hw.hscx.tx_skb, FREE_WRITE); + bcs->hw.hscx.tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +void +modem_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + if (pr == (PH_DATA | REQUEST)) { + save_flags(flags); + cli(); + if (st->l1.bcs->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); + write_modem(st->l1.bcs); + } + } else if (pr == (PH_ACTIVATE | REQUEST)) { + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); + set_arcofi(st->l1.bcs->cs, st->l1.bc); + st->l1.bcs->cs->hw.elsa.MFlag=2; + } else if (pr == (PH_DEACTIVATE | REQUEST)) { + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + send_arcofi(st->l1.bcs->cs, ARCOFI_XOP_0, st->l1.bc, 0); + st->l1.bcs->cs->hw.elsa.MFlag=1; + } else { + printk(KERN_WARNING"ElsaSer: unknown pr %x\n", pr); + } +} + +void +modem_write_cmd(struct IsdnCardState *cs, u_char *buf, int len) { + int count, fp; + u_char *msg = buf; + long flags; + + if (!len) + return; + save_flags(flags); + cli(); + if (len > (MAX_MODEM_BUF - cs->hw.elsa.transcnt)) { + restore_flags(flags); + return; + } + fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp; + fp &= (MAX_MODEM_BUF -1); + count = MIN(len, MAX_MODEM_BUF - fp); + if (count < len) { + memcpy(cs->hw.elsa.transbuf + fp, msg, count); + cs->hw.elsa.transcnt += count; + msg += count; + count = len - count; + fp = 0; + } + memcpy(cs->hw.elsa.transbuf + fp, msg, count); + cs->hw.elsa.transcnt += count; + if (cs->hw.elsa.transcnt && + !(cs->hw.elsa.IER & UART_IER_THRI)) { + cs->hw.elsa.IER |= UART_IER_THRI; + serial_outp(cs, UART_IER, cs->hw.elsa.IER); + } + restore_flags(flags); +} + +void +modem_set_init(struct IsdnCardState *cs) { + long flags; + int timeout; + + save_flags(flags); + sti(); + modem_write_cmd(cs, MInit_1, strlen(MInit_1)); + timeout = 1000; + while(timeout-- && cs->hw.elsa.transcnt) + udelay(1000); + udelay(50000); + modem_write_cmd(cs, MInit_2, strlen(MInit_2)); + timeout = 1000; + while(timeout-- && cs->hw.elsa.transcnt) + udelay(1000); + udelay(50000); + modem_write_cmd(cs, MInit_3, strlen(MInit_3)); + timeout = 1000; + while(timeout-- && cs->hw.elsa.transcnt) + udelay(1000); + udelay(50000); + restore_flags(flags); +} + +int +setstack_elsa(struct PStack *st, struct BCState *bcs) +{ + + switch (st->l1.mode) { + case L1_MODE_HDLC: + case L1_MODE_TRANS: + if (open_hscxstate(st->l1.hardware, bcs->channel)) + return (-1); + st->l2.l2l1 = hscx_l2l1; + break; + case L1_MODE_MODEM: + bcs->mode = L1_MODE_MODEM; + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + bcs->hw.hscx.rcvbuf = bcs->cs->hw.elsa.rcvbuf; + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->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; + bcs->cs->hw.elsa.bcs = bcs; + st->l2.l2l1 = modem_l2l1; + break; + } + st->l1.bcs = bcs; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + +void +init_modem(struct IsdnCardState *cs) { + + cs->bcs[0].BC_SetStack = setstack_elsa; + cs->bcs[1].BC_SetStack = setstack_elsa; + cs->bcs[0].BC_Close = close_elsastate; + cs->bcs[1].BC_Close = close_elsastate; + if (!(cs->hw.elsa.rcvbuf = kmalloc(MAX_MODEM_BUF, + GFP_ATOMIC))) { + printk(KERN_WARNING + "Elsa: No modem mem hw.elsa.rcvbuf\n"); + return; + } + if (!(cs->hw.elsa.transbuf = kmalloc(MAX_MODEM_BUF, + GFP_ATOMIC))) { + printk(KERN_WARNING + "Elsa: No modem mem hw.elsa.transbuf\n"); + kfree(cs->hw.elsa.rcvbuf); + cs->hw.elsa.rcvbuf = NULL; + return; + } + if (mstartup(cs)) { + printk(KERN_WARNING "Elsa: problem startup modem\n"); + } +// modem_set_init(cs); +} + +void +release_modem(struct IsdnCardState *cs) { + + cs->hw.elsa.MFlag = 0; + if (cs->hw.elsa.transbuf) { + if (cs->hw.elsa.rcvbuf) { + mshutdown(cs); + kfree(cs->hw.elsa.rcvbuf); + cs->hw.elsa.rcvbuf = NULL; + } + kfree(cs->hw.elsa.transbuf); + cs->hw.elsa.transbuf = NULL; + } +} diff --git a/drivers/isdn/hisax/fsm.c b/drivers/isdn/hisax/fsm.c index d0bb2f14f0ea..f6b1894eac5e 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.4.2.4 1998/05/27 18:05:21 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,6 +7,24 @@ * Fritz Elfert * * $Log: fsm.c,v $ + * Revision 1.4.2.4 1998/05/27 18:05:21 keil + * HiSax 3.0 + * + * Revision 1.4.2.3 1998/03/07 23:15:20 tsbogend + * made HiSax working on Linux/Alpha + * + * Revision 1.4.2.2 1997/11/15 18:54:29 keil + * cosmetics + * + * Revision 1.4.2.1 1997/10/17 22:13:49 keil + * update to last hisax version + * + * Revision 1.6 1997/07/27 21:42:25 keil + * proof Fsm routines + * + * Revision 1.5 1997/06/26 11:10:05 keil + * Restart timer function added + * * Revision 1.4 1997/04/06 22:56:42 keil * Some cosmetic changes * @@ -26,19 +44,24 @@ #define FSM_TIMER_DEBUG 0 -void +HISAX_INITFUNC(void FsmNew(struct Fsm *fsm, - struct FsmNode *fnlist, int fncount) + struct FsmNode *fnlist, int fncount)) { int i; - fsm->jumpmatrix = (int *) - kmalloc(4L * fsm->state_count * fsm->event_count, GFP_KERNEL); - memset(fsm->jumpmatrix, 0, 4L * fsm->state_count * fsm->event_count); - - for (i = 0; i < fncount; i++) - fsm->jumpmatrix[fsm->state_count * fnlist[i].event + - fnlist[i].state] = (int) fnlist[i].routine; + fsm->jumpmatrix = (FSMFNPTR *) + kmalloc(sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count, GFP_KERNEL); + memset(fsm->jumpmatrix, 0, sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count); + + for (i = 0; i < fncount; i++) + if ((fnlist[i].state>=fsm->state_count) || (fnlist[i].event>=fsm->event_count)) { + printk(KERN_ERR "FsmNew Error line %d st(%ld/%ld) ev(%ld/%ld)\n", + i,(long)fnlist[i].state,(long)fsm->state_count, + (long)fnlist[i].event,(long)fsm->event_count); + } else + fsm->jumpmatrix[fsm->state_count * fnlist[i].event + + fnlist[i].state] = (FSMFNPTR) fnlist[i].routine; } void @@ -50,10 +73,15 @@ FsmFree(struct Fsm *fsm) int FsmEvent(struct FsmInst *fi, int event, void *arg) { - void (*r) (struct FsmInst *, int, void *); + FSMFNPTR r; char str[80]; - r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; + if ((fi->state>=fi->fsm->state_count) || (event >= fi->fsm->event_count)) { + printk(KERN_ERR "FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n", + (long)fi->state,(long)fi->fsm->state_count,event,(long)fi->fsm->event_count); + return(1); + } + r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; if (r) { if (fi->debug) { sprintf(str, "State %s Event %s", @@ -155,10 +183,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..10f0e27f9156 --- /dev/null +++ b/drivers/isdn/hisax/hfc_2bds0.c @@ -0,0 +1,1275 @@ +/* $Id: hfc_2bds0.c,v 1.1.2.6 1998/06/27 22:54:07 keil Exp $ + * + * specific routines for CCD's HFC 2BDS0 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hfc_2bds0.c,v $ + * Revision 1.1.2.6 1998/06/27 22:54:07 keil + * make 16.3c working with 3.0 + * + * Revision 1.1.2.5 1998/05/27 18:05:23 keil + * HiSax 3.0 + * + * Revision 1.1.2.4 1998/04/08 21:54:35 keil + * Fix "ll_trans ..." message + * + * Revision 1.1.2.3 1998/04/04 21:59:20 keil + * Fixed B-channel access + * + * Revision 1.1.2.2 1998/01/27 22:40:35 keil + * fixed IRQ latency, B-channel selection and more + * + * Revision 1.1.2.1 1998/01/11 22:54:00 keil + * Teles 16.3c (HFC 2BDS0) first version + * + * + */ +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_2bds0.h" +#include "isdnl1.h" +#include +/* +#define KDEBUG_DEF +#include "kdebug.h" +*/ + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +static void +dummyf(struct IsdnCardState *cs, u_char * data, int size) +{ + printk(KERN_WARNING "HiSax: hfcd dummy fifo called\n"); +} + +static inline u_char +ReadReg(struct IsdnCardState *cs, int data, u_char reg) +{ + register u_char ret; + + if (data) { + if (cs->hw.hfcD.cip != reg) { + cs->hw.hfcD.cip = reg; + byteout(cs->hw.hfcD.addr | 1, reg); + } + ret = bytein(cs->hw.hfcD.addr); +#if HFC_REG_DEBUG + if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) { + 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 { + SET_SKB_FREE(skb); + ptr = skb_put(skb, count - 3); + idx = 0; + cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel); + cli(); + while (idx < (count - 3)) { + cli(); + if (!WaitNoBusy(cs)) + break; + *ptr = ReadReg(cs, HFCD_DATA_NODEB, cip); + sti(); + ptr++; + idx++; + } + if (idx != count - 3) { + sti(); + debugl1(cs, "RFIFO BUSY error"); + printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel); + dev_kfree_skb(skb, FREE_READ); + skb = NULL; + } else { + cli(); + WaitNoBusy(cs); + chksum = (ReadReg(cs, HFCD_DATA, cip) << 8); + WaitNoBusy(cs); + chksum += ReadReg(cs, HFCD_DATA, cip); + WaitNoBusy(cs); + stat = ReadReg(cs, HFCD_DATA, cip); + sti(); + if (cs->debug & L1_DEB_HSCX) { + 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, FREE_READ); + skb = NULL; + } + } + } + sti(); + WaitForBusy(cs); + cli(); + WaitNoBusy(cs); + stat = ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F2_INC | + HFCB_REC | HFCB_CHANNEL(bcs->channel)); + sti(); + WaitForBusy(cs); + restore_flags(flags); + return (skb); +} + +static void +hfc_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + long flags; + int idx, fcnt; + int count; + u_char cip; + 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(%ld/%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, FREE_WRITE); + 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 | REQUEST): + 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 | INDICATION): + 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 | REQUEST): + 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 | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (PH_ACTIVATE | REQUEST): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + mode_2bs0(st->l1.bcs, st->l1.mode, st->l1.bc); + st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); + break; + case (PH_DEACTIVATE | REQUEST): + 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; + } +} + +void +close_2bs0(struct BCState *bcs) +{ + mode_2bs0(bcs, 0, 0); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + discard_queue(&bcs->rqueue); + discard_queue(&bcs->squeue); + if (bcs->hw.hfc.tx_skb) { + dev_kfree_skb(bcs->hw.hfc.tx_skb, FREE_WRITE); + 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); +} + +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; + setstack_manager(st); + bcs->st = st; + return (0); +} + +static void +hfcd_bh(struct IsdnCardState *cs) +{ +/* struct PStack *stptr; +*/ + if (!cs) + return; +#if 0 + if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy cleared"); + stptr = cs->stlist; + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL); + stptr = stptr->next; + } + } +#endif + if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) { + switch (cs->ph_state) { + case (0): + l1_msg(cs, HW_RESET | INDICATION, NULL); + break; + case (3): + l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL); + break; + case (8): + l1_msg(cs, HW_RSYNC | INDICATION, NULL); + break; + case (6): + l1_msg(cs, HW_INFO2 | INDICATION, NULL); + break; + case (7): + l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL); + break; + default: + break; + } + } + if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) + DChannel_proc_rcv(cs); + if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) + DChannel_proc_xmt(cs); +} + +void +sched_event_D(struct IsdnCardState *cs, int event) +{ + test_and_set_bit(event, &cs->event); + queue_task(&cs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static +int receive_dmsg(struct IsdnCardState *cs) +{ + struct sk_buff *skb; + long flags; + int idx; + int rcnt, z1, z2; + u_char stat, cip, f1, f2; + int chksum; + int count=5; + u_char *ptr; + 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))) { + SET_SKB_FREE(skb); + ptr = skb_put(skb, rcnt - 3); + while (idx < (rcnt - 3)) { + cli(); + if (!(WaitNoBusy(cs))) + break; + *ptr = ReadReg(cs, HFCD_DATA_NODEB, cip); + sti(); + idx++; + ptr++; + } + if (idx != (rcnt - 3)) { + sti(); + debugl1(cs, "RFIFO D BUSY error"); + printk(KERN_WARNING "HFC DFIFO channel BUSY Error\n"); + dev_kfree_skb(skb, FREE_READ); + skb = NULL; + } else { + cli(); + WaitNoBusy(cs); + chksum = (ReadReg(cs, HFCD_DATA, cip) << 8); + WaitNoBusy(cs); + chksum += ReadReg(cs, HFCD_DATA, cip); + WaitNoBusy(cs); + stat = ReadReg(cs, HFCD_DATA, cip); + sti(); + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "empty_dfifo chksum %x stat %x", + chksum, stat); + debugl1(cs, tmp); + } + if (stat) { + debugl1(cs, "FIFO CRC error"); + dev_kfree_skb(skb, FREE_READ); + skb = NULL; + } else { + skb_queue_tail(&cs->rq, skb); + sched_event_D(cs, D_RCVBUFREADY); + } + } + } else + printk(KERN_WARNING "HFC: D receive out of memory\n"); + sti(); + WaitForBusy(cs); + cip = HFCD_FIFO | HFCD_F2_INC | HFCD_REC; + cli(); + WaitNoBusy(cs); + stat = ReadReg(cs, HFCD_DATA, cip); + sti(); + WaitForBusy(cs); + cip = HFCD_FIFO | HFCD_F2 | HFCD_REC; + cli(); + WaitNoBusy(cs); + f2 = cs->readisac(cs, cip) & 0xf; + } + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + restore_flags(flags); + return(1); +} + +static void +hfc_fill_dfifo(struct IsdnCardState *cs) +{ + long flags; + int idx, fcnt; + int count; + u_char cip; + 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(%ld/%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, FREE_WRITE); + cs->tx_skb = NULL; + sti(); + WaitForBusy(cs); + restore_flags(flags); + return; +} + +static +struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel) +{ + if (cs->bcs[0].mode && (cs->bcs[0].channel == channel)) + return(&cs->bcs[0]); + else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel)) + return(&cs->bcs[1]); + else + return(NULL); +} + +void +hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val) +{ + u_char exval; + struct BCState *bcs; + 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, FREE_WRITE); + cs->tx_cnt = 0; + cs->tx_skb = NULL; + } + if ((cs->tx_skb = skb_dequeue(&cs->sq))) { + cs->tx_cnt = 0; + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + debugl1(cs, "hfc_fill_dfifo irq blocked"); + } + } else + sched_event_D(cs, D_XMTBUFREADY); + } + afterXPR: + if (cs->hw.hfcD.int_s1 && count--) { + val = cs->hw.hfcD.int_s1; + cs->hw.hfcD.int_s1 = 0; + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "HFCD irq %x loop %d", val, 15-count); + debugl1(cs, tmp); + } + } else + val = 0; + restore_flags(flags); + } +} + +static void +HFCD_l1hw(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + struct sk_buff *skb = arg; + char str[64]; + switch (pr) { + case (PH_DATA | REQUEST): + 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 | INDICATION): + if (cs->tx_skb) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, " l2l1 tx_skb exist this shouldn't happen"); + skb_queue_tail(&cs->sq, skb); + break; + } + if ((cs->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 | REQUEST): +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + debugl1(cs, "-> PH_REQUEST_PULL"); +#endif + if (!cs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (HW_RESET | REQUEST): + cs->writeisac(cs, HFCD_STATES, HFCD_LOAD_STATE | 3); /* HFC ST 3 */ + udelay(6); + cs->writeisac(cs, HFCD_STATES, 3); /* HFC ST 2 */ + cs->hw.hfcD.mst_m |= HFCD_MASTER; + cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m); + cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION); + l1_msg(cs, HW_POWERUP | CONFIRM, NULL); + break; + case (HW_ENABLE | REQUEST): + cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION); + break; + case (HW_DEACTIVATE | REQUEST): + cs->hw.hfcD.mst_m &= ~HFCD_MASTER; + cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m); + break; + case (HW_INFO3 | REQUEST): + cs->hw.hfcD.mst_m |= HFCD_MASTER; + cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m); + break; +#if 0 + case (HW_TESTLOOP | REQUEST): + u_char val = 0; + if (1 & (int) arg) + val |= 0x0c; + if (2 & (int) arg) + val |= 0x3; + if (test_bit(HW_IOM1, &cs->HW_Flags)) { + /* IOM 1 Mode */ + if (!val) { + cs->writeisac(cs, ISAC_SPCR, 0xa); + cs->writeisac(cs, ISAC_ADF1, 0x2); + } else { + cs->writeisac(cs, ISAC_SPCR, val); + cs->writeisac(cs, ISAC_ADF1, 0xa); + } + } else { + /* IOM 2 Mode */ + cs->writeisac(cs, ISAC_SPCR, val); + if (val) + cs->writeisac(cs, ISAC_ADF1, 0x8); + else + cs->writeisac(cs, ISAC_ADF1, 0x0); + } + break; +#endif + default: + if (cs->debug & L1_DEB_WARN) { + sprintf(str, "hfcd_l1hw unknown pr %4x", pr); + debugl1(cs, str); + } + break; + } +} + +void +setstack_hfcd(struct PStack *st, struct IsdnCardState *cs) +{ + st->l1.l1hw = HFCD_l1hw; +} + +static void +hfc_dbusy_timer(struct IsdnCardState *cs) +{ +#if 0 + struct PStack *stptr; + if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy"); + test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); + stptr = cs->stlist; + + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL); + stptr = stptr->next; + } + } +#endif +} + +__initfunc(unsigned int +*init_send_hfcd(int cnt)) +{ + int i, *send; + + if (!(send = kmalloc(cnt * sizeof(unsigned int), GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for hfcd.send\n"); + return(NULL); + } + for (i = 0; i < cnt; i++) + send[i] = 0x1fff; + return(send); +} + +__initfunc(void +init2bds0(struct IsdnCardState *cs)) +{ + cs->setstack_d = setstack_hfcd; + cs->dbusytimer.function = (void *) hfc_dbusy_timer; + cs->dbusytimer.data = (long) cs; + init_timer(&cs->dbusytimer); + cs->tqueue.routine = (void *) (void *) hfcd_bh; + if (!cs->hw.hfcD.send) + cs->hw.hfcD.send = init_send_hfcd(16); + if (!cs->bcs[0].hw.hfc.send) + cs->bcs[0].hw.hfc.send = init_send_hfcd(32); + if (!cs->bcs[1].hw.hfc.send) + cs->bcs[1].hw.hfc.send = init_send_hfcd(32); + cs->BC_Send_Data = &hfc_send_data; + cs->bcs[0].BC_SetStack = setstack_2b; + cs->bcs[1].BC_SetStack = setstack_2b; + cs->bcs[0].BC_Close = close_2bs0; + cs->bcs[1].BC_Close = close_2bs0; + mode_2bs0(cs->bcs, 0, 0); + mode_2bs0(cs->bcs + 1, 0, 1); +} + +void +release2bds0(struct IsdnCardState *cs) +{ + if (cs->bcs[0].hw.hfc.send) { + kfree(cs->bcs[0].hw.hfc.send); + cs->bcs[0].hw.hfc.send = NULL; + } + if (cs->bcs[1].hw.hfc.send) { + kfree(cs->bcs[1].hw.hfc.send); + cs->bcs[1].hw.hfc.send = NULL; + } + if (cs->hw.hfcD.send) { + kfree(cs->hw.hfcD.send); + cs->hw.hfcD.send = NULL; + } +} diff --git a/drivers/isdn/hisax/hfc_2bds0.h b/drivers/isdn/hisax/hfc_2bds0.h new file mode 100644 index 000000000000..b435a6c6185b --- /dev/null +++ b/drivers/isdn/hisax/hfc_2bds0.h @@ -0,0 +1,133 @@ +/* $Id: hfc_2bds0.h,v 1.1.2.2 1998/01/27 22:41:36 keil Exp $ + + * specific defines for CCD's HFC 2BDS0 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hfc_2bds0.h,v $ + * Revision 1.1.2.2 1998/01/27 22:41:36 keil + * add set_cs_func() + * + * Revision 1.1.2.1 1998/01/11 22:54:02 keil + * Teles 16.3c (HFC 2BDS0) first version + * + * + */ + +#define HFCD_CIRM 0x18 +#define HFCD_CTMT 0x19 +#define HFCD_INT_M1 0x1A +#define HFCD_INT_M2 0x1B +#define HFCD_INT_S1 0x1E +#define HFCD_STAT 0x1C +#define HFCD_STAT_DISB 0x1D +#define HFCD_STATES 0x30 +#define HFCD_SCTRL 0x31 +#define HFCD_TEST 0x32 +#define HFCD_SQ 0x34 +#define HFCD_CLKDEL 0x37 +#define HFCD_MST_MODE 0x2E +#define HFCD_CONN 0x2F + +#define HFCD_FIFO 0x80 +#define HFCD_Z1 0x10 +#define HFCD_Z2 0x18 +#define HFCD_Z_LOW 0x00 +#define HFCD_Z_HIGH 0x04 +#define HFCD_F1_INC 0x12 +#define HFCD_FIFO_IN 0x16 +#define HFCD_F1 0x1a +#define HFCD_F2 0x1e +#define HFCD_F2_INC 0x22 +#define HFCD_FIFO_OUT 0x26 +#define HFCD_REC 0x01 +#define HFCD_SEND 0x00 + +#define HFCB_FIFO 0x80 +#define HFCB_Z1 0x00 +#define HFCB_Z2 0x08 +#define HFCB_Z_LOW 0x00 +#define HFCB_Z_HIGH 0x04 +#define HFCB_F1_INC 0x28 +#define HFCB_FIFO_IN 0x2c +#define HFCB_F1 0x30 +#define HFCB_F2 0x34 +#define HFCB_F2_INC 0x38 +#define HFCB_FIFO_OUT 0x3c +#define HFCB_REC 0x01 +#define HFCB_SEND 0x00 +#define HFCB_B1 0x00 +#define HFCB_B2 0x02 +#define HFCB_CHANNEL(ch) (ch ? HFCB_B2 : HFCB_B1) + +#define HFCD_STATUS 0 +#define HFCD_DATA 1 +#define HFCD_DATA_NODEB 2 + +/* Status (READ) */ +#define HFCD_BUSY 0x01 +#define HFCD_BUSY_NBUSY 0x04 +#define HFCD_TIMER_ELAP 0x10 +#define HFCD_STATINT 0x20 +#define HFCD_FRAMEINT 0x40 +#define HFCD_ANYINT 0x80 + +/* CTMT (Write) */ +#define HFCD_CLTIMER 0x80 +#define HFCD_TIM25 0x00 +#define HFCD_TIM50 0x08 +#define HFCD_TIM400 0x10 +#define HFCD_TIM800 0x18 +#define HFCD_AUTO_TIMER 0x20 +#define HFCD_TRANSB2 0x02 +#define HFCD_TRANSB1 0x01 + +/* CIRM (Write) */ +#define HFCD_RESET 0x08 +#define HFCD_MEM8K 0x10 +#define HFCD_INTA 0x01 +#define HFCD_INTB 0x02 +#define HFCD_INTC 0x03 +#define HFCD_INTD 0x04 +#define HFCD_INTE 0x05 +#define HFCD_INTF 0x06 + +/* INT_M1;INT_S1 */ +#define HFCD_INTS_B1TRANS 0x01 +#define HFCD_INTS_B2TRANS 0x02 +#define HFCD_INTS_DTRANS 0x04 +#define HFCD_INTS_B1REC 0x08 +#define HFCD_INTS_B2REC 0x10 +#define HFCD_INTS_DREC 0x20 +#define HFCD_INTS_L1STATE 0x40 +#define HFCD_INTS_TIMER 0x80 + +/* INT_M2 */ +#define HFCD_IRQ_ENABLE 0x08 + +/* STATES */ +#define HFCD_LOAD_STATE 0x10 +#define HFCD_ACTIVATE 0x20 +#define HFCD_DO_ACTION 0x40 + +/* HFCD_MST_MODE */ +#define HFCD_MASTER 0x01 + +/* HFCD_SCTRL */ +#define SCTRL_B1_ENA 0x01 +#define SCTRL_B2_ENA 0x02 +#define SCTRL_LOW_PRIO 0x08 +#define SCTRL_SQ_ENA 0x10 +#define SCTRL_TEST 0x20 +#define SCTRL_NONE_CAP 0x40 +#define SCTRL_PWR_DOWN 0x80 + +/* HFCD_TEST */ +#define HFCD_AUTO_AWAKE 0x01 + +extern void main_irq_2bds0(struct BCState *bcs); +extern void init2bds0(struct IsdnCardState *cs); +extern void release2bds0(struct IsdnCardState *cs); +extern void hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val); +extern void set_cs_func(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/hfc_2bs0.c b/drivers/isdn/hisax/hfc_2bs0.c new file mode 100644 index 000000000000..08209e4a0d9d --- /dev/null +++ b/drivers/isdn/hisax/hfc_2bs0.c @@ -0,0 +1,608 @@ +/* $Id: hfc_2bs0.c,v 1.1.2.5 1998/05/27 18:05:27 keil Exp $ + + * specific routines for CCD's HFC 2BS0 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hfc_2bs0.c,v $ + * Revision 1.1.2.5 1998/05/27 18:05:27 keil + * HiSax 3.0 + * + * Revision 1.1.2.4 1998/04/08 21:54:38 keil + * Fix "ll_trans ..." message + * + * Revision 1.1.2.3 1998/04/04 21:59:23 keil + * Fixed B-channel access + * + * Revision 1.1.2.2 1997/11/15 18:54:27 keil + * cosmetics + * + * Revision 1.1.2.1 1997/10/17 22:10:41 keil + * new files on 2.0 + * + * Revision 1.1 1997/09/11 17:31:33 keil + * Common part for HFC 2BS0 based cards + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_2bs0.h" +#include "isac.h" +#include "isdnl1.h" +#include + +static inline int +WaitForBusy(struct IsdnCardState *cs) +{ + int to = 130; + long flags; + u_char val; + + save_flags(flags); + cli(); + while (!(cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) { + val = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2 | + (cs->hw.hfc.cip & 3)); + udelay(1); + to--; + } + restore_flags(flags); + if (!to) { + printk(KERN_WARNING "HiSax: waitforBusy timeout\n"); + return (0); + } else + return (to); +} + +static inline int +WaitNoBusy(struct IsdnCardState *cs) +{ + int to = 125; + + while ((cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) { + udelay(1); + to--; + } + if (!to) { + printk(KERN_WARNING "HiSax: waitforBusy timeout\n"); + return (0); + } else + return (to); +} + +int +GetFreeFifoBytes(struct BCState *bcs) +{ + int s; + + if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2) + return (bcs->cs->hw.hfc.fifosize); + s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2]; + if (s <= 0) + s += bcs->cs->hw.hfc.fifosize; + s = bcs->cs->hw.hfc.fifosize - s; + return (s); +} + +int +ReadZReg(struct BCState *bcs, u_char reg) +{ + int val; + + WaitNoBusy(bcs->cs); + val = 256 * bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_HIGH); + WaitNoBusy(bcs->cs); + val += bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_LOW); + return (val); +} + +void +hfc_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void +hfc_clear_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + long flags; + int idx, cnt; + int rcnt, z1, z2; + u_char cip, f1, f2; + 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 { + SET_SKB_FREE(skb); + ptr = skb_put(skb, count - 3); + idx = 0; + cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel); + while ((idx < count - 3) && WaitNoBusy(cs)) { + *ptr++ = cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip); + idx++; + } + if (idx != count - 3) { + debugl1(cs, "RFIFO BUSY error"); + printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel); + dev_kfree_skb(skb, FREE_READ); + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); + return (NULL); + } + WaitNoBusy(cs); + chksum = (cs->BC_Read_Reg(cs, HFC_DATA, cip) << 8); + WaitNoBusy(cs); + chksum += cs->BC_Read_Reg(cs, HFC_DATA, cip); + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, cip); + if (cs->debug & L1_DEB_HSCX) { + 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, FREE_READ); + skb = NULL; + } + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); + } + return (skb); +} + +static void +hfc_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + long flags; + int idx, fcnt; + int count; + u_char cip; + 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(%ld/%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, FREE_WRITE); + 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; + bcs->channel = bc; + switch (mode) { + case (L1_MODE_NULL): + if (bc) + cs->hw.hfc.isac_spcr &= ~0x03; + else + cs->hw.hfc.isac_spcr &= ~0x0c; + break; + case (L1_MODE_TRANS): + if (bc) { + cs->hw.hfc.ctmt |= 1; + cs->hw.hfc.isac_spcr &= ~0x03; + cs->hw.hfc.isac_spcr |= 0x02; + } else { + cs->hw.hfc.ctmt |= 2; + cs->hw.hfc.isac_spcr &= ~0x0c; + cs->hw.hfc.isac_spcr |= 0x08; + } + break; + case (L1_MODE_HDLC): + if (bc) { + cs->hw.hfc.ctmt &= ~1; + cs->hw.hfc.isac_spcr &= ~0x03; + cs->hw.hfc.isac_spcr |= 0x02; + } else { + cs->hw.hfc.ctmt &= ~2; + cs->hw.hfc.isac_spcr &= ~0x0c; + cs->hw.hfc.isac_spcr |= 0x08; + } + break; + } + cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt); + cs->writeisac(cs, ISAC_SPCR, cs->hw.hfc.isac_spcr); + if (mode) + hfc_clear_fifo(bcs); +} + +static void +hfc_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA | REQUEST): + save_flags(flags); + cli(); + if (st->l1.bcs->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 | INDICATION): + 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 | REQUEST): + 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 | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (PH_ACTIVATE | REQUEST): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + mode_hfc(st->l1.bcs, st->l1.mode, st->l1.bc); + st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); + break; + case (PH_DEACTIVATE | REQUEST): + 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; + } +} + + +void +close_hfcstate(struct BCState *bcs) +{ + mode_hfc(bcs, 0, 0); + if (test_bit(BC_FLG_INIT, &bcs->Flag)) { + discard_queue(&bcs->rqueue); + discard_queue(&bcs->squeue); + if (bcs->hw.hfc.tx_skb) { + dev_kfree_skb(bcs->hw.hfc.tx_skb, FREE_WRITE); + 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); +} + +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; + 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..1f0a3dc95bd7 --- /dev/null +++ b/drivers/isdn/hisax/hfc_2bs0.h @@ -0,0 +1,65 @@ +/* $Id: hfc_2bs0.h,v 1.1.2.1 1997/10/17 22:10:43 keil Exp $ + + * specific defines for CCD's HFC 2BS0 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hfc_2bs0.h,v $ + * Revision 1.1.2.1 1997/10/17 22:10:43 keil + * new files on 2.0 + * + * Revision 1.1 1997/09/11 17:31:34 keil + * Common part for HFC 2BS0 based cards + * + * + */ + +#define HFC_CTMT 0xe0 +#define HFC_CIRM 0xc0 +#define HFC_CIP 0x80 +#define HFC_Z1 0x00 +#define HFC_Z2 0x08 +#define HFC_Z_LOW 0x00 +#define HFC_Z_HIGH 0x04 +#define HFC_F1_INC 0x28 +#define HFC_FIFO_IN 0x2c +#define HFC_F1 0x30 +#define HFC_F2 0x34 +#define HFC_F2_INC 0x38 +#define HFC_FIFO_OUT 0x3c +#define HFC_B1 0x00 +#define HFC_B2 0x02 +#define HFC_REC 0x01 +#define HFC_SEND 0x00 +#define HFC_CHANNEL(ch) (ch ? HFC_B2 : HFC_B1) + +#define HFC_STATUS 0 +#define HFC_DATA 1 +#define HFC_DATA_NODEB 2 + +/* Status (READ) */ +#define HFC_BUSY 0x01 +#define HFC_TIMINT 0x02 +#define HFC_EXTINT 0x04 + +/* CTMT (Write) */ +#define HFC_CLTIMER 0x10 +#define HFC_TIM50MS 0x08 +#define HFC_TIMIRQE 0x04 +#define HFC_TRANSB2 0x02 +#define HFC_TRANSB1 0x01 + +/* CIRM (Write) */ +#define HFC_RESET 0x08 +#define HFC_MEM8K 0x10 +#define HFC_INTA 0x01 +#define HFC_INTB 0x02 +#define HFC_INTC 0x03 +#define HFC_INTD 0x04 +#define HFC_INTE 0x05 +#define HFC_INTF 0x06 + +extern void main_irq_hfc(struct BCState *bcs); +extern void inithfc(struct IsdnCardState *cs); +extern void releasehfc(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h index efe7d53290df..92ad7a62eefb 100644 --- a/drivers/isdn/hisax/hisax.h +++ b/drivers/isdn/hisax/hisax.h @@ -1,52 +1,68 @@ -/* $Id: hisax.h,v 1.13 1997/04/06 22:54:12 keil Exp $ +/* $Id: hisax.h,v 1.13.2.11 1998/05/27 18:05:30 keil Exp $ * Basic declarations, defines and prototypes * * $Log: hisax.h,v $ - * Revision 1.13 1997/04/06 22:54:12 keil - * Using SKB's + * Revision 1.13.2.11 1998/05/27 18:05:30 keil + * HiSax 3.0 * - * Revision 1.12 1997/03/23 21:45:45 keil - * Add support for ELSA PCMCIA + * Revision 1.13.2.10 1998/04/11 18:43:16 keil + * New cards * - * Revision 1.11 1997/02/11 01:36:02 keil - * New Param structure + * Revision 1.13.2.9 1998/03/07 23:15:21 tsbogend + * made HiSax working on Linux/Alpha * - * Revision 1.10 1997/02/09 00:23:52 keil - * new interface handling, one interface per card + * Revision 1.13.2.8 1998/02/11 14:23:10 keil + * support for Dr Neuhaus Niccy PnP and PCI * - * Revision 1.9 1997/01/27 23:18:44 keil - * prototype for releasestack_isdnl3 + * Revision 1.13.2.7 1998/02/09 11:21:22 keil + * Sedlbauer PCMCIA support from Marcus Niemann * - * Revision 1.8 1997/01/27 16:02:37 keil - * new cards, callc timers, HZDELAY macro, HiSax_getrev prototype + * Revision 1.13.2.6 1998/02/03 23:16:12 keil + * german AOC * - * Revision 1.7 1997/01/21 22:22:14 keil - * changes for 2.0; Elsa Quickstep support + * Revision 1.13.2.5 1998/01/27 22:42:42 keil + * changes for new teles 16.3c and dynalink ---> asuscom * - * Revision 1.6 1997/01/04 13:48:28 keil - * primitiv for MDL_REMOVE added + * Revision 1.13.2.4 1998/01/11 22:55:17 keil + * 16.3c support * - * Revision 1.5 1996/12/08 19:49:19 keil - * Monitor channel support + * Revision 1.13.2.3 1997/11/27 12:31:59 keil + * Working netjet driver * - * Revision 1.4 1996/11/18 15:35:39 keil - * some changes for ELSA cards + * Revision 1.13.2.2 1997/11/15 18:55:43 keil + * New init, new cards * - * Revision 1.3 1996/11/05 19:37:23 keil - * using config.h + * Revision 1.13.2.1 1997/10/17 22:13:51 keil + * update to last hisax version * - * Revision 1.2 1996/10/27 22:21:52 keil - * CallFlags for broadcast messages + * Revision 2.6 1997/09/11 17:25:51 keil + * Add new cards * - * Revision 1.1 1996/10/13 20:03:46 keil - * Initial revision + * Revision 2.5 1997/08/03 14:36:31 keil + * Implement RESTART procedure * + * Revision 2.4 1997/07/31 19:25:20 keil + * PTP_DATA_LINK support * + * Revision 2.3 1997/07/31 11:50:17 keil + * ONE TEI and FIXED TEI handling + * + * Revision 2.2 1997/07/30 17:13:02 keil + * more changes for 'One TEI per card' + * + * Revision 2.1 1997/07/27 21:45:13 keil + * new main structures + * + * Revision 2.0 1997/06/26 11:06:27 keil + * New card and L1 interface. + * Eicon.Diehl Diva and Dynalink IS64PH support + * + * old changes removed KKe * */ -#include #include +#include #include #include #include @@ -64,142 +80,110 @@ #include #include #include +#include + +#define REQUEST 0 +#define CONFIRM 1 +#define INDICATION 2 +#define RESPONSE 3 + +#define HW_ENABLE 0x0000 +#define HW_RESET 0x0004 +#define HW_POWERUP 0x0008 +#define HW_ACTIVATE 0x0010 +#define HW_DEACTIVATE 0x0018 +#define HW_INFO2 0x0020 +#define HW_INFO3 0x0030 +#define HW_INFO4_P8 0x0040 +#define HW_INFO4_P10 0x0048 +#define HW_RSYNC 0x0060 +#define HW_TESTLOOP 0x0070 +#define CARD_RESET 0x00F0 +#define CARD_SETIRQ 0x00F1 +#define CARD_INIT 0x00F2 +#define CARD_RELEASE 0x00F3 +#define CARD_TEST 0x00F4 +#define CARD_AUX_IND 0x00F5 + +#define PH_ACTIVATE 0x0100 +#define PH_DEACTIVATE 0x0110 +#define PH_DATA 0x0120 +#define PH_PULL 0x0130 +#define PH_TESTLOOP 0x0140 +#define PH_PAUSE 0x0150 +#define MPH_ACTIVATE 0x0180 +#define MPH_DEACTIVATE 0x0190 +#define MPH_INFORMATION 0x01A0 + +#define DL_ESTABLISH 0x0200 +#define DL_RELEASE 0x0210 +#define DL_DATA 0x0220 +#define DL_FLUSH 0x0224 +#define DL_UNIT_DATA 0x0230 +#define MDL_ASSIGN 0x0280 +#define MDL_REMOVE 0x0284 +#define MDL_ERROR 0x0288 +#define MDL_INFO_SETUP 0x02E0 +#define MDL_INFO_CONN 0x02E4 +#define MDL_INFO_REL 0x02E8 + + +#define CC_SETUP 0x0300 +#define CC_RESUME 0x0304 +#define CC_MORE_INFO 0x0310 +#define CC_IGNORE 0x0320 +#define CC_REJECT 0x0324 +#define CC_SETUP_COMPL 0x0330 +#define CC_PROCEEDING 0x0340 +#define CC_ALERTING 0x0344 +#define CC_CONNECT 0x0350 +#define CC_CHARGE 0x0354 +#define CC_DISCONNECT 0x0360 +#define CC_RELEASE 0x0368 +#define CC_SUSPEND 0x0370 +#define CC_T303 0x0383 +#define CC_T304 0x0384 +#define CC_T305 0x0385 +#define CC_T308_1 0x0388 +#define CC_T308_2 0x0389 +#define CC_T310 0x0390 +#define CC_T313 0x0393 +#define CC_T318 0x0398 +#define CC_T319 0x0399 +#define CC_NOSETUP_RSP 0x03E0 +#define CC_SETUP_ERR 0x03E1 +#define CC_SUSPEND_ERR 0x03E2 +#define CC_RESUME_ERR 0x03E3 +#define CC_CONNECT_ERR 0x03E4 +#define CC_RELEASE_ERR 0x03E5 +#define CC_DLRL 0x03F0 +#define CC_RESTART 0x03F4 -#define PH_ACTIVATE 1 -#define PH_DATA 2 -#define PH_DEACTIVATE 3 - -#define MDL_ASSIGN 4 -#define DL_UNIT_DATA 5 -#define SC_STARTUP 6 -#define CC_ESTABLISH 7 -#define DL_ESTABLISH 8 -#define DL_DATA 9 -#define CC_S_STATUS_ENQ 10 - -#define CC_CONNECT 15 -#define CC_CONNECT_ACKNOWLEDGE 16 -#define CO_EOF 17 -#define SC_DISCONNECT 18 -#define CO_DTMF 19 -#define DL_RELEASE 20 -#define DL_FLUSH 21 - -#define CO_ALARM 22 -#define CC_REJECT 23 - -#define CC_SETUP_REQ 24 -#define CC_SETUP_CNF 25 -#define CC_SETUP_IND 26 -#define CC_SETUP_RSP 27 -#define CC_SETUP_COMPLETE_IND 28 - -#define CC_DISCONNECT_REQ 29 -#define CC_DISCONNECT_IND 30 - -#define CC_RELEASE_CNF 31 -#define CC_RELEASE_IND 32 -#define CC_RELEASE_REQ 33 - -#define CC_REJECT_REQ 34 - -#define CC_PROCEEDING_IND 35 - -#define CC_DLRL 36 -#define CC_DLEST 37 - -#define CC_ALERTING_REQ 38 -#define CC_ALERTING_IND 39 - -#define DL_STOP 40 -#define DL_START 41 - -#define MDL_NOTEIPROC 46 - -#define LC_ESTABLISH 47 -#define LC_RELEASE 48 - -#define PH_REQUEST_PULL 49 -#define PH_PULL_ACK 50 -#define PH_DATA_PULLED 51 -#define CC_INFO_CHARGE 52 - -#define CC_MORE_INFO 53 -#define CC_IGNORE 54 - -#define MDL_REMOVE 56 -#define MDL_VERIFY 57 - -#define CC_T303 60 -#define CC_T304 61 -#define CC_T305 62 -#define CC_T308_1 64 -#define CC_T308_2 65 -#define CC_T310 66 -#define CC_T313 67 -#define CC_T318 68 -#define CC_T319 69 - -#define CC_NOSETUP_RSP_ERR 70 -#define CC_SETUP_ERR 71 -#define CC_CONNECT_ERR 72 -#define CC_RELEASE_ERR 73 - -/* - * Message-Types - */ - -#define MT_ALERTING 0x01 -#define MT_CALL_PROCEEDING 0x02 -#define MT_CONNECT 0x07 -#define MT_CONNECT_ACKNOWLEDGE 0x0f -#define MT_PROGRESS 0x03 -#define MT_SETUP 0x05 -#define MT_SETUP_ACKNOWLEDGE 0x0d -#define MT_RESUME 0x26 -#define MT_RESUME_ACKNOWLEDGE 0x2e -#define MT_RESUME_REJECT 0x22 -#define MT_SUSPEND 0x25 -#define MT_SUSPEND_ACKNOWLEDGE 0x2d -#define MT_SUSPEND_REJECT 0x21 -#define MT_USER_INFORMATION 0x20 -#define MT_DISCONNECT 0x45 -#define MT_RELEASE 0x4d -#define MT_RELEASE_COMPLETE 0x5a -#define MT_RESTART 0x46 -#define MT_RESTART_ACKNOWLEDGE 0x4e -#define MT_SEGMENT 0x60 -#define MT_CONGESTION_CONTROL 0x79 -#define MT_INFORMATION 0x7b -#define MT_FACILITY 0x62 -#define MT_NOTIFY 0x6e -#define MT_STATUS 0x7d -#define MT_STATUS_ENQUIRY 0x75 - -#define IE_CAUSE 0x08 - -struct HscxIoctlArg { - int channel; - int mode; - int transbufsize; -}; #ifdef __KERNEL__ -#undef DEBUG_MAGIC - -#define MAX_DFRAME_LEN 3072 +#define 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 FsmInst; + +typedef void (* FSMFNPTR)(struct FsmInst *, int, void *); + struct Fsm { - int *jumpmatrix; + FSMFNPTR *jumpmatrix; int state_count, event_count; char **strEvent, **strState; }; @@ -226,73 +210,111 @@ 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 (*l1hw) (struct PStack *, int, void *); + void (*l1tei) (struct PStack *, int, void *); + int mode, bc; + int delay; }; +#define GROUP_TEI 127 +#define TEI_SAPI 63 +#define CTRL_SAPI 0 +#define PACKET_NOACK 250 + +/* Layer2 Flags */ + +#define FLG_LAPB 0 +#define FLG_LAPD 1 +#define FLG_ORIG 2 +#define FLG_MOD128 3 +#define FLG_PEND_REL 4 +#define FLG_L3_INIT 5 +#define FLG_T200_RUN 6 +#define FLG_ACK_PEND 7 +#define FLG_REJEXC 8 +#define FLG_OWN_BUSY 9 +#define FLG_PEER_BUSY 10 +#define FLG_DCHAN_BUSY 11 +#define FLG_L1_ACTIV 12 +#define FLG_ESTAB_PEND 13 +#define FLG_PTP 14 +#define FLG_FIXED_TEI 15 + struct Layer2 { - int sap, tei, ces; - int extended, laptype; - int uihsize, ihsize; + int tei; + int sap; + int maxlen; + unsigned int flag; int vs, va, vr; - struct sk_buff_head i_queue; - int window, orig; - int rejexp; - int debug; - struct sk_buff *windowar[MAX_WINDOW]; + int rc; + int window; int sow; - struct FsmInst l2m; + struct sk_buff *windowar[MAX_WINDOW]; + struct sk_buff_head i_queue; + struct sk_buff_head ui_queue; void (*l2l1) (struct PStack *, int, void *); - void (*l2l1discardq) (struct PStack *, int, void *, int); - void (*l2man) (struct PStack *, int, void *); void (*l2l3) (struct PStack *, int, void *); void (*l2tei) (struct PStack *, int, void *); - struct FsmTimer t200_timer, t203_timer; - int t200, n200, t203; - int rc, t200_running; + 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 (*l3l2) (struct PStack *, int, void *); - int state, callref; - struct L3Timer timer; - int t303, t304, t305, t308, t310, t313, t318, t319; - int n_t303; + struct FsmInst l3m; + struct sk_buff_head squeue; + struct l3_process *proc; + struct l3_process *global; + int N303; int debug; - int channr; + char debug_id[32]; }; -struct Layer4 { +struct LLInterface { void (*l4l3) (struct PStack *, int, void *); void *userdata; - void (*l1writewakeup) (struct PStack *); - void (*l2writewakeup) (struct PStack *); + void (*l1writewakeup) (struct PStack *, int); + void (*l2writewakeup) (struct PStack *, int); }; + struct Management { - void (*manl1) (struct PStack *, int, void *); - void (*manl2) (struct PStack *, int, void *); - void (*teil2) (struct PStack *, int, void *); + int ri; + struct FsmInst tei_m; + struct FsmTimer t202; + int T202, N202, debug; + void (*layer) (struct PStack *, int, void *); }; + struct Param { int cause; int loc; int bchannel; - int callref; /* Callreferenz Number */ setup_parm setup; /* from isdnif.h numbers and Serviceindicator */ int chargeinfo; /* Charge Info - only for 1tr6 in * the moment @@ -300,95 +322,325 @@ 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 +#define L1_MODE_MODEM 7 + +struct BCState { + int channel; + int mode; + int Flag; + struct IsdnCardState *cs; int tx_cnt; /* B-Channel transmit counter */ - int count; /* Current skb sent count */ struct sk_buff_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 -}; - -struct LcFsm { - struct FsmInst lcfi; - int type; - struct Channel *ch; - void (*lccall) (struct LcFsm *, int, void *); - struct PStack *st; - int l2_establish; - int l2_start; - struct FsmTimer act_timer; - char debug_id[32]; + int (*BC_SetStack) (struct PStack *, struct BCState *); + void (*BC_Close) (struct BCState *); + union { + struct hscx_hw hscx; + struct hfcB_hw hfc; + struct tiger_hw tiger; + struct amd7930_hw amd7930; + } hw; }; struct Channel { - struct PStack ds, is; - struct IsdnCardState *sp; - int hscx; + struct PStack *b_st, *d_st; + struct IsdnCardState *cs; + struct BCState *bcs; int chan; int incoming; struct FsmInst fi; - struct LcFsm lc_d, lc_b; - struct Param para; struct FsmTimer drel_timer, dial_timer; int debug; -#ifdef DEBUG_MAGIC - int magic; /* 301272 */ -#endif int l2_protocol, l2_active_protocol; - int l2_primitive, l2_headersize; int data_open; - int outcallref; - int impair; + struct l3_process *proc; + setup_parm setup; /* from isdnif.h numbers and Serviceindicator */ int Flags; /* for remembering action done in l4 */ int leased; }; -struct IsdnCardState { -#ifdef DEBUG_MAGIC - int magic; -#endif - unsigned char typ; - unsigned char subtyp; - int protocol; - unsigned int irq; +struct elsa_hw { + unsigned int base; + unsigned int cfg; + unsigned int ctrl; + unsigned int ale; + unsigned int isac; + unsigned int itac; + unsigned int hscx; + unsigned int trig; + unsigned int timer; + unsigned int counter; + unsigned int status; + struct timer_list tl; + unsigned int MFlag; + struct BCState *bcs; + u_char *transbuf; + u_char *rcvbuf; + unsigned int transp; + unsigned int rcvp; + unsigned int transcnt; + unsigned int rcvcnt; + u_char IER; + u_char FCR; + u_char LCR; + u_char MCR; + u_char ctrl_reg; +}; + +struct teles3_hw { + unsigned int cfg_reg; + signed int isac; + signed int hscx[2]; + signed int isacfifo; + signed int hscxfifo[2]; +}; + +struct teles0_hw { unsigned int cfg_reg; unsigned int membase; +}; + +struct avm_hw { + unsigned int cfg_reg; unsigned int isac; unsigned int hscx[2]; + unsigned int isacfifo; + unsigned int hscxfifo[2]; unsigned int counter; +}; + +struct ix1_hw { + unsigned int cfg_reg; + unsigned int isac_ale; + unsigned int isac; + unsigned int hscx_ale; + unsigned int hscx; +}; + +struct diva_hw { + unsigned int cfg_reg; + unsigned int ctrl; + unsigned int isac_adr; + unsigned int isac; + unsigned int hscx_adr; + unsigned int hscx; + unsigned int status; + struct timer_list tl; + u_char ctrl_reg; +}; + +struct asus_hw { + unsigned int cfg_reg; + unsigned int adr; + unsigned int isac; + unsigned int hscx; + unsigned int u7; + unsigned int pots; +}; + + +struct hfc_hw { + unsigned int addr; + unsigned int fifosize; + unsigned char cirm; + unsigned char ctmt; + unsigned char cip; + u_char isac_spcr; + struct timer_list timer; +}; + +struct sedl_hw { + unsigned int cfg_reg; + unsigned int adr; + unsigned int isac; + unsigned int hscx; + unsigned int reset_on; + unsigned int reset_off; +}; + +struct 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 *); struct Channel channel[2]; + struct BCState bcs[2]; struct PStack *stlist; u_char *rcvbuf; int rcvidx; @@ -396,16 +648,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 @@ -413,101 +669,274 @@ struct IsdnCardState { #define MON0_TX 4 #define MON1_TX 8 +#define HISAX_MAX_CARDS 8 + #define ISDN_CTYPE_16_0 1 #define ISDN_CTYPE_8_0 2 #define ISDN_CTYPE_16_3 3 #define ISDN_CTYPE_PNP 4 #define ISDN_CTYPE_A1 5 #define ISDN_CTYPE_ELSA 6 -#define ISDN_CTYPE_ELSA_QS1000 7 +#define ISDN_CTYPE_ELSA_PNP 7 #define ISDN_CTYPE_TELESPCMCIA 8 #define ISDN_CTYPE_IX1MICROR2 9 +#define ISDN_CTYPE_ELSA_PCMCIA 10 +#define ISDN_CTYPE_DIEHLDIVA 11 +#define ISDN_CTYPE_ASUSCOM 12 +#define ISDN_CTYPE_TELEINT 13 +#define ISDN_CTYPE_TELES3C 14 +#define ISDN_CTYPE_SEDLBAUER 15 +#define ISDN_CTYPE_SPORTSTER 16 +#define ISDN_CTYPE_MIC 17 +#define ISDN_CTYPE_ELSA_PCI 18 +#define ISDN_CTYPE_COMPAQ_ISA 19 +#define ISDN_CTYPE_NETJET 20 +#define ISDN_CTYPE_TELESPCI 21 +#define ISDN_CTYPE_SEDLBAUER_PCMCIA 22 +#define ISDN_CTYPE_AMD7930 23 +#define ISDN_CTYPE_NICCY 24 +#define ISDN_CTYPE_S0BOX 25 + +#define ISDN_CTYPE_COUNT 25 + +#ifdef ISDN_CHIP_ISAC +#undef ISDN_CHIP_ISAC +#endif + +#ifndef __initfunc +#define __initfunc(__arginit) __arginit +#endif -#define ISDN_CTYPE_COUNT 9 +#ifndef __initdata +#define __initdata +#endif + +#define HISAX_INITFUNC(__arginit) __initfunc(__arginit) +#define HISAX_INITDATA __initdata #ifdef CONFIG_HISAX_16_0 #define CARD_TELES0 (1<< ISDN_CTYPE_16_0) | (1<< ISDN_CTYPE_8_0) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else #define CARD_TELES0 0 #endif #ifdef CONFIG_HISAX_16_3 #define CARD_TELES3 (1<< ISDN_CTYPE_16_3) | (1<< ISDN_CTYPE_PNP) | \ - (1<< ISDN_CTYPE_TELESPCMCIA) + (1<< ISDN_CTYPE_TELESPCMCIA) | (1<< ISDN_CTYPE_COMPAQ_ISA) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else #define CARD_TELES3 0 #endif +#ifdef CONFIG_HISAX_TELESPCI +#define CARD_TELESPCI (1<< ISDN_CTYPE_TELESPCI) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_TELESPCI 0 +#endif + #ifdef CONFIG_HISAX_AVM_A1 #define CARD_AVM_A1 (1<< ISDN_CTYPE_A1) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else #define CARD_AVM_A1 0 #endif -#ifdef CONFIG_HISAX_ELSA_PCC -#define CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_QS1000) +#ifdef CONFIG_HISAX_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 + +#ifdef CONFIG_HISAX_S0BOX +#define CARD_S0BOX (1 << ISDN_CTYPE_S0BOX) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_S0BOX 0 +#endif + #define SUPORTED_CARDS (CARD_TELES0 | CARD_TELES3 | CARD_AVM_A1 | CARD_ELSA \ - | CARD_IX1MICROR2) + | CARD_IX1MICROR2 | CARD_DIEHLDIVA | CARD_ASUSCOM \ + | CARD_TELEINT | CARD_SEDLBAUER | CARD_SPORTSTER \ + | CARD_MIC | CARD_NETJET | CARD_TELES3C | CARD_AMD7930 \ + | CARD_NICCY | CARD_S0BOX | CARD_TELESPCI) + +#define TEI_PER_CARD 0 + +#ifdef CONFIG_HISAX_1TR6 +#undef TEI_PER_CARD +#define TEI_PER_CARD 1 +#endif + +#ifdef CONFIG_HISAX_EURO +#undef TEI_PER_CARD +#define TEI_PER_CARD 1 +#define HISAX_EURO_SENDCOMPLETE 1 +#ifdef CONFIG_HISAX_ML +#undef HISAX_EURO_SENDCOMPLETE +#endif +#undef HISAX_DE_AOC +#ifdef CONFIG_DE_AOC +#define HISAX_DE_AOC 1 +#endif +#endif struct IsdnCard { int typ; int protocol; /* EDSS1 or 1TR6 */ - unsigned int para[3]; - struct IsdnCardState *sp; + unsigned int para[4]; + struct IsdnCardState *cs; }; +int HiSax_inithardware(int *); +void HiSax_closecard(int cardnr); -#define LAPD 0 -#define LAPB 1 +void setstack_HiSax(struct PStack *st, struct IsdnCardState *cs); +unsigned int random_ri(void); +void HiSax_addlist(struct IsdnCardState *sp, struct PStack *st); +void HiSax_rmlist(struct IsdnCardState *sp, struct PStack *st); -void l2down(struct PStack *st, u_char pr, struct sk_buff *skb); -void l2up(struct PStack *st, u_char pr, struct sk_buff *skb); -void acceptph(struct PStack *st, struct sk_buff *skb); -void setstack_isdnl2(struct PStack *st, char *debug_id); -int HiSax_inithardware(void); -void HiSax_closehardware(void); +void setstack_l1_B(struct PStack *st); -void setstack_HiSax(struct PStack *st, struct IsdnCardState *sp); -unsigned int randomces(void); -void setstack_isdnl3(struct PStack *st, struct Channel *chanp); -void HiSax_addlist(struct IsdnCardState *sp, struct PStack *st); +void setstack_tei(struct PStack *st); +void setstack_manager(struct PStack *st); + +void setstack_isdnl2(struct PStack *st, char *debug_id); void releasestack_isdnl2(struct PStack *st); +void setstack_transl2(struct PStack *st); +void releasestack_transl2(struct PStack *st); + +void setstack_l3dc(struct PStack *st, struct Channel *chanp); +void setstack_l3bc(struct PStack *st, struct Channel *chanp); void releasestack_isdnl3(struct PStack *st); -void HiSax_rmlist(struct IsdnCardState *sp, struct PStack *st); -void newcallref(struct PStack *st); -int setstack_hscx(struct PStack *st, struct HscxState *hs); u_char *findie(u_char * p, int size, u_char ie, int wanted_set); int getcallref(u_char * p); +int newcallref(void); void FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount); void FsmFree(struct Fsm *fsm); int FsmEvent(struct FsmInst *fi, int event, void *arg); void FsmChangeState(struct FsmInst *fi, int newstate); void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft); -int FsmAddTimer(struct FsmTimer *ft, int millisec, - int event, void *arg, int where); +int FsmAddTimer(struct FsmTimer *ft, int millisec, int event, + void *arg, int where); +void FsmRestartTimer(struct FsmTimer *ft, int millisec, int event, + void *arg, int where); void FsmDelTimer(struct FsmTimer *ft, int where); -int FsmTimerRunning(struct FsmTimer *ft); void jiftime(char *s, long mark); int HiSax_command(isdn_ctrl * ic); @@ -518,12 +947,11 @@ int QuickHex(char *txt, u_char * p, int cnt); void LogFrame(struct IsdnCardState *sp, u_char * p, int size); void dlogframe(struct IsdnCardState *sp, u_char * p, int size, char *comment); void iecpy(u_char * dest, u_char * iestart, int ieoffset); -void setstack_transl2(struct PStack *st); -void releasestack_transl2(struct PStack *st); -void close_hscxstate(struct HscxState *); -void setstack_tei(struct PStack *st); - -#endif /* __KERNEL__ */ +int discard_queue(struct sk_buff_head *q); +#ifdef ISDN_CHIP_ISAC +void setstack_isac(struct PStack *st, struct IsdnCardState *cs); +#endif /* ISDN_CHIP_ISAC */ +#endif /* __KERNEL__ */ #define HZDELAY(jiffs) {int tout = jiffs; while (tout--) udelay(1000000/HZ);} @@ -533,8 +961,14 @@ 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 Isdnl3New(void); +void Isdnl3Free(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..13a7e709bf8e --- /dev/null +++ b/drivers/isdn/hisax/hscx.c @@ -0,0 +1,300 @@ +/* $Id: hscx.c,v 1.3.2.7 1998/06/26 22:02:55 keil Exp $ + + * hscx.c HSCX specific routines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hscx.c,v $ + * Revision 1.3.2.7 1998/06/26 22:02:55 keil + * send flags between hdlc frames + * + * Revision 1.3.2.6 1998/06/09 18:26:32 keil + * PH_DEACTIVATE B-channel every time signaled to higher layer + * + * Revision 1.3.2.5 1998/05/27 18:05:34 keil + * HiSax 3.0 + * + * Revision 1.3.2.4 1998/04/08 21:57:04 keil + * Fix "lltrans ..." message + * New init code to fix problems during init if S0 is allready activ + * + * Revision 1.3.2.3 1997/11/27 12:30:55 keil + * cosmetic changes + * + * Revision 1.3.2.2 1997/11/15 18:54:25 keil + * cosmetics + * + * Revision 1.3.2.1 1997/10/17 22:10:44 keil + * new files on 2.0 + * + * Revision 1.3 1997/07/27 21:38:34 keil + * new B-channel interface + * + * Revision 1.2 1997/06/26 11:16:17 keil + * first version + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hscx.h" +#include "isac.h" +#include "isdnl1.h" +#include + +static char *HSCXVer[] HISAX_INITDATA = +{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7", + "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"}; + +HISAX_INITFUNC(int +HscxVersion(struct IsdnCardState *cs, char *s)) +{ + int verA, verB; + + verA = cs->BC_Read_Reg(cs, 0, HSCX_VSTR) & 0xf; + verB = cs->BC_Read_Reg(cs, 1, HSCX_VSTR) & 0xf; + printk(KERN_INFO "%s HSCX version A: %s B: %s\n", s, + HSCXVer[verA], HSCXVer[verB]); + if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) + return (1); + else + return (0); +} + +void +modehscx(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + int hscx = bcs->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_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_CCR1, + test_bit(HW_IPAC, &cs->HW_Flags) ? 0x8a : 0x8d); + cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x8c); + break; + } + if (mode) + cs->BC_Write_Reg(cs, hscx, HSCX_CMDR, 0x41); + cs->BC_Write_Reg(cs, hscx, HSCX_ISTA, 0x00); +} + +void +hscx_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +void +hscx_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA | REQUEST): + save_flags(flags); + cli(); + if (st->l1.bcs->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 | INDICATION): + 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 | REQUEST): + 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 | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (PH_ACTIVATE | REQUEST): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + modehscx(st->l1.bcs, st->l1.mode, st->l1.bc); + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | REQUEST): + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | CONFIRM): + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + modehscx(st->l1.bcs, 0, st->l1.bc); + st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + break; + } +} + +void +close_hscxstate(struct BCState *bcs) +{ + modehscx(bcs, 0, 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; + } + discard_queue(&bcs->rqueue); + discard_queue(&bcs->squeue); + if (bcs->hw.hscx.tx_skb) { + dev_kfree_skb(bcs->hw.hscx.tx_skb, FREE_WRITE); + bcs->hw.hscx.tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +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); +} + +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; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + +HISAX_INITFUNC(void +clear_pending_hscx_ints(struct IsdnCardState *cs)) +{ + int val, eval; + 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) { + eval = cs->BC_Read_Reg(cs, 1, HSCX_EXIR); + sprintf(tmp, "HSCX B EXIR %x", eval); + debugl1(cs, tmp); + } + if (val & 0x02) { + eval = cs->BC_Read_Reg(cs, 0, HSCX_EXIR); + sprintf(tmp, "HSCX A EXIR %x", eval); + 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); + /* disable all IRQ */ + cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0xFF); + cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0xFF); +} + +HISAX_INITFUNC(void +inithscx(struct IsdnCardState *cs)) +{ + cs->bcs[0].BC_SetStack = setstack_hscx; + cs->bcs[1].BC_SetStack = setstack_hscx; + cs->bcs[0].BC_Close = close_hscxstate; + cs->bcs[1].BC_Close = close_hscxstate; + modehscx(cs->bcs, 0, 0); + modehscx(cs->bcs + 1, 0, 0); +} + +HISAX_INITFUNC(void +inithscxisac(struct IsdnCardState *cs, int part)) +{ + if (part & 1) { + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + } + if (part & 2) { + /* Reenable all IRQ */ + cs->writeisac(cs, ISAC_MASK, 0); + cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0); + cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0); + /* RESET Receiver and Transmitter */ + cs->writeisac(cs, ISAC_CMDR, 0x41); + } +} diff --git a/drivers/isdn/hisax/hscx.h b/drivers/isdn/hisax/hscx.h new file mode 100644 index 000000000000..ca32839f40ad --- /dev/null +++ b/drivers/isdn/hisax/hscx.h @@ -0,0 +1,53 @@ +/* $Id: hscx.h,v 1.3.2.2 1998/04/08 21:57:30 keil Exp $ + + * hscx.h HSCX specific defines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hscx.h,v $ + * Revision 1.3.2.2 1998/04/08 21:57:30 keil + * New init code to fix problems during init if S0 is allready activ + * + * Revision 1.3.2.1 1997/10/17 22:10:45 keil + * new files on 2.0 + * + * Revision 1.3 1997/07/27 21:38:35 keil + * new B-channel interface + * + * Revision 1.2 1997/06/26 11:16:18 keil + * first version + * + * + */ + +/* All Registers original Siemens Spec */ + +#define HSCX_ISTA 0x20 +#define HSCX_CCR1 0x2f +#define HSCX_CCR2 0x2c +#define HSCX_TSAR 0x31 +#define HSCX_TSAX 0x30 +#define HSCX_XCCR 0x32 +#define HSCX_RCCR 0x33 +#define HSCX_MODE 0x22 +#define HSCX_CMDR 0x21 +#define HSCX_EXIR 0x24 +#define HSCX_XAD1 0x24 +#define HSCX_XAD2 0x25 +#define HSCX_RAH2 0x27 +#define HSCX_RSTA 0x27 +#define HSCX_TIMR 0x23 +#define HSCX_STAR 0x21 +#define HSCX_RBCL 0x25 +#define HSCX_XBCH 0x2d +#define HSCX_VSTR 0x2e +#define HSCX_RLCR 0x2e +#define HSCX_MASK 0x20 + +extern int HscxVersion(struct IsdnCardState *cs, char *s); +extern void hscx_sched_event(struct BCState *bcs, int event); +extern void modehscx(struct BCState *bcs, int mode, int bc); +extern void clear_pending_hscx_ints(struct IsdnCardState *cs); +extern void inithscx(struct IsdnCardState *cs); +extern void inithscxisac(struct IsdnCardState *cs, int part); diff --git a/drivers/isdn/hisax/hscx_irq.c b/drivers/isdn/hisax/hscx_irq.c new file mode 100644 index 000000000000..46da67f15d7e --- /dev/null +++ b/drivers/isdn/hisax/hscx_irq.c @@ -0,0 +1,321 @@ +/* $Id: hscx_irq.c,v 1.5.2.3 1998/06/24 14:43:56 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.5.2.3 1998/06/24 14:43:56 keil + * Fix recovery of TX IRQ loss + * + * Revision 1.5.2.2 1998/05/27 18:05:36 keil + * HiSax 3.0 + * + * Revision 1.5.2.1 1997/10/17 22:10:46 keil + * new files on 2.0 + * + * Revision 1.4 1997/08/15 17:48:02 keil + * cosmetic + * + * Revision 1.3 1997/07/27 21:38:36 keil + * new B-channel interface + * + * Revision 1.2 1997/06/26 11:16:19 keil + * first version + * + * + */ + + +static inline void +waitforCEC(struct IsdnCardState *cs, int hscx) +{ + int to = 50; + + while ((READHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: waitforCEC timeout\n"); +} + + +static inline void +waitforXFW(struct IsdnCardState *cs, int hscx) +{ + int to = 50; + + while ((!(READHSCX(cs, hscx, HSCX_STAR) & 0x44) == 0x40) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: waitforXFW timeout\n"); +} + +static inline void +WriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + waitforCEC(cs, hscx); + WRITEHSCX(cs, hscx, HSCX_CMDR, data); + restore_flags(flags); +} + + + +static void +hscx_empty_fifo(struct BCState *bcs, int count) +{ + u_char *ptr; + struct IsdnCardState *cs = bcs->cs; + long flags; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hscx_empty_fifo"); + + if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hscx_empty_fifo: incoming packet too large"); + WriteHSCXCMDR(cs, bcs->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 { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count); + skb_queue_tail(&bcs->rqueue, skb); + } + } + } + bcs->hw.hscx.rcvidx = 0; + hscx_sched_event(bcs, B_RCVBUFREADY); + } + if (val & 0x40) { /* RPF */ + hscx_empty_fifo(bcs, fifo_size); + if (bcs->mode == L1_MODE_TRANS) { + /* receive audio data */ + if (!(skb = dev_alloc_skb(fifo_size))) + printk(KERN_WARNING "HiSax: receive out of memory\n"); + else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size); + skb_queue_tail(&bcs->rqueue, skb); + } + bcs->hw.hscx.rcvidx = 0; + hscx_sched_event(bcs, B_RCVBUFREADY); + } + } + if (val & 0x10) { /* XPR */ + if (bcs->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, FREE_WRITE); + 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..aa074d6cfb55 --- /dev/null +++ b/drivers/isdn/hisax/ipac.h @@ -0,0 +1,36 @@ +/* $Id: ipac.h,v 1.1.2.2 1998/04/11 18:49:48 keil Exp $ + + * ipac.h IPAC specific defines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: ipac.h,v $ + * Revision 1.1.2.2 1998/04/11 18:49:48 keil + * add IPAC_ATX + * + * Revision 1.1.2.1 1997/10/17 22:10:48 keil + * new files on 2.0 + * + * + * + */ + + +/* All Registers original Siemens Spec */ + +#define IPAC_CONF 0xC0 +#define IPAC_MASK 0xC1 +#define IPAC_ISTA 0xC1 +#define IPAC_ID 0xC2 +#define IPAC_ACFG 0xC3 +#define IPAC_AOE 0xC4 +#define IPAC_ARX 0xC5 +#define IPAC_ATX 0xC5 +#define IPAC_PITA1 0xC6 +#define IPAC_PITA2 0xC7 +#define IPAC_POTA1 0xC8 +#define IPAC_POTA2 0xC9 +#define IPAC_PCFG 0xCA +#define IPAC_SCFG 0xCB +#define IPAC_TIMR2 0xCC diff --git a/drivers/isdn/hisax/isac.c b/drivers/isdn/hisax/isac.c new file mode 100644 index 000000000000..ee6ec7b695cd --- /dev/null +++ b/drivers/isdn/hisax/isac.c @@ -0,0 +1,703 @@ +/* $Id: isac.c,v 1.7.2.7 1998/05/27 18:05:38 keil Exp $ + + * isac.c ISAC specific routines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: isac.c,v $ + * Revision 1.7.2.7 1998/05/27 18:05:38 keil + * HiSax 3.0 + * + * Revision 1.7.2.6 1998/04/08 21:57:31 keil + * New init code to fix problems during init if S0 is allready activ + * + * Revision 1.7.2.5 1998/03/07 23:15:24 tsbogend + * made HiSax working on Linux/Alpha + * + * Revision 1.7.2.4 1998/02/09 11:24:06 keil + * New leased line support (Read README.HiSax!) + * + * Revision 1.7.2.3 1998/01/11 22:58:55 keil + * new setstack interface + * + * Revision 1.7.2.2 1997/11/15 18:54:23 keil + * cosmetics + * + * Revision 1.7.2.1 1997/10/17 22:10:49 keil + * new files on 2.0 + * + * Revision 1.6 1997/08/15 17:47:08 keil + * avoid oops because a uninitialised timer + * + * Revision 1.5 1997/08/07 17:48:49 keil + * fix wrong parenthesis + * + * Revision 1.4 1997/07/30 17:11:59 keil + * fixed Timer3 + * + * Revision 1.3 1997/07/27 21:37:40 keil + * T3 implemented; supervisor l1timer; B-channel TEST_LOOP + * + * Revision 1.2 1997/06/26 11:16:15 keil + * first version + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "isdnl1.h" +#include + +#define DBUSY_TIMER_VALUE 80 +#define ARCOFI_USE 1 + +static char *ISACVer[] HISAX_INITDATA = +{"2086/2186 V1.1", "2085 B1", "2085 B2", + "2085 V2.3"}; + +void +ISACVersion(struct IsdnCardState *cs, char *s) +{ + int val; + + val = cs->readisac(cs, ISAC_RBCH); + printk(KERN_INFO "%s ISAC version (%x): %s\n", s, val, ISACVer[(val >> 5) & 3]); +} + +static void +ph_command(struct IsdnCardState *cs, unsigned int command) +{ + if (cs->debug & L1_DEB_ISAC) { + char tmp[32]; + sprintf(tmp, "ph_command %x", command); + debugl1(cs, tmp); + } + cs->writeisac(cs, ISAC_CIX0, (command << 2) | 3); +} + + +static void +isac_new_ph(struct IsdnCardState *cs) +{ + switch (cs->ph_state) { + case (ISAC_IND_RS): + case (ISAC_IND_EI): + ph_command(cs, ISAC_CMD_DUI); + l1_msg(cs, HW_RESET | INDICATION, NULL); + break; + case (ISAC_IND_DID): + l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL); + break; + case (ISAC_IND_DR): + l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL); + break; + case (ISAC_IND_PU): + l1_msg(cs, HW_POWERUP | CONFIRM, NULL); + break; + case (ISAC_IND_RSY): + l1_msg(cs, HW_RSYNC | INDICATION, NULL); + break; + case (ISAC_IND_ARD): + l1_msg(cs, HW_INFO2 | INDICATION, NULL); + break; + case (ISAC_IND_AI8): + l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL); + break; + case (ISAC_IND_AI10): + l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL); + break; + default: + break; + } +} + +static void +isac_bh(struct IsdnCardState *cs) +{ + struct PStack *stptr; + + if (!cs) + return; + + if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy cleared"); + stptr = cs->stlist; + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL); + stptr = stptr->next; + } + } + if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) + isac_new_ph(cs); + if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) + DChannel_proc_rcv(cs); + if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) + DChannel_proc_xmt(cs); + if (test_and_clear_bit(D_RX_MON0, &cs->event)) + test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags); + if (test_and_clear_bit(D_RX_MON1, &cs->event)) + test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags); + if (test_and_clear_bit(D_TX_MON0, &cs->event)) + test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags); + if (test_and_clear_bit(D_TX_MON1, &cs->event)) + test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags); +} + +void +isac_empty_fifo(struct IsdnCardState *cs, int count) +{ + u_char *ptr; + long flags; + + if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO)) + debugl1(cs, "isac_empty_fifo"); + + if ((cs->rcvidx + count) >= MAX_DFRAME_LEN) { + 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 { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, count), cs->rcvbuf, count); + skb_queue_tail(&cs->rq, skb); + } + } + restore_flags(flags); + } + cs->rcvidx = 0; + isac_sched_event(cs, D_RCVBUFREADY); + } + if (val & 0x40) { /* RPF */ + isac_empty_fifo(cs, 32); + } + if (val & 0x20) { /* RSC */ + /* never */ + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC RSC interrupt"); + } + if (val & 0x10) { /* XPR */ + if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) + del_timer(&cs->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + isac_sched_event(cs, D_CLEARBUSY); + if (cs->tx_skb) { + if (cs->tx_skb->len) { + isac_fill_fifo(cs); + goto afterXPR; + } else { + dev_kfree_skb(cs->tx_skb, FREE_WRITE); + cs->tx_cnt = 0; + cs->tx_skb = NULL; + } + } + if ((cs->tx_skb = skb_dequeue(&cs->sq))) { + cs->tx_cnt = 0; + isac_fill_fifo(cs); + } else + isac_sched_event(cs, D_XMTBUFREADY); + } + afterXPR: + if (val & 0x04) { /* CISQ */ + exval = cs->readisac(cs, ISAC_CIR0); + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "ISAC CIR0 %02X", exval ); + debugl1(cs, tmp); + } + if (exval & 2) { + cs->ph_state = (exval >> 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 (exval & 1) { + exval = cs->readisac(cs, ISAC_CIR1); + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "ISAC CIR1 %02X", exval ); + debugl1(cs, tmp); + } + } + } + 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_MONITOR) { + 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_MONITOR) { + 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_MONITOR) { + sprintf(tmp, "ISAC MOR1 %02x", cs->mon_rx[cs->mon_rxp -1]); + debugl1(cs, tmp); + } + cs->mocr |= 0x40; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + } + afterMONR1: + if (v1 & 0x04) { + cs->mocr &= 0xf0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags); + } + if (v1 & 0x40) { + cs->mocr &= 0x0f; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags); + } + if (v1 & 0x02) { + if ((!cs->mon_tx) || (cs->mon_txc && + (cs->mon_txp >= cs->mon_txc) && + !(v1 & 0x08))) { + cs->mocr &= 0xf0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + if (cs->mon_txc && + (cs->mon_txp >= cs->mon_txc)) + test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags); + goto AfterMOX0; + } + if (cs->mon_txc && (cs->mon_txp >= cs->mon_txc)) { + test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags); + goto AfterMOX0; + } + cs->writeisac(cs, ISAC_MOX0, + cs->mon_tx[cs->mon_txp++]); + if (cs->debug & L1_DEB_MONITOR) { + sprintf(tmp, "ISAC %02x -> MOX0", cs->mon_tx[cs->mon_txp -1]); + debugl1(cs, tmp); + } + } + AfterMOX0: + if (v1 & 0x20) { + if ((!cs->mon_tx) || (cs->mon_txc && + (cs->mon_txp >= cs->mon_txc) && + !(v1 & 0x80))) { + cs->mocr &= 0x0f; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + if (cs->mon_txc && + (cs->mon_txp >= cs->mon_txc)) + test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags); + goto AfterMOX1; + } + if (cs->mon_txc && (cs->mon_txp >= cs->mon_txc)) { + test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags); + goto AfterMOX1; + } + cs->writeisac(cs, ISAC_MOX1, + cs->mon_tx[cs->mon_txp++]); + if (cs->debug & L1_DEB_MONITOR) { + sprintf(tmp, "ISAC %02x -> MOX1", cs->mon_tx[cs->mon_txp -1]); + debugl1(cs, tmp); + } + } + AfterMOX1: +#endif + } + } +} + +static void +ISAC_l1hw(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + struct sk_buff *skb = arg; + int val; + char str[64]; + + switch (pr) { + case (PH_DATA |REQUEST): + 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 |INDICATION): + if (cs->tx_skb) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, " l2l1 tx_skb exist this shouldn't happen"); + skb_queue_tail(&cs->sq, skb); + break; + } + if ((cs->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 | REQUEST): +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + debugl1(cs, "-> PH_REQUEST_PULL"); +#endif + if (!cs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (HW_RESET | REQUEST): + if ((cs->ph_state == ISAC_IND_EI) || + (cs->ph_state == ISAC_IND_DR) || + (cs->ph_state == ISAC_IND_RS)) + ph_command(cs, ISAC_CMD_TIM); + else + ph_command(cs, ISAC_CMD_RS); + break; + case (HW_ENABLE | REQUEST): + ph_command(cs, ISAC_CMD_TIM); + break; + case (HW_INFO3 | REQUEST): + ph_command(cs, ISAC_CMD_AR8); + break; + case (HW_TESTLOOP | REQUEST): + val = 0; + if (1 & (long) arg) + val |= 0x0c; + if (2 & (long) arg) + val |= 0x3; + if (test_bit(HW_IOM1, &cs->HW_Flags)) { + /* IOM 1 Mode */ + if (!val) { + cs->writeisac(cs, ISAC_SPCR, 0xa); + cs->writeisac(cs, ISAC_ADF1, 0x2); + } else { + cs->writeisac(cs, ISAC_SPCR, val); + cs->writeisac(cs, ISAC_ADF1, 0xa); + } + } else { + /* IOM 2 Mode */ + cs->writeisac(cs, ISAC_SPCR, val); + if (val) + cs->writeisac(cs, ISAC_ADF1, 0x8); + else + cs->writeisac(cs, ISAC_ADF1, 0x0); + } + break; + case (HW_DEACTIVATE | RESPONSE): + discard_queue(&cs->rq); + discard_queue(&cs->sq); + if (cs->tx_skb) { + dev_kfree_skb(cs->tx_skb, FREE_WRITE); + cs->tx_skb = NULL; + } + if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) + del_timer(&cs->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + isac_sched_event(cs, D_CLEARBUSY); + break; + default: + if (cs->debug & L1_DEB_WARN) { + sprintf(str, "isac_l1hw unknown %04x", pr); + debugl1(cs, str); + } + break; + } +} + +void +setstack_isac(struct PStack *st, struct IsdnCardState *cs) +{ + st->l1.l1hw = ISAC_l1hw; +} + +static void +dbusy_timer_handler(struct IsdnCardState *cs) +{ + struct PStack *stptr; + int val; + + if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { + if (cs->debug) { + debugl1(cs, "D-Channel Busy"); + val = cs->readisac(cs, ISAC_RBCH); + if (val & ISAC_RBCH_XAC) + debugl1(cs, "ISAC XAC"); + else + debugl1(cs, "ISAC No XAC"); + } + test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); + stptr = cs->stlist; + + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL); + stptr = stptr->next; + } + } +} + +HISAX_INITFUNC(void +initisac(struct IsdnCardState *cs)) +{ + cs->tqueue.routine = (void *) (void *) isac_bh; + cs->setstack_d = setstack_isac; + cs->dbusytimer.function = (void *) dbusy_timer_handler; + cs->dbusytimer.data = (long) cs; + init_timer(&cs->dbusytimer); + cs->writeisac(cs, ISAC_MASK, 0xff); + cs->mocr = 0xaa; + if (test_bit(HW_IOM1, &cs->HW_Flags)) { + /* IOM 1 Mode */ + cs->writeisac(cs, ISAC_ADF2, 0x0); + cs->writeisac(cs, ISAC_SPCR, 0xa); + cs->writeisac(cs, ISAC_ADF1, 0x2); + cs->writeisac(cs, ISAC_STCR, 0x70); + cs->writeisac(cs, ISAC_MODE, 0xc9); + } else { + /* IOM 2 Mode */ + cs->writeisac(cs, ISAC_ADF2, 0x80); + cs->writeisac(cs, ISAC_SQXR, 0x2f); + cs->writeisac(cs, ISAC_SPCR, 0x00); + cs->writeisac(cs, ISAC_STCR, 0x70); + cs->writeisac(cs, ISAC_MODE, 0xc9); + cs->writeisac(cs, ISAC_TIMR, 0x00); + cs->writeisac(cs, ISAC_ADF1, 0x00); + } + ph_command(cs, ISAC_CMD_RS); + cs->writeisac(cs, ISAC_MASK, 0x0); +} + +HISAX_INITFUNC(void +clear_pending_isac_ints(struct IsdnCardState *cs)) +{ + int val, eval; + 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) { + eval = cs->readisac(cs, ISAC_EXIR); + sprintf(tmp, "ISAC EXIR %x", eval); + debugl1(cs, tmp); + } + val = cs->readisac(cs, ISAC_CIR0); + sprintf(tmp, "ISAC CIR0 %x", val); + debugl1(cs, tmp); + cs->ph_state = (val >> 2) & 0xf; + isac_sched_event(cs, D_L1STATECHANGE); + /* Disable all IRQ */ + cs->writeisac(cs, ISAC_MASK, 0xFF); +} diff --git a/drivers/isdn/hisax/isac.h b/drivers/isdn/hisax/isac.h new file mode 100644 index 000000000000..b904ffb046c7 --- /dev/null +++ b/drivers/isdn/hisax/isac.h @@ -0,0 +1,84 @@ +/* $Id: isac.h,v 1.3.2.3 1998/05/27 18:05:41 keil Exp $ + + * isac.h ISAC specific defines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: isac.h,v $ + * Revision 1.3.2.3 1998/05/27 18:05:41 keil + * HiSax 3.0 + * + * Revision 1.3.2.2 1997/11/15 19:01:14 keil + * ipac changes + * + * Revision 1.3.2.1 1997/10/17 22:10:50 keil + * new files on 2.0 + * + * Revision 1.3 1997/07/27 21:37:41 keil + * T3 implemented; supervisor l1timer; B-channel TEST_LOOP + * + * Revision 1.2 1997/06/26 11:16:16 keil + * first version + * + * + */ + + +/* All Registers original Siemens Spec */ + +#define ISAC_MASK 0x20 +#define ISAC_ISTA 0x20 +#define ISAC_STAR 0x21 +#define ISAC_CMDR 0x21 +#define ISAC_EXIR 0x24 +#define ISAC_ADF2 0x39 +#define ISAC_SPCR 0x30 +#define ISAC_ADF1 0x38 +#define ISAC_CIR0 0x31 +#define ISAC_CIX0 0x31 +#define ISAC_CIR1 0x33 +#define ISAC_CIX1 0x33 +#define ISAC_STCR 0x37 +#define ISAC_MODE 0x22 +#define ISAC_RSTA 0x27 +#define ISAC_RBCL 0x25 +#define ISAC_RBCH 0x2A +#define ISAC_TIMR 0x23 +#define ISAC_SQXR 0x3b +#define ISAC_MOSR 0x3a +#define ISAC_MOCR 0x3a +#define ISAC_MOR0 0x32 +#define ISAC_MOX0 0x32 +#define ISAC_MOR1 0x34 +#define ISAC_MOX1 0x34 + +#define ISAC_RBCH_XAC 0x80 + +#define ISAC_CMD_TIM 0x0 +#define ISAC_CMD_RS 0x1 +#define ISAC_CMD_SCZ 0x4 +#define ISAC_CMD_SSZ 0x2 +#define ISAC_CMD_AR8 0x8 +#define ISAC_CMD_AR10 0x9 +#define ISAC_CMD_ARL 0xA +#define ISAC_CMD_DUI 0xF + +#define ISAC_IND_RS 0x1 +#define ISAC_IND_PU 0x7 +#define ISAC_IND_DR 0x0 +#define ISAC_IND_SD 0x2 +#define ISAC_IND_DIS 0x3 +#define ISAC_IND_EI 0x6 +#define ISAC_IND_RSY 0x4 +#define ISAC_IND_ARD 0x8 +#define ISAC_IND_TI 0xA +#define ISAC_IND_ATI 0xB +#define ISAC_IND_AI8 0xC +#define ISAC_IND_AI10 0xD +#define ISAC_IND_DID 0xF + +extern void ISACVersion(struct IsdnCardState *cs, char *s); +extern void initisac(struct IsdnCardState *cs); +extern void isac_interrupt(struct IsdnCardState *cs, u_char val); +extern void clear_pending_isac_ints(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c index f0aaba0c9035..5608600fe71e 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 1.15.2.12 1998/05/27 18:05:43 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,264 @@ * * * $Log: isdnl1.c,v $ - * Revision 1.15 1997/05/27 15:17:55 fritz - * Added changes for recent 2.1.x kernels: - * changed return type of isdn_close - * queue_task_* -> queue_task - * clear/set_bit -> test_and_... where apropriate. - * changed type of hard_header_cache parameter. + * Revision 1.15.2.12 1998/05/27 18:05:43 keil + * HiSax 3.0 + * + * Revision 1.15.2.11 1998/05/26 10:36:51 keil + * fixes from certification + * + * Revision 1.15.2.10 1998/04/11 18:47:45 keil + * Fixed bug which was overwriting nrcards + * New card support * - * Revision 1.14 1997/04/07 23:00:08 keil - * GFP_KERNEL ---> GFP_ATOMIC + * Revision 1.15.2.9 1998/04/08 21:52:00 keil + * new debug * - * Revision 1.13 1997/04/06 22:55:50 keil - * Using SKB's + * Revision 1.15.2.8 1998/03/07 23:15:26 tsbogend + * made HiSax working on Linux/Alpha * - * Revision 1.12 1997/03/26 13:43:57 keil - * small cosmetics + * Revision 1.15.2.7 1998/02/11 14:23:14 keil + * support for Dr Neuhaus Niccy PnP and PCI * - * Revision 1.11 1997/03/25 23:11:23 keil - * US NI-1 protocol + * Revision 1.15.2.6 1998/02/09 11:24:11 keil + * New leased line support (Read README.HiSax!) * - * Revision 1.10 1997/03/13 14:45:05 keil - * using IRQ proof queue_task + * Revision 1.15.2.5 1998/01/27 22:33:55 keil + * dynalink ----> asuscom * - * Revision 1.9 1997/03/12 21:44:21 keil - * change Interrupt routine from atomic quick to normal + * Revision 1.15.2.4 1998/01/11 22:55:20 keil + * 16.3c support * - * Revision 1.8 1997/02/09 00:24:31 keil - * new interface handling, one interface per card + * Revision 1.15.2.3 1997/11/15 18:50:34 keil + * new common init function * - * Revision 1.7 1997/01/27 15:56:03 keil - * PCMCIA Teles card and ITK ix1 micro added + * Revision 1.15.2.2 1997/10/17 22:13:54 keil + * update to last hisax version * - * Revision 1.6 1997/01/21 22:20:00 keil - * changes for D-channel log; Elsa Quickstep support + * Revision 2.6 1997/09/12 10:05:16 keil + * ISDN_CTRL_DEBUG define * - * Revision 1.5 1997/01/10 12:51:19 keil - * cleanup; set newversion + * Revision 2.5 1997/09/11 17:24:45 keil + * Add new cards * - * Revision 1.4 1996/12/08 19:44:53 keil - * L2FRAME_DEBUG and other changes from Pekka Sarnila + * Revision 2.4 1997/08/15 17:47:09 keil + * avoid oops because a uninitialised timer * - * Revision 1.3 1996/11/18 15:34:47 keil - * fix HSCX version code + * Revision 2.3 1997/08/01 11:16:40 keil + * cosmetics * - * Revision 1.2 1996/10/27 22:16:54 keil - * ISAC/HSCX version lookup + * Revision 2.2 1997/07/30 17:11:08 keil + * L1deactivated exported * - * Revision 1.1 1996/10/13 20:04:53 keil - * Initial revision + * Revision 2.1 1997/07/27 21:35:38 keil + * new layer1 interface * + * Revision 2.0 1997/06/26 11:02:53 keil + * New Layer and card interface * + * Revision 1.15 1997/05/27 15:17:55 fritz + * Added changes for recent 2.1.x kernels: + * changed return type of isdn_close + * queue_task_* -> queue_task + * clear/set_bit -> test_and_... where apropriate. + * changed type of hard_header_cache parameter. + * + * old changes removed KKe * */ -const char *l1_revision = "$Revision: 1.15 $"; +const char *l1_revision = "$Revision: 1.15.2.12 $"; #define __NO_VERSION__ #include #include "hisax.h" #include "isdnl1.h" +#include +#define kstat_irqs( PAR ) kstat.interrupts[PAR] #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_S0BOX +extern int setup_s0box(struct IsdnCard *card); +#endif + +#if CARD_TELESPCI +extern int setup_telespci(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", "S0Box" +}; extern struct IsdnCard cards[]; extern int nrcards; extern char *HiSax_id; +extern struct IsdnBuffers *tracebuf; + +#define TIMER3_VALUE 7000 + +static +struct Fsm l1fsm_b = +{NULL, 0, 0, NULL, NULL}; + +static +struct Fsm l1fsm_d = +{NULL, 0, 0, NULL, NULL}; + +enum { + ST_L1_F2, + ST_L1_F3, + ST_L1_F4, + ST_L1_F5, + ST_L1_F6, + ST_L1_F7, + ST_L1_F8, +}; + +#define L1D_STATE_COUNT (ST_L1_F8+1) + +static char *strL1DState[] = +{ + "ST_L1_F2", + "ST_L1_F3", + "ST_L1_F4", + "ST_L1_F5", + "ST_L1_F6", + "ST_L1_F7", + "ST_L1_F8", +}; + +enum { + ST_L1_NULL, + ST_L1_WAIT_ACT, + ST_L1_WAIT_DEACT, + ST_L1_ACTIV, +}; + +#define L1B_STATE_COUNT (ST_L1_ACTIV+1) + +static char *strL1BState[] = +{ + "ST_L1_NULL", + "ST_L1_WAIT_ACT", + "ST_L1_WAIT_DEACT", + "ST_L1_ACTIV", +}; + +enum { + EV_PH_ACTIVATE, + EV_PH_DEACTIVATE, + EV_RESET_IND, + EV_DEACT_CNF, + EV_DEACT_IND, + EV_POWER_UP, + EV_RSYNC_IND, + EV_INFO2_IND, + EV_INFO4_IND, + EV_TIMER_DEACT, + EV_TIMER_ACT, + EV_TIMER3, +}; + +#define L1_EVENT_COUNT (EV_TIMER3 + 1) + +static char *strL1Event[] = +{ + "EV_PH_ACTIVATE", + "EV_PH_DEACTIVATE", + "EV_RESET_IND", + "EV_DEACT_CNF", + "EV_DEACT_IND", + "EV_POWER_UP", + "EV_RSYNC_IND", + "EV_INFO2_IND", + "EV_INFO4_IND", + "EV_TIMER_DEACT", + "EV_TIMER_ACT", + "EV_TIMER3", +}; /* * 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 +295,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 +328,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 +389,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.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); else - st = st->next; - return (0); -} - -void -isac_new_ph(struct IsdnCardState *sp) -{ - int enq; - - enq = act_wanted(sp); - - switch (sp->ph_state) { - case (6): - sp->ph_active = 0; - sp->ph_command(sp, 15); - break; - case (15): - sp->ph_active = 0; - if (enq) - sp->ph_command(sp, 0); - break; - case (0): - sp->ph_active = 0; - if (enq) - sp->ph_command(sp, 0); -#if 0 - else - sp->ph_command(sp, 15); -#endif - break; - case (7): - sp->ph_active = 0; - if (enq) - sp->ph_command(sp, 9); - break; - case (12): - sp->ph_command(sp, 8); - sp->ph_active = 5; - isac_sched_event(sp, ISAC_PHCHANGE); - if (!sp->tx_skb) - sp->tx_skb = skb_dequeue(&sp->sq); - if (sp->tx_skb) { - sp->tx_cnt = 0; - sp->isac_fill_fifo(sp); - } - break; - case (13): - sp->ph_command(sp, 9); - sp->ph_active = 5; - isac_sched_event(sp, ISAC_PHCHANGE); - if (!sp->tx_skb) - sp->tx_skb = skb_dequeue(&sp->sq); - if (sp->tx_skb) { - sp->tx_cnt = 0; - sp->isac_fill_fifo(sp); - } - break; - case (4): - case (8): - sp->ph_active = 0; - break; - default: - sp->ph_active = 0; - break; - } -} - -static void -restart_ph(struct IsdnCardState *sp) -{ - if (!sp->ph_active) { - if ((sp->ph_state == 6) || (sp->ph_state == 0)) { - sp->ph_command(sp, 0); - sp->ph_active = 2; - } else { - sp->ph_command(sp, 1); - sp->ph_active = 1; - } - } else if (sp->ph_active == 2) { - sp->ph_command(sp, 1); - sp->ph_active = 1; + st->l1.l1l2(st, PH_ACTIVATE | INDICATION, NULL); + st = st->next; } } - -static void -act_ivated(struct IsdnCardState *sp) +void +L1deactivated(struct IsdnCardState *cs) { struct PStack *st; - st = sp->stlist; + st = cs->stlist; while (st) { - if (st->l1.act_state == 1) { - st->l1.act_state = 2; - st->l1.l1man(st, PH_ACTIVATE, NULL); - } + if (test_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + st->l1.l1l2(st, PH_PAUSE | CONFIRM, NULL); + st->l1.l1l2(st, PH_DEACTIVATE | INDICATION, NULL); st = st->next; } + test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags); } -static void -process_new_ph(struct IsdnCardState *sp) -{ - if (sp->ph_active == 5) - act_ivated(sp); -} - -static void -process_xmt(struct IsdnCardState *sp) +void +DChannel_proc_xmt(struct IsdnCardState *cs) { struct PStack *stptr; - if (sp->tx_skb) + if (cs->tx_skb) return; - stptr = sp->stlist; + stptr = cs->stlist; while (stptr != NULL) - if (stptr->l1.requestpull) { - stptr->l1.requestpull = 0; - stptr->l1.l1l2(stptr, PH_PULL_ACK, NULL); + if (test_and_clear_bit(FLG_L1_PULL_REQ, &stptr->l1.Flags)) { + stptr->l1.l1l2(stptr, PH_PULL | CONFIRM, NULL); break; } else stptr = stptr->next; } -static void -process_rcv(struct IsdnCardState *sp) +void +DChannel_proc_rcv(struct IsdnCardState *cs) { struct sk_buff *skb, *nskb; - struct PStack *stptr; - int found, broadc; + 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 | INDICATION, nskb); else printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n"); - stptr = stptr->next; + stptr = stptr->next; + } + } else if (sapi == TEI_SAPI) { + while (stptr != NULL) { + if ((nskb = skb_clone(skb, GFP_ATOMIC))) + stptr->l1.l1tei(stptr, PH_DATA | INDICATION, nskb); + else + printk(KERN_WARNING "HiSax: tei broadcast buffer shortage\n"); + stptr = stptr->next; + } } - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - } else { + } else if (sapi == CTRL_SAPI) { found = 0; while (stptr != NULL) - if (((skb->data[0] >> 2) == stptr->l2.sap) && - ((skb->data[1] >> 1) == stptr->l2.tei)) { - stptr->l1.l1l2(stptr, PH_DATA, skb); + if (tei == stptr->l2.tei) { + stptr->l1.l1l2(stptr, PH_DATA | INDICATION, skb); found = !0; break; } else @@ -474,167 +516,76 @@ 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, FREE_READ); } } - - } - -} - -static void -isac_bh(struct IsdnCardState *sp) -{ - if (!sp) - return; - - if (test_and_clear_bit(ISAC_PHCHANGE, &sp->event)) - process_new_ph(sp); - if (test_and_clear_bit(ISAC_RCVBUFREADY, &sp->event)) - process_rcv(sp); - if (test_and_clear_bit(ISAC_XMTBUFREADY, &sp->event)) - process_xmt(sp); -} - -static void -l2l1(struct PStack *st, int pr, void *arg) -{ - struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware; - struct sk_buff *skb = arg; - char str[64]; - - switch (pr) { - case (PH_DATA): - if (sp->tx_skb) { - skb_queue_tail(&sp->sq, skb); -#ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - Logl2Frame(sp, skb, "PH_DATA Queued", 0); -#endif - } else { - if ((sp->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ - LogFrame(sp, skb->data, skb->len); - sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); - dlogframe(sp, skb->data + st->l2.ihsize, skb->len - st->l2.ihsize, - str); - } - sp->tx_skb = skb; - sp->tx_cnt = 0; -#ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - Logl2Frame(sp, skb, "PH_DATA", 0); -#endif - sp->isac_fill_fifo(sp); - } - break; - case (PH_DATA_PULLED): - if (sp->tx_skb) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, " l2l1 tx_skb exist this shouldn't happen"); - break; - } - if ((sp->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ - LogFrame(sp, skb->data, skb->len); - sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); - dlogframe(sp, skb->data + st->l2.ihsize, skb->len - st->l2.ihsize, - str); - } - sp->tx_skb = skb; - sp->tx_cnt = 0; -#ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - Logl2Frame(sp, skb, "PH_DATA_PULLED", 0); -#endif - sp->isac_fill_fifo(sp); - break; - case (PH_REQUEST_PULL): -#ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - debugl1(sp, "-> PH_REQUEST_PULL"); -#endif - if (!sp->tx_skb) { - st->l1.requestpull = 0; - st->l1.l1l2(st, PH_PULL_ACK, NULL); - } else - st->l1.requestpull = !0; - break; } } - static void -hscx_process_xmt(struct HscxState *hsp) +BChannel_proc_xmt(struct BCState *bcs) { - struct PStack *st = hsp->st; + struct PStack *st = bcs->st; - if (hsp->tx_skb) + if (test_bit(BC_FLG_BUSY, &bcs->Flag)) return; - if (st->l1.requestpull) { - st->l1.requestpull = 0; - st->l1.l1l2(st, PH_PULL_ACK, NULL); + if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + if (!test_bit(BC_FLG_ACTIV, &bcs->Flag)) { + if (!test_bit(BC_FLG_BUSY, &bcs->Flag) && (!skb_queue_len(&bcs->squeue))) { + st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL); + } } - if (!hsp->active) - if ((!hsp->tx_skb) && (!skb_queue_len(&hsp->squeue))) - hsp->sp->modehscx(hsp, 0, 0); } static void -hscx_process_rcv(struct HscxState *hsp) +BChannel_proc_rcv(struct BCState *bcs) { struct sk_buff *skb; -#ifdef DEBUG_MAGIC - if (hsp->magic != 301270) { - printk(KERN_DEBUG "hscx_process_rcv magic not 301270\n"); - return; + if (bcs->st->l1.l1m.state == ST_L1_WAIT_ACT) { + FsmDelTimer(&bcs->st->l1.timer, 4); + FsmEvent(&bcs->st->l1.l1m, EV_TIMER_ACT, NULL); } -#endif - while ((skb = skb_dequeue(&hsp->rqueue))) { - hsp->st->l1.l1l2(hsp->st, PH_DATA, skb); + while ((skb = skb_dequeue(&bcs->rqueue))) { + bcs->st->l1.l1l2(bcs->st, PH_DATA | INDICATION, skb); } } static void -hscx_bh(struct HscxState *hsp) +BChannel_bh(struct BCState *bcs) { - - if (!hsp) + if (!bcs) return; - - if (test_and_clear_bit(HSCX_RCVBUFREADY, &hsp->event)) - hscx_process_rcv(hsp); - if (test_and_clear_bit(HSCX_XMTBUFREADY, &hsp->event)) - hscx_process_xmt(hsp); - + if (test_and_clear_bit(B_RCVBUFREADY, &bcs->event)) + BChannel_proc_rcv(bcs); + if (test_and_clear_bit(B_XMTBUFREADY, &bcs->event)) + BChannel_proc_xmt(bcs); } -/* - * interrupt stuff ends here - */ - void -HiSax_addlist(struct IsdnCardState *sp, +HiSax_addlist(struct IsdnCardState *cs, struct PStack *st) { - st->next = sp->stlist; - sp->stlist = st; + st->next = cs->stlist; + cs->stlist = st; } void -HiSax_rmlist(struct IsdnCardState *sp, +HiSax_rmlist(struct IsdnCardState *cs, struct PStack *st) { struct PStack *p; - if (sp->stlist == st) - sp->stlist = st->next; + FsmDelTimer(&st->l1.timer, 0); + if (cs->stlist == st) + cs->stlist = st->next; else { - p = sp->stlist; + p = cs->stlist; while (p) if (p->next == st) { p->next = st->next; @@ -644,253 +595,129 @@ HiSax_rmlist(struct IsdnCardState *sp, } } -static void -check_ph_act(struct IsdnCardState *sp) +void +init_bcstate(struct IsdnCardState *cs, + int bc) { - 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; + 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 -HiSax_manl1(struct PStack *st, int pr, - void *arg) +closecard(int cardnr) { - 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; + struct IsdnCardState *csta = cards[cardnr].cs; + + if (csta->bcs->BC_Close != NULL) { + csta->bcs->BC_Close(csta->bcs + 1); + csta->bcs->BC_Close(csta->bcs); } -} -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; + if (csta->rcvbuf) { + kfree(csta->rcvbuf); + csta->rcvbuf = NULL; } -#endif - - while ((skb = skb_dequeue(&sp->sq))) - dev_kfree_skb(skb, FREE_WRITE); -} - -void -setstack_HiSax(struct PStack *st, struct IsdnCardState *sp) -{ - st->l1.hardware = sp; - st->protocol = sp->protocol; - - setstack_tei(st); - - st->l1.stlistp = &(sp->stlist); - st->l1.act_state = 0; - st->l2.l2l1 = l2l1; - st->l2.l2l1discardq = HiSax_l2l1discardq; - st->ma.manl1 = HiSax_manl1; - st->l1.requestpull = 0; -} - -void -init_hscxstate(struct IsdnCardState *sp, - int hscx) -{ - struct HscxState *hsp = sp->hs + hscx; - - hsp->sp = sp; - hsp->hscx = hscx; - - hsp->tqueue.next = 0; - hsp->tqueue.sync = 0; - hsp->tqueue.routine = (void *) (void *) hscx_bh; - hsp->tqueue.data = hsp; - - hsp->inuse = 0; - hsp->init = 0; - hsp->active = 0; - -#ifdef DEBUG_MAGIC - hsp->magic = 301270; -#endif + discard_queue(&csta->rq); + discard_queue(&csta->sq); + if (csta->tx_skb) { + dev_kfree_skb(csta->tx_skb, FREE_WRITE); + csta->tx_skb = NULL; + } + if (csta->mon_rx) { + kfree(csta->mon_rx); + csta->mon_rx = NULL; + } + if (csta->mon_tx) { + kfree(csta->mon_tx); + csta->mon_tx = NULL; + } + csta->cardmsg(csta, CARD_RELEASE, NULL); + del_timer(&csta->dbusytimer); + ll_unload(csta); } -int -get_irq(int cardnr, void *routine) +HISAX_INITFUNC(static int init_card(struct IsdnCardState *cs)) { - struct IsdnCard *card = cards + cardnr; + int irq_cnt, cnt = 3; long flags; save_flags(flags); cli(); - if (request_irq(card->sp->irq, routine, - I4L_IRQ_FLAG, "HiSax", NULL)) { + 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", - card->sp->irq); + 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); - return (0); + 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); + } } - irq2dev_map[card->sp->irq] = (void *) card->sp; restore_flags(flags); - return (1); + return(3); } -static void -release_irq(int cardnr) +HISAX_INITFUNC(static int +checkcard(int cardnr, char *id, int *busy_flag)) { + long flags; + int ret = 0; struct IsdnCard *card = cards + cardnr; + struct IsdnCardState *cs; - irq2dev_map[card->sp->irq] = NULL; - free_irq(card->sp->irq, NULL); -} - -void -close_hscxstate(struct HscxState *hs) -{ - struct sk_buff *skb; - - hs->sp->modehscx(hs, 0, 0); - hs->inuse = 0; - if (hs->init) { - if (hs->rcvbuf) { - kfree(hs->rcvbuf); - hs->rcvbuf = NULL; - } - while ((skb = skb_dequeue(&hs->rqueue))) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); - } - while ((skb = skb_dequeue(&hs->squeue))) - dev_kfree_skb(skb, FREE_WRITE); - if (hs->tx_skb) { - dev_kfree_skb(hs->tx_skb, FREE_WRITE); - hs->tx_skb = NULL; - } + save_flags(flags); + cli(); + if (!(cs = (struct IsdnCardState *) + kmalloc(sizeof(struct IsdnCardState), GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for IsdnCardState(card %d)\n", + cardnr + 1); + restore_flags(flags); + return (0); } - hs->init = 0; -} - -static void -closecard(int cardnr) -{ - struct IsdnCardState *csta = cards[cardnr].sp; - struct sk_buff *skb; - - close_hscxstate(csta->hs + 1); - close_hscxstate(csta->hs); - - if (csta->rcvbuf) { - kfree(csta->rcvbuf); - csta->rcvbuf = NULL; - } - while ((skb = skb_dequeue(&csta->rq))) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); - } - while ((skb = skb_dequeue(&csta->sq))) - dev_kfree_skb(skb, FREE_WRITE); - if (csta->tx_skb) { - dev_kfree_skb(csta->tx_skb, FREE_WRITE); - csta->tx_skb = NULL; - } - switch (csta->typ) { -#if CARD_TELES0 - case ISDN_CTYPE_16_0: - case ISDN_CTYPE_8_0: - release_io_teles0(cards + cardnr); - break; -#endif -#if CARD_TELES3 - case ISDN_CTYPE_PNP: - case ISDN_CTYPE_16_3: - case ISDN_CTYPE_TELESPCMCIA: - release_io_teles3(cards + cardnr); - break; -#endif -#if CARD_AVM_A1 - case ISDN_CTYPE_A1: - release_io_avm_a1(cards + cardnr); - break; -#endif -#if CARD_ELSA - case ISDN_CTYPE_ELSA: - case ISDN_CTYPE_ELSA_QS1000: - release_io_elsa(cards + cardnr); - break; -#endif -#if CARD_IX1MICROR2 - case ISDN_CTYPE_IX1MICROR2: - release_io_ix1micro(cards + cardnr); - break; + 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 - default: - break; - } - ll_unload(csta); -} - -static int -checkcard(int cardnr, char *id) -{ - long flags; - int ret = 0; - struct IsdnCard *card = cards + cardnr; - struct IsdnCardState *sp; - - save_flags(flags); - cli(); - if (!(sp = (struct IsdnCardState *) - kmalloc(sizeof(struct IsdnCardState), GFP_ATOMIC))) { - printk(KERN_WARNING - "HiSax: No memory for IsdnCardState(card %d)\n", - cardnr + 1); - restore_flags(flags); - return (0); - } - card->sp = sp; - sp->cardnr = cardnr; - sp->cfg_reg = 0; - sp->protocol = card->protocol; + cs->protocol = card->protocol; if ((card->typ > 0) && (card->typ < 31)) { if (!((1 << card->typ) & SUPORTED_CARDS)) { @@ -907,33 +734,37 @@ 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_MODEM | ISDN_FEATURE_L2_TRANS | ISDN_FEATURE_L3_TRANS | #ifdef CONFIG_HISAX_1TR6 @@ -947,21 +778,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: @@ -973,9 +802,20 @@ 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 +#if CARD_S0BOX + case ISDN_CTYPE_S0BOX: + ret = setup_s0box(card); + break; +#endif +#if CARD_TELESPCI + case ISDN_CTYPE_TELESPCI: + ret = setup_telespci(card); + break; +#endif #if CARD_AVM_A1 case ISDN_CTYPE_A1: ret = setup_avm_a1(card); @@ -983,7 +823,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 @@ -992,99 +834,111 @@ 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); + restore_flags(flags); return (1); } -void -HiSax_shiftcards(int idx) +HISAX_INITFUNC(void +HiSax_shiftcards(int idx)) { int i; - for (i = idx; i < 15; i++) + for (i = idx; i < (HISAX_MAX_CARDS - 1); i++) 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; @@ -1114,15 +968,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); } } @@ -1130,165 +984,73 @@ HiSax_inithardware(void) } void -HiSax_closehardware(void) +HiSax_closecard(int cardnr) { - int i; - long flags; + int i,last=nrcards - 1; - save_flags(flags); - cli(); - for (i = 0; i < nrcards; i++) - if (cards[i].sp) { - ll_stop(cards[i].sp); - CallcFreeChan(cards[i].sp); - release_tei(cards[i].sp); - release_irq(i); - closecard(i); - kfree((void *) cards[i].sp); - cards[i].sp = NULL; - } - Isdnl2Free(); - CallcFree(); - restore_flags(flags); -} - -static void -hscx_l2l1(struct PStack *st, int pr, void *arg) -{ - struct sk_buff *skb = arg; - struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware; - struct HscxState *hsp = sp->hs + st->l1.hscx; - long flags; - - switch (pr) { - case (PH_DATA): - save_flags(flags); - cli(); - if (hsp->tx_skb) { - skb_queue_tail(&hsp->squeue, skb); - restore_flags(flags); - } else { - restore_flags(flags); - hsp->tx_skb = skb; - hsp->count = 0; - sp->hscx_fill_fifo(hsp); - } - break; - case (PH_DATA_PULLED): - if (hsp->tx_skb) { - printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n"); - break; - } - hsp->tx_skb = skb; - hsp->count = 0; - sp->hscx_fill_fifo(hsp); - break; - case (PH_REQUEST_PULL): - if (!hsp->tx_skb) { - st->l1.requestpull = 0; - st->l1.l1l2(st, PH_PULL_ACK, NULL); - } else - st->l1.requestpull = !0; - break; - } - -} -extern struct IsdnBuffers *tracebuf; - -static void -hscx_l2l1discardq(struct PStack *st, int pr, void *heldby, - int releasetoo) -{ - struct IsdnCardState *sp = (struct IsdnCardState *) - st->l1.hardware; - struct HscxState *hsp = sp->hs + st->l1.hscx; - struct sk_buff *skb; - -#ifdef DEBUG_MAGIC - if (hsp->magic != 301270) { - printk(KERN_DEBUG "hscx_discardq magic not 301270\n"); + if (cardnr>last) return; + if (cards[cardnr].cs) { + ll_stop(cards[cardnr].cs); + release_tei(cards[cardnr].cs); + closecard(cardnr); + free_irq(cards[cardnr].cs->irq, cards[cardnr].cs); + kfree((void *) cards[cardnr].cs); + cards[cardnr].cs = NULL; } -#endif - - while ((skb = skb_dequeue(&hsp->squeue))) - dev_kfree_skb(skb, FREE_WRITE); -} - -static int -open_hscxstate(struct IsdnCardState *sp, - int hscx) -{ - struct HscxState *hsp = sp->hs + hscx; - - if (!hsp->init) { - if (!(hsp->rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) { - printk(KERN_WARNING - "HiSax: No memory for hscx_rcvbuf\n"); - return (1); + i = cardnr; + while (i!=last) { + cards[i] = cards[i+1]; + i++; } - 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); + nrcards--; } 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 */ @@ -1325,7 +1087,7 @@ l2cmd(u_char cmd) } } -static char tmp[20]; +static char tmp[24]; char * l2frames(u_char * ptr) @@ -1358,7 +1120,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; @@ -1366,13 +1128,381 @@ 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; + + FsmChangeState(fi, ST_L1_F3); + if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) + st->l1.l1hw(st, HW_ENABLE | REQUEST, 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; + + if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) { + FsmChangeState(fi, ST_L1_F4); + st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL); + FsmDelTimer(&st->l1.timer, 1); + FsmAddTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2); + test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags); + } else + FsmChangeState(fi, ST_L1_F3); +} + +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; + + FsmChangeState(fi, ST_L1_F6); + st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL); +} + +static void +l1_info4_ind(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L1_F7); + st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL); + if (test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags)) + FsmDelTimer(&st->l1.timer, 4); + if (!test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) { + if (test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags)) + FsmDelTimer(&st->l1.timer, 3); + FsmAddTimer(&st->l1.timer, 110, EV_TIMER_ACT, NULL, 2); + test_and_set_bit(FLG_L1_ACTTIMER, &st->l1.Flags); + } +} + +static void +l1_timer3(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags); + if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) + L1deactivated(st->l1.hardware); + if (st->l1.l1m.state != ST_L1_F6) { + FsmChangeState(fi, ST_L1_F3); + st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL); + } +} + +static void +l1_timer_act(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + test_and_clear_bit(FLG_L1_ACTTIMER, &st->l1.Flags); + test_and_set_bit(FLG_L1_ACTIVATED, &st->l1.Flags); + L1activated(st->l1.hardware); +} + +static void +l1_timer_deact(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags); + test_and_clear_bit(FLG_L1_ACTIVATED, &st->l1.Flags); + L1deactivated(st->l1.hardware); + st->l1.l1hw(st, HW_DEACTIVATE | RESPONSE, NULL); +} + +static void +l1_activate(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + st->l1.l1hw(st, HW_RESET | REQUEST, NULL); +} + +static struct FsmNode L1DFnList[] 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 L1D_FN_COUNT (sizeof(L1DFnList)/sizeof(struct FsmNode)) + +static void +l1b_activate(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L1_WAIT_ACT); + FsmAddTimer(&st->l1.timer, st->l1.delay, EV_TIMER_ACT, NULL, 2); +} + +static void +l1b_deactivate(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L1_WAIT_DEACT); + FsmAddTimer(&st->l1.timer, 10, EV_TIMER_DEACT, NULL, 2); +} + +static void +l1b_timer_act(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L1_ACTIV); + st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); +} + +static void +l1b_timer_deact(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L1_NULL); + st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL); +} + +static struct FsmNode L1BFnList[] HISAX_INITDATA = +{ + {ST_L1_NULL, EV_PH_ACTIVATE, l1b_activate}, + {ST_L1_WAIT_ACT, EV_TIMER_ACT, l1b_timer_act}, + {ST_L1_ACTIV, EV_PH_DEACTIVATE, l1b_deactivate}, + {ST_L1_WAIT_DEACT, EV_TIMER_DEACT, l1b_timer_deact}, +}; + +#define L1B_FN_COUNT (sizeof(L1BFnList)/sizeof(struct FsmNode)) + +HISAX_INITFUNC(void Isdnl1New(void)) +{ + l1fsm_d.state_count = L1D_STATE_COUNT; + l1fsm_d.event_count = L1_EVENT_COUNT; + l1fsm_d.strEvent = strL1Event; + l1fsm_d.strState = strL1DState; + FsmNew(&l1fsm_d, L1DFnList, L1D_FN_COUNT); + l1fsm_b.state_count = L1B_STATE_COUNT; + l1fsm_b.event_count = L1_EVENT_COUNT; + l1fsm_b.strEvent = strL1Event; + l1fsm_b.strState = strL1BState; + FsmNew(&l1fsm_b, L1BFnList, L1B_FN_COUNT); +} + +void Isdnl1Free(void) +{ + FsmFree(&l1fsm_d); + FsmFree(&l1fsm_b); +} + +static void +dch_l2l1(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + char tmp[32]; + + switch (pr) { + case (PH_DATA | REQUEST): + case (PH_PULL | REQUEST): + case (PH_PULL |INDICATION): + st->l1.l1hw(st, pr, arg); + break; + case (PH_ACTIVATE | REQUEST): + if (cs->debug) { + sprintf(tmp, "PH_ACTIVATE_REQ %s", + strL1DState[st->l1.l1m.state]); + debugl1(cs, tmp); + } + if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) + st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); + else { + test_and_set_bit(FLG_L1_ACTIVATING, &st->l1.Flags); + FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, arg); + } + break; + case (PH_TESTLOOP | REQUEST): + if (1 & (long) arg) + debugl1(cs, "PH_TEST_LOOP B1"); + if (2 & (long) arg) + debugl1(cs, "PH_TEST_LOOP B2"); + if (!(3 & (long) arg)) + debugl1(cs, "PH_TEST_LOOP DISABLED"); + st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg); + break; + default: + if (cs->debug) { + sprintf(tmp, "dch_l2l1 msg %04X unhandled", pr); + debugl1(cs, tmp); + } + break; + } +} + +void +l1_msg(struct IsdnCardState *cs, int pr, void *arg) { + struct PStack *st; + + st = cs->stlist; + + while (st) { + switch(pr) { + case (HW_RESET | INDICATION): + FsmEvent(&st->l1.l1m, EV_RESET_IND, arg); + break; + case (HW_DEACTIVATE | CONFIRM): + FsmEvent(&st->l1.l1m, EV_DEACT_CNF, arg); + break; + case (HW_DEACTIVATE | INDICATION): + FsmEvent(&st->l1.l1m, EV_DEACT_IND, arg); + break; + case (HW_POWERUP | CONFIRM): + FsmEvent(&st->l1.l1m, EV_POWER_UP, arg); + break; + case (HW_RSYNC | INDICATION): + FsmEvent(&st->l1.l1m, EV_RSYNC_IND, arg); + break; + case (HW_INFO2 | INDICATION): + FsmEvent(&st->l1.l1m, EV_INFO2_IND, arg); + break; + case (HW_INFO4_P8 | INDICATION): + case (HW_INFO4_P10 | INDICATION): + FsmEvent(&st->l1.l1m, EV_INFO4_IND, arg); + break; + default: + if (cs->debug) { + sprintf(tmp, "l1msg %04X unhandled", pr); + debugl1(cs, tmp); + } + break; + } + st = st->next; + } +} + +void +l1_msg_b(struct PStack *st, int pr, void *arg) { + switch(pr) { + case (PH_ACTIVATE | REQUEST): + FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, NULL); + break; + case (PH_DEACTIVATE | REQUEST): + FsmEvent(&st->l1.l1m, EV_PH_DEACTIVATE, NULL); + break; + } +} + +void +setstack_HiSax(struct PStack *st, struct IsdnCardState *cs) +{ + st->l1.hardware = cs; + st->protocol = cs->protocol; + st->l1.l1m.fsm = &l1fsm_d; + st->l1.l1m.state = ST_L1_F3; + st->l1.l1m.debug = cs->debug; + st->l1.l1m.userdata = st; + st->l1.l1m.userint = 0; + st->l1.l1m.printdebug = l1m_debug; + FsmInitTimer(&st->l1.l1m, &st->l1.timer); + setstack_tei(st); + setstack_manager(st); + st->l1.stlistp = &(cs->stlist); + st->l2.l2l1 = dch_l2l1; + st->l1.Flags = 0; + cs->setstack_d(st, cs); +} + +void +setstack_l1_B(struct PStack *st) +{ + struct IsdnCardState *cs = st->l1.hardware; + + st->l1.l1m.fsm = &l1fsm_b; + st->l1.l1m.state = ST_L1_NULL; + st->l1.l1m.debug = cs->debug; + st->l1.l1m.userdata = st; + st->l1.l1m.userint = 0; + st->l1.l1m.printdebug = l1m_debug; + FsmInitTimer(&st->l1.l1m, &st->l1.timer); +} diff --git a/drivers/isdn/hisax/isdnl1.h b/drivers/isdn/hisax/isdnl1.h index 98418c821841..d40a3bb32a66 100644 --- a/drivers/isdn/hisax/isdnl1.h +++ b/drivers/isdn/hisax/isdnl1.h @@ -1,18 +1,26 @@ -/* $Id: isdnl1.h,v 1.4 1997/04/06 22:55:52 keil Exp $ - * +/* $Id: isdnl1.h,v 1.4.2.4 1998/05/27 18:05:49 keil Exp $ + * $Log: isdnl1.h,v $ - * Revision 1.4 1997/04/06 22:55:52 keil - * Using SKB's + * Revision 1.4.2.4 1998/05/27 18:05:49 keil + * HiSax 3.0 + * + * Revision 1.4.2.3 1997/12/01 09:09:08 keil + * more l1 debug * - * Revision 1.3 1996/12/08 19:41:55 keil - * L2FRAME_DEBUG + * Revision 1.4.2.2 1997/11/15 18:50:40 keil + * new common init function * - * Revision 1.2 1996/10/27 22:26:27 keil - * ISAC/HSCX version functions + * Revision 1.4.2.1 1997/10/17 22:13:58 keil + * update to last hisax version * - * Revision 1.1 1996/10/13 20:03:47 keil - * Initial revision + * Revision 2.2 1997/07/30 17:11:09 keil + * L1deactivated exported * + * 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 +37,28 @@ #define L1_DEB_HSCX 0x10 #define L1_DEB_HSCX_FIFO 0x20 #define L1_DEB_LAPD 0x40 +#define L1_DEB_IPAC 0x80 +#define L1_DEB_RECEIVE_FRAME 0x100 +#define L1_DEB_MONITOR 0x200 +#define 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 get_irq(int cardnr, void *routine); +extern void DChannel_proc_xmt(struct IsdnCardState *cs); +extern void DChannel_proc_rcv(struct IsdnCardState *cs); +extern void l1_msg(struct IsdnCardState *cs, int pr, void *arg); +extern void l1_msg_b(struct PStack *st, int pr, void *arg); + #ifdef L2FRAME_DEBUG extern void Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir); diff --git a/drivers/isdn/hisax/isdnl2.c b/drivers/isdn/hisax/isdnl2.c index 87f4814a5a1d..9bb4f3599b15 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 1.10.2.9 1998/06/19 15:17:56 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,56 +7,58 @@ * Fritz Elfert * * $Log: isdnl2.c,v $ - * Revision 1.10 1997/05/06 09:38:13 keil - * Bugfixes: - clear ack queue entries after resend - * - acknowlege each frame to linklevel - * - UA for SABM is Response, not command - * - only RR was send as supervisor frame (X.75 hangs after a - * sequence error) + * Revision 1.10.2.9 1998/06/19 15:17:56 keil + * fix LAPB tx_cnt for none I-frames * - * Revision 1.9 1997/04/07 23:02:11 keil - * missing braces + * Revision 1.10.2.8 1998/06/18 23:12:05 keil + * LAPB bugfix * - * Revision 1.8 1997/04/06 22:59:59 keil - * Using SKB's; changing function names; some minor changes + * Revision 1.10.2.7 1998/05/27 18:05:51 keil + * HiSax 3.0 * - * Revision 1.7 1997/02/09 00:25:44 keil - * new interface handling, one interface per card + * Revision 1.10.2.6 1998/05/26 10:36:57 keil + * fixes from certification * - * Revision 1.6 1997/01/21 22:23:42 keil - * D-channel log changed + * Revision 1.10.2.5 1998/03/07 23:15:31 tsbogend + * made HiSax working on Linux/Alpha * - * Revision 1.5 1997/01/04 13:47:06 keil - * handling of MDL_REMOVE added (Thanks to Sim Yskes) + * Revision 1.10.2.4 1998/01/27 22:44:38 keil + * fixed window size calculation * - * Revision 1.4 1996/12/08 19:51:51 keil - * many fixes from Pekka Sarnila + * Revision 1.10.2.3 1997/11/15 18:54:03 keil + * cosmetics * - * Revision 1.3 1996/11/05 19:39:12 keil - * X.75 bugfixes Thank to Martin Maurer + * Revision 1.10.2.2 1997/10/17 22:13:59 keil + * update to last hisax version * - * Revision 1.2 1996/10/30 10:20:58 keil - * X.75 answer of SABMX fixed to response address (AVM X.75 problem) + * Revision 2.2 1997/07/31 11:49:05 keil + * Eroor handling for no TEI assign * - * Revision 1.1 1996/10/13 20:04:54 keil - * Initial revision + * Revision 2.1 1997/07/27 21:34:38 keil + * cosmetics * + * Revision 2.0 1997/06/26 11:07:29 keil + * New q.921 and X.75 Layer2 * * + * Old log removed KKe + * */ #define __NO_VERSION__ #include "hisax.h" #include "isdnl2.h" -const char *l2_revision = "$Revision: 1.10 $"; +const char *l2_revision = "$Revision: 1.10.2.9 $"; 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 +72,7 @@ enum { static char *strL2State[] = { "ST_L2_1", + "ST_L2_2", "ST_L2_3", "ST_L2_4", "ST_L2_5", @@ -81,53 +84,51 @@ 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_NOTEIPROC, + EV_L2_MDL_ERROR, + EV_L1_DEACTIVATE, EV_L2_T200, - EV_L2_ACK_PULL, EV_L2_T203, - EV_L2_RNR, }; -#define L2_EVENT_COUNT (EV_L2_RNR+1) +#define L2_EVENT_COUNT (EV_L2_T203+1) static char *strL2Event[] = { "EV_L2_UI", "EV_L2_SABMX", - "EV_L2_UA", "EV_L2_DISC", - "EV_L2_I", - "EV_L2_RR", - "EV_L2_REJ", + "EV_L2_DM", + "EV_L2_UA", "EV_L2_FRMR", + "EV_L2_SUPER", + "EV_L2_I", "EV_L2_DL_DATA", + "EV_L2_ACK_PULL", + "EV_L2_DL_UNIT_DATA", "EV_L2_DL_ESTABLISH", + "EV_L2_DL_RELEASE", "EV_L2_MDL_ASSIGN", "EV_L2_MDL_REMOVE", - "EV_L2_DL_UNIT_DATA", - "EV_L2_DL_RELEASE", - "EV_L2_MDL_NOTEIPROC", + "EV_L2_MDL_ERROR", + "EV_L1_DEACTIVATE", "EV_L2_T200", - "EV_L2_ACK_PULL", "EV_L2_T203", - "EV_L2_RNR", }; -int errcount = 0; - -static int l2addrsize(struct Layer2 *tsp); +static int l2addrsize(struct Layer2 *l2); static void InitWin(struct Layer2 *l2) @@ -143,7 +144,6 @@ ReleaseWin(struct Layer2 *l2) { int i, cnt = 0; - for (i = 0; i < MAX_WINDOW; i++) { if (l2->windowar[i]) { cnt++; @@ -155,55 +155,51 @@ 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 -discard_i_queue(struct PStack *st) +inline void +clear_exception(struct Layer2 *l2) { - struct sk_buff *skb; - - while ((skb = skb_dequeue(&st->l2.i_queue))) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); - } + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + test_and_clear_bit(FLG_REJEXC, &l2->flag); + test_and_clear_bit(FLG_OWN_BUSY, &l2->flag); + test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); } -int -l2headersize(struct Layer2 *tsp, int ui) +inline int +l2headersize(struct Layer2 *l2, int ui) { - return ((tsp->extended && (!ui) ? 2 : 1) + (tsp->laptype == LAPD ? 2 : 1)); + return (((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) + + (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1)); } -int -l2addrsize(struct Layer2 *tsp) +inline int +l2addrsize(struct Layer2 *l2) { - return (tsp->laptype == LAPD ? 2 : 1); + return (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1); } static int -sethdraddr(struct Layer2 *tsp, - u_char * header, int rsp) +sethdraddr(struct Layer2 *l2, u_char * header, int rsp) { u_char *ptr = header; - int crbit; + int crbit = rsp; - if (tsp->laptype == LAPD) { - crbit = rsp; - if (!tsp->orig) - crbit = !crbit; - *ptr++ = (tsp->sap << 2) | (crbit ? 2 : 0); - *ptr++ = (tsp->tei << 1) | 1; + if (test_bit(FLG_LAPD, &l2->flag)) { + *ptr++ = (l2->sap << 2) | (rsp ? 2 : 0); + *ptr++ = (l2->tei << 1) | 1; return (2); } else { - crbit = rsp; - if (tsp->orig) + if (test_bit(FLG_ORIG, &l2->flag)) crbit = !crbit; if (crbit) *ptr++ = 1; @@ -213,82 +209,108 @@ sethdraddr(struct Layer2 *tsp, } } -static void -enqueue_ui(struct PStack *st, - struct sk_buff *skb) +inline static void +enqueue_super(struct PStack *st, + struct sk_buff *skb) { - st->l2.l2l1(st, PH_DATA, skb); + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l1.bcs->tx_cnt += skb->len; + st->l2.l2l1(st, PH_DATA | REQUEST, skb); } -static void -enqueue_super(struct PStack *st, - struct sk_buff *skb) +#define enqueue_ui(a, b) enqueue_super(a, b) + +inline int +IsUI(u_char * data, int ext) { - st->l2.l2l1(st, PH_DATA, skb); + return ((data[0] & 0xef) == UI); } -static int -legalnr(struct PStack *st, int nr) +inline int +IsUA(u_char * data, int ext) { - struct Layer2 *l2 = &st->l2; - int lnr, lvs; + return ((data[0] & 0xef) == UA); +} - lvs = (l2->vs >= l2->va) ? l2->vs : (l2->vs + l2->extended ? 128 : 8); - lnr = (nr >= l2->va) ? nr : (nr + l2->extended ? 128 : 8); - return (lnr <= lvs); +inline int +IsDM(u_char * data, int ext) +{ + return ((data[0] & 0xef) == DM); } -static void -setva(struct PStack *st, int nr) +inline int +IsDISC(u_char * data, int ext) { - struct Layer2 *l2 = &st->l2; + return ((data[0] & 0xef) == DISC); +} - if (l2->va != nr) { - while (l2->va != nr) { - l2->va = (l2->va + 1) % (l2->extended ? 128 : 8); - dev_kfree_skb(l2->windowar[l2->sow], FREE_WRITE); - l2->windowar[l2->sow] = NULL; - l2->sow = (l2->sow + 1) % l2->window; - if (st->l4.l2writewakeup) - st->l4.l2writewakeup(st); - } - } +inline int +IsRR(u_char * data, int ext) +{ + if (ext) + return (data[0] == RR); + else + return ((data[0] & 0xf) == 1); } -static void -l2s1(struct FsmInst *fi, int event, void *arg) +inline int +IsSABMX(u_char * data, int ext) { - struct PStack *st = fi->userdata; + u_char d = data[0] & ~0x10; - st->l2.l2tei(st, MDL_ASSIGN, (void *) st->l2.ces); - FsmChangeState(fi, ST_L2_3); + 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], FREE_WRITE); + l2->windowar[l2->sow] = NULL; + l2->sow = (l2->sow + 1) % l2->window; + if (st->lli.l2writewakeup && (len >=0)) + st->lli.l2writewakeup(st, len); + } +} + +static void send_uframe(struct PStack *st, u_char cmd, u_char cr) { struct sk_buff *skb; @@ -306,53 +328,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, FREE_READ); +} + + +inline u_char +get_PollFlagFree(struct PStack *st, struct sk_buff *skb) +{ + u_char PF; + + PF = get_PollFlag(st, skb); + FreeSkb(skb); + return (PF); +} + static void establishlink(struct FsmInst *fi) { struct PStack *st = fi->userdata; u_char cmd; - FsmChangeState(fi, ST_L2_5); + clear_exception(&st->l2); st->l2.rc = 0; + cmd = (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM) | 0x10; + send_uframe(st, cmd, CMD); + FsmDelTimer(&st->l2.t203, 1); + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 1); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + FsmChangeState(fi, ST_L2_5); +} - 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"); +static void +l2_mdl_error(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct PStack *st = fi->userdata; + switch (event) { + case EV_L2_UA: + if (get_PollFlagFree(st, skb)) + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'C'); + else + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'D'); + break; + case EV_L2_DM: + if (get_PollFlagFree(st, skb)) + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B'); + else { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); + } + break; + } +} - cmd = (st->l2.extended ? SABME : SABM) | 0x10; - send_uframe(st, cmd, CMD); +static void +l2_dl_establish(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + int state = fi->state; + + FsmChangeState(fi, ST_L2_3); + if (state == ST_L2_1) + st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL); } static void -l2_establish(struct FsmInst *fi, int event, void *arg) +l2_send_ui(struct PStack *st) { - establishlink(fi); + struct sk_buff *skb; + u_char header[MAX_HEADER_LEN]; + int i; + + i = sethdraddr(&(st->l2), header, CMD); + header[i++] = UI; + while ((skb = skb_dequeue(&st->l2.ui_queue))) { + memcpy(skb_push(skb, i), header, i); + enqueue_ui(st, skb); + } } static void -l2_send_disconn(struct FsmInst *fi, int event, void *arg) +l2_put_ui(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; - - FsmChangeState(fi, ST_L2_6); + struct sk_buff *skb = arg; - FsmDelTimer(&st->l2.t203_timer, 1); - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 2); - st->l2.t200_running = 0; + skb_queue_tail(&st->l2.ui_queue, skb); + if (fi->state == ST_L2_1) { + FsmChangeState(fi, ST_L2_2); + st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL); } - st->l2.rc = 0; - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 2)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 2"); + if (fi->state > ST_L2_3) + l2_send_ui(st); +} +static void +l2_got_ui(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; - 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 | INDICATION, (void *) 'O'); + FreeSkb(skb); + } else + st->l2.l2l3(st, DL_UNIT_DATA | INDICATION, skb); +} + +static void +l2_establish(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + if (fi->state != ST_L2_4) + discard_queue(&st->l2.i_queue); + if (fi->state != ST_L2_5) + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &st->l2.flag); +} - discard_i_queue(st); +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.l2l3(st, DL_RELEASE | CONFIRM, NULL); + return; + } else if (fi->state == ST_L2_5) { + test_and_set_bit(FLG_PEND_REL, &st->l2.flag); + return; + } + discard_queue(&st->l2.i_queue); + FsmChangeState(fi, ST_L2_6); + st->l2.rc = 0; + send_uframe(st, DISC | 0x10, CMD); + FsmDelTimer(&st->l2.t203, 1); + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 2); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); } static void @@ -360,46 +485,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, FREE_READ); - - if (ST_L2_4 != state) + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; + if (rsp) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + if (skb->len != (l2addrsize(&st->l2) + 1)) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + PollFlag = get_PollFlagFree(st, skb); + if (ST_L2_6 == state) { + send_uframe(st, DM | PollFlag, RSP); + return; + } else + send_uframe(st, UA | PollFlag, RSP); + if (ST_L2_5 == state) + return; + if (ST_L2_4 != state) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'F'); if (st->l2.vs != st->l2.va) { - discard_i_queue(st); + discard_queue(&st->l2.i_queue); est = 1; } else est = 0; - + } + clear_exception(&st->l2); st->l2.vs = 0; st->l2.va = 0; st->l2.vr = 0; st->l2.sow = 0; - if (ST_L2_7 != state) - FsmChangeState(fi, ST_L2_7); - - send_uframe(st, UA | PollFlag, RSP); - - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 15); - st->l2.t200_running = 0; - } - if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 3)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 3"); + FsmChangeState(fi, ST_L2_7); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + FsmRestartTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3); if (est) - st->l2.l2man(st, DL_ESTABLISH, NULL); + st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL); if (ST_L2_8 == state) if (skb_queue_len(&st->l2.i_queue) && cansend(st)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); } static void @@ -407,87 +545,166 @@ l2_got_disconn(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - struct Channel *chanp = st->l4.userdata; - u_char PollFlag; - - skb_pull(skb, l2addrsize(&(st->l2))); - PollFlag = *skb->data & 0x10; - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); + u_char PollFlag, cmd = UA; + int state, rel = 1, cst = 1, rsp; - FsmChangeState(fi, ST_L2_4); + state = fi->state; + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; - FsmDelTimer(&st->l2.t203_timer, 3); - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 4); - st->l2.t200_running = 0; + if (rsp) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + if (skb->len != (l2addrsize(&st->l2) + 1)) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + PollFlag = get_PollFlagFree(st, skb); + if ((state == ST_L2_4) || (state == ST_L2_5)) { + rel = 0; + cst = 0; + cmd = DM; + } else if (state == ST_L2_6) { + rel = 0; + cst = 0; + } + if (cst) { + FsmChangeState(fi, ST_L2_4); + FsmDelTimer(&st->l2.t203, 3); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + } + send_uframe(st, cmd | PollFlag, RSP); + if (rel) { + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); } - if (!((chanp->impair == 1) && (st->l2.laptype == LAPB))) - send_uframe(st, UA | PollFlag, RSP); - - st->l2.l2man(st, DL_RELEASE, NULL); } + static void -l2_got_st4_disc(struct FsmInst *fi, int event, void *arg) +l2_got_ua(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - struct Channel *chanp = st->l4.userdata; + int pr=-1; u_char PollFlag; + int state,rsp; - skb_pull(skb, l2addrsize(&(st->l2))); - PollFlag = *skb->data & 0x10; - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); - - if (!((chanp->impair == 1) && (st->l2.laptype == LAPB))) - send_uframe(st, DM | (PollFlag ? 0x10 : 0x0), RSP); - -} - -static void -l2_got_ua_establish(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; - u_char f; + state = fi->state; + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; - skb_pull(skb, l2addrsize(&(st->l2))); - f = *skb->data & 0x10; - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); - if (f) { - st->l2.vs = 0; - st->l2.va = 0; - st->l2.vr = 0; - st->l2.sow = 0; - FsmChangeState(fi, ST_L2_7); - - FsmDelTimer(&st->l2.t200_timer, 5); - if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 4)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 4"); - - st->l2.l2man(st, DL_ESTABLISH, NULL); + if (!rsp) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + if (skb->len != (l2addrsize(&st->l2) + 1)) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8)) + establishlink(fi); + return; + } + PollFlag = get_PollFlag(st, skb); + if (!PollFlag) { + l2_mdl_error(fi, event, arg); + return; + } + FreeSkb(skb); + + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + if (fi->state == ST_L2_5) { + if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag)) { + discard_queue(&st->l2.i_queue); + st->l2.rc = 0; + send_uframe(st, DISC | 0x10, CMD); + FsmChangeState(fi, ST_L2_6); + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 4); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + } else { + if (test_and_clear_bit(FLG_L3_INIT, &st->l2.flag)) { + pr = DL_ESTABLISH | CONFIRM; + } else if (st->l2.vs != st->l2.va) { + discard_queue(&st->l2.i_queue); + pr = DL_ESTABLISH | INDICATION; + } + st->l2.vs = 0; + st->l2.va = 0; + st->l2.vr = 0; + st->l2.sow = 0; + FsmChangeState(fi, ST_L2_7); + if (pr > -1) + st->l2.l2l3(st, pr, NULL); + } + } else { /* ST_L2_6 */ + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); + FsmChangeState(fi, ST_L2_4); } } static void -l2_got_ua_disconn(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, FREE_READ); - if (f) { - FsmDelTimer(&st->l2.t200_timer, 6); + state = fi->state; + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; + + if (!rsp) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + if (skb->len != (l2addrsize(&st->l2) + 1)) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8)) + establishlink(fi); + return; + } + PollFlag = get_PollFlagFree(st, skb); + if (!PollFlag) { + if (fi->state == ST_L2_4) { + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); + FsmChangeState(fi, ST_L2_5); + } + } else if (fi->state != ST_L2_4) { + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + if (fi->state == ST_L2_5) + discard_queue(&st->l2.i_queue); + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + if (fi->state == ST_L2_6) + st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); + else + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); FsmChangeState(fi, ST_L2_4); - st->l2.l2man(st, DL_RELEASE, NULL); } } @@ -501,7 +718,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,83 +733,141 @@ 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 | INDICATION, (void *) 'J'); establishlink(fi); } static void -l2_got_st7_RR(struct FsmInst *fi, int event, void *arg) +invoke_retransmission(struct PStack *st, int nr) +{ + struct Layer2 *l2 = &st->l2; + int p1; + long flags; + + if (l2->vs != nr) { + save_flags(flags); + cli(); + while (l2->vs != nr) { + l2->vs = l2->vs - 1; + if (l2->vs < 0) + l2->vs += (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + p1 = l2->vs - l2->va; + if (p1 < 0) + p1 += (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + p1 = (p1 + l2->sow) % l2->window; + if (test_bit(FLG_LAPB, &l2->flag)) + st->l1.bcs->tx_cnt += l2->windowar[p1]->len + l2headersize(l2, 0); + skb_queue_head(&l2->i_queue, l2->windowar[p1]); + l2->windowar[p1] = NULL; + } + restore_flags(flags); + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); + } +} + +static void +l2_got_st7_super(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; struct sk_buff *skb = arg; - int PollFlag, seq, rsp; - struct Layer2 *l2; + int PollFlag, nr, rsp, typ = RR; + struct Layer2 *l2 = &st->l2; - l2 = &st->l2; - if (l2->laptype == LAPD) - rsp = *skb->data & 0x2; - else - rsp = *skb->data == 0x3; - if (l2->orig) + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) rsp = !rsp; skb_pull(skb, l2addrsize(l2)); - if (l2->extended) { - PollFlag = (skb->data[1] & 0x1) == 0x1; - seq = skb->data[1] >> 1; + if (IsRNR(skb->data, test_bit(FLG_MOD128, &l2->flag))) { + test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + typ = RNR; + } else + test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); + if (IsREJ(skb->data, test_bit(FLG_MOD128, &l2->flag))) + typ = REJ; + if (test_bit(FLG_MOD128, &l2->flag)) { + if (skb->len == 2) { + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; + } else { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } } else { - PollFlag = (skb->data[0] & 0x10); - seq = (skb->data[0] >> 5) & 0x7; + if (skb->len == 1) { + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; + } else { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } } - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); + FreeSkb(skb); - if (!((chanp->impair == 4) && (st->l2.laptype == LAPB))) - if ((!rsp) && PollFlag) - enquiry_response(st, RR, PollFlag); - - if (legalnr(st, seq)) { - if (seq == l2->vs) { - setva(st, seq); - FsmDelTimer(&l2->t200_timer, 7); - l2->t200_running = 0; - FsmDelTimer(&l2->t203_timer, 8); - if (FsmAddTimer(&l2->t203_timer, l2->t203, EV_L2_T203, NULL, 5)) - if (l2->l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 5"); - - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); - } else if (l2->va != seq) { - setva(st, seq); - FsmDelTimer(&st->l2.t200_timer, 9); - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 6)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 6"); - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + if ((!rsp) && PollFlag) + enquiry_response(st); + if (rsp && PollFlag) + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'A'); + if (legalnr(st, nr)) { + if (typ == REJ) { + setva(st, nr); + invoke_retransmission(st, nr); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 9); + if (FsmAddTimer(&st->l2.t203, st->l2.T203, + EV_L2_T203, NULL, 6)) + l2m_debug(&st->l2.l2m, "Restart T203 ST7 REJ"); + } else if ((nr == l2->vs) && (typ == RR)) { + setva(st, nr); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 9); + FsmRestartTimer(&st->l2.t203, st->l2.T203, + EV_L2_T203, NULL, 7); + } else if ((l2->va != nr) || (typ == RNR)) { + setva(st, nr); + FsmDelTimer(&st->l2.t203, 9); + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 6); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); } + if (skb_queue_len(&st->l2.i_queue) && (typ == RR)) + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); } else nrerrorrecovery(fi); if ((fi->userint & LC_FLUSH_WAIT) && rsp && !(skb_queue_len(&st->l2.i_queue))) { fi->userint &= ~LC_FLUSH_WAIT; - st->l2.l2man(st, DL_FLUSH, NULL); + st->l2.l2l3(st, DL_FLUSH | INDICATION, NULL); } } @@ -602,251 +877,228 @@ 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); + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0); + if (!((fi->state == ST_L2_5) && test_bit(FLG_L3_INIT, &st->l2.flag))) + skb_queue_tail(&st->l2.i_queue, skb); + if (fi->state == ST_L2_7) + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); } -static int -icommandreceived(struct FsmInst *fi, int event, void *arg, int *nr) +static void +l2_got_iframe(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; struct sk_buff *skb = arg; struct IsdnCardState *sp = st->l1.hardware; struct Layer2 *l2 = &(st->l2); - int i, p, seq, wasok; + int PollFlag, ns, nr, i, hs, rsp; char str[64]; + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + + if (rsp) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); + FreeSkb(skb); + establishlink(fi); + return; + } i = l2addrsize(l2); - if (l2->extended) { - p = (skb->data[i + 1] & 0x1) == 0x1; - seq = skb->data[i] >> 1; - *nr = (skb->data[i + 1] >> 1) & 0x7f; + if (test_bit(FLG_MOD128, &l2->flag)) { + if (skb->len <= (i + 1)) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } else if ((skb->len - i - 1) > l2->maxlen) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O'); + FreeSkb(skb); + establishlink(fi); + return; + } + PollFlag = ((skb->data[i + 1] & 0x1) == 0x1); + ns = skb->data[i] >> 1; + nr = (skb->data[i + 1] >> 1) & 0x7f; } else { - p = (skb->data[i] & 0x10); - seq = (skb->data[i] >> 1) & 0x7; - *nr = (skb->data[i] >> 5) & 0x7; + if (skb->len <= i) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } else if ((skb->len - i) > l2->maxlen) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O'); + FreeSkb(skb); + establishlink(fi); + return; + } + PollFlag = (skb->data[i] & 0x10); + ns = (skb->data[i] >> 1) & 0x7; + nr = (skb->data[i] >> 5) & 0x7; } - - if (l2->vr == seq) { - wasok = !0; - - l2->vr = (l2->vr + 1) % (l2->extended ? 128 : 8); - l2->rejexp = 0; - - if (st->l2.laptype == LAPD) + 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 + l2->ihsize, - skb->len - l2->ihsize, str); + dlogframe(st->l1.hardware, skb->data + hs, + skb->len - hs, str); } - if (!((chanp->impair == 3) && (st->l2.laptype == LAPB))) - if (p || (!skb_queue_len(&st->l2.i_queue))) - enquiry_response(st, RR, p); + if (PollFlag) + enquiry_response(st); + else + test_and_set_bit(FLG_ACK_PEND, &l2->flag); skb_pull(skb, l2headersize(l2, 0)); + st->l2.l2l3(st, DL_DATA | INDICATION, skb); } else { /* n(s)!=v(r) */ - wasok = 0; - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); - if (st->l2.rejexp) { - if (p) - if (!((chanp->impair == 3) && (st->l2.laptype == LAPB))) - enquiry_response(st, RR, p); + FreeSkb(skb); + if (test_and_set_bit(FLG_REJEXC, &l2->flag)) { + if (PollFlag) + enquiry_response(st); } else { - st->l2.rejexp = !0; - enquiry_command(st, REJ, 1); + enquiry_cr(st, REJ, RSP, PollFlag); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); } } - return wasok; -} - -static void -l2_got_st7_data(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; - int nr, wasok; - - wasok = icommandreceived(fi, event, arg, &nr); if (legalnr(st, nr)) { - if (nr == st->l2.vs) { - setva(st, nr); - FsmDelTimer(&st->l2.t200_timer, 10); - st->l2.t200_running = 0; - FsmDelTimer(&st->l2.t203_timer, 11); - if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 7)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 5"); - - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); - } else if (nr != st->l2.va) { - setva(st, nr); - FsmDelTimer(&st->l2.t200_timer, 12); - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 8)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 6"); - - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + setva(st, nr); + if (!test_bit(FLG_PEER_BUSY, &st->l2.flag) && (fi->state == ST_L2_7)) { + if (nr == st->l2.vs) { + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 10); + FsmRestartTimer(&st->l2.t203, st->l2.T203, + EV_L2_T203, NULL, 7); + } else if (nr != st->l2.va) { + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, + NULL, 8); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + } } - } else + } else { nrerrorrecovery(fi); + return; + } - if (wasok) - st->l2.l2l3(st, DL_DATA, skb); + if (skb_queue_len(&st->l2.i_queue) && (fi->state == ST_L2_7)) + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); + if (test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag)) + enquiry_cr(st, RR, RSP, 0); } static void -l2_got_st8_data(struct FsmInst *fi, int event, void *arg) +l2_got_tei(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; - int nr, wasok; - wasok = icommandreceived(fi, event, arg, &nr); + st->l2.tei = (long) arg; - if (legalnr(st, nr)) { - setva(st, nr); - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + if (fi->state == ST_L2_3) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &st->l2.flag); } else - nrerrorrecovery(fi); - - if (wasok) - st->l2.l2l3(st, DL_DATA, skb); + FsmChangeState(fi, ST_L2_4); + if (skb_queue_len(&st->l2.ui_queue)) + l2_send_ui(st); } static void -l2_got_tei(struct FsmInst *fi, int event, void *arg) +l2_st5_tout_200(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - st->l2.tei = (int) arg; - establishlink(fi); -} - -static void -invoke_retransmission(struct PStack *st, int nr) -{ - struct Layer2 *l2 = &st->l2; - int p1; - - if (l2->vs != nr) { - while (l2->vs != nr) { - - l2->vs = l2->vs - 1; - if (l2->vs < 0) - l2->vs += l2->extended ? 128 : 8; - - p1 = l2->vs - l2->va; - if (p1 < 0) - p1 += l2->extended ? 128 : 8; - p1 = (p1 + l2->sow) % l2->window; - - skb_queue_head(&l2->i_queue, l2->windowar[p1]); - l2->windowar[p1] = NULL; - } - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + } else if (st->l2.rc == st->l2.N200) { + FsmChangeState(fi, ST_L2_4); + test_and_clear_bit(FLG_T200_RUN, &st->l2.flag); + discard_queue(&st->l2.i_queue); + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'G'); + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + } else { + st->l2.rc++; + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + send_uframe(st, (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM) + | 0x10, CMD); } } static void -l2_got_st7_rej(struct FsmInst *fi, int event, void *arg) +l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; - int PollFlag, seq, rsp; - struct Layer2 *l2; - l2 = &st->l2; - if (l2->laptype == LAPD) - rsp = *skb->data & 0x2; - else - rsp = *skb->data == 0x3; - if (l2->orig) - rsp = !rsp; - - skb_pull(skb, l2addrsize(l2)); - if (l2->extended) { - PollFlag = (skb->data[1] & 0x1) == 0x1; - seq = skb->data[1] >> 1; + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + } else if (st->l2.rc == st->l2.N200) { + FsmChangeState(fi, ST_L2_4); + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'H'); + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); } else { - PollFlag = (skb->data[0] & 0x10); - seq = (skb->data[0] >> 5) & 0x7; + st->l2.rc++; + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, + NULL, 9); + send_uframe(st, DISC | 0x10, CMD); } - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); - - if ((!rsp) && PollFlag) - enquiry_response(st, RR, PollFlag); - - if (!legalnr(st, seq)) - return; - - setva(st, seq); - invoke_retransmission(st, seq); -} - -static void -l2_no_tei(struct FsmInst *fi, int event, void *arg) -{ - FsmChangeState(fi, ST_L2_4); } static void -l2_st5_tout_200(struct FsmInst *fi, int event, void *arg) +l2_st78_tout_200(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - u_char cmd; - if (st->l2.rc == st->l2.n200) { - FsmChangeState(fi, ST_L2_4); - st->l2.l2tei(st, MDL_VERIFY, (void *) st->l2.tei); - st->l2.l2man(st, DL_RELEASE, NULL); + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + return; + } + test_and_clear_bit(FLG_T200_RUN, &st->l2.flag); + if (fi->state == ST_L2_7) { + st->l2.rc = 0; + FsmChangeState(fi, ST_L2_8); + } + if (st->l2.rc == st->l2.N200) { + establishlink(fi); } else { + transmit_enquiry(st); st->l2.rc++; - - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 9)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 7"); - - cmd = (st->l2.extended ? SABME : SABM) | 0x10; - send_uframe(st, cmd, CMD); } } static void -l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) +l2_st7_tout_203(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; - - if (st->l2.rc == st->l2.n200) { - FsmChangeState(fi, ST_L2_4); - st->l2.l2man(st, DL_RELEASE, NULL); - } else { - st->l2.rc++; - - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 10)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 8"); - - - if (!((chanp->impair == 2) && (st->l2.laptype == LAPB))) - send_uframe(st, DISC | 0x10, CMD); + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 9); + return; } + FsmChangeState(fi, ST_L2_8); + transmit_enquiry(st); + st->l2.rc = 0; } static void l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct sk_buff *skb; + struct sk_buff *skb, *oskb; struct Layer2 *l2 = &st->l2; u_char header[MAX_HEADER_LEN]; int p1, i; @@ -860,7 +1112,7 @@ 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", @@ -871,7 +1123,7 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) i = sethdraddr(&st->l2, header, CMD); - if (l2->extended) { + if (test_bit(FLG_MOD128, &l2->flag)) { header[i++] = l2->vs << 1; header[i++] = l2->vr << 1; l2->vs = (l2->vs + 1) % 128; @@ -879,125 +1131,101 @@ 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); + SET_SKB_FREE(skb); + memcpy(skb_put(skb, i), header, i); + memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len); + FreeSkb(oskb); + } + st->l2.l2l1(st, PH_PULL | INDICATION, skb); + test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag); + if (!test_and_set_bit(FLG_T200_RUN, &st->l2.flag)) { + FsmDelTimer(&st->l2.t203, 13); + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 11); } if (skb_queue_len(&l2->i_queue) && cansend(st)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + st->l2.l2l1(st, PH_PULL | REQUEST, 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 | INDICATION, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } } else { - PollFlag = (skb->data[0] & 0x10); - seq = (skb->data[0] >> 5) & 0x7; + if (skb->len == 1) { + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; + } else { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } } - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); + FreeSkb(skb); if (rsp && PollFlag) { - if (legalnr(st, seq)) { + if (legalnr(st, nr)) { + setva(st, nr); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 7); + FsmDelTimer(&l2->t203, 8); + if (rnr) { + FsmRestartTimer(&l2->t200, l2->T200, + EV_L2_T200, NULL, 14); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + } else + FsmAddTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 5); + invoke_retransmission(st, nr); FsmChangeState(fi, ST_L2_7); - setva(st, seq); - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 14); - st->l2.t200_running = 0; - } - if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 13)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 11"); - - invoke_retransmission(st, seq); - if (skb_queue_len(&l2->i_queue) && cansend(st)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); else if (fi->userint & LC_FLUSH_WAIT) { fi->userint &= ~LC_FLUSH_WAIT; - st->l2.l2man(st, DL_FLUSH, NULL); + st->l2.l2l3(st, DL_FLUSH | INDICATION, NULL); } } } else { if (!rsp && PollFlag) - enquiry_response(st, RR, PollFlag); - if (legalnr(st, seq)) { - setva(st, seq); + enquiry_response(st); + if (legalnr(st, nr)) { + setva(st, nr); } } } -static void -l2_st7_tout_203(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - - st->l2.rc = 0; - FsmChangeState(fi, ST_L2_8); - transmit_enquiry(st); -} - -static void -l2_st8_tout_200(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - - if (st->l2.rc == st->l2.n200) { - establishlink(fi); - } else { - st->l2.rc++; - transmit_enquiry(st); - } -} - static void l2_got_FRMR(struct FsmInst *fi, int event, void *arg) { @@ -1005,20 +1233,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 | INDICATION, (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 | INDICATION, (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, FREE_READ); + if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */ + (IsUA(skb->data, 0) && (fi->state == ST_L2_7))) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'K'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); + } + FreeSkb(skb); } static void @@ -1026,135 +1266,139 @@ 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_queue(&st->l2.i_queue); + discard_queue(&st->l2.ui_queue); + st->l2.tei = -1; + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 18); + FsmDelTimer(&st->l2.t203, 19); + if (fi->state != ST_L2_4) + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); FsmChangeState(fi, ST_L2_1); } -inline int -IsUI(u_char * data, int ext) -{ - return ((data[0] & 0xef) == UI); -} - -inline int -IsUA(u_char * data, int ext) -{ - return ((data[0] & 0xef) == UA); -} - -inline int -IsDISC(u_char * data, int ext) -{ - return ((data[0] & 0xef) == DISC); -} - -inline int -IsRR(u_char * data, int ext) -{ - if (ext) - return (data[0] == RR); - else - return ((data[0] & 0xf) == 1); -} - -inline int -IsI(u_char * data, int ext) -{ - return ((data[0] & 0x1) == 0x0); -} - -inline int -IsSABMX(u_char * data, int ext) +static void +l2_persistant_da(struct FsmInst *fi, int event, void *arg) { - u_char d = data[0] & ~0x10; + struct PStack *st = fi->userdata; + int rel = DL_RELEASE | INDICATION; - return (ext ? d == SABME : d == SABM); -} - -inline int -IsREJ(u_char * data, int ext) -{ - return (ext ? data[0] == REJ : (data[0] & 0xf) == 0x9); -} - -inline int -IsFRMR(u_char * data, int ext) -{ - return ((data[0] & 0xef) == FRMR); -} - -inline int -IsRNR(u_char * data, int ext) -{ - if (ext) - return (data[0] == RNR); - else - return ((data[0] & 0xf) == 5); + + discard_queue(&st->l2.i_queue); + discard_queue(&st->l2.ui_queue); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 18); + FsmDelTimer(&st->l2.t203, 19); + clear_exception(&st->l2); + switch (fi->state) { + case ST_L2_1: + if (!test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag)) + break; + case ST_L2_3: + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + case ST_L2_2: + FsmChangeState(fi, ST_L2_1); + break; + case ST_L2_6: + rel = DL_RELEASE | CONFIRM; + case ST_L2_5: + if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag)) + rel = DL_RELEASE | CONFIRM; + case ST_L2_7: + case ST_L2_8: + st->l2.l2l3(st, rel, NULL); + FsmChangeState(fi, ST_L2_4); + break; + case ST_L2_4: + if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag)) + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + break; + } + test_and_clear_bit(FLG_PEND_REL, &st->l2.flag); + test_and_clear_bit(FLG_L1_ACTIV, &st->l2.flag); } -static struct FsmNode L2FnList[] = +static struct FsmNode L2FnList[] HISAX_INITDATA = { - {ST_L2_1, EV_L2_DL_ESTABLISH, l2s1}, - {ST_L2_1, EV_L2_MDL_NOTEIPROC, l2_no_tei}, - {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, - {ST_L2_4, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {ST_L2_1, EV_L2_DL_ESTABLISH, l2_dl_establish}, + {ST_L2_2, EV_L2_DL_ESTABLISH, l2_dl_establish}, {ST_L2_4, EV_L2_DL_ESTABLISH, l2_establish}, - {ST_L2_7, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {ST_L2_5, EV_L2_DL_ESTABLISH, l2_establish}, + {ST_L2_7, EV_L2_DL_ESTABLISH, l2_establish}, + {ST_L2_8, EV_L2_DL_ESTABLISH, l2_establish}, + {ST_L2_4, EV_L2_DL_RELEASE, l2_dl_release}, + {ST_L2_5, EV_L2_DL_RELEASE, l2_dl_release}, + {ST_L2_7, EV_L2_DL_RELEASE, l2_dl_release}, + {ST_L2_8, EV_L2_DL_RELEASE, l2_dl_release}, + {ST_L2_5, EV_L2_DL_DATA, l2_feed_iqueue}, {ST_L2_7, EV_L2_DL_DATA, l2_feed_iqueue}, - {ST_L2_7, EV_L2_DL_RELEASE, l2_send_disconn}, - {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue}, {ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue}, - {ST_L2_8, EV_L2_DL_RELEASE, l2_send_disconn}, - - {ST_L2_1, EV_L2_UI, l2_receive_ui}, - {ST_L2_4, EV_L2_UI, l2_receive_ui}, + {ST_L2_1, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_2, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_3, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_4, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_5, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_6, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_7, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_8, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ERROR, l2_tei_remove}, + {ST_L2_3, EV_L2_MDL_ERROR, l2_tei_remove}, + {ST_L2_4, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_5, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_6, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove}, {ST_L2_4, EV_L2_SABMX, l2_got_SABMX}, - {ST_L2_4, EV_L2_DISC, l2_got_st4_disc}, - {ST_L2_5, EV_L2_UA, l2_got_ua_establish}, - {ST_L2_6, EV_L2_UA, l2_got_ua_disconn}, - {ST_L2_7, EV_L2_UI, l2_receive_ui}, - {ST_L2_7, EV_L2_DISC, l2_got_disconn}, - {ST_L2_7, EV_L2_I, l2_got_st7_data}, - {ST_L2_7, EV_L2_RR, l2_got_st7_RR}, - {ST_L2_7, EV_L2_REJ, l2_got_st7_rej}, + {ST_L2_5, EV_L2_SABMX, l2_got_SABMX}, + {ST_L2_6, EV_L2_SABMX, l2_got_SABMX}, {ST_L2_7, EV_L2_SABMX, l2_got_SABMX}, - {ST_L2_7, EV_L2_FRMR, l2_got_FRMR}, - {ST_L2_8, EV_L2_RR, l2_got_st8_rr_rej}, - {ST_L2_8, EV_L2_REJ, l2_got_st8_rr_rej}, {ST_L2_8, EV_L2_SABMX, l2_got_SABMX}, + {ST_L2_4, EV_L2_DISC, l2_got_disconn}, + {ST_L2_5, EV_L2_DISC, l2_got_disconn}, + {ST_L2_6, EV_L2_DISC, l2_got_disconn}, + {ST_L2_7, EV_L2_DISC, l2_got_disconn}, {ST_L2_8, EV_L2_DISC, l2_got_disconn}, + {ST_L2_4, EV_L2_UA, l2_mdl_error}, + {ST_L2_5, EV_L2_UA, l2_got_ua}, + {ST_L2_6, EV_L2_UA, l2_got_ua}, + {ST_L2_7, EV_L2_UA, l2_mdl_error}, + {ST_L2_8, EV_L2_UA, l2_mdl_error}, + {ST_L2_4, EV_L2_DM, l2_got_dm}, + {ST_L2_5, EV_L2_DM, l2_got_dm}, + {ST_L2_6, EV_L2_DM, l2_got_dm}, + {ST_L2_7, EV_L2_DM, l2_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_1, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_2, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_3, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_4, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_5, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_6, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da}, }; #define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode)) @@ -1164,40 +1408,69 @@ isdnl2_l1l2(struct PStack *st, int pr, void *arg) { struct sk_buff *skb = arg; u_char *datap; - int ret = !0; + char tmp[32]; + int ret = 1, len; switch (pr) { - case (PH_DATA): + case (PH_DATA | INDICATION): datap = skb->data; - datap += l2addrsize(&st->l2); - - if (IsI(datap, st->l2.extended)) + len = l2addrsize(&st->l2); + if (skb->len > len) + datap += len; + else { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + return; + } + if (!(*datap & 1)) /* I-Frame */ ret = FsmEvent(&st->l2.l2m, EV_L2_I, skb); - else if (IsRR(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_RR, skb); - else if (IsUI(datap, st->l2.extended)) + else if ((*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 | INDICATION, (void *) 'L'); + FreeSkb(skb); + } if (ret) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); + FreeSkb(skb); } break; - case (PH_PULL_ACK): + case (PH_PULL | CONFIRM): FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg); break; + case (PH_PAUSE | INDICATION): + test_and_set_bit(FLG_DCHAN_BUSY, &st->l2.flag); + break; + case (PH_PAUSE | CONFIRM): + test_and_clear_bit(FLG_DCHAN_BUSY, &st->l2.flag); + break; + case (PH_ACTIVATE | CONFIRM): + case (PH_ACTIVATE | INDICATION): + test_and_set_bit(FLG_L1_ACTIV, &st->l2.flag); + if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag)) + FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg); + break; + case (PH_DEACTIVATE | INDICATION): + case (PH_DEACTIVATE | CONFIRM): + test_and_clear_bit(FLG_L1_ACTIV, &st->l2.flag); + FsmEvent(&st->l2.l2m, EV_L1_DEACTIVATE, arg); + break; + default: + sprintf(tmp, "l2 unknown pr %04x", pr); + l2m_debug(&st->l2.l2m, tmp); + break; } } @@ -1205,57 +1478,58 @@ static void isdnl2_l3l2(struct PStack *st, int pr, void *arg) { switch (pr) { - case (DL_DATA): + case (DL_DATA | REQUEST): if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg)) { dev_kfree_skb((struct sk_buff *) arg, FREE_READ); } break; - case (DL_UNIT_DATA): + case (DL_UNIT_DATA | REQUEST): if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg)) { dev_kfree_skb((struct sk_buff *) arg, FREE_READ); } break; - } -} - -static void -isdnl2_manl2(struct PStack *st, int pr, void *arg) -{ - switch (pr) { - case (DL_ESTABLISH): - FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg); + case (DL_ESTABLISH | REQUEST): + if (test_bit(FLG_L1_ACTIV, &st->l2.flag)) { + if (test_bit(FLG_LAPD, &st->l2.flag) || + test_bit(FLG_ORIG, &st->l2.flag)) { + FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg); + } + } else { + if (test_bit(FLG_LAPD, &st->l2.flag) || + test_bit(FLG_ORIG, &st->l2.flag)) { + test_and_set_bit(FLG_ESTAB_PEND, &st->l2.flag); + } + st->l2.l2l1(st, PH_ACTIVATE, NULL); + } break; - case (DL_RELEASE): + case (DL_RELEASE | REQUEST): + if (test_bit(FLG_LAPB, &st->l2.flag)) { + st->l2.l2l1(st, PH_DEACTIVATE, NULL); + } FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE, arg); break; - case (MDL_NOTEIPROC): - FsmEvent(&st->l2.l2m, EV_L2_MDL_NOTEIPROC, NULL); - break; - case (DL_FLUSH): + case (DL_FLUSH | REQUEST): (&st->l2.l2m)->userint |= LC_FLUSH_WAIT; break; - } -} - -static void -isdnl2_teil2(struct PStack *st, int pr, void *arg) -{ - switch (pr) { - case (MDL_ASSIGN): + case (MDL_ASSIGN | REQUEST): FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg); break; - case (MDL_REMOVE): + case (MDL_REMOVE | REQUEST): FsmEvent(&st->l2.l2m, EV_L2_MDL_REMOVE, arg); break; + case (MDL_ERROR | RESPONSE): + FsmEvent(&st->l2.l2m, EV_L2_MDL_ERROR, arg); + break; } } void releasestack_isdnl2(struct PStack *st) { - FsmDelTimer(&st->l2.t200_timer, 15); - FsmDelTimer(&st->l2.t203_timer, 16); - discard_i_queue(st); + FsmDelTimer(&st->l2.t200, 15); + FsmDelTimer(&st->l2.t203, 16); + discard_queue(&st->l2.i_queue); + discard_queue(&st->l2.ui_queue); ReleaseWin(&st->l2); } @@ -1275,17 +1549,16 @@ 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; + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l2.l2m.state = ST_L2_4; + else st->l2.l2m.state = ST_L2_1; st->l2.l2m.debug = 0; st->l2.l2m.userdata = st; @@ -1293,14 +1566,31 @@ 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); +} + +static void +transl2_l3l2(struct PStack *st, int pr, void *arg) +{ + switch (pr) { + case (DL_DATA | REQUEST): + case (DL_UNIT_DATA | REQUEST): + st->l2.l2l1(st, PH_DATA, arg); + break; + case (DL_ESTABLISH | REQUEST): + st->l2.l2l1(st, PH_ACTIVATE, NULL); + break; + case (DL_RELEASE | REQUEST): + st->l2.l2l1(st, PH_DEACTIVATE, NULL); + break; + } } void setstack_transl2(struct PStack *st) { + st->l3.l3l2 = transl2_l3l2; } void @@ -1308,8 +1598,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 e945bd4bdf0c..5b1663c689b5 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 1.10.2.4 1998/05/27 18:05:59 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,38 +7,34 @@ * Fritz Elfert * * $Log: isdnl3.c,v $ - * Revision 1.10 1997/04/06 22:54:16 keil - * Using SKB's - * - * Revision 1.9 1997/03/25 23:11:25 keil - * US NI-1 protocol - * - * Revision 1.8 1997/03/21 18:53:44 keil - * Report no protocol error to syslog too + * Revision 1.10.2.4 1998/05/27 18:05:59 keil + * HiSax 3.0 * - * Revision 1.7 1997/03/17 18:34:38 keil - * fixed oops if no protocol selected during config + * Revision 1.10.2.3 1997/11/15 18:54:09 keil + * cosmetics * - * Revision 1.6 1997/02/16 01:04:08 fritz - * Bugfix: Changed timer handling caused hang with 2.1.X + * Revision 1.10.2.2 1997/10/17 22:14:05 keil + * update to last hisax version * - * Revision 1.5 1997/02/09 00:26:27 keil - * new interface handling, one interface per card - * leased line changes + * Revision 2.1 1997/08/03 14:36:32 keil + * Implement RESTART procedure * - * Revision 1.4 1997/01/27 23:17:44 keil - * delete timers while unloading + * Revision 2.0 1997/07/27 21:15:42 keil + * New Callref based layer3 * - * Revision 1.3 1997/01/21 22:31:12 keil - * new statemachine; L3 timers + * Revision 1.11 1997/06/26 11:11:44 keil + * SET_SKBFREE now on creation of a SKB * - * Revision 1.2 1996/11/05 19:42:04 keil - * using config.h + * Revision 1.10 1997/04/06 22:54:16 keil + * Using SKB's * - * Revision 1.1 1996/10/13 20:04:54 keil - * Initial revision + * Revision 1.9 1997/03/25 23:11:25 keil + * US NI-1 protocol * + * Revision 1.8 1997/03/21 18:53:44 keil + * Report no protocol error to syslog too * + * Remove old logs /KKe * */ #define __NO_VERSION__ @@ -46,42 +42,162 @@ #include "isdnl3.h" #include -const char *l3_revision = "$Revision: 1.10 $"; +const char *l3_revision = "$Revision: 1.10.2.4 $"; -void -l3_debug(struct PStack *st, char *s) +static +struct Fsm l3fsm = +{NULL, 0, 0, NULL, NULL}; + +enum { + ST_L3_LC_REL, + ST_L3_LC_ESTAB_WAIT, + ST_L3_LC_REL_WAIT, + ST_L3_LC_ESTAB, +}; + +#define L3_STATE_COUNT (ST_L3_LC_ESTAB+1) + +static char *strL3State[] = { - char str[256], tm[32]; + "ST_L3_LC_REL", + "ST_L3_LC_ESTAB_WAIT", + "ST_L3_LC_REL_WAIT", + "ST_L3_LC_ESTAB", +}; + +enum { + EV_ESTABLISH_REQ, + EV_ESTABLISH_IND, + EV_ESTABLISH_CNF, + EV_RELEASE_REQ, + EV_RELEASE_CNF, + EV_RELEASE_IND, +}; + +#define L3_EVENT_COUNT (EV_RELEASE_IND+1) + +static char *strL3Event[] = +{ + "EV_ESTABLISH_REQ", + "EV_ESTABLISH_IND", + "EV_ESTABLISH_CNF", + "EV_RELEASE_REQ", + "EV_RELEASE_CNF", + "EV_RELEASE_IND", +}; + +static void +l3m_debug(struct FsmInst *fi, char *s) +{ + struct PStack *st = fi->userdata; + char tm[32], str[256]; jiftime(tm, jiffies); - sprintf(str, "%s Channel %d l3 %s\n", tm, st->l3.channr, s); + sprintf(str, "%s %s %s\n", tm, st->l3.debug_id, s); HiSax_putstatus(st->l1.hardware, str); } +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 -newl3state(struct PStack *st, int state) +l3_debug(struct PStack *st, const char *fmt, ...) { - char tmp[80]; + va_list args; + char str[256], tm[32]; + char *t = str; - if (st->l3.debug & L3_DEB_STATE) { - sprintf(tmp, "newstate %d --> %d", st->l3.state, state); - l3_debug(st, tmp); - } - st->l3.state = state; + va_start(args, fmt); + jiftime(tm, jiffies); + t += sprintf(str, "%s l3 ", tm); + t += vsprintf(t, fmt, args); + va_end(args); + *t++ = '\n'; + *t++ = 0; + HiSax_putstatus(st->l1.hardware, str); +} + +void +newl3state(struct l3_process *pc, int state) +{ + if (pc->debug & L3_DEB_STATE) + l3_debug(pc->st, "newstate cr %d %d --> %d", pc->callref, + pc->state, state); + pc->state = state; } static void L3ExpireTimer(struct L3Timer *t) { - t->st->l4.l4l3(t->st, t->event, NULL); + t->pc->st->lli.l4l3(t->pc->st, t->event, t->pc); } void -L3InitTimer(struct PStack *st, struct L3Timer *t) +L3InitTimer(struct l3_process *pc, struct L3Timer *t) { - t->st = st; + t->pc = pc; t->tl.function = (void *) L3ExpireTimer; t->tl.data = (long) t; init_timer(&t->tl); @@ -109,9 +225,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 * @@ -133,9 +249,10 @@ no_l3_proto(struct PStack *st, int pr, void *arg) { struct sk_buff *skb = arg; - l3_debug(st, "no protocol"); - if (skb) + HiSax_putstatus(st->l1.hardware, "L3 no D protocol\n"); + if (skb) { dev_kfree_skb(skb, FREE_READ); + } } #ifdef CONFIG_HISAX_EURO @@ -150,14 +267,86 @@ 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) +setstack_l3dc(struct PStack *st, struct Channel *chanp) { char tmp[64]; - st->l3.debug = L3_DEB_WARN; - st->l3.channr = chanp->chan; - L3InitTimer(st, &st->l3.timer); + st->l3.proc = NULL; + st->l3.global = NULL; + skb_queue_head_init(&st->l3.squeue); + st->l3.l3m.fsm = &l3fsm; + st->l3.l3m.state = ST_L3_LC_REL; + st->l3.l3m.debug = 1; + st->l3.l3m.userdata = st; + st->l3.l3m.userint = 0; + st->l3.l3m.printdebug = l3m_debug; + strcpy(st->l3.debug_id, "L3DC"); #ifdef CONFIG_HISAX_EURO if (st->protocol == ISDN_PTYPE_EURO) { @@ -175,11 +364,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" : @@ -187,15 +376,155 @@ 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 +isdnl3_trans(struct PStack *st, int pr, void *arg) { + st->l3.l3l2(st, pr, arg); } void releasestack_isdnl3(struct PStack *st) { - StopAllL3Timer(st); + while (st->l3.proc) + release_l3_process(st->l3.proc); + if (st->l3.global) { + StopAllL3Timer(st->l3.global); + kfree(st->l3.global); + st->l3.global = NULL; + } + discard_queue(&st->l3.squeue); +} + +void +setstack_l3bc(struct PStack *st, struct Channel *chanp) +{ + + st->l3.proc = NULL; + st->l3.global = NULL; + skb_queue_head_init(&st->l3.squeue); + st->l3.l3m.fsm = &l3fsm; + st->l3.l3m.state = ST_L3_LC_REL; + st->l3.l3m.debug = 1; + st->l3.l3m.userdata = st; + st->l3.l3m.userint = 0; + st->l3.l3m.printdebug = l3m_debug; + strcpy(st->l3.debug_id, "L3BC"); + st->lli.l4l3 = isdnl3_trans; +} + +static void +lc_activate(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L3_LC_ESTAB_WAIT); + st->l3.l3l2(st, DL_ESTABLISH | REQUEST, NULL); +} + +static void +lc_connect(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + FsmChangeState(fi, ST_L3_LC_ESTAB); + while ((skb = skb_dequeue(&st->l3.squeue))) { + st->l3.l3l2(st, DL_DATA | REQUEST, skb); + } + st->l3.l3l4(st, DL_ESTABLISH | INDICATION, NULL); +} + +static void +lc_release_req(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + if (fi->state == ST_L3_LC_ESTAB_WAIT) + FsmChangeState(fi, ST_L3_LC_REL); + else + FsmChangeState(fi, ST_L3_LC_REL_WAIT); + st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL); +} + +static void +lc_release_ind(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L3_LC_REL); + discard_queue(&st->l3.squeue); + st->l3.l3l4(st, DL_RELEASE | INDICATION, NULL); +} + +/* *INDENT-OFF* */ +static struct FsmNode L3FnList[] HISAX_INITDATA = +{ + {ST_L3_LC_REL, EV_ESTABLISH_REQ, lc_activate}, + {ST_L3_LC_REL, EV_ESTABLISH_IND, lc_connect}, + {ST_L3_LC_REL, EV_ESTABLISH_CNF, lc_connect}, + {ST_L3_LC_ESTAB_WAIT, EV_ESTABLISH_CNF, lc_connect}, + {ST_L3_LC_ESTAB_WAIT, EV_RELEASE_REQ, lc_release_req}, + {ST_L3_LC_ESTAB_WAIT, EV_RELEASE_IND, lc_release_ind}, + {ST_L3_LC_ESTAB, EV_RELEASE_IND, lc_release_ind}, + {ST_L3_LC_ESTAB, EV_RELEASE_REQ, lc_release_req}, + {ST_L3_LC_REL_WAIT, EV_RELEASE_CNF, lc_release_ind}, + {ST_L3_LC_REL_WAIT, EV_ESTABLISH_REQ, lc_activate}, +}; +/* *INDENT-ON* */ + +#define L3_FN_COUNT (sizeof(L3FnList)/sizeof(struct FsmNode)) + +void +l3_msg(struct PStack *st, int pr, void *arg) +{ + + switch (pr) { + case (DL_DATA | REQUEST): + if (st->l3.l3m.state == ST_L3_LC_ESTAB) { + st->l3.l3l2(st, pr, arg); + } else { + struct sk_buff *skb = arg; + + skb_queue_head(&st->l3.squeue, skb); + FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL); + } + break; + case (DL_ESTABLISH | REQUEST): + FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL); + break; + case (DL_ESTABLISH | CONFIRM): + FsmEvent(&st->l3.l3m, EV_ESTABLISH_CNF, NULL); + break; + case (DL_ESTABLISH | INDICATION): + FsmEvent(&st->l3.l3m, EV_ESTABLISH_IND, NULL); + break; + case (DL_RELEASE | INDICATION): + FsmEvent(&st->l3.l3m, EV_RELEASE_IND, NULL); + break; + case (DL_RELEASE | CONFIRM): + FsmEvent(&st->l3.l3m, EV_RELEASE_CNF, NULL); + break; + case (DL_RELEASE | REQUEST): + FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL); + break; + } +} + +HISAX_INITFUNC(void +Isdnl3New(void)) +{ + l3fsm.state_count = L3_STATE_COUNT; + l3fsm.event_count = L3_EVENT_COUNT; + l3fsm.strEvent = strL3Event; + l3fsm.strState = strL3State; + FsmNew(&l3fsm, L3FnList, L3_FN_COUNT); +} + +void +Isdnl3Free(void) +{ + FsmFree(&l3fsm); } diff --git a/drivers/isdn/hisax/isdnl3.h b/drivers/isdn/hisax/isdnl3.h index bed989a18613..4ff2e570010c 100644 --- a/drivers/isdn/hisax/isdnl3.h +++ b/drivers/isdn/hisax/isdnl3.h @@ -1,6 +1,18 @@ -/* $Id: isdnl3.h,v 1.3 1997/04/06 22:54:17 keil Exp $ - * +/* $Id: isdnl3.h,v 1.3.2.2 1998/05/27 18:06:02 keil Exp $ + * $Log: isdnl3.h,v $ + * Revision 1.3.2.2 1998/05/27 18:06:02 keil + * HiSax 3.0 + * + * Revision 1.3.2.1 1997/10/17 22:14:08 keil + * update to last hisax version + * + * Revision 2.0 1997/07/27 21:15:42 keil + * New Callref based layer3 + * + * Revision 1.4 1997/06/26 11:20:57 keil + * ? + * * Revision 1.3 1997/04/06 22:54:17 keil * Using SKB's * @@ -24,15 +36,19 @@ #define L3_DEB_CHARGE 0x08 struct stateentry { - int state; - u_char primitive; - void (*rout) (struct PStack *, u_char, void *); + int state; + int primitive; + void (*rout) (struct l3_process *, u_char, void *); }; -extern void l3_debug(struct PStack *st, char *s); -extern void newl3state(struct PStack *st, int state); -extern void L3InitTimer(struct PStack *st, struct L3Timer *t); +extern void l3_debug(struct PStack *st, const char *fmt, ...); +extern void newl3state(struct l3_process *pc, int state); +extern void L3InitTimer(struct l3_process *pc, struct L3Timer *t); extern void L3DelTimer(struct L3Timer *t); -extern int L3AddTimer(struct L3Timer *t, int millisec, int event); -extern void StopAllL3Timer(struct PStack *st); +extern int L3AddTimer(struct L3Timer *t, int millisec, int event); +extern void StopAllL3Timer(struct l3_process *pc); extern struct sk_buff *l3_alloc_skb(int len); +extern struct l3_process *new_l3_process(struct PStack *st, int cr); +extern void release_l3_process(struct l3_process *p); +extern struct l3_process *getl3proc(struct PStack *st, int cr); +extern void l3_msg(struct PStack *st, int pr, void *arg); diff --git a/drivers/isdn/hisax/ix1_micro.c b/drivers/isdn/hisax/ix1_micro.c index dadeb8f17899..c8f6a4a8e075 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 1.3.2.8 1998/04/08 21:58:41 keil Exp $ * ix1_micro.c low level stuff for ITK ix1-micro Rev.2 isdn cards * derived from the original file teles3.c from Karsten Keil @@ -11,6 +11,27 @@ * Beat Doebeli * * $Log: ix1_micro.c,v $ + * Revision 1.3.2.8 1998/04/08 21:58:41 keil + * New init code + * + * Revision 1.3.2.7 1998/02/11 14:21:01 keil + * cosmetic fixes + * + * Revision 1.3.2.6 1998/01/27 22:37:33 keil + * fast io + * + * Revision 1.3.2.5 1997/11/15 18:50:51 keil + * new common init function + * + * Revision 1.3.2.4 1997/10/17 22:14:09 keil + * update to last hisax version + * + * Revision 2.1 1997/07/27 21:47:09 keil + * new interface structures + * + * Revision 2.0 1997/06/26 11:02:50 keil + * New Layer and card interface + * * Revision 1.3 1997/04/13 19:54:02 keil * Change in IRQ check delay for SMP * @@ -54,17 +75,16 @@ #define __NO_VERSION__ -#include "siemens.h" #include "hisax.h" -#include "teles3.h" +#include "isac.h" +#include "hscx.h" #include "isdnl1.h" -#include extern const char *CardType[]; -const char *ix1_revision = "$Revision: 1.3 $"; +const char *ix1_revision = "$Revision: 1.3.2.8 $"; -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) #define SPECIAL_PORT_OFFSET 3 @@ -73,865 +93,247 @@ 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 { - dev_kfree_skb(hsp->tx_skb, FREE_WRITE); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } + readfifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size); } -/* - * ISAC stuff goes here - */ - static void -isac_empty_fifo(struct IsdnCardState *sp, int count) +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - IsacWriteReg(sp->isac, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; - save_flags(flags); - cli(); - IsacReadFifo(sp->isac, ptr, count); - IsacWriteReg(sp->isac, ISAC_CMDR, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writefifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size); } -static void -isac_fill_fifo(struct IsdnCardState *sp) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - IsacWriteFifo(sp->isac, ptr, count); - IsacWriteReg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_fill_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (readreg(cs->hw.ix1.hscx_ale, + cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0))); } static void -ph_command(struct IsdnCardState *sp, unsigned int command) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - if (sp->debug & L1_DEB_ISAC) { - char tmp[32]; - sprintf(tmp, "ph_command %d", command); - debugl1(sp, tmp); - } - IsacWriteReg(sp->isac, ISAC_CIX0, (command << 2) | 3); + writereg(cs->hw.ix1.hscx_ale, + cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0), value); } +#define READHSCX(cs, nr, reg) readreg(cs->hw.ix1.hscx_ale, \ + cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ix1.hscx_ale, \ + cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0), data) -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; - - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = IsacReadReg(sp->isac, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!(exval & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - IsacWriteReg(sp->isac, ISAC_CMDR, 0x80); - } else { - count = IsacReadReg(sp->isac, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - sp->rcvidx = 0; - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "IX1: D receive out of memory\n"); - else { - SET_SKB_FREE(skb); - memcpy(skb_put(skb, count), sp->rcvbuf, count); - skb_queue_tail(&sp->rq, skb); - } - } - } - sp->rcvidx = 0; - isac_sched_event(sp, ISAC_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - isac_empty_fifo(sp, 32); - } - if (val & 0x20) { /* RSC */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RSC interrupt"); - } - if (val & 0x10) { /* XPR */ - if (sp->tx_skb) - if (sp->tx_skb->len) { - isac_fill_fifo(sp); - goto afterXPR; - } else { - dev_kfree_skb(sp->tx_skb, FREE_WRITE); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (IsacReadReg(sp->isac, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = IsacReadReg(sp->isac, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - } -} +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ix1.hscx_ale, \ + cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt) -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ix1.hscx_ale, \ + cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt) - u_char exval; - struct HscxState *hsp; - char tmp[32]; - - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = HscxReadReg(sp->hscx[1], 1, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = HscxReadReg(sp->hscx[0], 0, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = HscxReadReg(sp->hscx[0], 0, HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs) { - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val, stat = 0; - sp = (struct IsdnCardState *) irq2dev_map[intno]; - - if (!sp) { - printk(KERN_WARNING "Teles: Spurious interrupt!\n"); + if (!cs) { + printk(KERN_WARNING "IX1: Spurious interrupt!\n"); return; } - val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA); + val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40); Start_HSCX: if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); stat |= 1; } - val = IsacReadReg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA); Start_ISAC: if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); stat |= 2; } - val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA); + val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40); if (val) { - if (sp->debug & L1_DEB_HSCX) - debugl1(sp, "HSCX IntStat after IntRoutine"); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); goto Start_HSCX; } - val = IsacReadReg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA); if (val) { - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "ISAC IntStat after IntRoutine"); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } if (stat & 1) { - HscxWriteReg(sp->hscx[0], 0, HSCX_MASK, 0xFF); - HscxWriteReg(sp->hscx[1], 1, HSCX_MASK, 0xFF); - HscxWriteReg(sp->hscx[0], 0, HSCX_MASK, 0x0); - HscxWriteReg(sp->hscx[1], 1, HSCX_MASK, 0x0); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0); } if (stat & 2) { - IsacWriteReg(sp->isac, ISAC_MASK, 0xFF); - IsacWriteReg(sp->isac, ISAC_MASK, 0x0); - } -} - - -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.interrupts[sp->irq]; - sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter); - debugl1(sp, tmp); - clear_pending_ints(sp); - ret = get_irq(sp->cardnr, &ix1micro_interrupt); - if (ret) { - initisac(sp); - sp->modehscx(sp->hs, 0, 0); - sp->modehscx(sp->hs + 1, 0, 0); - while (loop++ < 10) { - /* At least 1-3 irqs must happen - * (one from HSCX A, one from HSCX B, 3rd from ISAC) - */ - if (kstat.interrupts[sp->irq] > sp->counter) - break; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 1; - schedule(); - } - sprintf(tmp, "IRQ %d count %d", sp->irq, - kstat.interrupts[sp->irq]); - debugl1(sp, tmp); - if (kstat.interrupts[sp->irq] == sp->counter) { - printk(KERN_WARNING - "ix1-Micro: IRQ(%d) getting no interrupts during init\n", - sp->irq); - irq2dev_map[sp->irq] = NULL; - free_irq(sp->irq, NULL); - return (0); - } - } - return (ret); + switch (mt) { + case CARD_RESET: + ix1_reset(cs); + return(0); + case CARD_RELEASE: + release_io_ix1micro(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &ix1micro_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); + } + return(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/l3_1tr6.c b/drivers/isdn/hisax/l3_1tr6.c index 99e318ac1660..1448185dd6da 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 1.11.2.3 1998/05/27 18:06:04 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.11.2.3 1998/05/27 18:06:04 keil + * HiSax 3.0 * - * Revision 1.8 1997/01/27 23:20:21 keil - * report revision only ones + * Revision 1.11.2.2 1997/11/15 18:54:12 keil + * cosmetics * - * Revision 1.7 1997/01/21 22:30:07 keil - * new statemachine; L3 timers + * Revision 1.11.2.1 1997/10/17 22:14:12 keil + * update to last hisax version * - * Revision 1.6 1996/12/14 21:07:20 keil - * additional states for CC_REJECT + * Revision 2.1 1997/08/03 15:28:09 keil + * release L3 empty processes * - * Revision 1.5 1996/12/08 19:55:17 keil - * change CC_REJECT_REQ routine + * Revision 2.0 1997/07/27 21:15:45 keil + * New Callref based layer3 * - * Revision 1.4 1996/10/30 10:18:01 keil - * bugfixes in debugging output + * Revision 1.12 1997/06/26 11:11:45 keil + * SET_SKBFREE now on creation of a SKB * - * Revision 1.3 1996/10/27 22:15:37 keil - * bugfix reject handling - * - * Revision 1.2 1996/10/13 23:08:56 keil - * added missing state for callback reject - * - * Revision 1.1 1996/10/13 20:04:55 keil - * Initial revision + * 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: 1.11.2.3 $"; #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 | REQUEST, 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 | REQUEST, skb); } static void -l3_1tr6_setup(struct PStack *st, u_char pr, void *arg) +l3_1tr6_setup(struct l3_process *pc, u_char pr, void *arg) { u_char *p; int bcfound = 0; @@ -172,110 +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, FREE_READ); /* Signal all services, linklevel takes care of Service-Indicator */ if (bcfound) { - if ((st->pa->setup.si1 != 7) && (st->l3.debug & L3_DEB_WARN)) { + if ((pc->para.setup.si1 != 7) && (pc->st->l3.debug & L3_DEB_WARN)) { sprintf(tmp, "non-digital call: %s -> %s", - st->pa->setup.phone, - st->pa->setup.eazmsn); - l3_debug(st, tmp); + pc->para.setup.phone, + pc->para.setup.eazmsn); + l3_debug(pc->st, tmp); } - newl3state(st, 6); - st->l3.l3l4(st, CC_SETUP_IND, NULL); - } + newl3state(pc, 6); + pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc); + } else + release_l3_process(pc); } static void -l3_1tr6_setup_ack(struct PStack *st, u_char pr, void *arg) +l3_1tr6_setup_ack(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); p = skb->data; - newl3state(st, 2); + newl3state(pc, 2); if ((p = findie(p, skb->len, WE0_chanID, 0))) { - st->pa->bchannel = p[2] & 0x3; - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup answer without bchannel"); - SET_SKB_FREE(skb); + 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, FREE_READ); - L3AddTimer(&st->l3.timer, st->l3.t304, CC_T304); - st->l3.l3l4(st, CC_MORE_INFO, NULL); + L3AddTimer(&pc->timer, T304, CC_T304); + pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc); } static void -l3_1tr6_call_sent(struct PStack *st, u_char pr, void *arg) +l3_1tr6_call_sent(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); p = skb->data; if ((p = findie(p, skb->len, WE0_chanID, 0))) { - st->pa->bchannel = p[2] & 0x3; - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup answer without bchannel"); - SET_SKB_FREE(skb); + 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, FREE_READ); - L3AddTimer(&st->l3.timer, st->l3.t310, CC_T310); - newl3state(st, 3); - st->l3.l3l4(st, CC_PROCEEDING_IND, NULL); + L3AddTimer(&pc->timer, T310, CC_T310); + newl3state(pc, 3); + pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc); } static void -l3_1tr6_alert(struct PStack *st, u_char pr, void *arg) +l3_1tr6_alert(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - L3DelTimer(&st->l3.timer); /* T304 */ - newl3state(st, 4); - st->l3.l3l4(st, CC_ALERTING_IND, NULL); + L3DelTimer(&pc->timer); /* T304 */ + newl3state(pc, 4); + pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc); } static void -l3_1tr6_info(struct PStack *st, u_char pr, void *arg) +l3_1tr6_info(struct l3_process *pc, u_char pr, void *arg) { u_char *p; int i, tmpcharge = 0; @@ -289,45 +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->st, CC_CHARGE | INDICATION, pc); } - if (st->l3.debug & L3_DEB_CHARGE) { - sprintf(tmp, "charging info %d", st->pa->chargeinfo); - l3_debug(st, tmp); + if (pc->st->l3.debug & L3_DEB_CHARGE) { + sprintf(tmp, "charging info %d", pc->para.chargeinfo); + l3_debug(pc->st, tmp); } - } else if (st->l3.debug & L3_DEB_CHARGE) - l3_debug(st, "charging info not found"); - SET_SKB_FREE(skb); + } else if (pc->st->l3.debug & L3_DEB_CHARGE) + l3_debug(pc->st, "charging info not found"); dev_kfree_skb(skb, FREE_READ); } static void -l3_1tr6_info_s2(struct PStack *st, u_char pr, void *arg) +l3_1tr6_info_s2(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); } static void -l3_1tr6_connect(struct PStack *st, u_char pr, void *arg) +l3_1tr6_connect(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); /* T310 */ - newl3state(st, 10); - SET_SKB_FREE(skb); + L3DelTimer(&pc->timer); /* T310 */ + newl3state(pc, 10); dev_kfree_skb(skb, FREE_READ); - st->pa->chargeinfo = 0; - st->l3.l3l4(st, CC_SETUP_CNF, NULL); + pc->para.chargeinfo = 0; + pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc); } static void -l3_1tr6_rel(struct PStack *st, u_char pr, void *arg) +l3_1tr6_rel(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; u_char *p; @@ -335,47 +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, FREE_READ); - StopAllL3Timer(st); - newl3state(st, 0); - l3_1TR6_message(st, MT_N1_REL_ACK, PROTO_DIS_N1); - st->l3.l3l4(st, CC_RELEASE_IND, NULL); + StopAllL3Timer(pc); + newl3state(pc, 0); + l3_1TR6_message(pc, MT_N1_REL_ACK, PROTO_DIS_N1); + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + release_l3_process(pc); } static void -l3_1tr6_rel_ack(struct PStack *st, u_char pr, void *arg) +l3_1tr6_rel_ack(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - StopAllL3Timer(st); - newl3state(st, 0); - st->pa->cause = -1; - st->l3.l3l4(st, CC_RELEASE_CNF, NULL); + StopAllL3Timer(pc); + newl3state(pc, 0); + pc->para.cause = -1; + pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc); + release_l3_process(pc); } static void -l3_1tr6_disc(struct PStack *st, u_char pr, void *arg) +l3_1tr6_disc(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; u_char *p; int i, tmpcharge = 0; char a_charge[8], tmp[32]; - StopAllL3Timer(st); + StopAllL3Timer(pc); p = skb->data; if ((p = findie(p, skb->len, WE6_chargingInfo, 6))) { iecpy(a_charge, p, 1); @@ -383,104 +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->st, CC_CHARGE | INDICATION, pc); } - if (st->l3.debug & L3_DEB_CHARGE) { - sprintf(tmp, "charging info %d", st->pa->chargeinfo); - l3_debug(st, tmp); + if (pc->st->l3.debug & L3_DEB_CHARGE) { + sprintf(tmp, "charging info %d", pc->para.chargeinfo); + l3_debug(pc->st, tmp); } - } else if (st->l3.debug & L3_DEB_CHARGE) - l3_debug(st, "charging info not found"); + } 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, FREE_READ); - newl3state(st, 12); - st->l3.l3l4(st, CC_DISCONNECT_IND, NULL); + newl3state(pc, 12); + pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc); } static void -l3_1tr6_connect_ack(struct PStack *st, u_char pr, void *arg) +l3_1tr6_connect_ack(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - newl3state(st, 10); - st->pa->chargeinfo = 0; - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL); + newl3state(pc, 10); + pc->para.chargeinfo = 0; + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc); } static void -l3_1tr6_alert_req(struct PStack *st, u_char pr, void *arg) +l3_1tr6_alert_req(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 7); - l3_1TR6_message(st, MT_N1_ALERT, PROTO_DIS_N1); + newl3state(pc, 7); + l3_1TR6_message(pc, MT_N1_ALERT, PROTO_DIS_N1); } static void -l3_1tr6_setup_rsp(struct PStack *st, u_char pr, void *arg) +l3_1tr6_setup_rsp(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[24]; u_char *p = tmp; int l; - MsgHead(p, st->l3.callref, MT_N1_CONN, PROTO_DIS_N1); - if (st->pa->spv) { /* SPV ? */ + MsgHead(p, pc->callref, MT_N1_CONN, PROTO_DIS_N1); + if (pc->para.spv) { /* SPV ? */ /* NSF SPV */ *p++ = WE0_netSpecFac; *p++ = 4; /* Laenge */ *p++ = 0; *p++ = FAC_SPV; /* SPV */ - *p++ = st->pa->setup.si1; - *p++ = st->pa->setup.si2; + *p++ = pc->para.setup.si1; + *p++ = pc->para.setup.si2; *p++ = WE0_netSpecFac; *p++ = 4; /* Laenge */ *p++ = 0; *p++ = FAC_Activate; /* aktiviere SPV */ - *p++ = st->pa->setup.si1; - *p++ = st->pa->setup.si2; + *p++ = pc->para.setup.si1; + *p++ = pc->para.setup.si2; } - newl3state(st, 8); + newl3state(pc, 8); l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - st->l3.l3l2(st, DL_DATA, skb); - L3DelTimer(&st->l3.timer); - L3AddTimer(&st->l3.timer, st->l3.t313, CC_T313); + pc->st->l3.l3l2(pc->st, DL_DATA | REQUEST, skb); + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T313, CC_T313); } static void -l3_1tr6_reset(struct PStack *st, u_char pr, void *arg) +l3_1tr6_reset(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 0); + release_l3_process(pc); } static void -l3_1tr6_disconnect_req(struct PStack *st, u_char pr, void *arg) +l3_1tr6_disconnect_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; @@ -489,8 +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 | REQUEST, 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->st, CC_NOSETUP_RSP, pc); + 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->st, CC_SETUP_ERR, pc); } static void -l3_1tr6_t305(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t305(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; @@ -559,9 +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,74 +603,74 @@ 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 | REQUEST, skb); + L3AddTimer(&pc->timer, T308, CC_T308_1); } static void -l3_1tr6_t310(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t310(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3_1tr6_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_SETUP_ERR, NULL); + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3_1tr6_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); } static void -l3_1tr6_t313(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t313(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3_1tr6_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_CONNECT_ERR, NULL); + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3_1tr6_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc); } static void -l3_1tr6_t308_1(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t308_1(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_2); - newl3state(st, 19); + L3DelTimer(&pc->timer); + l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1); + L3AddTimer(&pc->timer, T308, CC_T308_2); + newl3state(pc, 19); } static void -l3_1tr6_t308_2(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t308_2(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_RELEASE_ERR, NULL); - newl3state(st, 0); + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc); + release_l3_process(pc); } /* *INDENT-OFF* */ static struct stateentry downstl[] = { {SBIT(0), - CC_SETUP_REQ, l3_1tr6_setup_req}, + CC_SETUP | REQUEST, l3_1tr6_setup_req}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(10), - CC_DISCONNECT_REQ, l3_1tr6_disconnect_req}, + CC_DISCONNECT | REQUEST, l3_1tr6_disconnect_req}, {SBIT(12), - CC_RELEASE_REQ, l3_1tr6_release_req}, + CC_RELEASE | REQUEST, l3_1tr6_release_req}, {ALL_STATES, - CC_DLRL, l3_1tr6_reset}, + CC_DLRL | REQUEST, l3_1tr6_reset}, {SBIT(6), - CC_IGNORE, l3_1tr6_reset}, + CC_IGNORE | REQUEST, l3_1tr6_reset}, {SBIT(6), - CC_REJECT_REQ, l3_1tr6_disconnect_req}, + CC_REJECT | REQUEST, l3_1tr6_disconnect_req}, {SBIT(6), - CC_ALERTING_REQ, l3_1tr6_alert_req}, + CC_ALERTING | REQUEST, l3_1tr6_alert_req}, {SBIT(6) | SBIT(7), - CC_SETUP_RSP, l3_1tr6_setup_rsp}, + CC_SETUP | RESPONSE, l3_1tr6_setup_rsp}, {SBIT(1), CC_T303, l3_1tr6_t303}, {SBIT(2), @@ -688,60 +720,102 @@ 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]; + switch (pr) { + case (DL_DATA | INDICATION): + case (DL_UNIT_DATA | INDICATION): + break; + case (DL_ESTABLISH | CONFIRM): + case (DL_ESTABLISH | INDICATION): + case (DL_RELEASE | INDICATION): + case (DL_RELEASE | CONFIRM): + l3_msg(st, pr, arg); + return; + break; + } + if (skb->len < 4) { + if (st->l3.debug & L3_DEB_PROTERR) { + sprintf(tmp, "up1tr6 len only %ld", skb->len); + l3_debug(st, tmp); + } + dev_kfree_skb(skb, FREE_READ); + return; + } if ((skb->data[0] & 0xfe) != PROTO_DIS_N0) { if (st->l3.debug & L3_DEB_PROTERR) { - sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %ld state %d", - (pr == DL_DATA) ? " " : "(broadcast) ", - skb->data[0], skb->len, st->l3.state); + sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %ld", + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + skb->data[0], skb->len); + l3_debug(st, tmp); + } + dev_kfree_skb(skb, FREE_READ); + return; + } + if (skb->data[1] != 1) { + if (st->l3.debug & L3_DEB_PROTERR) { + sprintf(tmp, "up1tr6 CR len not 1"); l3_debug(st, tmp); } - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); return; } - mt = skb->data[skb->data[1] + 2]; + cr = skb->data[2]; + mt = skb->data[3]; if (skb->data[0] == PROTO_DIS_N0) { - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); if (st->l3.debug & L3_DEB_STATE) { - sprintf(tmp, "up1tr6%s N0 state %d mt %x unhandled", - (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); + sprintf(tmp, "up1tr6%s N0 mt %x unhandled", + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", mt); l3_debug(st, tmp); } } else if (skb->data[0] == PROTO_DIS_N1) { + 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, FREE_READ); + return; + } + } else { + dev_kfree_skb(skb, FREE_READ); + 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, FREE_READ); if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "up1tr6%sstate %d mt %x unhandled", - (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + proc->state, mt); l3_debug(st, tmp); } return; } else { if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "up1tr6%sstate %d mt %x", - (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + proc->state, mt); l3_debug(st, tmp); } - datastln1[i].rout(st, pr, skb); + datastln1[i].rout(proc, pr, skb); } } } @@ -749,26 +823,47 @@ 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 (((DL_ESTABLISH | REQUEST)== pr) || ((DL_RELEASE | REQUEST)== pr)) { + l3_msg(st, pr, NULL); + return; + } else if ((CC_SETUP | REQUEST) == pr) { + chan = arg; + cr = newcallref(); + cr |= 0x80; + if (!(proc = new_l3_process(st, cr))) { + return; + } else { + proc->chan = chan; + chan->proc = proc; + proc->para.setup = chan->setup; + proc->callref = cr; + } + } else { + proc = arg; + } + for (i = 0; i < downstl_len; i++) if ((pr == downstl[i].primitive) && - ((1 << st->l3.state) & downstl[i].state)) + ((1 << proc->state) & downstl[i].state)) break; if (i == downstl_len) { if (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 +872,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..f192de49c32b 100644 --- a/drivers/isdn/hisax/l3_1tr6.h +++ b/drivers/isdn/hisax/l3_1tr6.h @@ -1,12 +1,16 @@ -/* $Id: l3_1tr6.h,v 1.1 1996/10/13 20:03:48 keil Exp $ +/* $Id: l3_1tr6.h,v 1.1.2.1 1997/10/17 22:14:15 keil Exp $ * * German 1TR6 D-channel protocol defines * * $Log: l3_1tr6.h,v $ - * Revision 1.1 1996/10/13 20:03:48 keil - * Initial revision + * Revision 1.1.2.1 1997/10/17 22:14:15 keil + * update to last hisax version * + * Revision 2.0 1997/07/27 21:15:47 keil + * New Callref based layer3 * + * Revision 1.1 1996/10/13 20:03:48 keil + * Initial revision * */ #ifndef l3_1tr6 @@ -29,7 +33,6 @@ #define MT_N0_CLOSE 0x75 #define MT_N0_CLO_ACK 0x77 - /* * MsgType N1 */ @@ -65,8 +68,6 @@ #define MT_N1_REG_REJ 0x6F #define MT_N1_STAT 0x63 - - /* * W Elemente */ @@ -156,5 +157,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 73c49d9d2809..217a440ebf35 100644 --- a/drivers/isdn/hisax/l3dss1.c +++ b/drivers/isdn/hisax/l3dss1.c @@ -1,4 +1,4 @@ -/* $Id: l3dss1.c,v 1.16 1997/06/03 20:43:46 keil Exp $ +/* $Id: l3dss1.c,v 1.16.2.4 1998/05/27 18:06:08 keil Exp $ * EURO/DSS1 D-channel protocol * @@ -9,74 +9,264 @@ * Fritz Elfert * * $Log: l3dss1.c,v $ - * Revision 1.16 1997/06/03 20:43:46 keil - * Display numbers as default + * Revision 1.16.2.4 1998/05/27 18:06:08 keil + * HiSax 3.0 * - * Revision 1.15 1997/04/17 11:50:48 keil - * pa->loc was undefined, if it was not send by the exchange - * - * Revision 1.14 1997/04/06 22:54:20 keil - * Using SKB's - * - * Revision 1.13 1997/03/13 20:37:28 keil - * CLIR and channel request added - * - * Revision 1.12 1997/02/17 00:34:26 keil - * Bugfix: Wrong cause delivered - * - * Revision 1.11 1997/02/16 12:12:47 fritz - * Bugfix: SI2 was nont initialized on incoming calls. - * - * Revision 1.10 1997/02/11 01:37:24 keil - * Changed setup-interface (incoming and outgoing) - * - * Revision 1.9 1997/01/27 23:20:52 keil - * report revision only ones - * - * Revision 1.8 1997/01/21 22:29:41 keil - * new statemachine; L3 timers - * - * Revision 1.7 1996/12/14 21:06:59 keil - * additional states for CC_REJECT + * Revision 1.16.2.3 1998/02/03 23:16:06 keil + * german AOC * - * Revision 1.6 1996/12/08 22:59:16 keil - * fixed calling party number without octet 3a + * Revision 1.16.2.2 1997/11/15 18:54:15 keil + * cosmetics * - * Revision 1.5 1996/12/08 19:53:31 keil - * fixes from Pekka Sarnila + * Revision 1.16.2.1 1997/10/17 22:14:16 keil + * update to last hisax version * - * Revision 1.4 1996/11/05 19:44:36 keil - * some fixes from Henner Eisen + * Revision 2.2 1997/08/07 17:44:36 keil + * Fix RESTART * - * Revision 1.3 1996/10/30 10:18:01 keil - * bugfixes in debugging output + * Revision 2.1 1997/08/03 14:36:33 keil + * Implement RESTART procedure * - * Revision 1.2 1996/10/27 22:15:16 keil - * bugfix reject handling + * Revision 2.0 1997/07/27 21:15:43 keil + * New Callref based layer3 * - * Revision 1.1 1996/10/13 20:04:55 keil - * Initial revision + * Revision 1.17 1997/06/26 11:11:46 keil + * SET_SKBFREE now on creation of a SKB * + * Revision 1.15 1997/04/17 11:50:48 keil + * pa->loc was undefined, if it was not send by the exchange * + * Old log removed /KKe * */ #define __NO_VERSION__ #include "hisax.h" #include "isdnl3.h" +#include "l3dss1.h" #include extern char *HiSax_getrev(const char *revision); -const char *dss1_revision = "$Revision: 1.16 $"; +const char *dss1_revision = "$Revision: 1.16.2.4 $"; + +#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; + + 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->st, CC_CHARGE | INDICATION, pc); + } + if (pc->st->l3.debug & L3_DEB_CHARGE) { + if (*(p+2) == 0) { + l3_debug(pc->st, "charging info during %d", pc->para.chargeinfo); + } else { + l3_debug(pc->st, "charging info final %d", pc->para.chargeinfo); + } + } + }))))) + break; + case 0x24: /* final */ + FOO1("2A",0x30,FOO1("2B",0x30,FOO1("2C",0xA1,FOO1("2D",0x30,FOO1("2E",0x02,({ + ident = 0; + while(ilen > 0) { + ident = (ident<<8) | *p++; + ilen--; + } + if (ident > pc->para.chargeinfo) { + pc->para.chargeinfo = ident; + pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc); + } + if (pc->st->l3.debug & L3_DEB_CHARGE) { + l3_debug(pc->st, "charging info final %d", pc->para.chargeinfo); + } + })))))) + break; + } + #undef FOO1 + + } + break; + case 2: /* return result */ + l3_debug(pc->st, "return result break"); + break; + case 3: /* return error */ + l3_debug(pc->st, "return error break"); + break; + default: + l3_debug(pc->st, "default break"); + break; + } +} +#endif + +static int +l3dss1_check_messagetype_validity(int mt) { +/* verify if a message type exists */ + switch(mt) { + case MT_ALERTING: + case MT_CALL_PROCEEDING: + case MT_CONNECT: + case MT_CONNECT_ACKNOWLEDGE: + case MT_PROGRESS: + case MT_SETUP: + case MT_SETUP_ACKNOWLEDGE: + case MT_RESUME: + case MT_RESUME_ACKNOWLEDGE: + case MT_RESUME_REJECT: + case MT_SUSPEND: + case MT_SUSPEND_ACKNOWLEDGE: + case MT_SUSPEND_REJECT: + case MT_USER_INFORMATION: + case MT_DISCONNECT: + case MT_RELEASE: + case MT_RELEASE_COMPLETE: + case MT_RESTART: + case MT_RESTART_ACKNOWLEDGE: + case MT_SEGMENT: + case MT_CONGESTION_CONTROL: + case MT_INFORMATION: + case MT_FACILITY: + case MT_NOTIFY: + case MT_STATUS: + case MT_STATUS_ENQUIRY: + return(1); + default: + return(0); + } + return(0); +} + +static void +l3dss1_message(struct l3_process *pc, u_char mt) { struct sk_buff *skb; u_char *p; @@ -84,44 +274,204 @@ 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); + l3_msg(pc->st, DL_DATA | REQUEST, skb); } static void -l3dss1_release_req(struct PStack *st, u_char pr, void *arg) +l3dss1_release_req(struct l3_process *pc, u_char pr, void *arg) { - StopAllL3Timer(st); - newl3state(st, 19); - l3dss1_message(st, MT_RELEASE); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1); + StopAllL3Timer(pc); + newl3state(pc, 19); + l3dss1_message(pc, MT_RELEASE); + L3AddTimer(&pc->timer, T308, CC_T308_1); } static void -l3dss1_release_cmpl(struct PStack *st, u_char pr, void *arg) +l3dss1_release_cmpl(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; int cause = -1; p = skb->data; - st->pa->loc = 0; + pc->para.loc = 0; if ((p = findie(p, skb->len, IE_CAUSE, 0))) { p++; if (*p++ == 2) - st->pa->loc = *p++; + pc->para.loc = *p++; cause = *p & 0x7f; } - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - StopAllL3Timer(st); - st->pa->cause = cause; - newl3state(st, 0); - st->l3.l3l4(st, CC_RELEASE_CNF, NULL); + StopAllL3Timer(pc); + pc->para.cause = cause; + newl3state(pc, 0); + pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc); + release_l3_process(pc); +} + +#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; @@ -131,16 +481,19 @@ l3dss1_setup_req(struct PStack *st, u_char pr, 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 */ @@ -160,7 +513,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)) { @@ -182,19 +535,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); @@ -207,241 +569,377 @@ 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); + } else { + *p++ = 0x7c; *p++ = 0x02; *p++ = 0x88; *p++ = 0x90; + } +#endif l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - L3DelTimer(&st->l3.timer); - L3AddTimer(&st->l3.timer, st->l3.t303, CC_T303); - newl3state(st, 1); - st->l3.l3l2(st, DL_DATA, skb); - + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T303, CC_T303); + newl3state(pc, 1); + l3_msg(pc->st, DL_DATA | REQUEST, skb); } static void -l3dss1_call_proc(struct PStack *st, u_char pr, void *arg) +l3dss1_call_proc(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); p = skb->data; - if ((p = findie(p, skb->len, 0x18, 0))) { - st->pa->bchannel = p[2] & 0x3; - if ((!st->pa->bchannel) && (st->l3.debug & L3_DEB_WARN)) - l3_debug(st, "setup answer without bchannel"); - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup answer without bchannel"); - SET_SKB_FREE(skb); + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + pc->para.bchannel = p[2] & 0x3; + if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN)) + l3_debug(pc->st, "setup answer without bchannel"); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer without bchannel"); dev_kfree_skb(skb, FREE_READ); - newl3state(st, 3); - L3AddTimer(&st->l3.timer, st->l3.t310, CC_T310); - st->l3.l3l4(st, CC_PROCEEDING_IND, NULL); + newl3state(pc, 3); + L3AddTimer(&pc->timer, T310, CC_T310); + pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc); } static void -l3dss1_setup_ack(struct PStack *st, u_char pr, void *arg) +l3dss1_setup_ack(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); p = skb->data; - if ((p = findie(p, skb->len, 0x18, 0))) { - st->pa->bchannel = p[2] & 0x3; - if ((!st->pa->bchannel) && (st->l3.debug & L3_DEB_WARN)) - l3_debug(st, "setup answer without bchannel"); - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup answer without bchannel"); - SET_SKB_FREE(skb); + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + pc->para.bchannel = p[2] & 0x3; + if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN)) + l3_debug(pc->st, "setup answer without bchannel"); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer without bchannel"); dev_kfree_skb(skb, FREE_READ); - newl3state(st, 2); - L3AddTimer(&st->l3.timer, st->l3.t304, CC_T304); - st->l3.l3l4(st, CC_MORE_INFO, NULL); + newl3state(pc, 2); + L3AddTimer(&pc->timer, T304, CC_T304); + pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc); } static void -l3dss1_disconnect(struct PStack *st, u_char pr, void *arg) +l3dss1_disconnect(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; int cause = -1; - StopAllL3Timer(st); + StopAllL3Timer(pc); p = skb->data; - st->pa->loc = 0; + pc->para.loc = 0; if ((p = findie(p, skb->len, IE_CAUSE, 0))) { p++; if (*p++ == 2) - st->pa->loc = *p++; + pc->para.loc = *p++; cause = *p & 0x7f; } - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - newl3state(st, 12); - st->pa->cause = cause; - st->l3.l3l4(st, CC_DISCONNECT_IND, NULL); + newl3state(pc, 12); + pc->para.cause = cause; + pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc); } static void -l3dss1_connect(struct PStack *st, u_char pr, void *arg) +l3dss1_connect(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - L3DelTimer(&st->l3.timer); /* T310 */ - newl3state(st, 10); - st->l3.l3l4(st, CC_SETUP_CNF, NULL); + L3DelTimer(&pc->timer); /* T310 */ + newl3state(pc, 10); + pc->para.chargeinfo = 0; + pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc); } static void -l3dss1_alerting(struct PStack *st, u_char pr, void *arg) +l3dss1_alerting(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - L3DelTimer(&st->l3.timer); /* T304 */ - newl3state(st, 4); - st->l3.l3l4(st, CC_ALERTING_IND, NULL); + L3DelTimer(&pc->timer); /* T304 */ + newl3state(pc, 4); + pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc); } static void -l3dss1_setup(struct PStack *st, u_char pr, void *arg) +l3dss1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; + /* This routine is called if here was no SETUP made (checks in dss1up and in + * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code + * MT_STATUS_ENQUIRE in the NULL state is handled too + */ + u_char tmp[16]; + u_char *p=tmp; + int l; + struct sk_buff *skb; + + switch (pc->para.cause) { + case 81: /* 0x51 invalid callreference */ + case 88: /* 0x58 incomp destination */ + case 96: /* 0x60 mandory IE missing */ + case 101: /* 0x65 incompatible Callstate */ + MsgHead(p, pc->callref, MT_RELEASE_COMPLETE); + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = pc->para.cause | 0x80; + break; + default: + printk(KERN_ERR "HiSax internal error l3dss1_msg_without_setup\n"); + return; + } + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + release_l3_process(pc); +} + +static void +l3dss1_setup(struct l3_process *pc, u_char pr, void *arg) +{ + u_char *p, *ptmp[8]; + int i; int bcfound = 0; char tmp[80]; struct sk_buff *skb = arg; + /* ETS 300-104 1.3.4 and 1.3.5 + * we need to detect unknown inform. element from 0 to 7 + */ p = skb->data; - st->pa->callref = getcallref(p); - st->l3.callref = 0x80 + st->pa->callref; + for(i = 0; i < 8; i++) + ptmp[i] = skb->data; + if (findie(ptmp[1], skb->len, 0x01, 0) + || findie(ptmp[2], skb->len, 0x02, 0) + || findie(ptmp[3], skb->len, 0x03, 0) + || findie(ptmp[5], skb->len, 0x05, 0) + || findie(ptmp[6], skb->len, 0x06, 0) + || findie(ptmp[7], skb->len, 0x07, 0)) { + /* if ie is < 8 and not 0 nor 4, send RELEASE_COMPLETE + * cause 0x60 + */ + pc->para.cause = 0x60; + dev_kfree_skb(skb, FREE_READ); + l3dss1_msg_without_setup(pc, pr, NULL); + return; + } /* * Channel Identification */ p = skb->data; - if ((p = findie(p, skb->len, 0x18, 0))) { - st->pa->bchannel = p[2] & 0x3; - if (st->pa->bchannel) + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + pc->para.bchannel = p[2] & 0x3; + if (pc->para.bchannel) bcfound++; - else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup without bchannel"); - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup without bchannel"); - + else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without bchannel"); + } else { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without bchannel"); + pc->para.cause = 0x60; + dev_kfree_skb(skb, FREE_READ); + l3dss1_msg_without_setup(pc, pr, NULL); + return; + } /* * Bearer Capabilities */ p = skb->data; if ((p = findie(p, skb->len, 0x04, 0))) { - st->pa->setup.si2 = 0; + pc->para.setup.si2 = 0; switch (p[2] & 0x1f) { case 0x00: /* Speech */ case 0x10: /* 3.1 Khz audio */ - st->pa->setup.si1 = 1; + pc->para.setup.si1 = 1; break; case 0x08: /* Unrestricted digital information */ - st->pa->setup.si1 = 7; + pc->para.setup.si1 = 7; +/* JIM, 05.11.97 I wanna set service indicator 2 */ +#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, FREE_READ); + l3dss1_msg_without_setup(pc, pr, NULL); + return; + } p = skb->data; if ((p = findie(p, skb->len, 0x70, 0))) - iecpy(st->pa->setup.eazmsn, p, 1); + iecpy(pc->para.setup.eazmsn, p, 1); else - st->pa->setup.eazmsn[0] = 0; + pc->para.setup.eazmsn[0] = 0; + p = skb->data; + if ((p = findie(p, skb->len, 0x71, 0))) { + /* Called party subaddress */ + if ((p[1]>=2) && (p[2]==0x80) && (p[3]==0x50)) { + tmp[0]='.'; + iecpy(&tmp[1], p, 2); + strcat(pc->para.setup.eazmsn, tmp); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "wrong called subaddress"); + } p = skb->data; if ((p = findie(p, skb->len, 0x6c, 0))) { - st->pa->setup.plan = p[2]; + pc->para.setup.plan = p[2]; if (p[2] & 0x80) { - iecpy(st->pa->setup.phone, p, 1); - st->pa->setup.screen = 0; + iecpy(pc->para.setup.phone, p, 1); + pc->para.setup.screen = 0; } else { - iecpy(st->pa->setup.phone, p, 2); - st->pa->setup.screen = p[3]; + iecpy(pc->para.setup.phone, p, 2); + pc->para.setup.screen = p[3]; } } else { - st->pa->setup.phone[0] = 0; - st->pa->setup.plan = 0; - st->pa->setup.screen = 0; + pc->para.setup.phone[0] = 0; + pc->para.setup.plan = 0; + pc->para.setup.screen = 0; + } + p = skb->data; + if ((p = findie(p, skb->len, 0x6d, 0))) { + /* Calling party subaddress */ + if ((p[1]>=2) && (p[2]==0x80) && (p[3]==0x50)) { + tmp[0]='.'; + iecpy(&tmp[1], p, 2); + strcat(pc->para.setup.phone, tmp); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "wrong calling subaddress"); } - SET_SKB_FREE(skb); + dev_kfree_skb(skb, FREE_READ); if (bcfound) { - if ((st->pa->setup.si1 != 7) && (st->l3.debug & L3_DEB_WARN)) { - sprintf(tmp, "non-digital call: %s -> %s", - st->pa->setup.phone, - st->pa->setup.eazmsn); - l3_debug(st, tmp); + if ((pc->para.setup.si1 != 7) && (pc->debug & L3_DEB_WARN)) { + l3_debug(pc->st, "non-digital call: %s -> %s", + pc->para.setup.phone, pc->para.setup.eazmsn); } - newl3state(st, 6); - st->l3.l3l4(st, CC_SETUP_IND, NULL); - } + if ((pc->para.setup.si1 != 7) && + test_bit(FLG_PTP, &pc->st->l2.flag)) { + pc->para.cause = 0x58; + l3dss1_msg_without_setup(pc, pr, NULL); + return; + } + newl3state(pc, 6); + pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc); + } else + release_l3_process(pc); } static void -l3dss1_reset(struct PStack *st, u_char pr, void *arg) +l3dss1_reset(struct l3_process *pc, u_char pr, void *arg) { - StopAllL3Timer(st); - newl3state(st, 0); + release_l3_process(pc); } static void -l3dss1_setup_rsp(struct PStack *st, u_char pr, +l3dss1_setup_rsp(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 8); - l3dss1_message(st, MT_CONNECT); - L3DelTimer(&st->l3.timer); - L3AddTimer(&st->l3.timer, st->l3.t313, CC_T313); + newl3state(pc, 8); + l3dss1_message(pc, MT_CONNECT); + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T313, CC_T313); } static void -l3dss1_connect_ack(struct PStack *st, u_char pr, void *arg) +l3dss1_connect_ack(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - newl3state(st, 10); - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL); + newl3state(pc, 10); + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc); } static void -l3dss1_disconnect_req(struct PStack *st, u_char pr, void *arg) +l3dss1_disconnect_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; @@ -449,12 +947,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; @@ -465,13 +963,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); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + L3AddTimer(&pc->timer, T305, CC_T305); } static void -l3dss1_reject_req(struct PStack *st, u_char pr, void *arg) +l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; @@ -479,10 +977,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; @@ -493,13 +991,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); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + newl3state(pc, 0); + release_l3_process(pc); } static void -l3dss1_release(struct PStack *st, u_char pr, void *arg) +l3dss1_release(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; @@ -509,38 +1008,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, FREE_READ); - StopAllL3Timer(st); - st->pa->cause = cause; - newl3state(st, 0); - l3dss1_message(st, MT_RELEASE_COMPLETE); - st->l3.l3l4(st, CC_RELEASE_IND, NULL); + StopAllL3Timer(pc); + pc->para.cause = cause; + l3dss1_message(pc, MT_RELEASE_COMPLETE); + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + newl3state(pc, 0); + release_l3_process(pc); } static void -l3dss1_alert_req(struct PStack *st, u_char pr, +l3dss1_alert_req(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 7); - l3dss1_message(st, MT_ALERTING); + newl3state(pc, 7); + l3dss1_message(pc, MT_ALERTING); } static void -l3dss1_status_enq(struct PStack *st, u_char pr, void *arg) +l3dss1_status_enq(struct l3_process *pc, u_char pr, void *arg) { u_char tmp[16]; u_char *p = tmp; int l; struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - MsgHead(p, st->l3.callref, MT_STATUS); + MsgHead(p, pc->callref, MT_STATUS); *p++ = IE_CAUSE; *p++ = 0x2; @@ -549,42 +1055,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); - st->l3.l3l2(st, DL_DATA, skb); + l3_msg(pc->st, DL_DATA | REQUEST, skb); +} + +static void +l3dss1_status_req(struct l3_process *pc, u_char pr, void *arg) +{ + /* ETS 300-104 7.4.1, 8.4.1, 10.3.1, 11.4.1, 12.4.1, 13.4.1, 14.4.1... + if setup has been made and a non expected message type is received, we must send MT_STATUS cause 0x62 */ + u_char tmp[16]; + u_char *p = tmp; + int l; + struct sk_buff *skb = arg; + + dev_kfree_skb(skb, FREE_READ); + + MsgHead(p, pc->callref, MT_STATUS); + + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = 0x62 | 0x80; /* status sending */ + + *p++ = 0x14; /* CallState */ + *p++ = 0x1; + *p++ = pc->state & 0x3f; + + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + l3_msg(pc->st, DL_DATA | REQUEST, skb); +} + +static void +l3dss1_release_ind(struct l3_process *pc, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + int callState = 0; + p = skb->data; + + if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) { + p++; + if (1== *p++) + callState = *p; + } + if(callState == 0) { + /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1 + * set down layer 3 without sending any message + */ + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + newl3state(pc, 0); + release_l3_process(pc); + } else { + pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc); + } } static void -l3dss1_t303(struct PStack *st, u_char pr, void *arg) +l3dss1_t303(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); + if (pc->N303 > 0) { + pc->N303--; + L3DelTimer(&pc->timer); + l3dss1_setup_req(pc, pr, arg); } else { - newl3state(st, 0); - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL); - st->l3.n_t303 = 1; + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc); + release_l3_process(pc); } } static void -l3dss1_t304(struct PStack *st, u_char pr, void *arg) +l3dss1_t304(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->para.cause = 0xE6; + l3dss1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); } static void -l3dss1_t305(struct PStack *st, u_char pr, void *arg) +l3dss1_t305(struct l3_process *pc, u_char pr, void *arg) { u_char tmp[16]; u_char *p = tmp; @@ -592,11 +1152,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; @@ -607,64 +1167,353 @@ 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); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + L3AddTimer(&pc->timer, T308, CC_T308_1); +} + +static void +l3dss1_t310(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3dss1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); +} + +static void +l3dss1_t313(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3dss1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc); +} + +static void +l3dss1_t308_1(struct l3_process *pc, u_char pr, void *arg) +{ + newl3state(pc, 19); + L3DelTimer(&pc->timer); + l3dss1_message(pc, MT_RELEASE); + L3AddTimer(&pc->timer, T308, CC_T308_2); } static void -l3dss1_t310(struct PStack *st, u_char pr, void *arg) +l3dss1_t308_2(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->st, CC_RELEASE_ERR, pc); + release_l3_process(pc); } static void -l3dss1_t313(struct PStack *st, u_char pr, void *arg) +l3dss1_t318(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); + L3DelTimer(&pc->timer); + pc->para.cause = 0x66; /* Timer expiry */ + pc->para.loc = 0; /* local */ + pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc); + newl3state(pc, 19); + l3dss1_message(pc, MT_RELEASE); + L3AddTimer(&pc->timer, T308, CC_T308_1); } static void -l3dss1_t308_1(struct PStack *st, u_char pr, void *arg) +l3dss1_t319(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); + L3DelTimer(&pc->timer); + pc->para.cause = 0x66; /* Timer expiry */ + pc->para.loc = 0; /* local */ + pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc); + newl3state(pc, 10); } static void -l3dss1_t308_2(struct PStack *st, u_char pr, void *arg) +l3dss1_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); + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc->st, CC_DLRL | INDICATION, pc); + release_l3_process(pc); } + +static void +l3dss1_status(struct l3_process *pc, u_char pr, void *arg) +{ + u_char *p; + char tmp[64], *t; + int l; + struct sk_buff *skb = arg; + int cause, callState; + + cause = callState = -1; + p = skb->data; + t = tmp; + if ((p = findie(p, skb->len, IE_CAUSE, 0))) { + p++; + l = *p++; + t += sprintf(t,"Status CR %x Cause:", pc->callref); + while (l--) { + cause = *p; + t += sprintf(t," %2x",*p++); + } + } else + sprintf(t,"Status CR %x no Cause", pc->callref); + l3_debug(pc->st, tmp); + p = skb->data; + t = tmp; + t += sprintf(t,"Status state %x ", pc->state); + if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) { + p++; + if (1== *p++) { + callState = *p; + t += sprintf(t,"peer state %x" , *p); + } else + t += sprintf(t,"peer state len error"); + } else + sprintf(t,"no peer state"); + l3_debug(pc->st, tmp); + if(((cause & 0x7f) == 0x6f) && (callState == 0)) { + /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... + * if received MT_STATUS with cause == 0x6f and call + * state == 0, then we must set down layer 3 + */ + l3dss1_release_ind(pc, pr, arg); + } else + dev_kfree_skb(skb, FREE_READ); +} + +static void +l3dss1_facility(struct l3_process *pc, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + + p = skb->data; + if ((p = findie(p, skb->len, IE_FACILITY, 0))) { +#ifdef HISAX_DE_AOC + l3dss1_parse_facility(pc,p); +#else + p = NULL; +#endif + } +} + +static void +l3dss1_suspend_req(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb; + u_char tmp[32]; + u_char *p = tmp; + u_char i,l; + u_char *msg = pc->chan->setup.phone; + + MsgHead(p, pc->callref, MT_SUSPEND); + + *p++ = IE_CALLID; + l = *msg++; + if (l && (l<=10)) { /* Max length 10 octets */ + *p++ = l; + for (i=0;ist, "SUS wrong CALLID len %d", l); + return; + } + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + newl3state(pc, 15); + L3AddTimer(&pc->timer, T319, CC_T319); +} + +static void +l3dss1_suspend_ack(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + + L3DelTimer(&pc->timer); + newl3state(pc, 0); + dev_kfree_skb(skb, FREE_READ); + pc->para.cause = -1; + pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc); + release_l3_process(pc); +} + +static void +l3dss1_suspend_rej(struct l3_process *pc, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + int cause = -1; + + L3DelTimer(&pc->timer); + p = skb->data; + if ((p = findie(p, skb->len, IE_CAUSE, 0))) { + p++; + if (*p++ == 2) + pc->para.loc = *p++; + cause = *p & 0x7f; + } + dev_kfree_skb(skb, FREE_READ); + pc->para.cause = cause; + pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc); + newl3state(pc, 10); +} + +static void +l3dss1_resume_req(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb; + u_char tmp[32]; + u_char *p = tmp; + u_char i,l; + u_char *msg = pc->para.setup.phone; + + MsgHead(p, pc->callref, MT_RESUME); + + *p++ = IE_CALLID; + l = *msg++; + if (l && (l<=10)) { /* Max length 10 octets */ + *p++ = l; + for (i=0;ist, "RES wrong CALLID len %d", l); + return; + } + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + newl3state(pc, 17); + L3AddTimer(&pc->timer, T319, CC_T319); +} + +static void +l3dss1_resume_ack(struct l3_process *pc, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + + L3DelTimer(&pc->timer); + p = skb->data; + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + pc->para.bchannel = p[2] & 0x3; + if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN)) + l3_debug(pc->st, "resume ack without bchannel"); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "resume ack without bchannel"); + dev_kfree_skb(skb, FREE_READ); + pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc); + newl3state(pc, 10); +} + +static void +l3dss1_resume_rej(struct l3_process *pc, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + int cause = -1; + + L3DelTimer(&pc->timer); + p = skb->data; + if ((p = findie(p, skb->len, IE_CAUSE, 0))) { + p++; + if (*p++ == 2) + pc->para.loc = *p++; + cause = *p & 0x7f; + } + dev_kfree_skb(skb, FREE_READ); + pc->para.cause = cause; + newl3state(pc, 0); + pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc); + release_l3_process(pc); +} + +static void +l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg) +{ + u_char tmp[32]; + u_char *p; + u_char ri, ch=0, chan=0; + int l; + struct sk_buff *skb = arg; + struct l3_process *up; + + newl3state(pc, 2); + L3DelTimer(&pc->timer); + p = skb->data; + if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) { + ri = p[2]; + l3_debug(pc->st, "Restart %x", ri); + } else { + l3_debug(pc->st, "Restart without restart IE"); + ri = 0x86; + } + p = skb->data; + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + chan = p[2] & 3; + ch = p[2]; + if (pc->st->l3.debug) + l3_debug(pc->st, "Restart for channel %d", chan); + } + dev_kfree_skb(skb, FREE_READ); + newl3state(pc, 2); + up = pc->st->l3.proc; + while (up) { + if ((ri & 7)==7) + up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up); + else if (up->para.bchannel == chan) + up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up); + up = up->next; + } + p = tmp; + MsgHead(p, pc->callref, MT_RESTART_ACKNOWLEDGE); + if (chan) { + *p++ = IE_CHANNEL_ID; + *p++ = 1; + *p++ = ch | 0x80; + } + *p++ = 0x79; /* RESTART Ind */ + *p++ = 1; + *p++ = ri; + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + newl3state(pc, 0); + l3_msg(pc->st, DL_DATA | REQUEST, skb); +} + /* *INDENT-OFF* */ static struct stateentry downstatelist[] = { {SBIT(0), - CC_SETUP_REQ, l3dss1_setup_req}, + CC_SETUP | REQUEST, l3dss1_setup_req}, + {SBIT(0), + CC_RESUME | REQUEST, l3dss1_resume_req}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(10), - CC_DISCONNECT_REQ, l3dss1_disconnect_req}, + CC_DISCONNECT | REQUEST, l3dss1_disconnect_req}, {SBIT(12), - CC_RELEASE_REQ, l3dss1_release_req}, + CC_RELEASE | REQUEST, l3dss1_release_req}, {ALL_STATES, - CC_DLRL, l3dss1_reset}, + CC_DLRL | REQUEST, l3dss1_reset}, + {ALL_STATES, + CC_RESTART | REQUEST, l3dss1_restart}, {SBIT(6), - CC_IGNORE, l3dss1_reset}, + CC_IGNORE | REQUEST, l3dss1_reset}, {SBIT(6), - CC_REJECT_REQ, l3dss1_reject_req}, + CC_REJECT | REQUEST, l3dss1_reject_req}, {SBIT(6), - CC_ALERTING_REQ, l3dss1_alert_req}, + CC_ALERTING | REQUEST, l3dss1_alert_req}, {SBIT(6) | SBIT(7), - CC_SETUP_RSP, l3dss1_setup_rsp}, + CC_SETUP | RESPONSE, l3dss1_setup_rsp}, + {SBIT(10), + CC_SUSPEND | REQUEST, l3dss1_suspend_req}, {SBIT(1), CC_T303, l3dss1_t303}, {SBIT(2), @@ -675,6 +1524,10 @@ static struct stateentry downstatelist[] = CC_T313, l3dss1_t313}, {SBIT(11), CC_T305, l3dss1_t305}, + {SBIT(15), + CC_T319, l3dss1_t319}, + {SBIT(17), + CC_T318, l3dss1_t318}, {SBIT(19), CC_T308_1, l3dss1_t308_1}, {SBIT(19), @@ -688,100 +1541,285 @@ static struct stateentry datastatelist[] = { {ALL_STATES, MT_STATUS_ENQUIRY, l3dss1_status_enq}, + {ALL_STATES, + MT_FACILITY, l3dss1_facility}, + {SBIT(19), + MT_STATUS, l3dss1_release_ind}, + {ALL_STATES, + MT_STATUS, l3dss1_status}, {SBIT(0) | SBIT(6), MT_SETUP, l3dss1_setup}, {SBIT(1) | SBIT(2), MT_CALL_PROCEEDING, l3dss1_call_proc}, + {SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_CALL_PROCEEDING, l3dss1_status_req}, {SBIT(1), MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack}, + {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_SETUP_ACKNOWLEDGE, l3dss1_status_req}, {SBIT(1) | SBIT(2) | SBIT(3), MT_ALERTING, l3dss1_alerting}, + {SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_ALERTING, l3dss1_status_req}, {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19), MT_RELEASE_COMPLETE, l3dss1_release_cmpl}, - {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | - SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19), + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | + SBIT(11) | SBIT(12) | SBIT(15) /* | SBIT(17) | SBIT(19)*/, MT_RELEASE, l3dss1_release}, - {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10), + {SBIT(19), MT_RELEASE, l3dss1_release_ind}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | SBIT(15), MT_DISCONNECT, l3dss1_disconnect}, + {SBIT(11), + MT_DISCONNECT, l3dss1_release_req}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4), MT_CONNECT, l3dss1_connect}, + {SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_CONNECT, l3dss1_status_req}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(11) | SBIT(19), + MT_CONNECT_ACKNOWLEDGE, l3dss1_status_req}, {SBIT(8), MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack}, + {SBIT(15), + MT_SUSPEND_ACKNOWLEDGE, l3dss1_suspend_ack}, + {SBIT(15), + MT_SUSPEND_REJECT, l3dss1_suspend_rej}, + {SBIT(17), + MT_RESUME_ACKNOWLEDGE, l3dss1_resume_ack}, + {SBIT(17), + MT_RESUME_REJECT, l3dss1_resume_rej}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(19), + MT_INVALID, l3dss1_status_req}, }; -/* *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 | REQUEST, l3dss1_status_req}, + {SBIT(0), + CC_RESTART | REQUEST, 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; + struct l3_process *proc = st->l3.global; + + for (i = 0; i < globalm_len; i++) + if ((mt == globalmes_list[i].primitive) && + ((1 << proc->state) & globalmes_list[i].state)) + break; + if (i == globalm_len) { + dev_kfree_skb(skb, FREE_READ); + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "dss1 global state %d mt %x unhandled", + proc->state, mt); + } + return; + } else { + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "dss1 global %d mt %x", + proc->state, mt); + } + globalmes_list[i].rout(proc, mt, skb); + } +} static void dss1up(struct PStack *st, int pr, void *arg) { - int i, mt; + int i, mt, cr, cause, callState; + char *ptr; struct sk_buff *skb = arg; - char tmp[80]; + struct l3_process *proc; + switch (pr) { + case (DL_DATA | INDICATION): + case (DL_UNIT_DATA | INDICATION): + break; + case (DL_ESTABLISH | CONFIRM): + case (DL_ESTABLISH | INDICATION): + case (DL_RELEASE | INDICATION): + case (DL_RELEASE | CONFIRM): + l3_msg(st, pr, arg); + return; + break; + } if (skb->data[0] != PROTO_DIS_EURO) { if (st->l3.debug & L3_DEB_PROTERR) { - sprintf(tmp, "dss1up%sunexpected discriminator %x message len %ld state %d", - (pr == DL_DATA) ? " " : "(broadcast) ", - skb->data[0], skb->len, st->l3.state); - l3_debug(st, tmp); + l3_debug(st, "dss1up%sunexpected discriminator %x message len %d", + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + skb->data[0], skb->len); } - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); return; } + cr = getcallref(skb->data); mt = skb->data[skb->data[1] + 2]; + if (!cr) { /* Global CallRef */ + global_handler(st, mt, skb); + return; + } else if (cr == -1) { /* Dummy Callref */ + dev_kfree_skb(skb, FREE_READ); + return; + } else if (!(proc = getl3proc(st, cr))) { + /* No transaction process exist, that means no call with + * this callreference is active + */ + if (mt == MT_SETUP) { + /* Setup creates a new transaction process */ + if (!(proc = new_l3_process(st, cr))) { + /* May be to answer with RELEASE_COMPLETE and + * CAUSE 0x2f "Resource unavailable", but this + * need a new_l3_process too ... arghh + */ + dev_kfree_skb(skb, FREE_READ); + return; + } + } else if (mt == MT_STATUS) { + cause = 0; + if((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) { + ptr++; + if (*ptr++ == 2) + ptr++; + cause = *ptr & 0x7f; + } + callState = 0; + if((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) { + ptr++; + if (*ptr++ == 2) + ptr++; + callState = *ptr; + } + if (callState == 0) { + /* ETS 300-104 part 2.4.1 + * if setup has not been made and a message type + * MT_STATUS is received with call state == 0, + * we must send nothing + */ + dev_kfree_skb(skb, FREE_READ); + return; + } else { + /* ETS 300-104 part 2.4.2 + * if setup has not been made and a message type + * MT_STATUS is received with call state != 0, + * we must send MT_RELEASE_COMPLETE cause 101 + */ + dev_kfree_skb(skb, FREE_READ); + if ((proc = new_l3_process(st, cr))) { + proc->para.cause = 0x65; /* 101 */ + l3dss1_msg_without_setup(proc, 0, NULL); + } + return; + } + } else if (mt == MT_RELEASE_COMPLETE){ + dev_kfree_skb(skb, FREE_READ); + return; + } else { + /* ETS 300-104 part 2 + * if setup has not been made and a message type + * (except MT_SETUP and RELEASE_COMPLETE) is received, + * we must send MT_RELEASE_COMPLETE cause 81 */ + dev_kfree_skb(skb, FREE_READ); + if ((proc = new_l3_process(st, cr))) { + proc->para.cause = 0x51; /* 81 */ + l3dss1_msg_without_setup(proc, 0, NULL); + } + return; + } + } else if (!l3dss1_check_messagetype_validity(mt)) { + /* ETS 300-104 7.4.2, 8.4.2, 10.3.2, 11.4.2, 12.4.2, 13.4.2, + * 14.4.2... + * if setup has been made and invalid message type is received, + * we must send MT_STATUS cause 0x62 + */ + mt = MT_INVALID; /* sorry, not clean, but do the right thing ;-) */ + } + for (i = 0; i < datasllen; i++) if ((mt == datastatelist[i].primitive) && - ((1 << st->l3.state) & datastatelist[i].state)) + ((1 << proc->state) & datastatelist[i].state)) break; if (i == datasllen) { - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); if (st->l3.debug & L3_DEB_STATE) { - sprintf(tmp, "dss1up%sstate %d mt %x unhandled", - (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); - l3_debug(st, tmp); + l3_debug(st, "dss1up%sstate %d mt %x unhandled", + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + proc->state, mt); } return; } else { if (st->l3.debug & L3_DEB_STATE) { - sprintf(tmp, "dss1up%sstate %d mt %x", - (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); - l3_debug(st, tmp); + l3_debug(st, "dss1up%sstate %d mt %x", + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + proc->state, mt); } - datastatelist[i].rout(st, pr, skb); + datastatelist[i].rout(proc, pr, skb); } } static void dss1down(struct PStack *st, int pr, void *arg) { - int i; - char tmp[80]; + int i, cr; + struct l3_process *proc; + struct Channel *chan; + if (((DL_ESTABLISH | REQUEST)== pr) || ((DL_RELEASE | REQUEST)== pr)) { + l3_msg(st, pr, NULL); + return; + } else if (((CC_SETUP | REQUEST) == pr) || ((CC_RESUME | REQUEST) == pr)) { + chan = arg; + cr = newcallref(); + cr |= 0x80; + if ((proc = new_l3_process(st, cr))) { + proc->chan = chan; + chan->proc = proc; + proc->para.setup = chan->setup; + proc->callref = cr; + } + } else { + proc = arg; + } + if (!proc) { + printk(KERN_ERR "HiSax dss1down without proc pr=%04x\n", pr); + return; + } for (i = 0; i < downsllen; i++) if ((pr == downstatelist[i].primitive) && - ((1 << st->l3.state) & downstatelist[i].state)) + ((1 << proc->state) & downstatelist[i].state)) break; if (i == downsllen) { if (st->l3.debug & L3_DEB_STATE) { - sprintf(tmp, "dss1down state %d prim %d unhandled", - st->l3.state, pr); - l3_debug(st, tmp); + l3_debug(st, "dss1down state %d prim %d unhandled", + proc->state, pr); } } else { if (st->l3.debug & L3_DEB_STATE) { - sprintf(tmp, "dss1down state %d prim %d", - st->l3.state, pr); - l3_debug(st, tmp); + l3_debug(st, "dss1down state %d prim %d", + proc->state, pr); } - downstatelist[i].rout(st, pr, arg); + downstatelist[i].rout(proc, pr, arg); } } @@ -790,20 +1828,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..fac2149b4b2e --- /dev/null +++ b/drivers/isdn/hisax/l3dss1.h @@ -0,0 +1,75 @@ +/* $Id: l3dss1.h,v 1.3.2.3 1998/05/27 18:06:14 keil Exp $ + * + * DSS1 (Euro) D-channel protocol defines + * + * $Log: l3dss1.h,v $ + * Revision 1.3.2.3 1998/05/27 18:06:14 keil + * HiSax 3.0 + * + * Revision 1.3.2.2 1998/02/03 23:16:10 keil + * german AOC + * + * Revision 1.3.2.1 1997/10/17 22:10:52 keil + * new files on 2.0 + * + * Revision 1.3 1997/08/07 17:44:37 keil + * Fix RESTART + * + * Revision 1.2 1997/08/03 14:36:34 keil + * Implement RESTART procedure + * + * Revision 1.1 1997/07/27 21:08:38 keil + * new + * + * + * + */ +#define T303 4000 +#define T304 30000 +#define T305 30000 +#define T308 4000 +#define T310 30000 +#define T313 4000 +#define T318 4000 +#define T319 4000 + +/* + * Message-Types + */ + +#define MT_ALERTING 0x01 +#define MT_CALL_PROCEEDING 0x02 +#define MT_CONNECT 0x07 +#define MT_CONNECT_ACKNOWLEDGE 0x0f +#define MT_PROGRESS 0x03 +#define MT_SETUP 0x05 +#define MT_SETUP_ACKNOWLEDGE 0x0d +#define MT_RESUME 0x26 +#define MT_RESUME_ACKNOWLEDGE 0x2e +#define MT_RESUME_REJECT 0x22 +#define MT_SUSPEND 0x25 +#define MT_SUSPEND_ACKNOWLEDGE 0x2d +#define MT_SUSPEND_REJECT 0x21 +#define MT_USER_INFORMATION 0x20 +#define MT_DISCONNECT 0x45 +#define MT_RELEASE 0x4d +#define MT_RELEASE_COMPLETE 0x5a +#define MT_RESTART 0x46 +#define MT_RESTART_ACKNOWLEDGE 0x4e +#define MT_SEGMENT 0x60 +#define MT_CONGESTION_CONTROL 0x79 +#define MT_INFORMATION 0x7b +#define MT_FACILITY 0x62 +#define MT_NOTIFY 0x6e +#define MT_STATUS 0x7d +#define MT_STATUS_ENQUIRY 0x75 + +#define MT_INVALID 0xff + +#define IE_BEARER 0x04 +#define IE_CAUSE 0x08 +#define IE_CALLID 0x10 +#define IE_FACILITY 0x1c +#define IE_CALL_STATE 0x14 +#define IE_CHANNEL_ID 0x18 +#define IE_RESTART_IND 0x79 diff --git a/drivers/isdn/hisax/lmgr.c b/drivers/isdn/hisax/lmgr.c new file mode 100644 index 000000000000..4943604a83f4 --- /dev/null +++ b/drivers/isdn/hisax/lmgr.c @@ -0,0 +1,67 @@ +/* $Id: lmgr.c,v 1.1.2.4 1998/05/27 18:06:15 keil Exp $ + + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * Layermanagement module + * + * $Log: lmgr.c,v $ + * Revision 1.1.2.4 1998/05/27 18:06:15 keil + * HiSax 3.0 + * + * Revision 1.1.2.3 1998/03/07 23:15:37 tsbogend + * made HiSax working on Linux/Alpha + * + * Revision 1.1.2.2 1997/11/15 18:54:19 keil + * cosmetics + * + * Revision 1.1.2.1 1997/10/17 22:10:53 keil + * new files on 2.0 + * + * Revision 1.1 1997/06/26 11:17:25 keil + * first version + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" + +static void +error_handling_dchan(struct PStack *st, int Error) +{ + switch (Error) { + case 'C': + case 'D': + case 'G': + case 'H': + st->l2.l2tei(st, MDL_ERROR | REQUEST, NULL); + break; + } +} + +static void +hisax_manager(struct PStack *st, int pr, void *arg) +{ + char tm[32], str[256]; + long Code; + + switch (pr) { + case (MDL_ERROR | INDICATION): + Code = (long) arg; + jiftime(tm, jiffies); + sprintf(str, "%s manager: MDL_ERROR %c %s\n", tm, + (char)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..c420d72ec6d8 --- /dev/null +++ b/drivers/isdn/hisax/mic.c @@ -0,0 +1,278 @@ +/* $Id: mic.c,v 1.1.2.5 1998/04/08 21:58:43 keil Exp $ + + * mic.c low level stuff for mic cards + * + * Copyright (C) 1997 + * + * Author Stephan von Krawczynski + * + * + * $Log: mic.c,v $ + * Revision 1.1.2.5 1998/04/08 21:58:43 keil + * New init code + * + * Revision 1.1.2.4 1998/02/17 15:39:20 keil + * fix reset problem + * + * Revision 1.1.2.3 1998/01/27 22:37:25 keil + * fast io + * + * Revision 1.1.2.2 1997/11/15 18:50:54 keil + * new common init function + * + * Revision 1.1.2.1 1997/10/17 22:10:54 keil + * new files on 2.0 + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *mic_revision = "$Revision: 1.1.2.5 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define MIC_ISAC 2 +#define MIC_HSCX 1 +#define MIC_ADR 7 + +/* CARD_ADR (Write) */ +#define MIC_RESET 0x3 /* same as DOS driver */ + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.mic.adr, cs->hw.mic.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.mic.adr, cs->hw.mic.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.mic.adr, + cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.mic.adr, + cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.mic.adr, \ + cs->hw.mic.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.mic.adr, \ + cs->hw.mic.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.mic.adr, \ + cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.mic.adr, \ + cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +mic_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "mic: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 1) { + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0x0); + } + if (stat & 2) { + writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0x0); + } +} + +void +release_io_mic(struct IsdnCardState *cs) +{ + int bytecnt = 8; + + if (cs->hw.mic.cfg_reg) + release_region(cs->hw.mic.cfg_reg, bytecnt); +} + +static int +mic_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + return(0); + case CARD_RELEASE: + release_io_mic(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &mic_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscx(cs); /* /RTSA := ISAC RST */ + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_mic(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, mic_revision); + printk(KERN_INFO "HiSax: mic driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_MIC) + return (0); + + bytecnt = 8; + cs->hw.mic.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + cs->hw.mic.adr = cs->hw.mic.cfg_reg + MIC_ADR; + cs->hw.mic.isac = cs->hw.mic.cfg_reg + MIC_ISAC; + cs->hw.mic.hscx = cs->hw.mic.cfg_reg + MIC_HSCX; + + if (check_region((cs->hw.mic.cfg_reg), bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.mic.cfg_reg, + cs->hw.mic.cfg_reg + bytecnt); + return (0); + } else { + request_region(cs->hw.mic.cfg_reg, bytecnt, "mic isdn"); + } + + printk(KERN_INFO + "mic: defined at 0x%x IRQ %d\n", + cs->hw.mic.cfg_reg, + cs->irq); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &mic_card_msg; + ISACVersion(cs, "mic:"); + if (HscxVersion(cs, "mic:")) { + printk(KERN_WARNING + "mic: wrong HSCX versions check IO address\n"); + release_io_mic(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/netjet.c b/drivers/isdn/hisax/netjet.c new file mode 100644 index 000000000000..83a0991d8d76 --- /dev/null +++ b/drivers/isdn/hisax/netjet.c @@ -0,0 +1,1189 @@ +/* $Id: netjet.c,v 1.1.2.7 1998/05/27 18:06:17 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.1.2.7 1998/05/27 18:06:17 keil + * HiSax 3.0 + * + * Revision 1.1.2.6 1998/04/08 22:05:23 keil + * Forgot PCI fix + * + * Revision 1.1.2.5 1998/04/08 21:49:29 keil + * New init; fix PCI for more as one card + * + * Revision 1.1.2.4 1998/01/27 22:37:27 keil + * fast io + * + * Revision 1.1.2.3 1997/12/01 09:09:57 keil + * IRQ bit clearing + * + * Revision 1.1.2.2 1997/11/27 12:32:01 keil + * Working netjet driver + * + * Revision 1.1.2.1 1997/11/15 18:58:13 keil + * new card + * + * + */ + +#define __NO_VERSION__ +#include +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" +#include +#include +#include +#include + +#ifndef bus_to_virt +#define bus_to_virt (u_int *) +#endif + +#ifndef virt_to_bus +#define virt_to_bus (u_int) +#endif + +extern const char *CardType[]; + +const char *NETjet_revision = "$Revision: 1.1.2.7 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +/* PCI stuff */ +#define PCI_VENDOR_TRAVERSE_TECH 0xe159 +#define PCI_NETJET_ID 0x0001 + +#define NETJET_CTRL 0x00 +#define NETJET_DMACTRL 0x01 +#define NETJET_AUXCTRL 0x02 +#define NETJET_AUXDATA 0x03 +#define NETJET_IRQMASK0 0x04 +#define NETJET_IRQMASK1 0x05 +#define NETJET_IRQSTAT0 0x06 +#define NETJET_IRQSTAT1 0x07 +#define NETJET_DMA_READ_START 0x08 +#define NETJET_DMA_READ_IRQ 0x0c +#define NETJET_DMA_READ_END 0x10 +#define NETJET_DMA_READ_ADR 0x14 +#define NETJET_DMA_WRITE_START 0x18 +#define NETJET_DMA_WRITE_IRQ 0x1c +#define NETJET_DMA_WRITE_END 0x20 +#define NETJET_DMA_WRITE_ADR 0x24 +#define NETJET_PULSE_CNT 0x28 + +#define NETJET_ISAC_OFF 0xc0 +#define NETJET_ISACIRQ 0x10 +#define NETJET_IRQM0_READ 0x0c +#define NETJET_IRQM0_READ_1 0x04 +#define NETJET_IRQM0_READ_2 0x08 +#define NETJET_IRQM0_WRITE 0x03 +#define NETJET_IRQM0_WRITE_1 0x01 +#define NETJET_IRQM0_WRITE_2 0x02 + +#define NETJET_DMA_SIZE 512 + +#define HDLC_ZERO_SEARCH 0 +#define HDLC_FLAG_SEARCH 1 +#define HDLC_FLAG_FOUND 2 +#define HDLC_FRAME_FOUND 3 +#define HDLC_NULL 4 +#define HDLC_PART 5 +#define HDLC_FULL 6 + +#define HDLC_FLAG_VALUE 0x7e + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + long flags; + u_char ret; + + save_flags(flags); + cli(); + cs->hw.njet.auxd &= 0xfc; + cs->hw.njet.auxd |= (offset>>4) & 3; + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + ret = bytein(cs->hw.njet.isac + ((offset & 0xf)<<2)); + restore_flags(flags); + return(ret); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + long flags; + + save_flags(flags); + cli(); + cs->hw.njet.auxd &= 0xfc; + cs->hw.njet.auxd |= (offset>>4) & 3; + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + byteout(cs->hw.njet.isac + ((offset & 0xf)<<2), value); + restore_flags(flags); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + cs->hw.njet.auxd &= 0xfc; + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + insb(cs->hw.njet.isac, data, size); +} + +__u16 fcstab[256] = +{ + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + cs->hw.njet.auxd &= 0xfc; + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + outsb(cs->hw.njet.isac, data, size); +} + +void fill_mem(struct BCState *bcs, u_int *pos, u_int cnt, int chan, u_char fill) +{ + u_int mask=0x000000ff, val = 0, *p=pos; + u_int i; + + val |= fill; + if (chan) { + val <<= 8; + mask <<= 8; + } + mask ^= 0xffffffff; + for (i=0; i bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send; + } +} + +void +mode_tiger(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + 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 int make_raw_data(struct BCState *bcs) { + register u_int i,s_cnt=0; + register u_char j; + register u_char val; + register u_char s_one = 0; + register u_char s_val = 0; + register u_char bitcnt = 0; + u_int fcs; + char tmp[64]; + + if (!bcs->hw.tiger.tx_skb) { + debugl1(bcs->cs, "tiger make_raw: NULL skb"); + return(1); + } + bcs->hw.tiger.sendbuf[s_cnt++] = HDLC_FLAG_VALUE; + fcs = PPP_INITFCS; + for (i=0; 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 %ld 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; + return(0); +} + +static void got_frame(struct BCState *bcs, int count) { + struct sk_buff *skb; + + if (!(skb = dev_alloc_skb(count))) + printk(KERN_WARNING "TIGER: receive out of memory\n"); + else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, count), bcs->hw.tiger.rcvbuf, count); + skb_queue_tail(&bcs->rqueue, skb); + } + bcs->event |= 1 << B_RCVBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + if (bcs->cs->debug & L1_DEB_RECEIVE_FRAME) + printframe(bcs->cs, bcs->hw.tiger.rcvbuf, count, "rec"); +} + + + +static void read_raw(struct BCState *bcs, u_int *buf, int cnt){ + int i; + register u_char j; + register u_char val; + u_int *pend = bcs->hw.tiger.rec +NETJET_DMA_SIZE -1; + register u_char state = bcs->hw.tiger.r_state; + register u_char r_one = bcs->hw.tiger.r_one; + register u_char r_val = bcs->hw.tiger.r_val; + register u_int bitcnt = bcs->hw.tiger.r_bitcnt; + u_int *p = buf; + 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; + char tmp[64]; + + if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_READ) { + sprintf(tmp,"tiger warn read double dma %x/%x", + cs->hw.njet.irqstat0, cs->hw.njet.last_is0); + debugl1(cs, tmp); + return; + } else { + cs->hw.njet.last_is0 &= ~NETJET_IRQM0_READ; + cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ); + } + if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ_1) + p = cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1; + else + p = cs->bcs[0].hw.tiger.rec + cnt - 1; + if (cs->bcs[0].mode == L1_MODE_HDLC) + read_raw(cs->bcs, p, cnt); + if (cs->bcs[1].mode == L1_MODE_HDLC) + read_raw(cs->bcs + 1, p, cnt); + cs->hw.njet.irqstat0 &= ~NETJET_IRQM0_READ; +} + +static void write_raw(struct BCState *bcs, u_int *buf, int cnt); + +static void fill_dma(struct BCState *bcs) +{ + 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; + if (make_raw_data(bcs)) + return; + 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, FREE_WRITE); + 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; + char tmp[64]; + + if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_WRITE) { + sprintf(tmp,"tiger warn write double dma %x/%x", + cs->hw.njet.irqstat0, cs->hw.njet.last_is0); + debugl1(cs, tmp); + return; + } else { + cs->hw.njet.last_is0 &= ~NETJET_IRQM0_WRITE; + cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE); + } + if (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE_1) + p = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1; + else + p = cs->bcs[0].hw.tiger.send + cnt - 1; + if (cs->bcs[0].mode == L1_MODE_HDLC) + write_raw(cs->bcs, p, cnt); + if (cs->bcs[1].mode == L1_MODE_HDLC) + write_raw(cs->bcs + 1, p, cnt); + cs->hw.njet.irqstat0 &= ~NETJET_IRQM0_WRITE; +} + +static void +tiger_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA | REQUEST): + save_flags(flags); + cli(); + if (st->l1.bcs->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 | INDICATION): + 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 | REQUEST): + 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 | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (PH_ACTIVATE | REQUEST): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + mode_tiger(st->l1.bcs, st->l1.mode, st->l1.bc); + st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); + break; + case (PH_DEACTIVATE | REQUEST): + 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; + } +} + + +void +close_tigerstate(struct BCState *bcs) +{ + 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; + } + discard_queue(&bcs->rqueue); + discard_queue(&bcs->squeue); + if (bcs->hw.tiger.tx_skb) { + dev_kfree_skb(bcs->hw.tiger.tx_skb, FREE_WRITE); + 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); +} + +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; + 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; + char tmp[128]; + long flags; + + if (!cs) { + printk(KERN_WARNING "NETjet: Spurious interrupt!\n"); + return; + } + if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) & + NETJET_ISACIRQ)) { + val = ReadISAC(cs, ISAC_ISTA); + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "tiger: i1 %x %x", sval, val); + debugl1(cs, tmp); + } + if (val) { + isac_interrupt(cs, val); + WriteISAC(cs, ISAC_MASK, 0xFF); + WriteISAC(cs, ISAC_MASK, 0x0); + } + } + save_flags(flags); + cli(); + if ((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT0))) { + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + restore_flags(flags); + return; + } + cs->hw.njet.irqstat0 = sval; + restore_flags(flags); +/* 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); +*/ +/* cs->hw.njet.irqmask0 = ((0x0f & cs->hw.njet.irqstat0) ^ 0x0f) | 0x30; +*/ byteout(cs->hw.njet.base + NETJET_IRQSTAT0, cs->hw.njet.irqstat0); +/* byteout(cs->hw.njet.base + NETJET_IRQMASK0, cs->hw.njet.irqmask0); +*/ if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) + read_tiger(cs); + if (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) + write_tiger(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + restore_flags(flags); + +/* if (!testcnt--) { + cs->hw.njet.dmactrl = 0; + byteout(cs->hw.njet.base + NETJET_DMACTRL, + cs->hw.njet.dmactrl); + byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0); + } +*/ +} + +static void +reset_netjet(struct IsdnCardState *cs) +{ + long flags; + + save_flags(flags); + sti(); + cs->hw.njet.ctrl_reg = 0xff; /* Reset On */ + byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + cs->hw.njet.ctrl_reg = 0x00; /* Reset Off and status read clear */ + byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + restore_flags(flags); + cs->hw.njet.auxd = 0; + cs->hw.njet.dmactrl = 0; + byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ); + byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ); + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); +} + +void +release_io_netjet(struct IsdnCardState *cs) +{ + byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0); + byteout(cs->hw.njet.base + NETJET_IRQMASK1, 0); + releasetiger(cs); + release_region(cs->hw.njet.base, 256); +} + + +static int +NETjet_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_netjet(cs); + return(0); + case CARD_RELEASE: + release_io_netjet(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &netjet_interrupt, + I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs)); + case CARD_INIT: + inittiger(cs); + clear_pending_isac_ints(cs); + initisac(cs); + /* Reenable all IRQ */ + cs->writeisac(cs, ISAC_MASK, 0); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + + + +static int pci_index __initdata = 0; + +__initfunc(int +setup_netjet(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + char tmp[64]; +#if CONFIG_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr, found; +#endif + + strcpy(tmp, NETjet_revision); + printk(KERN_INFO "HiSax: Traverse Tech. NETjet driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_NETJET) + return(0); +#if CONFIG_PCI + found = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_TRAVERSE_TECH, + PCI_NETJET_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + found = 1; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + if (found) + break; + } + if (!found) { + printk(KERN_WARNING "NETjet: No PCI card found\n"); + return(0); + } + pci_index++; + if (!pci_irq) { + printk(KERN_WARNING "NETjet: No IRQ for PCI card found\n"); + return(0); + } + if (!pci_ioaddr) { + printk(KERN_WARNING "NETjet: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.njet.base = pci_ioaddr; + cs->hw.njet.auxa = pci_ioaddr + NETJET_AUXDATA; + cs->hw.njet.isac = pci_ioaddr | NETJET_ISAC_OFF; + cs->irq = pci_irq; + bytecnt = 256; +#else + printk(KERN_WARNING "NETjet: NO_PCI_BIOS\n"); + printk(KERN_WARNING "NETjet: unable to config NETJET PCI\n"); + return (0); +#endif /* CONFIG_PCI */ + printk(KERN_INFO + "NETjet: PCI card configured at 0x%x IRQ %d\n", + cs->hw.njet.base, cs->irq); + if (check_region(cs->hw.njet.base, bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.njet.base, + cs->hw.njet.base + bytecnt); + return (0); + } else { + request_region(cs->hw.njet.base, bytecnt, "netjet isdn"); + } + reset_netjet(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &dummyrr; + cs->BC_Write_Reg = &dummywr; + cs->BC_Send_Data = &fill_dma; + cs->cardmsg = &NETjet_card_msg; + ISACVersion(cs, "NETjet:"); + return (1); +} diff --git a/drivers/isdn/hisax/niccy.c b/drivers/isdn/hisax/niccy.c new file mode 100644 index 000000000000..b1244352dbb8 --- /dev/null +++ b/drivers/isdn/hisax/niccy.c @@ -0,0 +1,406 @@ +/* $Id: niccy.c,v 1.1.2.4 1998/04/16 19:18:19 keil Exp $ + + * niccy.c low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and + * compatible (SAGEM cybermodem) + * + * Author Karsten Keil + * + * Thanks to Dr. Neuhaus and SAGEM for informations + * + * $Log: niccy.c,v $ + * Revision 1.1.2.4 1998/04/16 19:18:19 keil + * need config.h + * + * Revision 1.1.2.3 1998/04/08 22:05:26 keil + * Forgot PCI fix + * + * Revision 1.1.2.2 1998/04/08 21:48:23 keil + * New init; working Niccy PCI + * + * Revision 1.1.2.1 1998/02/11 14:23:20 keil + * support for Dr Neuhaus Niccy PnP and PCI + * + * + */ + + +#define __NO_VERSION__ +#include +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" +#include +#include + +extern const char *CardType[]; +const char *niccy_revision = "$Revision: 1.1.2.4 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ISAC_PCI_DATA 0 +#define HSCX_PCI_DATA 1 +#define ISAC_PCI_ADDR 2 +#define HSCX_PCI_ADDR 3 +#define ISAC_PNP 0 +#define HSCX_PNP 1 + +/* SUB Types */ +#define NICCY_PNP 1 +#define NICCY_PCI 2 + +/* PCI stuff */ +#define PCI_VENDOR_DR_NEUHAUS 0x1267 +#define PCI_NICCY_ID 0x1016 +#define PCI_IRQ_CTRL_REG 0x38 +#define PCI_IRQ_ENABLE 0x1f00 +#define PCI_IRQ_DISABLE 0xff0000 +#define PCI_IRQ_ASSERT 0x800000 + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.niccy.hscx_ale, + cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.niccy.hscx_ale, + cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0), value); +} + +#define READHSCX(cs, nr, reg) readreg(cs->hw.niccy.hscx_ale, \ + cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.niccy.hscx_ale, \ + cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.niccy.hscx_ale, \ + cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.niccy.hscx_ale, \ + cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "Niccy: Spurious interrupt!\n"); + return; + } + if (cs->subtyp == NICCY_PCI) { + int ival; + ival = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); + if (!(ival & PCI_IRQ_ASSERT)) /* IRQ not for us (shared) */ + return; + outl(ival, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); + } + val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 1) { + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0); + } + if (stat & 2) { + writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0); + } +} + +void +release_io_niccy(struct IsdnCardState *cs) +{ + if (cs->subtyp == NICCY_PCI) { + int val; + + val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); + val &= PCI_IRQ_DISABLE; + outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); + release_region(cs->hw.niccy.cfg_reg, 0x80); + release_region(cs->hw.niccy.isac, 4); + } else { + release_region(cs->hw.niccy.isac, 2); + release_region(cs->hw.niccy.isac_ale, 2); + } +} + +static void +niccy_reset(struct IsdnCardState *cs) +{ + int val, nval; + + val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); + nval = val | PCI_IRQ_ENABLE; + outl(nval, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); + + inithscxisac(cs, 3); +} + +static int +niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + int imode; + + switch (mt) { + case CARD_RESET: + niccy_reset(cs); + return(0); + case CARD_RELEASE: + release_io_niccy(cs); + return(0); + case CARD_SETIRQ: + if (cs->subtyp == NICCY_PCI) + imode = I4L_IRQ_FLAG | SA_SHIRQ; + else + imode = I4L_IRQ_FLAG; + return(request_irq(cs->irq, &niccy_interrupt, + imode, "HiSax", cs)); + break; + case CARD_INIT: + niccy_reset(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +static int pci_index __initdata = 0; + +__initfunc(int +setup_niccy(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, niccy_revision); + printk(KERN_INFO "HiSax: Niccy driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_NICCY) + return (0); + + if (card->para[1]) { + cs->hw.niccy.isac = card->para[1] + ISAC_PNP; + cs->hw.niccy.hscx = card->para[1] + HSCX_PNP; + cs->hw.niccy.isac_ale = card->para[2] + ISAC_PNP; + cs->hw.niccy.hscx_ale = card->para[2] + HSCX_PNP; + cs->hw.niccy.cfg_reg = 0; + cs->subtyp = NICCY_PNP; + cs->irq = card->para[0]; + if (check_region((cs->hw.niccy.isac), 2)) { + printk(KERN_WARNING + "HiSax: %s data port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.isac, + cs->hw.niccy.isac + 1); + return (0); + } else + request_region(cs->hw.niccy.isac, 2, "niccy data"); + if (check_region((cs->hw.niccy.isac_ale), 2)) { + printk(KERN_WARNING + "HiSax: %s address port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.isac_ale, + cs->hw.niccy.isac_ale + 1); + release_region(cs->hw.niccy.isac, 2); + return (0); + } else + request_region(cs->hw.niccy.isac_ale, 2, "niccy addr"); + } else { +#if CONFIG_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr; + + cs->subtyp = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_DR_NEUHAUS, + PCI_NICCY_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = NICCY_PCI; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO pci AMCC address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + if (!pci_ioaddr) { + printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n"); + return(0); + } + cs->hw.niccy.cfg_reg = pci_ioaddr & ~3 ; + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr); + if (cs->subtyp) + break; + } + if (!cs->subtyp) { + printk(KERN_WARNING "Niccy: No PCI card found\n"); + return(0); + } + pci_index++; + if (!pci_irq) { + printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n"); + return(0); + } + if (!pci_ioaddr) { + printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n"); + return(0); + } + + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.niccy.isac = pci_ioaddr + ISAC_PCI_DATA; + cs->hw.niccy.isac_ale = pci_ioaddr + ISAC_PCI_ADDR; + cs->hw.niccy.hscx = pci_ioaddr + HSCX_PCI_DATA; + cs->hw.niccy.hscx_ale = pci_ioaddr + HSCX_PCI_ADDR; + cs->irq = pci_irq; + if (check_region((cs->hw.niccy.isac), 4)) { + printk(KERN_WARNING + "HiSax: %s data port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.isac, + cs->hw.niccy.isac + 4); + return (0); + } else + request_region(cs->hw.niccy.isac, 4, "niccy"); + if (check_region(cs->hw.niccy.cfg_reg, 0x80)) { + printk(KERN_WARNING + "HiSax: %s pci port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.cfg_reg, + cs->hw.niccy.cfg_reg + 0x80); + release_region(cs->hw.niccy.isac, 4); + return (0); + } else { + request_region(cs->hw.niccy.cfg_reg, 0x80, "niccy pci"); + } +#else + printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n"); + printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n"); + return (0); +#endif /* CONFIG_PCI */ + } + printk(KERN_INFO + "HiSax: %s %s config irq:%d data:0x%X ale:0x%X\n", + CardType[cs->typ], (cs->subtyp==1) ? "PnP":"PCI", + cs->irq, cs->hw.niccy.isac, cs->hw.niccy.isac_ale); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &niccy_card_msg; + ISACVersion(cs, "Niccy:"); + if (HscxVersion(cs, "Niccy:")) { + printk(KERN_WARNING + "Niccy: wrong HSCX versions check IO address\n"); + release_io_niccy(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/q931.c b/drivers/isdn/hisax/q931.c index 1e1ee47723c0..15e05e0f646d 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.5.2.1 1997/10/17 22:14:20 keil Exp $ * q931.c code to decode ITU Q.931 call control messages * @@ -14,6 +14,12 @@ * * * $Log: q931.c,v $ + * Revision 1.5.2.1 1997/10/17 22:14:20 keil + * update to last hisax version + * + * Revision 1.6 1997/07/27 21:09:44 keil + * move functions to isdnl3.c + * * Revision 1.5 1997/04/06 22:56:43 keil * Some cosmetic changes * @@ -37,44 +43,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 +56,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/s0box.c b/drivers/isdn/hisax/s0box.c new file mode 100644 index 000000000000..539aa1cd2268 --- /dev/null +++ b/drivers/isdn/hisax/s0box.c @@ -0,0 +1,277 @@ +/* $Id: s0box.c,v 1.1.2.1 1998/04/11 18:44:41 keil Exp $ + + * s0box.c low level stuff for Creatix S0BOX + * + * Author S0BOX specific stuff: Enrik Berkhan (enrik@starfleet.inka.de) + * + * + */ +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; +const char *s0box_revision = "$Revision: 1.1.2.1 $"; + +static inline void +writereg(unsigned int padr, signed int addr, u_char off, u_char val) { + unsigned long flags; + + save_flags(flags); + cli(); + outb_p(0x1c,padr+2); + outb_p(0x14,padr+2); + outb_p((addr+off)&0x7f,padr); + outb_p(0x16,padr+2); + outb_p(val,padr); + outb_p(0x17,padr+2); + outb_p(0x14,padr+2); + outb_p(0x1c,padr+2); + restore_flags(flags); +} + +static u_char nibtab[] = { 1, 9, 5, 0xd, 3, 0xb, 7, 0xf, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 8, 4, 0xc, 2, 0xa, 6, 0xe } ; + +static inline u_char +readreg(unsigned int padr, signed int addr, u_char off) { + register u_char n1, n2; + unsigned long flags; + + save_flags(flags); + cli(); + outb_p(0x1c,padr+2); + outb_p(0x14,padr+2); + outb_p((addr+off)|0x80,padr); + outb_p(0x16,padr+2); + outb_p(0x17,padr+2); + n1 = (inb_p(padr+1) >> 3) & 0x17; + outb_p(0x16,padr+2); + n2 = (inb_p(padr+1) >> 3) & 0x17; + outb_p(0x14,padr+2); + outb_p(0x1c,padr+2); + restore_flags(flags); + return nibtab[n1] | (nibtab[n2] << 4); +} + +static inline void +read_fifo(unsigned int padr, signed int adr, u_char * data, int size) +{ + int i; + register u_char n1, n2; + + outb_p(0x1c, padr+2); + outb_p(0x14, padr+2); + outb_p(adr|0x80, padr); + outb_p(0x16, padr+2); + for (i=0; i> 3) & 0x17; + outb_p(0x16,padr+2); + n2 = (inb_p(padr+1) >> 3) & 0x17; + *(data++)=nibtab[n1] | (nibtab[n2] << 4); + } + outb_p(0x14,padr+2); + outb_p(0x1c,padr+2); + return; +} + +static inline void +write_fifo(unsigned int padr, signed int adr, u_char * data, int size) +{ + int i; + outb_p(0x1c, padr+2); + outb_p(0x14, padr+2); + outb_p(adr&0x7f, padr); + for (i=0; ihw.teles3.cfg_reg, cs->hw.teles3.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset)); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset, value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt) + +#include "hscx_irq.c" + +static void +s0box_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ +#define MAXCOUNT 20 + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + int count = 0; + + if (!cs) { + printk(KERN_WARNING "Teles: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + count++; + val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA); + if (val && count < MAXCOUNT) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA); + if (val && count < MAXCOUNT) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (count >= MAXCOUNT) + printk(KERN_WARNING "S0Box: more than %d loops in s0box_interrupt\n", count); + if (stat & 1) { + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0x0); + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0x0); + } + if (stat & 2) { + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0x0); + } +} + +void +release_io_s0box(struct IsdnCardState *cs) +{ + release_region(cs->hw.teles3.cfg_reg, 8); +} + +static int +S0Box_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + break; + case CARD_RELEASE: + release_io_s0box(cs); + break; + case CARD_SETIRQ: + return(request_irq(cs->irq, &s0box_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscxisac(cs, 3); + break; + case CARD_TEST: + break; + } + return(0); +} + +__initfunc(int +setup_s0box(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, s0box_revision); + printk(KERN_INFO "HiSax: S0Box IO driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_S0BOX) + return (0); + + cs->hw.teles3.cfg_reg = card->para[1]; + cs->hw.teles3.hscx[0] = -0x20; + cs->hw.teles3.hscx[1] = 0x0; + cs->hw.teles3.isac = 0x20; + cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e; + cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e; + cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e; + cs->irq = card->para[0]; + if (check_region(cs->hw.teles3.cfg_reg,8)) { + printk(KERN_WARNING + "HiSax: %s ports %x-%x already in use\n", + CardType[cs->typ], + cs->hw.teles3.cfg_reg, + cs->hw.teles3.cfg_reg + 7); + return 0; + } else + request_region(cs->hw.teles3.cfg_reg, 8, "S0Box parallel I/O"); + printk(KERN_INFO + "HiSax: %s config irq:%d isac:0x%x cfg:0x%x\n", + CardType[cs->typ], cs->irq, + cs->hw.teles3.isac, cs->hw.teles3.cfg_reg); + printk(KERN_INFO + "HiSax: hscx A:0x%x hscx B:0x%x\n", + cs->hw.teles3.hscx[0], cs->hw.teles3.hscx[1]); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &S0Box_card_msg; + ISACVersion(cs, "S0Box:"); + if (HscxVersion(cs, "S0Box:")) { + printk(KERN_WARNING + "S0Box: wrong HSCX versions check IO address\n"); + release_io_s0box(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/sedlbauer.c b/drivers/isdn/hisax/sedlbauer.c new file mode 100644 index 000000000000..d754c4142913 --- /dev/null +++ b/drivers/isdn/hisax/sedlbauer.c @@ -0,0 +1,353 @@ +/* $Id: sedlbauer.c,v 1.1.2.5 1998/04/08 21:58:44 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.1.2.5 1998/04/08 21:58:44 keil + * New init code + * + * Revision 1.1.2.4 1998/02/09 11:21:17 keil + * Sedlbauer PCMCIA support from Marcus Niemann + * + * Revision 1.1.2.3 1998/01/27 22:37:29 keil + * fast io + * + * Revision 1.1.2.2 1997/11/15 18:50:56 keil + * new common init function + * + * Revision 1.1.2.1 1997/10/17 22:10:56 keil + * new files on 2.0 + * + * Revision 1.1 1997/09/11 17:32:04 keil + * new + * + * + */ + +#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.1.2.5 $"; + +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: + inithscxisac(cs, 3); + 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/sportster.c b/drivers/isdn/hisax/sportster.c new file mode 100644 index 000000000000..6826840f1c5c --- /dev/null +++ b/drivers/isdn/hisax/sportster.c @@ -0,0 +1,286 @@ +/* $Id: sportster.c,v 1.1.2.4 1998/04/08 21:58:46 keil Exp $ + + * sportster.c low level stuff for USR Sportster internal TA + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation + * + * $Log: sportster.c,v $ + * Revision 1.1.2.4 1998/04/08 21:58:46 keil + * New init code + * + * Revision 1.1.2.3 1998/01/27 22:37:31 keil + * fast io + * + * Revision 1.1.2.2 1997/11/15 18:50:57 keil + * new common init function + * + * Revision 1.1.2.1 1997/10/17 22:10:58 keil + * new files on 2.0 + * + */ +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; +const char *sportster_revision = "$Revision: 1.1.2.4 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define SPORTSTER_ISAC 0xC000 +#define SPORTSTER_HSCXA 0x0000 +#define SPORTSTER_HSCXB 0x4000 +#define SPORTSTER_RES_IRQ 0x8000 +#define SPORTSTER_RESET 0x80 +#define SPORTSTER_INTE 0x40 + +static inline int +calc_off(unsigned int base, unsigned int off) +{ + return(base + ((off & 0xfc)<<8) + ((off & 3)<<1)); +} + +static inline void +read_fifo(unsigned int adr, u_char * data, int size) +{ + insb(adr, data, size); +} + +static void +write_fifo(unsigned int adr, u_char * data, int size) +{ + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (bytein(calc_off(cs->hw.spt.isac, offset))); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + byteout(calc_off(cs->hw.spt.isac, offset), value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + read_fifo(cs->hw.spt.isac, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + write_fifo(cs->hw.spt.isac, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (bytein(calc_off(cs->hw.spt.hscx[hscx], offset))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + byteout(calc_off(cs->hw.spt.hscx[hscx], offset), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) bytein(calc_off(cs->hw.spt.hscx[nr], reg)) +#define WRITEHSCX(cs, nr, reg, data) byteout(calc_off(cs->hw.spt.hscx[nr], reg), data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.spt.hscx[nr], ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.spt.hscx[nr], ptr, cnt) + +#include "hscx_irq.c" + +static void +sportster_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val; + + if (!cs) { + printk(KERN_WARNING "Sportster: Spurious interrupt!\n"); + return; + } + val = READHSCX(cs, 1, HSCX_ISTA); + Start_HSCX: + if (val) + hscx_int_main(cs, val); + val = ReadISAC(cs, ISAC_ISTA); + Start_ISAC: + if (val) + isac_interrupt(cs, val); + val = READHSCX(cs, 1, HSCX_ISTA); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = ReadISAC(cs, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + /* get a new irq impulse if there any pending */ + bytein(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ +1); +} + +void +release_io_sportster(struct IsdnCardState *cs) +{ + int i, adr; + + byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, 0); + for (i=0; i<64; i++) { + adr = cs->hw.spt.cfg_reg + i *1024; + release_region(adr, 8); + } +} + +void +reset_sportster(struct IsdnCardState *cs) +{ + long flags; + + cs->hw.spt.res_irq |= SPORTSTER_RESET; /* Reset On */ + byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq); + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + cs->hw.spt.res_irq &= ~SPORTSTER_RESET; /* Reset Off */ + byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + restore_flags(flags); +} + +static int +Sportster_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_sportster(cs); + return(0); + case CARD_RELEASE: + release_io_sportster(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &sportster_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscxisac(cs, 1); + cs->hw.spt.res_irq |= SPORTSTER_INTE; /* IRQ On */ + byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq); + inithscxisac(cs, 2); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +get_io_range(struct IsdnCardState *cs)) +{ + int i, j, adr; + + for (i=0;i<64;i++) { + adr = cs->hw.spt.cfg_reg + i *1024; + if (check_region(adr, 8)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[cs->typ], adr, adr + 8); + break; + } else + request_region(adr, 8, "sportster"); + } + if (i==64) + return(1); + else { + for (j=0; jhw.spt.cfg_reg + j *1024; + release_region(adr, 8); + } + return(0); + } +} + +__initfunc(int +setup_sportster(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, sportster_revision); + printk(KERN_INFO "HiSax: USR Sportster driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_SPORTSTER) + return (0); + + cs->hw.spt.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + if (!get_io_range(cs)) + return (0); + cs->hw.spt.isac = cs->hw.spt.cfg_reg + SPORTSTER_ISAC; + cs->hw.spt.hscx[0] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXA; + cs->hw.spt.hscx[1] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXB; + + switch(cs->irq) { + case 5: cs->hw.spt.res_irq = 1; + break; + case 7: cs->hw.spt.res_irq = 2; + break; + case 10:cs->hw.spt.res_irq = 3; + break; + case 11:cs->hw.spt.res_irq = 4; + break; + case 12:cs->hw.spt.res_irq = 5; + break; + case 14:cs->hw.spt.res_irq = 6; + break; + case 15:cs->hw.spt.res_irq = 7; + break; + default:release_io_sportster(cs); + printk(KERN_WARNING "Sportster: wrong IRQ\n"); + return(0); + } + reset_sportster(cs); + printk(KERN_INFO + "HiSax: %s config irq:%d cfg:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.spt.cfg_reg); + + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Sportster_card_msg; + ISACVersion(cs, "Sportster:"); + if (HscxVersion(cs, "Sportster:")) { + printk(KERN_WARNING + "Sportster: wrong HSCX versions check IO address\n"); + release_io_sportster(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/tei.c b/drivers/isdn/hisax/tei.c index f13f9cd1fa47..d9f2b50936ed 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 1.8.2.6 1998/05/27 18:06:21 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,52 +7,110 @@ * Fritz Elfert * * $Log: tei.c,v $ - * Revision 1.8 1997/04/07 22:59:08 keil - * GFP_KERNEL --> GFP_ATOMIC + * Revision 1.8.2.6 1998/05/27 18:06:21 keil + * HiSax 3.0 * - * Revision 1.7 1997/04/06 22:54:03 keil - * Using SKB's + * Revision 1.8.2.5 1998/03/07 23:15:38 tsbogend + * made HiSax working on Linux/Alpha * - * Revision 1.6 1997/02/09 00:25:12 keil - * new interface handling, one interface per card + * Revision 1.8.2.4 1998/01/27 22:43:49 keil + * fixed MDL_ASSIGN_REQ * - * Revision 1.5 1997/01/27 15:57:51 keil + * Revision 1.8.2.3 1997/11/15 18:54:20 keil * cosmetics * - * Revision 1.4 1997/01/21 22:32:44 keil - * Tei verify request + * Revision 1.8.2.2 1997/10/17 22:14:23 keil + * update to last hisax version + * + * Revision 2.2 1997/07/31 19:24:39 keil + * fixed a warning * - * Revision 1.3 1997/01/04 13:45:02 keil - * cleanup,adding remove tei request (thanks to Sim Yskes) + * Revision 2.1 1997/07/31 11:50:16 keil + * ONE TEI and FIXED TEI handling * - * Revision 1.2 1996/12/08 19:52:39 keil - * minor debug fix + * Revision 2.0 1997/07/27 21:13:30 keil + * New TEI managment * - * Revision 1.1 1996/10/13 20:04:57 keil - * Initial revision + * Revision 1.9 1997/06/26 11:18:02 keil + * New managment * + * Revision 1.8 1997/04/07 22:59:08 keil + * GFP_KERNEL --> GFP_ATOMIC + * + * Revision 1.7 1997/04/06 22:54:03 keil + * Using SKB's * + * Old log removed/ KKe * */ #define __NO_VERSION__ #include "hisax.h" +#include "isdnl2.h" +#include -extern struct IsdnCard cards[]; -extern int nrcards; +const char *tei_revision = "$Revision: 1.8.2.6 $"; -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 +130,369 @@ 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; } SET_SKB_FREE(skb); - skb_reserve(skb, MAX_HEADER_LEN); + bp = skb_put(skb, 3); + bp[0] = (TEI_SAPI << 2); + bp[1] = (GROUP_TEI << 1) | 0x1; + bp[2] = UI; bp = skb_put(skb, 5); - bp[0] = 0xf; + bp[0] = TEI_ENTITY_ID; bp[1] = ri >> 8; bp[2] = ri & 0xff; - bp[3] = mt; - bp[4] = (ai << 1) | 1; - st->l3.l3l2(st, DL_UNIT_DATA, skb); + bp[3] = m_id; + bp[4] = (tei << 1) | 1; + st->l2.l2l1(st, PH_DATA | REQUEST, skb); } static void -mdl_unit_data_ind(struct PStack *st, unsigned int ri, u_char mt, u_char ai) +tei_id_request(struct FsmInst *fi, int event, void *arg) { - unsigned int tces; - struct PStack *otsp, *ptr; + 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); - dev_kfree_skb(skb, FREE_READ); - 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 | RESPONSE, NULL); + } + } else if (ri == st->ma.ri) { + FsmDelTimer(&st->ma.t202, 1); + FsmChangeState(&st->ma.tei_m, ST_TEI_NOP); + st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) tei); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL); } } -unsigned int -randomces(void) +static void +tei_id_denied(struct FsmInst *fi, int event, void *arg) { - int x = jiffies & 0xffff; + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + int ri, tei; + char tmp[64]; - return (x); + 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_man(struct PStack *sp, int i, void *v) +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->l3.l3l2(st, MDL_REMOVE | REQUEST, 0); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL); + } +} + +static void +tei_id_verify(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + char tmp[64]; + + 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_id_req_tout(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + char tmp[64]; + struct IsdnCardState *cs; + + 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->l3.l3l2(st, MDL_ERROR | RESPONSE, 0); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL); + FsmChangeState(fi, ST_TEI_NOP); + } +} + +static void +tei_id_ver_tout(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + 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->l3.l3l2(st, MDL_REMOVE | REQUEST, 0); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL); + FsmChangeState(fi, ST_TEI_NOP); + } +} + +static void +tei_l1l2(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + int mt; + char tmp[64]; - printk(KERN_DEBUG "tei_man\n"); + if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) { + dev_kfree_skb(skb, FREE_READ); + return; + } + + if (pr == (PH_DATA | INDICATION)) { + if (skb->len < 3) { + sprintf(tmp, "short mgr frame %ld/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 %ld/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, FREE_READ); } static void tei_l2tei(struct PStack *st, int pr, void *arg) { - struct IsdnCardState *sp = st->l1.hardware; + struct IsdnCardState *cs; - tei_handler(sp->teistack, pr, arg); + if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) { + if (pr == (MDL_ASSIGN | INDICATION)) { + if (st->ma.debug) { + char tmp[64]; + sprintf(tmp, "fixed assign tei %d", st->l2.tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) st->l2.tei); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL); + } + return; + } + switch (pr) { + case (MDL_ASSIGN | INDICATION): + FsmEvent(&st->ma.tei_m, EV_IDREQ, arg); + break; + case (MDL_ERROR | REQUEST): + FsmEvent(&st->ma.tei_m, EV_VERIFY, arg); + break; + default: + break; + } +} + +static void +tei_debug(struct FsmInst *fi, char *s) +{ + struct PStack *st = fi->userdata; + char tm[32], str[256]; + + 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; +} - sprintf(tmp, "Card %d tei", sp->cardnr + 1); - setstack_isdnl2(st, tmp); - st->l2.debug = 0; - st->l3.debug = 0; +void +release_tei(struct IsdnCardState *cs) +{ + struct PStack *st = cs->stlist; + + 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..398786bdeee3 --- /dev/null +++ b/drivers/isdn/hisax/teleint.c @@ -0,0 +1,378 @@ +/* $Id: teleint.c,v 1.1.2.6 1998/05/27 18:06:24 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.1.2.6 1998/05/27 18:06:24 keil + * HiSax 3.0 + * + * Revision 1.1.2.5 1998/04/08 21:58:48 keil + * New init code + * + * Revision 1.1.2.4 1998/04/04 21:58:27 keil + * fix HFC BUSY on ISAC fifos + * + * Revision 1.1.2.3 1998/01/27 22:37:41 keil + * fast io + * + * Revision 1.1.2.2 1997/11/15 18:50:58 keil + * new common init function + * + * Revision 1.1.2.1 1997/10/17 22:11:00 keil + * new files on 2.0 + * + * Revision 1.1 1997/09/11 17:32:32 keil + * new + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hfc_2bs0.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *TeleInt_revision = "$Revision: 1.1.2.6 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + int max_delay = 2000; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = HFC_BUSY & bytein(ale); + while (ret && --max_delay) + ret = HFC_BUSY & bytein(ale); + if (!max_delay) { + printk(KERN_WARNING "TeleInt Busy not inaktive\n"); + restore_flags(flags); + return (0); + } + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + register u_char ret; + register int max_delay = 20000; + register int i; + + byteout(ale, off); + for (i = 0; ihw.hfc.cip = offset; + return (readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + cs->hw.hfc.cip = offset; + writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + cs->hw.hfc.cip = 0; + readfifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + cs->hw.hfc.cip = 0; + writefifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size); +} + +static u_char +ReadHFC(struct IsdnCardState *cs, int data, u_char reg) +{ + register u_char ret; + + if (data) { + cs->hw.hfc.cip = reg; + byteout(cs->hw.hfc.addr | 1, reg); + ret = bytein(cs->hw.hfc.addr); + if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) { + 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); + /* Reenable all IRQ */ + cs->writeisac(cs, ISAC_MASK, 0); + cs->writeisac(cs, ISAC_CMDR, 0x41); + cs->hw.hfc.timer.expires = jiffies + 1; + add_timer(&cs->hw.hfc.timer); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_TeleInt(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, TeleInt_revision); + printk(KERN_INFO "HiSax: TeleInt driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_TELEINT) + return (0); + + cs->hw.hfc.addr = card->para[1] & 0x3fe; + cs->irq = card->para[0]; + cs->hw.hfc.cirm = HFC_CIRM; + cs->hw.hfc.isac_spcr = 0x00; + cs->hw.hfc.cip = 0; + cs->hw.hfc.ctmt = HFC_CTMT | HFC_CLTIMER; + cs->bcs[0].hw.hfc.send = NULL; + cs->bcs[1].hw.hfc.send = NULL; + cs->hw.hfc.fifosize = 7 * 1024 + 512; + cs->hw.hfc.timer.function = (void *) TeleInt_Timer; + cs->hw.hfc.timer.data = (long) cs; + init_timer(&cs->hw.hfc.timer); + if (check_region((cs->hw.hfc.addr), 2)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.hfc.addr, + cs->hw.hfc.addr + 2); + return (0); + } else { + request_region(cs->hw.hfc.addr, 2, "TeleInt isdn"); + } + /* HW IO = IO */ + byteout(cs->hw.hfc.addr, cs->hw.hfc.addr & 0xff); + byteout(cs->hw.hfc.addr | 1, ((cs->hw.hfc.addr & 0x300) >> 8) | 0x54); + switch (cs->irq) { + case 3: + cs->hw.hfc.cirm |= HFC_INTA; + break; + case 4: + cs->hw.hfc.cirm |= HFC_INTB; + break; + case 5: + cs->hw.hfc.cirm |= HFC_INTC; + break; + case 7: + cs->hw.hfc.cirm |= HFC_INTD; + break; + case 10: + cs->hw.hfc.cirm |= HFC_INTE; + break; + case 11: + cs->hw.hfc.cirm |= HFC_INTF; + break; + default: + printk(KERN_WARNING "TeleInt: wrong IRQ\n"); + release_io_TeleInt(cs); + return (0); + } + byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); + byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.ctmt); + + printk(KERN_INFO + "TeleInt: defined at 0x%x IRQ %d\n", + cs->hw.hfc.addr, + cs->irq); + + reset_TeleInt(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHFC; + cs->BC_Write_Reg = &WriteHFC; + cs->cardmsg = &TeleInt_card_msg; + ISACVersion(cs, "TeleInt:"); + return (1); +} diff --git a/drivers/isdn/hisax/teles0.c b/drivers/isdn/hisax/teles0.c index 91f9e59b0ce3..fd2b281e9dc2 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 1.8.2.9 1998/04/08 21:58:49 keil Exp $ * teles0.c low level stuff for Teles Memory IO isdn cards * based on the teles driver from Jan den Ouden @@ -10,97 +10,103 @@ * Beat Doebeli * * $Log: teles0.c,v $ - * Revision 1.8 1997/04/13 19:54:04 keil - * Change in IRQ check delay for SMP + * Revision 1.8.2.9 1998/04/08 21:58:49 keil + * New init code * - * Revision 1.7 1997/04/06 22:54:04 keil - * Using SKB's + * Revision 1.8.2.8 1998/03/07 23:15:40 tsbogend + * made HiSax working on Linux/Alpha + * + * Revision 1.8.2.7 1998/02/03 23:17:16 keil + * IRQ 9 * - * Revision 1.6 1997/01/27 15:52:18 keil - * SMP proof,cosmetics + * Revision 1.8.2.6 1998/01/27 22:37:43 keil + * fast io * - * Revision 1.5 1997/01/21 22:25:59 keil - * cleanups + * Revision 1.8.2.5 1997/11/15 18:51:00 keil + * new common init function * - * Revision 1.4 1996/11/05 19:41:27 keil - * more changes for 2.1 + * Revision 1.8.2.4 1997/10/17 22:14:26 keil + * update to last hisax version * - * 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: 1.8.2.9 $"; -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) static inline u_char readisac(unsigned int adr, u_char off) { - return readb(adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off); + return readb(adr + ((off & 1) ? 0x2ff : 0x100) + off); } static inline void writeisac(unsigned int adr, u_char off, u_char data) { - writeb(data, adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off); + writeb(data, adr + ((off & 1) ? 0x2ff : 0x100) + off); mb(); } static inline u_char readhscx(unsigned int adr, int hscx, u_char off) { - return readb(adr + (hscx ? 0x1e0 : 0x1a0) + + return readb(adr + (hscx ? 0x1c0 : 0x180) + ((off & 1) ? 0x1ff : 0) + off); } static inline void writehscx(unsigned int adr, int hscx, u_char off, u_char data) { - writeb(data, adr + (hscx ? 0x1e0 : 0x1a0) + - ((off & 1) ? 0x1ff : 0) + off); + writeb(data, adr + (hscx ? 0x1c0 : 0x180) + + ((off & 1) ? 0x1ff : 0) + off); mb(); } static inline void read_fifo_isac(unsigned int adr, u_char * data, int size) { register int i; - register u_char *ad = (u_char *) (adr + 0x100); + register u_char *ad = (u_char *) ((long)adr + 0x100); for (i = 0; i < size; i++) data[i] = readb(ad); } -static void +static inline void write_fifo_isac(unsigned int adr, u_char * data, int size) { register int i; - register u_char *ad = (u_char *) (adr + 0x100); - for (i = 0; i < size; i++) - writeb(data[i], ad); + register u_char *ad = (u_char *) ((long)adr + 0x100); + for (i = 0; i < size; i++) { + writeb(data[i], ad); mb(); + } } static inline void read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) { register int i; - register u_char *ad = (u_char *) (adr + (hscx ? 0x1c0 : 0x180)); + register u_char *ad = (u_char *) ((long)adr + (hscx ? 0x1c0 : 0x180)); for (i = 0; i < size; i++) data[i] = readb(ad); } @@ -109,749 +115,206 @@ static inline void write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) { int i; - register u_char *ad = (u_char *) (adr + (hscx ? 0x1c0 : 0x180)); - for (i = 0; i < size; i++) - writeb(data[i], ad); -} -static inline void -waitforCEC(int adr, int hscx) -{ - int to = 50; - - while ((readhscx(adr, hscx, HSCX_STAR) & 0x04) && to) { - udelay(1); - to--; + register u_char *ad = (u_char *) ((long)adr + (hscx ? 0x1c0 : 0x180)); + for (i = 0; i < size; i++) { + writeb(data[i], ad); mb(); } - if (!to) - printk(KERN_WARNING "Teles0: waitforCEC timeout\n"); } +/* Interface functions */ -static inline void -waitforXFW(int adr, int hscx) -{ - int to = 50; - - while ((!(readhscx(adr, hscx, HSCX_STAR) & 0x44) == 0x40) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Teles0: waitforXFW timeout\n"); -} - -static inline void -writehscxCMDR(int adr, int hscx, u_char data) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - long flags; - - save_flags(flags); - cli(); - waitforCEC(adr, hscx); - writehscx(adr, hscx, HSCX_CMDR, data); - restore_flags(flags); + return (readisac(cs->hw.teles0.membase, offset)); } -/* - * fast interrupt here - */ - - static void -hscxreport(struct IsdnCardState *sp, int hscx) +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - printk(KERN_DEBUG "HSCX %d\n", hscx); - printk(KERN_DEBUG "ISTA %x\n", readhscx(sp->membase, hscx, HSCX_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readhscx(sp->membase, hscx, HSCX_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readhscx(sp->membase, hscx, HSCX_EXIR)); + writeisac(cs->hw.teles0.membase, offset, value); } -void -teles0_report(struct IsdnCardState *sp) -{ - printk(KERN_DEBUG "ISAC\n"); - printk(KERN_DEBUG "ISTA %x\n", readisac(sp->membase, ISAC_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readisac(sp->membase, ISAC_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readisac(sp->membase, ISAC_EXIR)); - hscxreport(sp, 0); - hscxreport(sp, 1); -} - -/* - * HSCX stuff goes here - */ - static void -hscx_empty_fifo(struct HscxState *hsp, int count) +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->membase, hsp->hscx, 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo_hscx(sp->membase, hsp->hscx, ptr, count); - writehscxCMDR(sp->membase, hsp->hscx, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + read_fifo_isac(cs->hw.teles0.membase, data, size); } static void -hscx_fill_fifo(struct HscxState *hsp) +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 "teles0: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "teles0: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - dev_kfree_skb(hsp->tx_skb, FREE_WRITE); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } -} - -/* - * ISAC stuff goes here - */ - -static void -isac_empty_fifo(struct IsdnCardState *sp, int count) -{ - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - writeisac(sp->membase, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo_isac(sp->membase, ptr, count); - writeisac(sp->membase, ISAC_CMDR, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } -} - -static void -isac_fill_fifo(struct IsdnCardState *sp) -{ - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - write_fifo_isac(sp->membase, ptr, count); - writeisac(sp->membase, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_fill_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (readhscx(cs->hw.teles0.membase, hscx, offset)); } static void -ph_command(struct IsdnCardState *sp, unsigned int command) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - if (sp->debug & L1_DEB_ISAC) { - char tmp[32]; - sprintf(tmp, "ph_command %d", command); - debugl1(sp, tmp); - } - writeisac(sp->membase, ISAC_CIX0, (command << 2) | 3); + writehscx(cs->hw.teles0.membase, hscx, offset, value); } -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; - - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = readisac(sp->membase, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!(exval & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - writeisac(sp->membase, ISAC_CMDR, 0x80); - } else { - count = readisac(sp->membase, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "teles0: D receive out of memory\n"); - else { - SET_SKB_FREE(skb); - memcpy(skb_put(skb, count), sp->rcvbuf, count); - skb_queue_tail(&sp->rq, skb); - } - } - } - sp->rcvidx = 0; - isac_sched_event(sp, ISAC_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - isac_empty_fifo(sp, 32); - } - if (val & 0x20) { /* RSC */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RSC interrupt"); - } - if (val & 0x10) { /* XPR */ - if (sp->tx_skb) - if (sp->tx_skb->len) { - isac_fill_fifo(sp); - goto afterXPR; - } else { - dev_kfree_skb(sp->tx_skb, FREE_WRITE); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (readisac(sp->membase, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = readisac(sp->membase, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - } -} +/* + * fast interrupt HSCX stuff goes here + */ -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ +#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg) +#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt) - u_char exval; - struct HscxState *hsp; - char tmp[32]; - - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = readhscx(sp->membase, 1, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->membase, hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = readhscx(sp->membase, 0, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->membase, hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = readhscx(sp->membase, 0, HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void -telesS0_interrupt(int intno, void *dev_id, struct pt_regs *regs) +teles0_interrupt(int intno, void *dev_id, struct pt_regs *regs) { - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val, stat = 0; + int count = 0; - sp = (struct IsdnCardState *) irq2dev_map[intno]; - - if (!sp) { + if (!cs) { printk(KERN_WARNING "Teles0: Spurious interrupt!\n"); return; } - val = readhscx(sp->membase, 1, HSCX_ISTA); + val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); Start_HSCX: if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); stat |= 1; } - val = readisac(sp->membase, ISAC_ISTA); + val = readisac(cs->hw.teles0.membase, ISAC_ISTA); Start_ISAC: if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); stat |= 2; } - val = readhscx(sp->membase, 1, HSCX_ISTA); - if (val) { - if (sp->debug & L1_DEB_HSCX) - debugl1(sp, "HSCX IntStat after IntRoutine"); + count++; + val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); + if (val && count < 20) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); goto Start_HSCX; } - val = readisac(sp->membase, ISAC_ISTA); - if (val) { - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "ISAC IntStat after IntRoutine"); + val = readisac(cs->hw.teles0.membase, ISAC_ISTA); + if (val && count < 20) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } if (stat & 1) { - writehscx(sp->membase, 0, HSCX_MASK, 0xFF); - writehscx(sp->membase, 1, HSCX_MASK, 0xFF); - writehscx(sp->membase, 0, HSCX_MASK, 0x0); - writehscx(sp->membase, 1, HSCX_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF); + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0); } if (stat & 2) { - writeisac(sp->membase, ISAC_MASK, 0xFF); - writeisac(sp->membase, ISAC_MASK, 0x0); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0); } } - -static void -initisac(struct IsdnCardState *sp) -{ - 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); mb(); + HZDELAY(HZ / 5 + 1); + writeb(1, cs->hw.teles0.membase + 0x80); mb(); + 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.interrupts[sp->irq]; - sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter); - debugl1(sp, tmp); - clear_pending_ints(sp); - ret = get_irq(sp->cardnr, &telesS0_interrupt); - if (ret) { - initisac(sp); - sp->modehscx(sp->hs, 0, 0); - sp->modehscx(sp->hs + 1, 0, 0); - while (loop++ < 10) { - /* At least 1-3 irqs must happen - * (one from HSCX A, one from HSCX B, 3rd from ISAC) - */ - if (kstat.interrupts[sp->irq] > sp->counter) - break; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 1; - schedule(); - } - sprintf(tmp, "IRQ %d count %d", sp->irq, - kstat.interrupts[sp->irq]); - debugl1(sp, tmp); - if (kstat.interrupts[sp->irq] == sp->counter) { - printk(KERN_WARNING - "Teles0: IRQ(%d) getting no interrupts during init\n", - sp->irq); - irq2dev_map[sp->irq] = NULL; - free_irq(sp->irq, NULL); - return (0); - } - } - return (ret); + switch (mt) { + case CARD_RESET: + reset_teles0(cs); + return(0); + case CARD_RELEASE: + release_io_teles0(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &teles0_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); + } + return(0); } -int -setup_teles0(struct IsdnCard *card) +__initfunc(int +setup_teles0(struct IsdnCard *card)) { - u_char cfval, val, verA, verB; - struct IsdnCardState *sp = card->sp; - long flags; + u_char val; + struct IsdnCardState *cs = card->cs; char tmp[64]; strcpy(tmp, teles0_revision); - printk(KERN_NOTICE "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp)); - if ((sp->typ != ISDN_CTYPE_16_0) && (sp->typ != ISDN_CTYPE_8_0)) + printk(KERN_INFO "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp)); + if ((cs->typ != ISDN_CTYPE_16_0) && (cs->typ != ISDN_CTYPE_8_0)) return (0); - if (sp->typ == ISDN_CTYPE_16_0) - sp->cfg_reg = card->para[2]; + if (cs->typ == ISDN_CTYPE_16_0) + cs->hw.teles0.cfg_reg = card->para[2]; else /* 8.0 */ - sp->cfg_reg = 0; + cs->hw.teles0.cfg_reg = 0; if (card->para[1] < 0x10000) { card->para[1] <<= 4; @@ -859,110 +322,69 @@ 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/teles3.c b/drivers/isdn/hisax/teles3.c index 23333e7f8ab2..ba6e3a206302 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 1.11.2.9 1998/04/08 21:58:52 keil Exp $ * teles3.c low level stuff for Teles 16.3 & PNP isdn cards * @@ -11,6 +11,30 @@ * Beat Doebeli * * $Log: teles3.c,v $ + * Revision 1.11.2.9 1998/04/08 21:58:52 keil + * New init code + * + * Revision 1.11.2.8 1998/01/27 22:37:46 keil + * fast io + * + * Revision 1.11.2.7 1998/01/11 22:58:01 keil + * make IRQ 9 working again + * + * Revision 1.11.2.6 1997/12/01 22:35:43 keil + * ID Byte for 16.3 version 1.1 + * + * Revision 1.11.2.5 1997/11/15 18:51:03 keil + * new common init function + * + * Revision 1.11.2.4 1997/10/17 22:14:30 keil + * update to last hisax version + * + * Revision 2.1 1997/07/27 21:47:12 keil + * new interface structures + * + * Revision 2.0 1997/06/26 11:02:46 keil + * New Layer and card interface + * * Revision 1.11 1997/04/13 19:54:05 keil * Change in IRQ check delay for SMP * @@ -35,36 +59,20 @@ * Revision 1.6 1997/01/27 15:52:55 keil * SMP proof,cosmetics, PCMCIA added * - * Revision 1.5 1997/01/21 22:28:32 keil - * cleanups - * - * Revision 1.4 1996/12/14 21:05:41 keil - * Reset for 16.3 PnP - * - * Revision 1.3 1996/11/05 19:56:54 keil - * debug output fixed - * - * Revision 1.2 1996/10/27 22:09:15 keil - * cosmetic changes - * - * Revision 1.1 1996/10/13 20:04:59 keil - * Initial revision - * - * + * removed old log info /KKe * */ #define __NO_VERSION__ -#include "siemens.h" #include "hisax.h" -#include "teles3.h" +#include "isac.h" +#include "hscx.h" #include "isdnl1.h" -#include extern const char *CardType[]; -const char *teles3_revision = "$Revision: 1.11 $"; +const char *teles3_revision = "$Revision: 1.11.2.9 $"; -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) static inline u_char readreg(unsigned int adr, u_char off) @@ -82,953 +90,407 @@ 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 "teles3: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "teles3: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - dev_kfree_skb(hsp->tx_skb, FREE_WRITE); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } + read_fifo(cs->hw.teles3.isacfifo, data, size); } -/* - * ISAC stuff goes here - */ - static void -isac_empty_fifo(struct IsdnCardState *sp, int count) +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo(sp->isac, ptr, count); - writereg(sp->isac, ISAC_CMDR, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + write_fifo(cs->hw.teles3.isacfifo, data, size); } -static void -isac_fill_fifo(struct IsdnCardState *sp) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - write_fifo(sp->isac, ptr, count); - writereg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_fill_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (readreg(cs->hw.teles3.hscx[hscx], offset)); } static void -ph_command(struct IsdnCardState *sp, unsigned int command) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - if (sp->debug & L1_DEB_ISAC) { - char tmp[32]; - sprintf(tmp, "ph_command %d", command); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_CIX0, (command << 2) | 3); + writereg(cs->hw.teles3.hscx[hscx], offset, value); } +/* + * fast interrupt HSCX stuff goes here + */ -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; - - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = readreg(sp->isac, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!(exval & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - writereg(sp->isac, ISAC_CMDR, 0x80); - } else { - count = readreg(sp->isac, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "AVM: D receive out of memory\n"); - else { - SET_SKB_FREE(skb); - memcpy(skb_put(skb, count), sp->rcvbuf, count); - skb_queue_tail(&sp->rq, skb); - } - } - } - sp->rcvidx = 0; - isac_sched_event(sp, ISAC_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - isac_empty_fifo(sp, 32); - } - if (val & 0x20) { /* RSC */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RSC interrupt"); - } - if (val & 0x10) { /* XPR */ - if (sp->tx_skb) - if (sp->tx_skb->len) { - isac_fill_fifo(sp); - goto afterXPR; - } else { - dev_kfree_skb(sp->tx_skb, FREE_WRITE); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (readreg(sp->isac, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = readreg(sp->isac, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - } -} - -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ - - 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 *) irq2dev_map[intno]; - - if (!sp) { + if (!cs) { printk(KERN_WARNING "Teles: Spurious interrupt!\n"); return; } - val = readreg(sp->hscx[1], HSCX_ISTA); + val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA); Start_HSCX: if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); stat |= 1; } - val = readreg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.teles3.isac, ISAC_ISTA); Start_ISAC: if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); stat |= 2; } count++; - val = readreg(sp->hscx[1], HSCX_ISTA); + val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA); if (val && count < MAXCOUNT) { - if (sp->debug & L1_DEB_HSCX) - debugl1(sp, "HSCX IntStat after IntRoutine"); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); goto Start_HSCX; } - val = readreg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.teles3.isac, ISAC_ISTA); if (val && count < MAXCOUNT) { - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "ISAC IntStat after IntRoutine"); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } if (count >= MAXCOUNT) printk(KERN_WARNING "Teles3: more than %d loops in teles3_interrupt\n", count); if (stat & 1) { - writereg(sp->hscx[0], HSCX_MASK, 0xFF); - writereg(sp->hscx[1], HSCX_MASK, 0xFF); - writereg(sp->hscx[0], HSCX_MASK, 0x0); - writereg(sp->hscx[1], HSCX_MASK, 0x0); + writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0x0); + writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0x0); } if (stat & 2) { - writereg(sp->isac, ISAC_MASK, 0xFF); - writereg(sp->isac, ISAC_MASK, 0x0); + 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.interrupts[sp->irq]; - sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter); - debugl1(sp, tmp); - clear_pending_ints(sp); - ret = get_irq(sp->cardnr, &teles3_interrupt); - if (ret) { - initisac(sp); - sp->modehscx(sp->hs, 0, 0); - writereg(sp->hscx[sp->hs->hscx], HSCX_CMDR, 0x01); - sp->modehscx(sp->hs + 1, 0, 0); - writereg(sp->hscx[(sp->hs + 1)->hscx], HSCX_CMDR, 0x01); - while (loop++ < 10) { - /* At least 1-3 irqs must happen - * (one from HSCX A, one from HSCX B, 3rd from ISAC) - */ - if (kstat.interrupts[sp->irq] > sp->counter) - break; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 1; - schedule(); - } - sprintf(tmp, "IRQ %d count %d loop %d", sp->irq, - kstat.interrupts[sp->irq], loop); - debugl1(sp, tmp); - if (kstat.interrupts[sp->irq] <= sp->counter) { - printk(KERN_WARNING - "Teles3: IRQ(%d) getting no interrupts during init\n", - sp->irq); - irq2dev_map[sp->irq] = NULL; - free_irq(sp->irq, NULL); - return (0); - } - } - return (ret); + switch (mt) { + case CARD_RESET: + reset_teles3(cs); + return(0); + case CARD_RELEASE: + release_io_teles3(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &teles3_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); + } + return(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/teles3c.c b/drivers/isdn/hisax/teles3c.c new file mode 100644 index 000000000000..e25a2c22ea48 --- /dev/null +++ b/drivers/isdn/hisax/teles3c.c @@ -0,0 +1,201 @@ +/* $Id: teles3c.c,v 1.1.2.2 1998/01/27 22:40:37 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.1.2.2 1998/01/27 22:40:37 keil + * fixed IRQ latency, B-channel selection and more + * + * Revision 1.1.2.1 1998/01/11 22:54:04 keil + * Teles 16.3c (HFC 2BDS0) first version + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_2bds0.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *teles163c_revision = "$Revision: 1.1.2.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/hisax/telespci.c b/drivers/isdn/hisax/telespci.c new file mode 100644 index 000000000000..547384158731 --- /dev/null +++ b/drivers/isdn/hisax/telespci.c @@ -0,0 +1,374 @@ +/* $Id: telespci.c,v 1.1.2.2 1998/04/20 08:52:46 keil Exp $ + + * telespci.c low level stuff for Teles PCI isdn cards + * + * Author Ton van Rosmalen + * Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: telespci.c,v $ + * Revision 1.1.2.2 1998/04/20 08:52:46 keil + * Fix register offsets + * + * Revision 1.1.2.1 1998/04/11 18:44:42 keil + * New files + * + * + */ +#define __NO_VERSION__ +#include +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" +#include +#include + +extern const char *CardType[]; + +const char *telespci_revision = "$Revision: 1.1.2.2 $"; + +#define ZORAN_PO_RQ_PEN 0x02000000 +#define ZORAN_PO_WR 0x00800000 +#define ZORAN_PO_GID0 0x00000000 +#define ZORAN_PO_GID1 0x00100000 +#define ZORAN_PO_GREG0 0x00000000 +#define ZORAN_PO_GREG1 0x00010000 +#define ZORAN_PO_DMASK 0xFF + +#define WRITE_ADDR_ISAC (ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG0) +#define READ_DATA_ISAC (ZORAN_PO_GID0 | ZORAN_PO_GREG1) +#define WRITE_DATA_ISAC (ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG1) +#define WRITE_ADDR_HSCX (ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG0) +#define READ_DATA_HSCX (ZORAN_PO_GID1 | ZORAN_PO_GREG1) +#define WRITE_DATA_HSCX (ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG1) + +#define ZORAN_WAIT_NOBUSY do { \ + portdata = readl(adr + 0x200); \ + } while (portdata & ZORAN_PO_RQ_PEN) + +static inline u_char +readisac(unsigned int adr, u_char off) +{ + register unsigned int portdata; + + ZORAN_WAIT_NOBUSY; + + /* set address for ISAC */ + writel(WRITE_ADDR_ISAC | off, adr + 0x200); + ZORAN_WAIT_NOBUSY; + + /* read data from ISAC */ + writel(READ_DATA_ISAC, adr + 0x200); + ZORAN_WAIT_NOBUSY; + return((u_char)(portdata & ZORAN_PO_DMASK)); +} + +static inline void +writeisac(unsigned int adr, u_char off, u_char data) +{ + register unsigned int portdata; + + ZORAN_WAIT_NOBUSY; + + /* set address for ISAC */ + writel(WRITE_ADDR_ISAC | off, adr + 0x200); + ZORAN_WAIT_NOBUSY; + + /* write data to ISAC */ + writel(WRITE_DATA_ISAC | data, adr + 0x200); + ZORAN_WAIT_NOBUSY; +} + +static inline u_char +readhscx(unsigned int adr, int hscx, u_char off) +{ + register unsigned int portdata; + + ZORAN_WAIT_NOBUSY; + /* set address for HSCX */ + writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200); + ZORAN_WAIT_NOBUSY; + + /* read data from HSCX */ + writel(READ_DATA_HSCX, adr + 0x200); + ZORAN_WAIT_NOBUSY; + return ((u_char)(portdata & ZORAN_PO_DMASK)); +} + +static inline void +writehscx(unsigned int adr, int hscx, u_char off, u_char data) +{ + register unsigned int portdata; + + ZORAN_WAIT_NOBUSY; + /* set address for HSCX */ + writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200); + ZORAN_WAIT_NOBUSY; + + /* write data to HSCX */ + writel(WRITE_DATA_HSCX | data, adr + 0x200); + ZORAN_WAIT_NOBUSY; +} + +static inline void +read_fifo_isac(unsigned int adr, u_char * data, int size) +{ + register unsigned int portdata; + register int i; + + ZORAN_WAIT_NOBUSY; + /* read data from ISAC */ + for (i = 0; i < size; i++) { + /* set address for ISAC fifo */ + writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200); + ZORAN_WAIT_NOBUSY; + writel(READ_DATA_ISAC, adr + 0x200); + ZORAN_WAIT_NOBUSY; + data[i] = (u_char)(portdata & ZORAN_PO_DMASK); + } +} + +static void +write_fifo_isac(unsigned int adr, u_char * data, int size) +{ + register unsigned int portdata; + register int i; + + ZORAN_WAIT_NOBUSY; + /* write data to ISAC */ + for (i = 0; i < size; i++) { + /* set address for ISAC fifo */ + writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200); + ZORAN_WAIT_NOBUSY; + writel(WRITE_DATA_ISAC | data[i], adr + 0x200); + ZORAN_WAIT_NOBUSY; + } +} + +static inline void +read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) +{ + register unsigned int portdata; + register int i; + + ZORAN_WAIT_NOBUSY; + /* read data from HSCX */ + for (i = 0; i < size; i++) { + /* set address for HSCX fifo */ + writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200); + ZORAN_WAIT_NOBUSY; + writel(READ_DATA_HSCX, adr + 0x200); + ZORAN_WAIT_NOBUSY; + data[i] = (u_char) (portdata & ZORAN_PO_DMASK); + } +} + +static inline void +write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) +{ + unsigned int portdata; + register int i; + + ZORAN_WAIT_NOBUSY; + /* write data to HSCX */ + for (i = 0; i < size; i++) { + /* set address for HSCX fifo */ + writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200); + ZORAN_WAIT_NOBUSY; + writel(WRITE_DATA_HSCX | data[i], adr + 0x200); + ZORAN_WAIT_NOBUSY; + udelay(10); + } +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readisac(cs->hw.teles0.membase, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writeisac(cs->hw.teles0.membase, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + read_fifo_isac(cs->hw.teles0.membase, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + write_fifo_isac(cs->hw.teles0.membase, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readhscx(cs->hw.teles0.membase, hscx, offset)); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writehscx(cs->hw.teles0.membase, hscx, offset, value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg) +#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt) + +#include "hscx_irq.c" + +static void +telespci_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ +#define MAXCOUNT 20 + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "TelesPCI: Spurious interrupt!\n"); + return; + } + val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readisac(cs->hw.teles0.membase, ISAC_ISTA); + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + /* Clear interrupt register for Zoran PCI controller */ + writel(0x70000000, cs->hw.teles0.membase + 0x3C); + + if (stat & 1) { + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF); + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0); + } + if (stat & 2) { + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0); + } +} + +void +release_io_telespci(struct IsdnCardState *cs) +{ + vfree((void *)cs->hw.teles0.membase); +} + +static int +TelesPCI_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + return(0); + case CARD_RELEASE: + release_io_telespci(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &telespci_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +static int pci_index __initdata = 0; + +__initfunc(int +setup_telespci(struct IsdnCard *card)) +{ + int found=0; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_memaddr; + + strcpy(tmp, telespci_revision); + printk(KERN_INFO "HiSax: Teles/PCI driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_TELESPCI) + return (0); + +#if CONFIG_PCI + for (pci_index = 0; pci_index < 0xff; pci_index++) { + if (pcibios_find_device (0x11DE, 0x6120, + pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) { + found = 1; + } else { + break; + } + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_memaddr); + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + printk(KERN_INFO "Found: Zoran, base-address: 0x%x," + " irq: 0x%x\n", pci_memaddr, pci_irq); + break; + } + if (!found) { + printk(KERN_WARNING "TelesPCI: No PCI card found\n"); + return(0); + } + pci_index++; + cs->hw.teles0.membase = (u_int) vremap(pci_memaddr, PAGE_SIZE); + cs->irq = pci_irq; +#else + printk(KERN_WARNING "HiSax: Teles/PCI and NO_PCI_BIOS\n"); + printk(KERN_WARNING "HiSax: Teles/PCI unable to config\n"); + return (0); +#endif /* CONFIG_PCI */ + + /* Initialize Zoran PCI controller */ + writel(0x00000000, cs->hw.teles0.membase + 0x28); + writel(0x01000000, cs->hw.teles0.membase + 0x28); + writel(0x01000000, cs->hw.teles0.membase + 0x28); + writel(0x7BFFFFFF, cs->hw.teles0.membase + 0x2C); + writel(0x70000000, cs->hw.teles0.membase + 0x3C); + writel(0x61000000, cs->hw.teles0.membase + 0x40); + /* writel(0x00800000, cs->hw.teles0.membase + 0x200); */ + + printk(KERN_INFO + "HiSax: %s config irq:%d mem:%x\n", + CardType[cs->typ], cs->irq, + cs->hw.teles0.membase); + + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &TelesPCI_card_msg; + ISACVersion(cs, "TelesPCI:"); + if (HscxVersion(cs, "TelesPCI:")) { + printk(KERN_WARNING + "TelesPCI: wrong HSCX versions check IO/MEM addresses\n"); + release_io_telespci(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c index a7b64702de04..81cf94a5ddd7 100644 --- a/drivers/isdn/icn/icn.c +++ b/drivers/isdn/icn/icn.c @@ -1,4 +1,4 @@ -/* $Id: icn.c,v 1.45 1997/06/21 10:42:06 fritz Exp $ + /* $Id: icn.c,v 1.45.2.3 1998/06/07 13:32:04 fritz 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.45.2.3 1998/06/07 13:32:04 fritz + * Minor bugfixes for broken Switches. + * + * Revision 1.45.2.2 1998/03/07 23:35:36 detabc + * added the abc-extension to the linux isdn-kernel + * for kernel-version 2.0.xx + * DO NOT USE FOR HIGHER KERNELS-VERSIONS + * all source-lines are switched with the define CONFIG_ISDN_WITH_ABC + * (make config and answer ABC-Ext. Support (Compress,TCP-Keepalive ...) with yes + * + * you need also a modified isdnctrl-source the switch on the + * features of the abc-extension + * + * please use carefully. more detail will be follow. + * thanks + * + * Revision 1.45.2.1 1997/08/21 15:56:50 fritz + * Synchronized 2.0.X branch with 2.0.31-pre7 + * * Revision 1.45 1997/06/21 10:42:06 fritz * Added availability to select leased mode on only one channel. * @@ -193,7 +212,7 @@ #undef MAP_DEBUG static char -*revision = "$Revision: 1.45 $"; +*revision = "$Revision: 1.45.2.3 $"; static int icn_addcard(int, char *, char *); @@ -531,8 +550,13 @@ static icn_stat icn_stat_table[] = { {"BCON_", ISDN_STAT_BCONN, 1}, /* B-Channel connected */ {"BDIS_", ISDN_STAT_BHUP, 2}, /* B-Channel disconnected */ - {"DCON_", ISDN_STAT_DCONN, 0}, /* D-Channel connected */ - {"DDIS_", ISDN_STAT_DHUP, 0}, /* D-Channel disconnected */ +#ifdef CONFIG_ISDN_WITH_ABC + {"DCON_", ISDN_STAT_DCONN, 10}, /* D-Channel connected */ + {"DDIS_", ISDN_STAT_DHUP, 11}, /* D-Channel disconnected */ +#else + {"DCON_", ISDN_STAT_DCONN, 0}, /* D-Channel connected */ + {"DDIS_", ISDN_STAT_DHUP, 0}, /* D-Channel disconnected */ +#endif {"DCAL_I", ISDN_STAT_ICALL, 3}, /* Incoming call dialup-line */ {"DSCA_I", ISDN_STAT_ICALL, 3}, /* Incoming call 1TR6-SPV */ {"FCALL", ISDN_STAT_ICALL, 4}, /* Leased line connection up */ @@ -582,7 +606,41 @@ icn_parse_status(u_char * status, int channel, icn_card * card) cmd.driver = card->myid; cmd.arg = channel; switch (action) { +#ifdef CONFIG_ISDN_WITH_ABC + case 11: + + save_flags(flags); + cli(); + icn_free_queue(card,channel); + card->rcvidx[channel] = 0; + + if( card->flags & + ((channel)?ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE)) { + + isdn_ctrl ncmd; + + printk(KERN_INFO "icn: D-Channel hangup before B-Channel hangup\n"); + + card->flags &= ~((channel)? + ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE); + + memset(&ncmd,0,sizeof(ncmd)); + + ncmd.driver = card->myid; + ncmd.arg = channel; + ncmd.command = ISDN_STAT_BHUP; + restore_flags(flags); + card->interface.statcallb(&cmd); + dflag |= (channel+1); + + } else restore_flags(flags); + + break; +#endif case 1: +#ifdef CONFIG_ISDN_WITH_ABC + icn_free_queue(card,channel); +#endif card->flags |= (channel) ? ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE; break; @@ -601,17 +659,22 @@ icn_parse_status(u_char * status, int channel, icn_card * card) char *t = status + 6; char *s = strpbrk(t, ","); + memset(&cmd.parm.setup, 0, sizeof(cmd.parm.setup)); + if (!s) + break; *s++ = '\0'; strncpy(cmd.parm.setup.phone, t, sizeof(cmd.parm.setup.phone)); - s = strpbrk(t = s, ","); + if (!(s = strpbrk(t = s, ","))) + break; *s++ = '\0'; if (!strlen(t)) cmd.parm.setup.si1 = 0; else cmd.parm.setup.si1 = simple_strtoul(t, NULL, 10); - s = strpbrk(t = s, ","); + if (!(s = strpbrk(t = s, ","))) + break; *s++ = '\0'; if (!strlen(t)) cmd.parm.setup.si2 = 0; @@ -621,8 +684,6 @@ icn_parse_status(u_char * status, int channel, icn_card * card) strncpy(cmd.parm.setup.eazmsn, s, sizeof(cmd.parm.setup.eazmsn)); } - cmd.parm.setup.plan = 0; - cmd.parm.setup.screen = 0; break; case 4: sprintf(cmd.parm.setup.phone, "LEASED%d", card->myid); diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c index dcd251a01922..3e24d29f5a1f 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.44.2.4 1998/06/07 13:47:44 fritz Exp $ * Linux ISDN subsystem, common used functions (linklevel). * @@ -21,6 +21,25 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.c,v $ + * Revision 1.44.2.4 1998/06/07 13:47:44 fritz + * ABC cleanup + * + * Revision 1.44.2.2 1998/03/16 09:55:44 cal + * Merged in TimRu-patches. Still needs validation in conjunction with ABC-patches. + * + * Revision 1.44.2.1 1998/03/07 23:35:03 detabc + * added the abc-extension to the linux isdn-kernel + * for kernel-version 2.0.xx + * DO NOT USE FOR HIGHER KERNELS-VERSIONS + * all source-lines are switched with the define CONFIG_ISDN_WITH_ABC + * (make config and answer ABC-Ext. Support (Compress,TCP-Keepalive ...) with yes + * + * you need also a modified isdnctrl-source the switch on the + * features of the abc-extension + * + * please use carefully. more detail will be follow. + * thanks + * * Revision 1.44 1997/05/27 15:17:23 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -218,7 +237,7 @@ isdn_dev *dev = (isdn_dev *) 0; -static char *isdn_revision = "$Revision: 1.44 $"; +static char *isdn_revision = "$Revision: 1.44.2.4 $"; extern char *isdn_net_revision; extern char *isdn_tty_revision; @@ -329,6 +348,7 @@ isdn_timer_funct(ulong dummy) #endif } } + if (tf) { int flags; @@ -1200,6 +1220,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) #define phone iocpar.phone #define cfg iocpar.cfg + if (minor == ISDN_MINOR_STATUS) { switch (cmd) { case IIOCGETDVR: diff --git a/drivers/isdn/isdn_common.h b/drivers/isdn/isdn_common.h index d0df7fe7965a..b90d879f7a36 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.6.2.1 1998/03/16 09:55:48 cal Exp $ * header for Linux ISDN subsystem, common used functions and debugging-switches (linklevel). * @@ -21,6 +21,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.h,v $ + * Revision 1.6.2.1 1998/03/16 09:55:48 cal + * Merged in TimRu-patches. Still needs validation in conjunction with ABC-patches. + * * Revision 1.6 1997/02/28 02:32:44 fritz * Cleanup: Moved some tty related stuff from isdn_common.c * to isdn_tty.c @@ -76,6 +79,6 @@ extern void isdn_export_syms(void); #else #define isdn_export_syms() #endif -#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) +#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) || defined(CONFIG_ISDN_TIMEOUT_RULES) extern void isdn_dumppkt(char *, u_char *, int, int); #endif diff --git a/drivers/isdn/isdn_ppp.c b/drivers/isdn/isdn_ppp.c index c13dadd485f7..fff79adfe57b 100644 --- a/drivers/isdn/isdn_ppp.c +++ b/drivers/isdn/isdn_ppp.c @@ -1,4 +1,4 @@ -/* $Id: isdn_ppp.c,v 1.28 1997/06/17 13:05:57 hipp Exp $ +/* $Id: isdn_ppp.c,v 1.28.2.1 1998/03/16 09:56:02 cal Exp $ * * Linux ISDN subsystem, functions for synchronous PPP (linklevel). * @@ -19,6 +19,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_ppp.c,v $ + * Revision 1.28.2.1 1998/03/16 09:56:02 cal + * Merged in TimRu-patches. Still needs validation in conjunction with ABC-patches. + * * Revision 1.28 1997/06/17 13:05:57 hipp * Applied Eric's underflow-patches (slightly modified) * more compression changes (but disabled at the moment) @@ -173,7 +176,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.28 $"; +char *isdn_ppp_revision = "$Revision: 1.28.2.1 $"; static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS]; static struct isdn_ppp_compressor *ipc_head = NULL; @@ -901,6 +904,7 @@ isdn_ppp_write(int min, struct file *file, const char *buf, int count) printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len); isdn_ppp_frame_log("xmit", skb->data, skb->len, 32); } + if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb)) != count) { if (lp->sav_skb) { dev_kfree_skb(lp->sav_skb, FREE_WRITE); @@ -1264,10 +1268,10 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff return; } - netif_rx(skb); - /* net_dev->local.stats.rx_packets++; *//* done in isdn_net.c */ /* Reset hangup-timer */ lp->huptimer = 0; + netif_rx(skb); + /* net_dev->local.stats.rx_packets++; *//* done in isdn_net.c */ return; } @@ -1376,10 +1380,10 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) */ /* Pull off the fake header we stuck on earlier to keep - * the fragemntation code happy. - * this will break the ISDN_SYNCPPP_READDRESS hack a few lines - * above. So, enabling this is no longer allowed - */ + * the fragemntation code happy. + * this will break the ISDN_SYNCPPP_READDRESS hack a few lines + * above. So, enabling this is no longer allowed + */ skb_pull(skb,IPPP_MAX_HEADER); if (ipt->debug & 0x4) diff --git a/drivers/isdn/isdn_tty.c b/drivers/isdn/isdn_tty.c index a629ce27922d..1058ed172e5e 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.41.2.7 1998/06/07 13:48:08 fritz Exp $ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel). * @@ -20,6 +20,34 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_tty.c,v $ + * Revision 1.41.2.7 1998/06/07 13:48:08 fritz + * ABC cleanup + * + * Revision 1.41.2.5 1998/04/08 21:42:35 keil + * Blocksize default 1024 + * + * Revision 1.41.2.4 1998/03/19 17:58:55 detabc + * remove 2 debug-messages (no longer needed) bug was fixed + * + * Revision 1.41.2.3 1998/03/07 23:35:20 detabc + * added the abc-extension to the linux isdn-kernel + * for kernel-version 2.0.xx + * DO NOT USE FOR HIGHER KERNELS-VERSIONS + * all source-lines are switched with the define CONFIG_ISDN_WITH_ABC + * (make config and answer ABC-Ext. Support (Compress,TCP-Keepalive ...) with yes + * + * you need also a modified isdnctrl-source the switch on the + * features of the abc-extension + * + * please use carefully. more detail will be follow. + * thanks + * + * Revision 1.41.2.2 1998/03/07 23:02:51 tsbogend + * fixed kernel unaligned traps on Linux/Alpha + * + * Revision 1.41.2.1 1997/08/21 15:56:11 fritz + * Synchronized 2.0.X branch with 2.0.31-pre7 + * * Revision 1.41 1997/05/27 15:17:31 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -199,6 +227,7 @@ #define VBUFX (VBUF/16) #endif + /* Prototypes */ static int isdn_tty_edit_at(const char *, int, modem_info *, int); @@ -223,7 +252,7 @@ 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.41.2.7 $"; #define DLE 0x10 #define ETX 0x03 @@ -1270,7 +1299,7 @@ isdn_tty_get_lsr_info(modem_info * info, uint * value) status = info->lsr; restore_flags(flags); result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); - put_user(result, (ulong *) value); + put_user(result, (uint *) value); return 0; } @@ -1294,7 +1323,7 @@ isdn_tty_get_modem_info(modem_info * info, uint * value) | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); - put_user(result, (ulong *) value); + put_user(result, (uint *) value); return 0; } @@ -1443,7 +1472,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); @@ -1927,7 +1955,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; } @@ -2534,7 +2562,7 @@ 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)) @@ -2643,7 +2671,7 @@ isdn_tty_check_ats(int mreg, int mval, modem_info * info, atemu * m) return 1; break; case 16: - if ((mval * 16) > ISDN_SERIAL_XMIT_SIZE) + if ((mval * 16) > ISDN_SERIAL_XMIT_MAX) return 1; #ifdef CONFIG_ISDN_AUDIO if ((m->mdmreg[18] & 1) && (mval > VBUFX)) @@ -3095,7 +3123,7 @@ isdn_tty_parse_at(modem_info * info) break; case 'D': /* D - Dial */ - isdn_tty_getdial(++p, ds, sizeof(ds)); + isdn_tty_getdial(++p, ds,sizeof(ds)); p += strlen(p); if (!strlen(m->msn)) isdn_tty_modem_result(10, info); diff --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..005706ef9d10 --- /dev/null +++ b/drivers/isdn/isdnloop/isdnloop.c @@ -0,0 +1,1560 @@ +/* $Id: isdnloop.c,v 1.1 1997/03/24 23:02:04 fritz 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.1 1997/03/24 23:02:04 fritz + * Added isdnloop driver. + * + */ + +#include "isdnloop.h" + +static char +*revision = "$Revision: 1.1 $"; + +static int isdnloop_addcard(char *); + +/* + * Free queue completely. + * + * Parameter: + * card = pointer to card struct + * channel = channel number + */ +static void +isdnloop_free_queue(isdnloop_card * card, int channel) +{ + struct sk_buff_head *queue = &card->bqueue[channel]; + struct sk_buff *skb; + + while ((skb = skb_dequeue(queue))) + dev_kfree_skb(skb, FREE_WRITE); + card->sndcount[channel] = 0; +} + +/* + * Send B-Channel data to another virtual card. + * This routine is called via timer-callback from isdnloop_pollbchan(). + * + * Parameter: + * card = pointer to card struct. + * ch = channel number (0-based) + */ +static void +isdnloop_bchan_send(isdnloop_card * card, int ch) +{ + isdnloop_card *rcard = card->rcard[ch]; + int rch = card->rch[ch]; + struct sk_buff *skb; + isdn_ctrl cmd; + + while (card->sndcount[ch]) { + if ((skb = skb_dequeue(&card->bqueue[ch]))) { + card->sndcount[ch] -= skb->len; + if (rcard) + rcard->interface.rcvcallb_skb(rcard->myid, rch, skb); + cmd.command = ISDN_STAT_BSENT; + cmd.driver = card->myid; + cmd.arg = ch; + card->interface.statcallb(&cmd); + } else + card->sndcount[ch] = 0; + } +} + +/* + * Send/Receive Data to/from the B-Channel. + * This routine is called via timer-callback. + * It schedules itself while any B-Channel is open. + * + * Parameter: + * data = pointer to card struct, set by kernel timer.data + */ +static void +isdnloop_pollbchan(unsigned long data) +{ + isdnloop_card *card = (isdnloop_card *) data; + unsigned long flags; + + if (card->flags & ISDNLOOP_FLAGS_B1ACTIVE) + isdnloop_bchan_send(card, 0); + if (card->flags & ISDNLOOP_FLAGS_B2ACTIVE) + isdnloop_bchan_send(card, 1); + if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE)) { + /* schedule b-channel polling again */ + save_flags(flags); + cli(); + card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD; + add_timer(&card->rb_timer); + card->flags |= ISDNLOOP_FLAGS_RBTIMER; + restore_flags(flags); + } else + card->flags &= ~ISDNLOOP_FLAGS_RBTIMER; +} + +/* + * Parse ICN-type setup string and fill fields of setup-struct + * with parsed data. + * + * Parameter: + * setup = setup string, format: [caller-id],si1,si2,[called-id] + * cmd = pointer to struct to be filled. + */ +static void +isdnloop_parse_setup(char *setup, isdn_ctrl * cmd) +{ + char *t = setup; + char *s = strpbrk(t, ","); + + *s++ = '\0'; + strncpy(cmd->parm.setup.phone, t, sizeof(cmd->parm.setup.phone)); + s = strpbrk(t = s, ","); + *s++ = '\0'; + if (!strlen(t)) + cmd->parm.setup.si1 = 0; + else + cmd->parm.setup.si1 = simple_strtoul(t, NULL, 10); + s = strpbrk(t = s, ","); + *s++ = '\0'; + if (!strlen(t)) + cmd->parm.setup.si2 = 0; + else + cmd->parm.setup.si2 = + simple_strtoul(t, NULL, 10); + strncpy(cmd->parm.setup.eazmsn, s, sizeof(cmd->parm.setup.eazmsn)); + cmd->parm.setup.plan = 0; + cmd->parm.setup.screen = 0; +} + +typedef struct isdnloop_stat { + char *statstr; + int command; + int action; +} isdnloop_stat; +/* *INDENT-OFF* */ +static isdnloop_stat isdnloop_stat_table[] = +{ + {"BCON_", ISDN_STAT_BCONN, 1}, /* B-Channel connected */ + {"BDIS_", ISDN_STAT_BHUP, 2}, /* B-Channel disconnected */ + {"DCON_", ISDN_STAT_DCONN, 0}, /* D-Channel connected */ + {"DDIS_", ISDN_STAT_DHUP, 0}, /* D-Channel disconnected */ + {"DCAL_I", ISDN_STAT_ICALL, 3}, /* Incoming call dialup-line */ + {"DSCA_I", ISDN_STAT_ICALL, 3}, /* Incoming call 1TR6-SPV */ + {"FCALL", ISDN_STAT_ICALL, 4}, /* Leased line connection up */ + {"CIF", ISDN_STAT_CINF, 5}, /* Charge-info, 1TR6-type */ + {"AOC", ISDN_STAT_CINF, 6}, /* Charge-info, DSS1-type */ + {"CAU", ISDN_STAT_CAUSE, 7}, /* Cause code */ + {"TEI OK", ISDN_STAT_RUN, 0}, /* Card connected to wallplug */ + {"NO D-CHAN", ISDN_STAT_NODCH, 0}, /* No D-channel available */ + {"E_L1: ACT FAIL", ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */ + {"E_L2: DATA LIN", ISDN_STAT_BHUP, 8}, /* Layer-2 data link lost */ + {"E_L1: ACTIVATION FAILED", + ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */ + {NULL, 0, -1} +}; +/* *INDENT-ON* */ + + +/* + * Parse Status message-strings from virtual card. + * Depending on status, call statcallb for sending messages to upper + * levels. Also set/reset B-Channel active-flags. + * + * Parameter: + * status = status string to parse. + * channel = channel where message comes from. + * card = card where message comes from. + */ +static void +isdnloop_parse_status(u_char * status, int channel, isdnloop_card * card) +{ + isdnloop_stat *s = isdnloop_stat_table; + int action = -1; + isdn_ctrl cmd; + + while (s->statstr) { + if (!strncmp(status, s->statstr, strlen(s->statstr))) { + cmd.command = s->command; + action = s->action; + break; + } + s++; + } + if (action == -1) + return; + cmd.driver = card->myid; + cmd.arg = channel; + switch (action) { + case 1: + /* BCON_x */ + card->flags |= (channel) ? + ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE; + break; + case 2: + /* BDIS_x */ + card->flags &= ~((channel) ? + ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE); + isdnloop_free_queue(card, channel); + break; + case 3: + /* DCAL_I and DSCA_I */ + isdnloop_parse_setup(status + 6, &cmd); + break; + case 4: + /* FCALL */ + sprintf(cmd.parm.setup.phone, "LEASED%d", card->myid); + sprintf(cmd.parm.setup.eazmsn, "%d", channel + 1); + cmd.parm.setup.si1 = 7; + cmd.parm.setup.si2 = 0; + cmd.parm.setup.plan = 0; + cmd.parm.setup.screen = 0; + break; + case 5: + /* CIF */ + strncpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num) - 1); + break; + case 6: + /* AOC */ + sprintf(cmd.parm.num, "%d", + (int) simple_strtoul(status + 7, NULL, 16)); + break; + case 7: + /* CAU */ + status += 3; + if (strlen(status) == 4) + sprintf(cmd.parm.num, "%s%c%c", + status + 2, *status, *(status + 1)); + else + strncpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num) - 1); + break; + case 8: + /* Misc Errors on L1 and L2 */ + card->flags &= ~ISDNLOOP_FLAGS_B1ACTIVE; + isdnloop_free_queue(card, 0); + cmd.arg = 0; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + cmd.command = ISDN_STAT_DHUP; + cmd.arg = 0; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + cmd.command = ISDN_STAT_BHUP; + card->flags &= ~ISDNLOOP_FLAGS_B2ACTIVE; + isdnloop_free_queue(card, 1); + cmd.arg = 1; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + cmd.command = ISDN_STAT_DHUP; + cmd.arg = 1; + cmd.driver = card->myid; + break; + } + card->interface.statcallb(&cmd); +} + +/* + * Store a cwcharacter into ringbuffer for reading from /dev/isdnctrl + * + * Parameter: + * card = pointer to card struct. + * c = char to store. + */ +static void +isdnloop_putmsg(isdnloop_card * card, unsigned char c) +{ + ulong flags; + + save_flags(flags); + cli(); + *card->msg_buf_write++ = (c == 0xff) ? '\n' : c; + if (card->msg_buf_write == card->msg_buf_read) { + if (++card->msg_buf_read > card->msg_buf_end) + card->msg_buf_read = card->msg_buf; + } + if (card->msg_buf_write > card->msg_buf_end) + card->msg_buf_write = card->msg_buf; + restore_flags(flags); +} + +/* + * Poll a virtual cards message queue. + * If there are new status-replies from the card, copy them to + * ringbuffer for reading on /dev/isdnctrl and call + * isdnloop_parse_status() for processing them. Watch for special + * Firmware bootmessage and parse it, to get the D-Channel protocol. + * If there are B-Channels open, initiate a timer-callback to + * isdnloop_pollbchan(). + * This routine is called periodically via timer interrupt. + * + * Parameter: + * data = pointer to card struct + */ +static void +isdnloop_polldchan(unsigned long data) +{ + isdnloop_card *card = (isdnloop_card *) data; + struct sk_buff *skb; + int avail; + int left; + u_char c; + int ch; + int flags; + u_char *p; + isdn_ctrl cmd; + + if ((skb = skb_dequeue(&card->dqueue))) + avail = skb->len; + else + avail = 0; + for (left = avail; left > 0; left--) { + c = *skb->data; + skb_pull(skb, 1); + isdnloop_putmsg(card, c); + card->imsg[card->iptr] = c; + if (card->iptr < 59) + card->iptr++; + if (!skb->len) { + avail++; + isdnloop_putmsg(card, '\n'); + card->imsg[card->iptr] = 0; + card->iptr = 0; + if (card->imsg[0] == '0' && card->imsg[1] >= '0' && + card->imsg[1] <= '2' && card->imsg[2] == ';') { + ch = (card->imsg[1] - '0') - 1; + p = &card->imsg[3]; + isdnloop_parse_status(p, ch, card); + } else { + p = card->imsg; + if (!strncmp(p, "DRV1.", 5)) { + printk(KERN_INFO "isdnloop: (%s) %s\n", CID, p); + if (!strncmp(p + 7, "TC", 2)) { + card->ptype = ISDN_PTYPE_1TR6; + card->interface.features |= ISDN_FEATURE_P_1TR6; + printk(KERN_INFO + "isdnloop: (%s) 1TR6-Protocol loaded and running\n", CID); + } + if (!strncmp(p + 7, "EC", 2)) { + card->ptype = ISDN_PTYPE_EURO; + card->interface.features |= ISDN_FEATURE_P_EURO; + printk(KERN_INFO + "isdnloop: (%s) Euro-Protocol loaded and running\n", CID); + } + continue; + + } + } + } + } + if (avail) { + cmd.command = ISDN_STAT_STAVAIL; + cmd.driver = card->myid; + cmd.arg = avail; + card->interface.statcallb(&cmd); + } + if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE)) + if (!(card->flags & ISDNLOOP_FLAGS_RBTIMER)) { + /* schedule b-channel polling */ + card->flags |= ISDNLOOP_FLAGS_RBTIMER; + save_flags(flags); + cli(); + del_timer(&card->rb_timer); + card->rb_timer.function = isdnloop_pollbchan; + card->rb_timer.data = (unsigned long) card; + card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD; + add_timer(&card->rb_timer); + restore_flags(flags); + } + /* schedule again */ + save_flags(flags); + cli(); + card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD; + add_timer(&card->st_timer); + restore_flags(flags); +} + +/* + * Append a packet to the transmit buffer-queue. + * + * Parameter: + * channel = Number of B-channel + * skb = packet to send. + * card = pointer to card-struct + * Return: + * Number of bytes transferred, -E??? on error + */ +static int +isdnloop_sendbuf(int channel, struct sk_buff *skb, isdnloop_card * card) +{ + int len = skb->len; + unsigned long flags; + struct sk_buff *nskb; + + if (len > 4000) { + printk(KERN_WARNING + "isdnloop: Send packet too large\n"); + return -EINVAL; + } + if (len) { + if (!(card->flags & (channel) ? ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE)) + return 0; + if (card->sndcount[channel] > ISDNLOOP_MAX_SQUEUE) + return 0; + save_flags(flags); + cli(); + nskb = skb_clone(skb, GFP_ATOMIC); + if (nskb) { + skb_queue_tail(&card->bqueue[channel], nskb); + dev_kfree_skb(skb, FREE_WRITE); + } else + len = 0; + card->sndcount[channel] += len; + restore_flags(flags); + } + return len; +} + +/* + * Read the messages from the card's ringbuffer + * + * Parameter: + * buf = pointer to buffer. + * len = number of bytes to read. + * user = flag, 1: called from userlevel 0: called from kernel. + * card = pointer to card struct. + * Return: + * number of bytes actually transferred. + */ +static int +isdnloop_readstatus(u_char * buf, int len, int user, isdnloop_card * card) +{ + int count; + u_char *p; + + for (p = buf, count = 0; count < len; p++, count++) { + if (card->msg_buf_read == card->msg_buf_write) + return count; + if (user) + put_user(*card->msg_buf_read++, p); + else + *p = *card->msg_buf_read++; + if (card->msg_buf_read > card->msg_buf_end) + card->msg_buf_read = card->msg_buf; + } + return count; +} + +/* + * Simulate a card's response by appending it to the cards + * message queue. + * + * Parameter: + * card = pointer to card struct. + * s = pointer to message-string. + * ch = channel: 0 = generic messages, 1 and 2 = D-channel messages. + * Return: + * 0 on success, 1 on memory squeeze. + */ +static int +isdnloop_fake(isdnloop_card * card, char *s, int ch) +{ + struct sk_buff *skb; + int len = strlen(s) + ((ch >= 0) ? 3 : 0); + + if (!(skb = dev_alloc_skb(len))) { + printk(KERN_WARNING "isdnloop: Out of memory in isdnloop_fake\n"); + return 1; + } + if (ch >= 0) + sprintf(skb_put(skb, 3), "%02d;", ch); + memcpy(skb_put(skb, strlen(s)), s, strlen(s)); + skb_queue_tail(&card->dqueue, skb); + return 0; +} +/* *INDENT-OFF* */ +static isdnloop_stat isdnloop_cmd_table[] = +{ + {"BCON_R", 0, 1}, /* B-Channel connect */ + {"BDIS_R", 0, 2}, /* B-Channel disconnect */ + {"DDIS_R", 0, 3}, /* D-Channel disconnect */ + {"DCON_R", 0, 16}, /* D-Channel connect */ + {"DSCA_R", 0, 4}, /* Dial 1TR6-SPV */ + {"DCAL_R", 0, 5}, /* Dial */ + {"EAZC", 0, 6}, /* Clear EAZ listener */ + {"EAZ", 0, 7}, /* Set EAZ listener */ + {"SEEAZ", 0, 8}, /* Get EAZ listener */ + {"MSN", 0, 9}, /* Set/Clear MSN listener */ + {"MSALL", 0, 10}, /* Set multi MSN listeners */ + {"SETSIL", 0, 11}, /* Set SI list */ + {"SEESIL", 0, 12}, /* Get SI list */ + {"SILC", 0, 13}, /* Clear SI list */ + {"LOCK", 0, -1}, /* LOCK channel */ + {"UNLOCK", 0, -1}, /* UNLOCK channel */ + {"FV2ON", 1, 14}, /* Leased mode on */ + {"FV2OFF", 1, 15}, /* Leased mode off */ + {NULL, 0, -1} +}; +/* *INDENT-ON* */ + + +/* + * Simulate an error-response from a card. + * + * Parameter: + * card = pointer to card struct. + */ +static void +isdnloop_fake_err(isdnloop_card * card) +{ + char buf[60]; + + sprintf(buf, "E%s", card->omsg); + isdnloop_fake(card, buf, -1); + isdnloop_fake(card, "NAK", -1); +} + +static u_char ctable_eu[] = +{0x00, 0x11, 0x01, 0x12}; +static u_char ctable_1t[] = +{0x00, 0x3b, 0x01, 0x3a}; + +/* + * Assemble a simplified cause message depending on the + * D-channel protocol used. + * + * Parameter: + * card = pointer to card struct. + * loc = location: 0 = local, 1 = remote. + * cau = cause: 1 = busy, 2 = nonexistent callerid, 3 = no user responding. + * Return: + * Pointer to buffer containing the assembled message. + */ +static char * +isdnloop_unicause(isdnloop_card * card, int loc, int cau) +{ + static char buf[6]; + + switch (card->ptype) { + case ISDN_PTYPE_EURO: + sprintf(buf, "E%02X%02X", (loc) ? 4 : 2, ctable_eu[cau]); + break; + case ISDN_PTYPE_1TR6: + sprintf(buf, "%02X44", ctable_1t[cau]); + break; + default: + return ("0000"); + } + return (buf); +} + +/* + * Release a virtual connection. Called from timer interrupt, when + * called party did not respond. + * + * Parameter: + * card = pointer to card struct. + * ch = channel (0-based) + */ +static void +isdnloop_atimeout(isdnloop_card * card, int ch) +{ + unsigned long flags; + char buf[60]; + + save_flags(flags); + cli(); + if (card->rcard) { + isdnloop_fake(card->rcard[ch], "DDIS_I", card->rch[ch] + 1); + card->rcard[ch]->rcard[card->rch[ch]] = NULL; + card->rcard[ch] = NULL; + } + isdnloop_fake(card, "DDIS_I", ch + 1); + /* No user responding */ + sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 3)); + isdnloop_fake(card, buf, ch + 1); + restore_flags(flags); +} + +/* + * Wrapper for isdnloop_atimeout(). + */ +static void +isdnloop_atimeout0(unsigned long data) +{ + isdnloop_card *card = (isdnloop_card *) data; + isdnloop_atimeout(card, 0); +} + +/* + * Wrapper for isdnloop_atimeout(). + */ +static void +isdnloop_atimeout1(unsigned long data) +{ + isdnloop_card *card = (isdnloop_card *) data; + isdnloop_atimeout(card, 1); +} + +/* + * Install a watchdog for a user, not responding. + * + * Parameter: + * card = pointer to card struct. + * ch = channel to watch for. + */ +static void +isdnloop_start_ctimer(isdnloop_card * card, int ch) +{ + unsigned long flags; + + save_flags(flags); + cli(); + init_timer(&card->c_timer[ch]); + card->c_timer[ch].expires = jiffies + ISDNLOOP_TIMER_ALERTWAIT; + if (ch) + card->c_timer[ch].function = isdnloop_atimeout1; + else + card->c_timer[ch].function = isdnloop_atimeout0; + card->c_timer[ch].data = (unsigned long) card; + add_timer(&card->c_timer[ch]); + restore_flags(flags); +} + +/* + * Kill a pending channel watchdog. + * + * Parameter: + * card = pointer to card struct. + * ch = channel (0-based). + */ +static void +isdnloop_kill_ctimer(isdnloop_card * card, int ch) +{ + unsigned long flags; + + save_flags(flags); + cli(); + del_timer(&card->c_timer[ch]); + restore_flags(flags); +} + +static u_char si2bit[] = +{0, 1, 0, 0, 0, 2, 0, 4, 0, 0}; +static u_char bit2si[] = +{1, 5, 7}; + +/* + * Try finding a listener for an outgoing call. + * + * Parameter: + * card = pointer to calling card. + * p = pointer to ICN-type setup-string. + * lch = channel of calling card. + * cmd = pointer to struct to be filled when parsing setup. + * Return: + * 0 = found match, alerting should happen. + * 1 = found matching number but it is busy. + * 2 = no matching listener. + * 3 = found matching number but SI does not match. + */ +static int +isdnloop_try_call(isdnloop_card * card, char *p, int lch, isdn_ctrl * cmd) +{ + isdnloop_card *cc = cards; + unsigned long flags; + int ch; + int num_match; + int i; + char *e; + char nbuf[32]; + + isdnloop_parse_setup(p, cmd); + while (cc) { + for (ch = 0; ch < 2; ch++) { + /* Exclude ourself */ + if ((cc == card) && (ch == lch)) + continue; + num_match = 0; + switch (cc->ptype) { + case ISDN_PTYPE_EURO: + for (i = 0; i < 3; i++) + if (!(strcmp(cc->s0num[i], cmd->parm.setup.phone))) + num_match = 1; + break; + case ISDN_PTYPE_1TR6: + e = cc->eazlist[ch]; + while (*e) { + sprintf(nbuf, "%s%c", cc->s0num[0], *e); + if (!(strcmp(nbuf, cmd->parm.setup.phone))) + num_match = 1; + e++; + } + } + if (num_match) { + save_flags(flags); + cli(); + /* channel idle? */ + if (!(cc->rcard[ch])) { + /* Check SI */ + if (!(si2bit[cmd->parm.setup.si1] & cc->sil[ch])) { + restore_flags(flags); + return 3; + } + /* ch is idle, si and number matches */ + cc->rcard[ch] = card; + cc->rch[ch] = lch; + card->rcard[lch] = cc; + card->rch[lch] = ch; + restore_flags(flags); + return 0; + } else { + restore_flags(flags); + /* num matches, but busy */ + if (ch == 1) + return 1; + } + } + } + cc = cc->next; + } + return 2; +} + +/* + * Depending on D-channel protocol and caller/called, modify + * phone number. + * + * Parameter: + * card = pointer to card struct. + * phone = pointer phone number. + * caller = flag: 1 = caller, 0 = called. + * Return: + * pointer to new phone number. + */ +static char * +isdnloop_vstphone(isdnloop_card * card, char *phone, int caller) +{ + int i; + static char nphone[30]; + + switch (card->ptype) { + case ISDN_PTYPE_EURO: + if (caller) { + for (i = 0; i < 2; i++) + if (!(strcmp(card->s0num[i], phone))) + return (phone); + return (card->s0num[0]); + } + return (phone); + break; + case ISDN_PTYPE_1TR6: + if (caller) { + sprintf(nphone, "%s%c", card->s0num[0], phone[0]); + return (nphone); + } else + return (&phone[strlen(phone) - 1]); + break; + } + return ("\0"); +} + +/* + * Parse an ICN-type command string sent to the 'card'. + * Perform misc. actions depending on the command. + * + * Parameter: + * card = pointer to card struct. + */ +static void +isdnloop_parse_cmd(isdnloop_card * card) +{ + char *p = card->omsg; + isdn_ctrl cmd; + char buf[60]; + isdnloop_stat *s = isdnloop_cmd_table; + int action = -1; + int i; + int ch; + + if ((card->omsg[0] != '0') && (card->omsg[2] != ';')) { + isdnloop_fake_err(card); + return; + } + ch = card->omsg[1] - '0'; + if ((ch < 0) || (ch > 2)) { + isdnloop_fake_err(card); + return; + } + p += 3; + while (s->statstr) { + if (!strncmp(p, s->statstr, strlen(s->statstr))) { + action = s->action; + if (s->command && (ch != 0)) { + isdnloop_fake_err(card); + return; + } + break; + } + s++; + } + if (action == -1) + return; + switch (action) { + case 1: + /* 0x;BCON_R */ + if (card->rcard[ch - 1]) { + isdnloop_fake(card, "BCON_C", ch); + isdnloop_fake(card->rcard[ch - 1], "BCON_I", + card->rch[ch - 1] + 1); + } + break; + case 2: + /* 0x;BDIS_R */ + isdnloop_fake(card, "BDIS_C", ch); + if (card->rcard[ch - 1]) { + isdnloop_fake(card->rcard[ch - 1], "BDIS_I", + card->rch[ch - 1] + 1); + } + break; + case 16: + /* 0x;DCON_R */ + isdnloop_kill_ctimer(card, ch - 1); + if (card->rcard[ch - 1]) { + isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]); + isdnloop_fake(card->rcard[ch - 1], "DCON_C", + card->rch[ch - 1] + 1); + isdnloop_fake(card, "DCON_C", ch); + } + break; + case 3: + /* 0x;DDIS_R */ + isdnloop_kill_ctimer(card, ch - 1); + if (card->rcard[ch - 1]) { + isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]); + isdnloop_fake(card->rcard[ch - 1], "DDIS_I", + card->rch[ch - 1] + 1); + card->rcard[ch - 1] = NULL; + } + isdnloop_fake(card, "DDIS_C", ch); + break; + case 4: + /* 0x;DSCA_Rdd,yy,zz,oo */ + if (card->ptype != ISDN_PTYPE_1TR6) { + isdnloop_fake_err(card); + return; + } + /* Fall through */ + case 5: + /* 0x;DCAL_Rdd,yy,zz,oo */ + p += 6; + switch (isdnloop_try_call(card, p, ch - 1, &cmd)) { + case 0: + /* Alerting */ + sprintf(buf, "D%s_I%s,%02d,%02d,%s", + (action == 4) ? "SCA" : "CAL", + isdnloop_vstphone(card, cmd.parm.setup.eazmsn, 1), + cmd.parm.setup.si1, + cmd.parm.setup.si2, + isdnloop_vstphone(card->rcard[ch], + cmd.parm.setup.phone, 0)); + isdnloop_fake(card->rcard[ch - 1], buf, card->rch[ch - 1] + 1); + /* Fall through */ + case 3: + /* si1 does not match, dont alert but start timer */ + isdnloop_start_ctimer(card, ch - 1); + break; + case 1: + /* Remote busy */ + isdnloop_fake(card, "DDIS_I", ch); + sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 1)); + isdnloop_fake(card, buf, ch); + break; + case 2: + /* No such user */ + isdnloop_fake(card, "DDIS_I", ch); + sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 2)); + isdnloop_fake(card, buf, ch); + break; + } + break; + case 6: + /* 0x;EAZC */ + card->eazlist[ch - 1][0] = '\0'; + break; + case 7: + /* 0x;EAZ */ + p += 3; + strcpy(card->eazlist[ch - 1], p); + break; + case 8: + /* 0x;SEEAZ */ + sprintf(buf, "EAZ-LIST: %s", card->eazlist[ch - 1]); + isdnloop_fake(card, buf, ch + 1); + break; + case 9: + /* 0x;MSN */ + break; + case 10: + /* 0x;MSNALL */ + break; + case 11: + /* 0x;SETSIL */ + p += 6; + i = 0; + while (strchr("0157", *p)) { + if (i) + card->sil[ch - 1] |= si2bit[*p - '0']; + i = (*p++ == '0'); + } + if (*p) + isdnloop_fake_err(card); + break; + case 12: + /* 0x;SEESIL */ + sprintf(buf, "SIN-LIST: "); + p = buf + 10; + for (i = 0; i < 3; i++) + if (card->sil[ch - 1] & (1 << i)) + p += sprintf(p, "%02d", bit2si[i]); + isdnloop_fake(card, buf, ch + 1); + break; + case 13: + /* 0x;SILC */ + card->sil[ch - 1] = 0; + break; + case 14: + /* 00;FV2ON */ + break; + case 15: + /* 00;FV2OFF */ + break; + } +} + +/* + * Put command-strings into the of the 'card'. In reality, execute them + * right in place by calling isdnloop_parse_cmd(). Also copy every + * command to the read message ringbuffer, preceeding it with a '>'. + * These mesagges can be read at /dev/isdnctrl. + * + * Parameter: + * buf = pointer to command buffer. + * len = length of buffer data. + * user = flag: 1 = called form userlevel, 0 called from kernel. + * card = pointer to card struct. + * Return: + * number of bytes transfered (currently always equals len). + */ +static int +isdnloop_writecmd(const u_char * buf, int len, int user, isdnloop_card * card) +{ + int xcount = 0; + int ocount = 1; + isdn_ctrl cmd; + + while (len) { + int count = MIN(255, len); + u_char *p; + u_char msg[0x100]; + + if (user) + copy_from_user(msg, buf, count); + else + memcpy(msg, buf, count); + isdnloop_putmsg(card, '>'); + for (p = msg; count > 0; count--, p++) { + len--; + xcount++; + isdnloop_putmsg(card, *p); + card->omsg[card->optr] = *p; + if (*p == '\n') { + card->omsg[card->optr] = '\0'; + card->optr = 0; + isdnloop_parse_cmd(card); + if (len) { + isdnloop_putmsg(card, '>'); + ocount++; + } + } else { + if (card->optr < 59) + card->optr++; + } + ocount++; + } + } + cmd.command = ISDN_STAT_STAVAIL; + cmd.driver = card->myid; + cmd.arg = ocount; + card->interface.statcallb(&cmd); + return xcount; +} + +/* + * Delete card's pending timers, send STOP to linklevel + */ +static void +isdnloop_stopcard(isdnloop_card * card) +{ + unsigned long flags; + isdn_ctrl cmd; + + save_flags(flags); + cli(); + if (card->flags & ISDNLOOP_FLAGS_RUNNING) { + card->flags &= ~ISDNLOOP_FLAGS_RUNNING; + del_timer(&card->st_timer); + del_timer(&card->rb_timer); + del_timer(&card->c_timer[0]); + del_timer(&card->c_timer[1]); + cmd.command = ISDN_STAT_STOP; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + } + restore_flags(flags); +} + +/* + * Stop all cards before unload. + */ +static void +isdnloop_stopallcards(void) +{ + isdnloop_card *p = cards; + + while (p) { + isdnloop_stopcard(p); + p = p->next; + } +} + +/* + * Start a 'card'. Simulate card's boot message and set the phone + * number(s) of the virtual 'S0-Interface'. Install D-channel + * poll timer. + * + * Parameter: + * card = pointer to card struct. + * sdefp = pointer to struct holding ioctl parameters. + * Return: + * 0 on success, -E??? otherwise. + */ +static int +isdnloop_start(isdnloop_card * card, isdnloop_sdef * sdefp) +{ + unsigned long flags; + isdnloop_sdef sdef; + int i; + + if (card->flags & ISDNLOOP_FLAGS_RUNNING) + return -EBUSY; + copy_from_user((char *) &sdef, (char *) sdefp, sizeof(sdef)); + save_flags(flags); + cli(); + switch (sdef.ptype) { + case ISDN_PTYPE_EURO: + if (isdnloop_fake(card, "DRV1.23EC-Q.931-CAPI-CNS-BASIS-20.02.96", + -1)) { + restore_flags(flags); + return -ENOMEM; + } + card->sil[0] = card->sil[1] = 4; + if (isdnloop_fake(card, "TEI OK", 0)) { + restore_flags(flags); + return -ENOMEM; + } + for (i = 0; i < 3; i++) + strcpy(card->s0num[i], sdef.num[i]); + break; + case ISDN_PTYPE_1TR6: + if (isdnloop_fake(card, "DRV1.04TC-1TR6-CAPI-CNS-BASIS-29.11.95", + -1)) { + restore_flags(flags); + return -ENOMEM; + } + card->sil[0] = card->sil[1] = 4; + if (isdnloop_fake(card, "TEI OK", 0)) { + restore_flags(flags); + return -ENOMEM; + } + strcpy(card->s0num[0], sdef.num[0]); + card->s0num[1][0] = '\0'; + card->s0num[2][0] = '\0'; + break; + default: + restore_flags(flags); + printk(KERN_WARNING "isdnloop: Illegal D-channel protocol %d\n", + sdef.ptype); + return -EINVAL; + } + init_timer(&card->st_timer); + card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD; + card->st_timer.function = isdnloop_polldchan; + card->st_timer.data = (unsigned long) card; + add_timer(&card->st_timer); + card->flags |= ISDNLOOP_FLAGS_RUNNING; + restore_flags(flags); + return 0; +} + +/* + * Main handler for commands sent by linklevel. + */ +static int +isdnloop_command(isdn_ctrl * c, isdnloop_card * card) +{ + ulong a; + int i; + char cbuf[60]; + isdn_ctrl cmd; + isdnloop_cdef cdef; + + switch (c->command) { + case ISDN_CMD_IOCTL: + memcpy(&a, c->parm.num, sizeof(ulong)); + switch (c->arg) { + case ISDNLOOP_IOCTL_DEBUGVAR: + return (ulong) card; + case ISDNLOOP_IOCTL_STARTUP: + if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(isdnloop_sdef)))) + return i; + return (isdnloop_start(card, (isdnloop_sdef *) a)); + break; + case ISDNLOOP_IOCTL_ADDCARD: + if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(isdnloop_cdef)))) + return i; + copy_from_user((char *) &cdef, (char *) a, sizeof(cdef)); + return (isdnloop_addcard(cdef.id1)); + break; + case ISDNLOOP_IOCTL_LEASEDCFG: + if (a) { + if (!card->leased) { + card->leased = 1; + while (card->ptype == ISDN_PTYPE_UNKNOWN) { + current->timeout = jiffies + 10; + schedule(); + } + current->timeout = jiffies + 10; + schedule(); + sprintf(cbuf, "00;FV2ON\n01;EAZ1\n02;EAZ2\n"); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + printk(KERN_INFO + "isdnloop: (%s) Leased-line mode enabled\n", + CID); + cmd.command = ISDN_STAT_RUN; + cmd.driver = card->myid; + cmd.arg = 0; + card->interface.statcallb(&cmd); + } + } else { + if (card->leased) { + card->leased = 0; + sprintf(cbuf, "00;FV2OFF\n"); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + printk(KERN_INFO + "isdnloop: (%s) Leased-line mode disabled\n", + CID); + cmd.command = ISDN_STAT_RUN; + cmd.driver = card->myid; + cmd.arg = 0; + card->interface.statcallb(&cmd); + } + } + return 0; + default: + return -EINVAL; + } + break; + case ISDN_CMD_DIAL: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (card->leased) + break; + if ((c->arg & 255) < ISDNLOOP_BCH) { + char *p; + char dial[50]; + char dcode[4]; + + a = c->arg; + p = c->parm.setup.phone; + if (*p == 's' || *p == 'S') { + /* Dial for SPV */ + p++; + strcpy(dcode, "SCA"); + } else + /* Normal Dial */ + strcpy(dcode, "CAL"); + strcpy(dial, p); + sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1), + dcode, dial, c->parm.setup.si1, + c->parm.setup.si2, c->parm.setup.eazmsn); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_ACCEPTD: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + cbuf[0] = 0; + switch (card->l2_proto[a - 1]) { + case ISDN_PROTO_L2_X75I: + sprintf(cbuf, "%02d;BX75\n", (int) a); + break; + case ISDN_PROTO_L2_HDLC: + sprintf(cbuf, "%02d;BTRA\n", (int) a); + break; + } + if (strlen(cbuf)) + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + sprintf(cbuf, "%02d;DCON_R\n", (int) a); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_ACCEPTB: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + switch (card->l2_proto[a - 1]) { + case ISDN_PROTO_L2_X75I: + sprintf(cbuf, "%02d;BCON_R,BX75\n", (int) a); + break; + case ISDN_PROTO_L2_HDLC: + sprintf(cbuf, "%02d;BCON_R,BTRA\n", (int) a); + break; + default: + sprintf(cbuf, "%02d;BCON_R\n", (int) a); + } + printk(KERN_DEBUG "isdnloop writecmd '%s'\n", cbuf); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + break; + case ISDN_CMD_HANGUP: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_SETEAZ: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (card->leased) + break; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + if (card->ptype == ISDN_PTYPE_EURO) { + sprintf(cbuf, "%02d;MS%s%s\n", (int) a, + c->parm.num[0] ? "N" : "ALL", c->parm.num); + } else + sprintf(cbuf, "%02d;EAZ%s\n", (int) a, + c->parm.num[0] ? c->parm.num : "0123456789"); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_CLREAZ: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (card->leased) + break; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + if (card->ptype == ISDN_PTYPE_EURO) + sprintf(cbuf, "%02d;MSNC\n", (int) a); + else + sprintf(cbuf, "%02d;EAZC\n", (int) a); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_SETL2: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg & 255) < ISDNLOOP_BCH) { + a = c->arg; + switch (a >> 8) { + case ISDN_PROTO_L2_X75I: + sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1); + break; + case ISDN_PROTO_L2_HDLC: + sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1); + break; + default: + return -EINVAL; + } + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + card->l2_proto[a & 255] = (a >> 8); + } + break; + case ISDN_CMD_GETL2: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg & 255) < ISDNLOOP_BCH) + return card->l2_proto[c->arg & 255]; + else + return -ENODEV; + case ISDN_CMD_SETL3: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + return 0; + case ISDN_CMD_GETL3: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg & 255) < ISDNLOOP_BCH) + return ISDN_PROTO_L3_TRANS; + else + return -ENODEV; + case ISDN_CMD_GETEAZ: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + break; + case ISDN_CMD_SETSIL: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + break; + case ISDN_CMD_GETSIL: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + break; + case ISDN_CMD_LOCK: + MOD_INC_USE_COUNT; + break; + case ISDN_CMD_UNLOCK: + MOD_DEC_USE_COUNT; + break; + default: + return -EINVAL; + } + } + return 0; +} + +/* + * Find card with given driverId + */ +static inline isdnloop_card * +isdnloop_findcard(int driverid) +{ + isdnloop_card *p = cards; + + while (p) { + if (p->myid == driverid) + return p; + p = p->next; + } + return (isdnloop_card *) 0; +} + +/* + * Wrapper functions for interface to linklevel + */ +static int +if_command(isdn_ctrl * c) +{ + isdnloop_card *card = isdnloop_findcard(c->driver); + + if (card) + return (isdnloop_command(c, card)); + printk(KERN_ERR + "isdnloop: if_command called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_writecmd(const u_char * buf, int len, int user, int id, int channel) +{ + isdnloop_card *card = isdnloop_findcard(id); + + if (card) { + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + return (isdnloop_writecmd(buf, len, user, card)); + } + printk(KERN_ERR + "isdnloop: if_writecmd called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_readstatus(u_char * buf, int len, int user, int id, int channel) +{ + isdnloop_card *card = isdnloop_findcard(id); + + if (card) { + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + return (isdnloop_readstatus(buf, len, user, card)); + } + printk(KERN_ERR + "isdnloop: if_readstatus called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_sendbuf(int id, int channel, struct sk_buff *skb) +{ + isdnloop_card *card = isdnloop_findcard(id); + + if (card) { + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + return (isdnloop_sendbuf(channel, skb, card)); + } + printk(KERN_ERR + "isdnloop: if_readstatus called with invalid driverId!\n"); + return -ENODEV; +} + +/* + * Allocate a new card-struct, initialize it + * link it into cards-list and register it at linklevel. + */ +static isdnloop_card * +isdnloop_initcard(char *id) +{ + isdnloop_card *card; + int i; + + if (!(card = (isdnloop_card *) kmalloc(sizeof(isdnloop_card), GFP_KERNEL))) { + printk(KERN_WARNING + "isdnloop: (%s) Could not allocate card-struct.\n", id); + return (isdnloop_card *) 0; + } + memset((char *) card, 0, sizeof(isdnloop_card)); + card->interface.channels = ISDNLOOP_BCH; + card->interface.maxbufsize = 4000; + card->interface.command = if_command; + card->interface.writebuf_skb = if_sendbuf; + card->interface.writecmd = if_writecmd; + card->interface.readstat = if_readstatus; + card->interface.features = ISDN_FEATURE_L2_X75I | + ISDN_FEATURE_L2_HDLC | + ISDN_FEATURE_L3_TRANS | + ISDN_FEATURE_P_UNKNOWN; + card->ptype = ISDN_PTYPE_UNKNOWN; + strncpy(card->interface.id, id, sizeof(card->interface.id) - 1); + card->msg_buf_write = card->msg_buf; + card->msg_buf_read = card->msg_buf; + card->msg_buf_end = &card->msg_buf[sizeof(card->msg_buf) - 1]; + for (i = 0; i < ISDNLOOP_BCH; i++) { + card->l2_proto[i] = ISDN_PROTO_L2_X75I; + skb_queue_head_init(&card->bqueue[i]); + } + skb_queue_head_init(&card->dqueue); + card->next = cards; + cards = card; + if (!register_isdn(&card->interface)) { + cards = cards->next; + printk(KERN_WARNING + "isdnloop: Unable to register %s\n", id); + kfree(card); + return (isdnloop_card *) 0; + } + card->myid = card->interface.channels; + return card; +} + +static int +isdnloop_addcard(char *id1) +{ + ulong flags; + isdnloop_card *card; + + save_flags(flags); + cli(); + if (!(card = isdnloop_initcard(id1))) { + restore_flags(flags); + return -EIO; + } + restore_flags(flags); + printk(KERN_INFO + "isdnloop: (%s) virtual card added\n", + card->interface.id); + return 0; +} + +#ifdef MODULE +#define isdnloop_init init_module +#else +void +isdnloop_setup(char *str, int *ints) +{ + static char sid[20]; + + if (strlen(str)) { + strcpy(sid, str); + isdnloop_id = sid; + } +} +#endif + +int +isdnloop_init(void) +{ + char *p; + char rev[10]; + + /* No symbols to export, hide all symbols */ +#if (LINUX_VERSION_CODE < 0x020111) + register_symtab(NULL); +#else + EXPORT_NO_SYMBOLS; +#endif + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else + strcpy(rev, " ??? "); + printk(KERN_NOTICE "isdnloop-ISDN-driver Rev%s\n", rev); + return (isdnloop_addcard(isdnloop_id)); +} + +#ifdef MODULE +void +cleanup_module(void) +{ + isdn_ctrl cmd; + isdnloop_card *card = cards; + isdnloop_card *last; + int i; + + isdnloop_stopallcards(); + while (card) { + cmd.command = ISDN_STAT_UNLOAD; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + for (i = 0; i < ISDNLOOP_BCH; i++) + isdnloop_free_queue(card, i); + card = card->next; + } + card = cards; + while (card) { + struct sk_buff *skb; + + last = card; + while ((skb = skb_dequeue(&card->dqueue))) + dev_kfree_skb(skb, FREE_WRITE); + card = card->next; + kfree(last); + } + printk(KERN_NOTICE "isdnloop-ISDN-driver unloaded\n"); +} +#endif diff --git a/drivers/isdn/isdnloop/isdnloop.h b/drivers/isdn/isdnloop/isdnloop.h new file mode 100644 index 000000000000..3dbd3fba0dec --- /dev/null +++ b/drivers/isdn/isdnloop/isdnloop.h @@ -0,0 +1,142 @@ +/* $Id: isdnloop.h,v 1.1 1997/03/24 23:02:05 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.1 1997/03/24 23:02:05 fritz + * Added isdnloop driver. + * + */ + +#ifndef isdnloop_h +#define isdnloop_h + +#define ISDNLOOP_IOCTL_DEBUGVAR 0 +#define ISDNLOOP_IOCTL_ADDCARD 1 +#define ISDNLOOP_IOCTL_LEASEDCFG 2 +#define ISDNLOOP_IOCTL_STARTUP 3 + +/* Struct for adding new cards */ +typedef struct isdnloop_cdef { + char id1[10]; +} isdnloop_cdef; + +/* Struct for configuring cards */ +typedef struct isdnloop_sdef { + int ptype; + char num[3][20]; +} isdnloop_sdef; + +#if defined(__KERNEL__) || defined(__DEBUGVAR__) + +#ifdef __KERNEL__ +/* Kernel includes */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* __KERNEL__ */ + +#define ISDNLOOP_FLAGS_B1ACTIVE 1 /* B-Channel-1 is open */ +#define ISDNLOOP_FLAGS_B2ACTIVE 2 /* B-Channel-2 is open */ +#define ISDNLOOP_FLAGS_RUNNING 4 /* Cards driver activated */ +#define ISDNLOOP_FLAGS_RBTIMER 8 /* scheduling of B-Channel-poll */ +#define ISDNLOOP_TIMER_BCREAD 1 /* B-Channel poll-cycle */ +#define ISDNLOOP_TIMER_DCREAD (HZ/2) /* D-Channel poll-cycle */ +#define ISDNLOOP_TIMER_ALERTWAIT (10*HZ) /* Alert timeout */ +#define ISDNLOOP_MAX_SQUEUE 65536 /* Max. outstanding send-data */ +#define ISDNLOOP_BCH 2 /* channels per card */ + +/* + * Per card driver data + */ +typedef struct isdnloop_card { + struct isdnloop_card *next; /* Pointer to next device struct */ + struct isdnloop_card + *rcard[ISDNLOOP_BCH]; /* Pointer to 'remote' card */ + int rch[ISDNLOOP_BCH]; /* 'remote' channel */ + int myid; /* Driver-Nr. assigned by linklevel */ + int leased; /* Flag: This Adapter is connected */ + /* to a leased line */ + int sil[ISDNLOOP_BCH]; /* SI's to listen for */ + char eazlist[ISDNLOOP_BCH][11]; + /* EAZ's to listen for */ + char s0num[3][20]; /* 1TR6 base-number or MSN's */ + unsigned short flags; /* Statusflags */ + int ptype; /* Protocol type (1TR6 or Euro) */ + struct timer_list st_timer; /* Timer for Status-Polls */ + struct timer_list rb_timer; /* Timer for B-Channel-Polls */ + struct timer_list + c_timer[ISDNLOOP_BCH]; /* Timer for Alerting */ + int l2_proto[ISDNLOOP_BCH]; /* Current layer-2-protocol */ + isdn_if interface; /* Interface to upper layer */ + int iptr; /* Index to imsg-buffer */ + char imsg[60]; /* Internal buf for status-parsing */ + int optr; /* Index to omsg-buffer */ + char omsg[60]; /* Internal buf for cmd-parsing */ + char msg_buf[2048]; /* Buffer for status-messages */ + char *msg_buf_write; /* Writepointer for statusbuffer */ + char *msg_buf_read; /* Readpointer for statusbuffer */ + char *msg_buf_end; /* Pointer to end of statusbuffer */ + int sndcount[ISDNLOOP_BCH]; /* Byte-counters for B-Ch.-send */ + struct sk_buff_head + bqueue[ISDNLOOP_BCH]; /* B-Channel queues */ + struct sk_buff_head dqueue; /* D-Channel queue */ +} isdnloop_card; + +/* + * Main driver data + */ +#ifdef __KERNEL__ +static isdnloop_card *cards = (isdnloop_card *) 0; +static char *isdnloop_id = "\0"; + +#ifdef MODULE +#if (LINUX_VERSION_CODE > 0x020111) +MODULE_AUTHOR("Fritz Elfert"); +MODULE_PARM(isdnloop_id, "s"); +MODULE_PARM_DESC(isdnloop_id, "ID-String of first card"); +#endif +#endif + +#endif /* __KERNEL__ */ + +/* Utility-Macros */ + +#define CID (card->interface.id) +#define MIN(a,b) ((ab)?a:b) + +#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */ +#endif /* isdnloop_h */ diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index 889229892ab6..c17e34b63940 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -192,7 +192,7 @@ XL, 3Com's PCI to 10/100baseT adapters. It also works with the 10Mbs versions of the FastEtherLink cards. The supported product IDs are 3c590, 3c592, 3c595, 3c597, 3c900, 3c905 -The ISA 3c515 is supported with a seperate driver, 3c515.c, included with +The ISA 3c515 is supported with a separate driver, 3c515.c, included with the kernel source or available from cesdis.gsfc.nasa.gov:/pub/linux/drivers/3c515.html @@ -210,7 +210,7 @@ The 3c59x series use an interface that's very similar to the previous 3c5x9 series. The primary interface is two programmed-I/O FIFOs, with an alternate single-contiguous-region bus-master transfer (see next). -The 3c900 "Boomerang" series uses a full-bus-master interface with seperate +The 3c900 "Boomerang" series uses a full-bus-master interface with separate lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet, DEC Tulip and Intel Speedo3. The first chip version retains a compatible programmed-I/O interface that will be removed in the 'B' and subsequent @@ -1340,7 +1340,7 @@ static void vortex_tx_timeout(struct device *dev) } /* - * Handle uncommon interrupt sources. This is a seperate routine to minimize + * Handle uncommon interrupt sources. This is a separate routine to minimize * the cache impact. */ static void diff --git a/drivers/net/Config.in b/drivers/net/Config.in index e2ee768b40d3..4f317160d503 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -147,3 +147,6 @@ if [ "$CONFIG_ARCNET" != "n" ]; then bool ' Enable arc0s (ARCnet RFC1051 packet format)' CONFIG_ARCNET_1051 fi +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Traffic Shaper (EXPERIMENTAL)' CONFIG_SHAPER +fi diff --git a/drivers/net/Makefile b/drivers/net/Makefile index a52ee71e434c..986ec3120aef 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -53,6 +53,14 @@ else endif endif +ifeq ($(CONFIG_SHAPER),y) +L_OBJS += shaper.o +else + ifeq ($(CONFIG_SHAPER),m) + M_OBJS += shaper.o + endif +endif + ifeq ($(CONFIG_SK_G16),y) L_OBJS += sk_g16.o endif diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c index bb30e041a24b..8fd6b87088e3 100644 --- a/drivers/net/eexpress.c +++ b/drivers/net/eexpress.c @@ -7,6 +7,11 @@ * Changed to support io= irq= by Alan Cox * Reworked 1995 by John Sullivan * More fixes by Philip Blundell + * Added the Compaq LTE Alan Cox + * + * Note - this driver is experimental still - it has problems on faster + * machines. Someone needs to sit down and go through it line by line with + * a databook... */ /* @@ -86,7 +91,8 @@ static char version[] = "eexpress.c: v0.10 04-May-95 John Sullivan \n" -" v0.14 19-May-96 Philip Blundell \n"; +" v0.14 19-May-96 Philip Blundell \n" +" v0.15 04-Aug-98 Alan Cox \n"; #include @@ -701,11 +707,13 @@ static int eexp_hw_probe(struct device *dev, unsigned short ioaddr) hw_addr[1] = eexp_hw_readeeprom(ioaddr,3); hw_addr[2] = eexp_hw_readeeprom(ioaddr,4); - if (hw_addr[2]!=0x00aa || ((hw_addr[1] & 0xff00)!=0x0000)) + /* Standard Address or Compaq LTE Address */ + if (!((hw_addr[2]==0x00aa && ((hw_addr[1] & 0xff00)==0x0000)) || + (hw_addr[2]==0x0080 && ((hw_addr[1] & 0xff00)==0x5F00)))) { printk("rejected: invalid address %04x%04x%04x\n", hw_addr[2],hw_addr[1],hw_addr[0]); - return ENODEV; + return -ENODEV; } dev->base_addr = ioaddr; diff --git a/drivers/net/shaper.c b/drivers/net/shaper.c new file mode 100644 index 000000000000..2b2085602ca7 --- /dev/null +++ b/drivers/net/shaper.c @@ -0,0 +1,661 @@ +/* + * Simple traffic shaper for Linux NET3. + * + * (c) Copyright 1996 Alan Cox , All Rights Reserved. + * http://www.cymru.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * + * Algorithm: + * + * Queue Frame: + * Compute time length of frame at regulated speed + * Add frame to queue at appropriate point + * Adjust time length computation for followup frames + * Any frame that falls outside of its boundaries is freed + * + * We work to the following constants + * + * SHAPER_QLEN Maximum queued frames + * SHAPER_LATENCY Bounding latency on a frame. Leaving this latency + * window drops the frame. This stops us queueing + * frames for a long time and confusing a remote + * host. + * SHAPER_MAXSLIP Maximum time a priority frame may jump forward. + * That bounds the penalty we will inflict on low + * priority traffic. + * SHAPER_BURST Time range we call "now" in order to reduce + * system load. The more we make this the burstier + * the behaviour, the better local performance you + * get through packet clustering on routers and the + * worse the remote end gets to judge rtts. + * + * This is designed to handle lower speed links ( < 200K/second or so). We + * run off a 100-150Hz base clock typically. This gives us a resolution at + * 200Kbit/second of about 2Kbit or 256 bytes. Above that our timer + * resolution may start to cause much more burstiness in the traffic. We + * could avoid a lot of that by calling kick_shaper() at the end of the + * tied device transmissions. If you run above about 100K second you + * may need to tune the supposed speed rate for the right values. + * + * BUGS: + * Downing the interface under the shaper before the shaper + * will render your machine defunct. Don't for now shape over + * PPP or SLIP therefore! + * This will be fixed in BETA4 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int sh_debug; /* Debug flag */ + +#define SHAPER_BANNER "Traffic Shaper 0.05 for Linux 2.0 \n" + +/* + * Locking + */ + +static int shaper_lock(struct shaper *sh) +{ + unsigned long flags; + save_flags(flags); + cli(); + /* + * Lock in an interrupt may fail + */ + if(sh->locked && intr_count) + { + restore_flags(flags); + return 0; + } + while(sh->locked) + sleep_on(&sh->wait_queue); + sh->locked=1; + restore_flags(flags); + return 1; +} + +static void shaper_kick(struct shaper *sh); + +static void shaper_unlock(struct shaper *sh) +{ + sh->locked=0; + wake_up(&sh->wait_queue); + shaper_kick(sh); +} + +/* + * Compute clocks on a buffer + */ + +static int shaper_clocks(struct shaper *shaper, struct sk_buff *skb) +{ + int t=skb->len/shaper->bytespertick; + return t; +} + +/* + * Set the speed of a shaper. We compute this in bytes per tick since + * thats how the machine wants to run. Quoted input is in bits per second + * as is traditional (note not BAUD). We assume 8 bit bytes. + */ + +static void shaper_setspeed(struct shaper *shaper, int bitspersec) +{ + shaper->bitspersdc=bitspersec; + shaper->bytespertick=(bitspersec/HZ)/8; + if(!shaper->bytespertick) + shaper->bytespertick++; +} + +/* + * Throw a frame at a shaper. + */ + +static int shaper_qframe(struct shaper *shaper, struct sk_buff *skb) +{ + struct sk_buff *ptr; + + /* + * Get ready to work on this shaper. Lock may fail if its + * an interrupt and locked. + */ + + if(!shaper_lock(shaper)) + return -1; + ptr=shaper->sendq.prev; + + /* + * Set up our packet details + */ + + skb->shapelatency=0; + skb->shapeclock=shaper->recovery; + if(skb->shapeclockshapeclock=jiffies; + skb->shapestamp=jiffies; + + /* + * Time slots for this packet. + */ + + skb->shapelen= shaper_clocks(shaper,skb); + +#ifdef SHAPER_COMPLEX /* and broken.. */ + + while(ptr && ptr!=(struct sk_buff *)&shaper->sendq) + { + if(ptr->pripri + && jiffies - ptr->shapeclock < SHAPER_MAXSLIP) + { + struct sk_buff *tmp=ptr->prev; + + /* + * It goes before us therefore we slip the length + * of the new frame. + */ + + ptr->shapeclock+=skb->shapelen; + ptr->shapelatency+=skb->shapelen; + + /* + * The packet may have slipped so far back it + * fell off. + */ + if(ptr->shapelatency > SHAPER_LATENCY) + { + skb_unlink(ptr); + dev_kfree_skb(ptr, FREE_WRITE); + } + ptr=tmp; + } + else + break; + } + if(ptr==NULL || ptr==(struct sk_buff *)&shaper->sendq) + skb_queue_head(&shaper->sendq,skb); + else + { + struct sk_buff *tmp; + /* + * Set the packet clock out time according to the + * frames ahead. Im sure a bit of thought could drop + * this loop. + */ + for(tmp=skb_peek(&shaper->sendq); tmp!=NULL && tmp!=ptr; tmp=tmp->next) + skb->shapeclock+=tmp->shapelen; + skb_append(ptr,skb); + } +#else + { + struct sk_buff *tmp; + /* + * Up our shape clock by the time pending on the queue + * (Should keep this in the shaper as a variable..) + */ + for(tmp=skb_peek(&shaper->sendq); tmp!=NULL && + tmp!=(struct sk_buff *)&shaper->sendq; tmp=tmp->next) + skb->shapeclock+=tmp->shapelen; + /* + * Queue over time. Spill packet. + */ + if(skb->shapeclock-jiffies > SHAPER_LATENCY) + dev_kfree_skb(skb, FREE_WRITE); + else + skb_queue_tail(&shaper->sendq, skb); + } +#endif + if(sh_debug) + printk("Frame queued.\n"); + if(skb_queue_len(&shaper->sendq)>SHAPER_QLEN) + { + ptr=skb_dequeue(&shaper->sendq); + dev_kfree_skb(ptr, FREE_WRITE); + } + shaper_unlock(shaper); + shaper_kick(shaper); + return 0; +} + +/* + * Transmit from a shaper + */ + +static void shaper_queue_xmit(struct shaper *shaper, struct sk_buff *skb) +{ + struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC); + if(sh_debug) + printk("Kick frame on %p\n",newskb); + if(newskb) + { + newskb->dev=shaper->dev; + newskb->arp=1; + if(sh_debug) + printk("Kick new frame to %s\n", + shaper->dev->name); + dev_queue_xmit(newskb,shaper->dev,2); + if(sh_debug) + printk("Kicked new frame out.\n"); + dev_kfree_skb(skb, FREE_WRITE); + } +} + +/* + * Timer handler for shaping clock + */ + +static void shaper_timer(unsigned long data) +{ + struct shaper *sh=(struct shaper *)data; + shaper_kick(sh); +} + +/* + * Kick a shaper queue and try and do something sensible with the + * queue. + */ + +static void shaper_kick(struct shaper *shaper) +{ + struct sk_buff *skb; + unsigned long flags; + + save_flags(flags); + cli(); + + del_timer(&shaper->timer); + + /* + * Shaper unlock will kick + */ + + if(shaper->locked) + { + if(sh_debug) + printk("Shaper locked.\n"); + shaper->timer.expires=jiffies+1; + add_timer(&shaper->timer); + restore_flags(flags); + return; + } + + + /* + * Walk the list (may be empty) + */ + + while((skb=skb_peek(&shaper->sendq))!=NULL) + { + /* + * Each packet due to go out by now (within an error + * of SHAPER_BURST) gets kicked onto the link + */ + + if(sh_debug) + printk("Clock = %d, jiffies = %ld\n", skb->shapeclock, jiffies); + if(skb->shapeclock <= jiffies + SHAPER_BURST) + { + /* + * Pull the frame and get interrupts back on. + */ + + skb_unlink(skb); + if (shaper->recovery < skb->shapeclock + skb->shapelen) + shaper->recovery = skb->shapeclock + skb->shapelen; + restore_flags(flags); + + /* + * Pass on to the physical target device via + * our low level packet thrower. + */ + + skb->shapepend=0; + shaper_queue_xmit(shaper, skb); /* Fire */ + cli(); + } + else + break; + } + + /* + * Next kick. + */ + + if(skb!=NULL) + { + del_timer(&shaper->timer); + shaper->timer.expires=skb->shapeclock; + add_timer(&shaper->timer); + } + + /* + * Interrupts on, mission complete + */ + + restore_flags(flags); +} + + +/* + * Flush the shaper queues on a closedown + */ + +static void shaper_flush(struct shaper *shaper) +{ + struct sk_buff *skb; + while((skb=skb_dequeue(&shaper->sendq))!=NULL) + dev_kfree_skb(skb, FREE_WRITE); +} + +/* + * Bring the interface up. We just disallow this until a + * bind. + */ + +static int shaper_open(struct device *dev) +{ + struct shaper *shaper=dev->priv; + + /* + * Can't open until attached. + */ + + if(shaper->dev==NULL) + return -ENODEV; + MOD_INC_USE_COUNT; + return 0; +} + +/* + * Closing a shaper flushes the queues. + */ + +static int shaper_close(struct device *dev) +{ + struct shaper *shaper=dev->priv; + shaper_flush(shaper); + del_timer(&shaper->timer); + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * Revectored calls. We alter the parameters and call the functions + * for our attached device. This enables us to bandwidth allocate after + * ARP and other resolutions and not before. + */ + + +static int shaper_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct shaper *sh=dev->priv; + return shaper_qframe(sh, skb); +} + +static struct enet_statistics *shaper_get_stats(struct device *dev) +{ + return NULL; +} + +static int shaper_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len) +{ + struct shaper *sh=dev->priv; + if(sh_debug) + printk("Shaper header\n"); + return sh->hard_header(skb,sh->dev,type,daddr,saddr,len); +} + +static int shaper_rebuild_header(void *eth, struct device *dev, unsigned long raddr, struct sk_buff *skb) +{ + struct shaper *sh=dev->priv; + if(sh_debug) + printk("Shaper rebuild header\n"); + return sh->rebuild_header(eth,sh->dev,raddr,skb); +} + +static int shaper_attach(struct device *shdev, struct shaper *sh, struct device *dev) +{ + sh->dev = dev; + sh->hard_start_xmit=dev->hard_start_xmit; + sh->get_stats=dev->get_stats; + if(dev->hard_header) + { + sh->hard_header=dev->hard_header; + shdev->hard_header = shaper_header; + } + else + shdev->hard_header = NULL; + + if(dev->rebuild_header) + { + sh->rebuild_header = dev->rebuild_header; + shdev->rebuild_header = shaper_rebuild_header; + } + else + shdev->rebuild_header = NULL; + + shdev->hard_header_len=dev->hard_header_len; + shdev->type=dev->type; + shdev->addr_len=dev->addr_len; + shdev->mtu=dev->mtu; + return 0; +} + +static int shaper_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + struct shaperconf *ss= (struct shaperconf *)&ifr->ifr_data; + struct shaper *sh=dev->priv; + switch(ss->ss_cmd) + { + case SHAPER_SET_DEV: + { + struct device *them=dev_get(ss->ss_name); + if(them==NULL) + return -ENODEV; + if(sh->dev) + return -EBUSY; + return shaper_attach(dev,dev->priv, them); + } + case SHAPER_GET_DEV: + if(sh->dev==NULL) + return -ENODEV; + memcpy(ss->ss_name, sh->dev->name, sizeof(ss->ss_name)); + return 0; + case SHAPER_SET_SPEED: + shaper_setspeed(sh,ss->ss_speed); + return 0; + case SHAPER_GET_SPEED: + ss->ss_speed=sh->bitspersec; + return 0; + default: + return -EINVAL; + } +} + +static struct shaper *shaper_alloc(struct device *dev) +{ + struct shaper *sh=kmalloc(sizeof(struct shaper), GFP_KERNEL); + if(sh==NULL) + return NULL; + memset(sh,0,sizeof(*sh)); + skb_queue_head_init(&sh->sendq); + init_timer(&sh->timer); + sh->timer.function=shaper_timer; + sh->timer.data=(unsigned long)sh; + return sh; +} + +/* + * Add a shaper device to the system + */ + +int shaper_probe(struct device *dev) +{ + int i; + + /* + * Set up the shaper. + */ + + dev->priv = shaper_alloc(dev); + if(dev->priv==NULL) + return -ENOMEM; + + dev->open = shaper_open; + dev->stop = shaper_close; + dev->hard_start_xmit = shaper_start_xmit; + dev->get_stats = shaper_get_stats; + dev->set_multicast_list = NULL; + + /* + * Intialise the packet queues + */ + + for(i=0;ibuffs[i]); + + /* + * Handlers for when we attach to a device. + */ + + dev->hard_header = shaper_header; + dev->rebuild_header = shaper_rebuild_header; + dev->do_ioctl = shaper_ioctl; + dev->hard_header_len = 0; + dev->type = ARPHRD_ETHER; /* initially */ + dev->set_mac_address = NULL; + dev->mtu = 1500; + dev->addr_len = 0; + dev->tx_queue_len = 10; + dev->flags = 0; + dev->family = AF_INET; + dev->pa_addr = 0; + dev->pa_brdaddr = 0; + dev->pa_mask = 0; + dev->pa_alen = 4; + + /* + * Shaper is ok + */ + + return 0; +} + +#ifdef MODULE + +static char devicename[9]; + +static struct device dev_shape = +{ + devicename, + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, shaper_probe +}; + +int init_module(void) +{ + int i; + for(i=0;i<99;i++) + { + sprintf(devicename,"shaper%d",i); + if(dev_get(devicename)==NULL) + break; + } + if(i==100) + return -ENFILE; + + printk(SHAPER_BANNER); + if (register_netdev(&dev_shape) != 0) + return -EIO; + printk("Traffic shaper initialised.\n"); + return 0; +} + +void cleanup_module(void) +{ + /* + * No need to check MOD_IN_USE, as sys_delete_module() checks. + * To be unloadable we must be closed and detached so we don't + * need to flush things. + */ + + unregister_netdev(&dev_shape); + + /* + * Free up the private structure, or leak memory :-) + */ + + kfree(dev_shape.priv); + dev_shape.priv = NULL; +} + +#else + +static struct device dev_sh0 = +{ + "shaper0", + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, shaper_probe +}; + + +static struct device dev_sh1 = +{ + "shaper1", + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, shaper_probe +}; + + +static struct device dev_sh2 = +{ + "shaper2", + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, shaper_probe +}; + +static struct device dev_sh3 = +{ + "shaper3", + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, shaper_probe +}; + +void shaper_init(void) +{ + register_netdev(&dev_sh0); + register_netdev(&dev_sh1); + register_netdev(&dev_sh2); + register_netdev(&dev_sh3); +} + +#endif /* MODULE */ diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c index 09f301cef6a5..8d7ba08311cb 100644 --- a/drivers/scsi/eata.c +++ b/drivers/scsi/eata.c @@ -1,6 +1,25 @@ /* * eata.c - Low-level driver for EATA/DMA SCSI host adapters. * + * 26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111 + * + Added command line option (rs:[y|n]) to reverse the scan order + * of PCI boards. The default is rs:y, which reverses the BIOS order + * while registering PCI boards. The default value rs:y generates + * the same order of all previous revisions of this driver. + * Pls. note that "BIOS order" might have been reversed itself + * after the 2.1.9x PCI modifications in the linux kernel. + * The rs value is ignored when the explicit list of addresses + * is used by the "eata=port0,port1,..." command line option. + * + Added command line option (et:[y|n]) to force use of extended + * translation (255 heads, 63 sectors) as disk geometry. + * The default is et:n, which uses the disk geometry returned + * by scsicam_bios_param. The default value et:n is compatible with + * all previous revisions of this driver. + * + * 28 May 1998 Rev. 4.32 for linux 2.0.33 and 2.1.104 + * Increased busy timeout from 10 msec. to 200 msec. while + * processing interrupts. + * * 16 May 1998 Rev. 4.31 for linux 2.0.33 and 2.1.102 * Improved abort handling during the eh recovery process. * @@ -9,7 +28,7 @@ * abort and reset routines. * Added command line options (eh:[y|n]) to choose between * new_eh_code and the old scsi code. - * If linux verion >= 2.1.101 the default is eh:y, while the eh + * If linux version >= 2.1.101 the default is eh:y, while the eh * option is ignored for previous releases and the old scsi code * is used. * @@ -189,6 +208,10 @@ * PM3222 - SmartRAID Adapter for EISA (PM3222W is 16-bit wide SCSI) * PM3224 - SmartRAID Adapter for PCI (PM3224W is 16-bit wide SCSI) * + * The above list is just an indication: as a matter of fact all DPT + * boards using the EATA/DMA protocol are supported by this driver, + * since they use exactely the same programming interface. + * * The DPT PM2001 provides only the EATA/PIO interface and hence is not * supported by this driver. * @@ -251,6 +274,10 @@ * * eh:y use new scsi code (linux 2.2 only); * eh:n use old scsi code; + * et:y force use of extended translation (255 heads, 63 sectors); + * et:n use disk geometry detected by scsicam_bios_param; + * rs:y reverse scan order while detecting PCI boards; + * rs:n use BIOS order while detecting PCI boards; * lc:y enables linked commands; * lc:n disables linked commands; * tc:y enables tagged commands; @@ -261,15 +288,16 @@ * tm:3 use only ordered queue tags; * mq:xx set the max queue depth to the value xx (2 <= xx <= 32). * - * The default value is: "eata=lc:n,tc:n,mq:16,tm:0". An example using - * the list of detection probes could be: - * "eata=0x7410,0x230,lc:y,tc:n,mq:4,eh:n". + * The default value is: "eata=lc:n,tc:n,mq:16,tm:0,et:n,rs:n". + * An example using the list of detection probes could be: + * "eata=0x7410,0x230,lc:y,tc:n,mq:4,eh:n,et:n". * * When loading as a module, parameters can be specified as well. * The above example would be (use 1 in place of y and 0 in place of n): * * modprobe eata io_port=0x7410,0x230 linked_comm=1 tagged_comm=0 \ - * max_queue_depth=4 tag_mode=0 use_new_eh_code=0 + * max_queue_depth=4 tag_mode=0 use_new_eh_code=0 \ + * ext_tran=0 rev_scan=1 * * ---------------------------------------------------------------------------- * In this implementation, linked commands are designed to work with any DISK @@ -303,6 +331,19 @@ * When the driver detects a batch including overlapping requests * (a really rare event) strict serial (pid) order is enforced. * ---------------------------------------------------------------------------- + * The extended translation option (et:y) is useful when using large physical + * disks/arrays. It could also be useful when switching between Adaptec boards + * and DPT boards without reformatting the disk. + * When a boot disk is partitioned with extended translation, in order to + * be able to boot it with a DPT board is could be necessary to add to + * lilo.conf additional commands as in the following example: + * + * fix-table + * disk=/dev/sda bios=0x80 sectors=63 heads=128 cylindres=546 + * + * where the above geometry should be replaced with the one reported at + * power up by the DPT controller. + * ---------------------------------------------------------------------------- * * The boards are named EATA0, EATA1,... according to the detection order. * @@ -326,6 +367,8 @@ MODULE_PARM(link_statistics, "i"); MODULE_PARM(max_queue_depth, "i"); MODULE_PARM(tag_mode, "i"); MODULE_PARM(use_new_eh_code, "i"); +MODULE_PARM(ext_tran, "i"); +MODULE_PARM(rev_scan, "i"); MODULE_AUTHOR("Dario Ballabio"); #endif @@ -409,6 +452,7 @@ struct proc_dir_entry proc_scsi_eata2x = { #undef DEBUG_RESET #undef DEBUG_GENERATE_ERRORS #undef DEBUG_GENERATE_ABORTS +#undef DEBUG_GEOMETRY #define MAX_ISA 4 #define MAX_VESA 0 @@ -659,6 +703,8 @@ static int do_trace = FALSE; static int setup_done = FALSE; static int link_statistics = 0; static int tag_mode = TAG_MIXED; +static int ext_tran = FALSE; +static int rev_scan = TRUE; #if defined(CONFIG_SCSI_EATA_TAGGED_QUEUE) static int tagged_comm = TRUE; @@ -1063,9 +1109,9 @@ __initfunc (static inline int port_detect \ if (j == 0) { printk("EATA/DMA 2.0x: Copyright (C) 1994-1998 Dario Ballabio.\n"); - printk("%s config options -> tc:%c, lc:%c, mq:%d, eh:%c.\n", - driver_name, tag_type, YESNO(linked_comm), - max_queue_depth, YESNO(use_new_eh_code)); + printk("%s config options -> tc:%c, lc:%c, mq:%d, eh:%c, rs:%c, et:%c.\n", + driver_name, tag_type, YESNO(linked_comm), max_queue_depth, + YESNO(use_new_eh_code), YESNO(rev_scan), YESNO(ext_tran)); } printk("%s: 2.0%c, %s 0x%03lx, IRQ %u, %s, SG %d, MB %d.\n", @@ -1128,6 +1174,8 @@ __initfunc (void eata2x_setup(char *str, int *ints)) { else if (!strncmp(cur, "mq:", 3)) max_queue_depth = val; else if (!strncmp(cur, "ls:", 3)) link_statistics = val; else if (!strncmp(cur, "eh:", 3)) use_new_eh_code = val; + else if (!strncmp(cur, "et:", 3)) ext_tran = val; + else if (!strncmp(cur, "rs:", 3)) rev_scan = val; if ((cur = strchr(cur, ','))) ++cur; } @@ -1161,8 +1209,8 @@ __initfunc (static void add_pci_ports(void)) { if ((addr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) continue; - /* Reverse the returned address order */ - io_port[MAX_INT_PARAM + MAX_PCI - k] = + /* Order addresses according to rev_scan value */ + io_port[MAX_INT_PARAM + (rev_scan ? (MAX_PCI - k) : (1 + k))] = (addr & PCI_BASE_ADDRESS_IO_MASK) + PCI_BASE_ADDRESS_0; } @@ -1189,8 +1237,8 @@ __initfunc (static void add_pci_ports(void)) { if ((addr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) continue; - /* Reverse the returned address order */ - io_port[MAX_INT_PARAM + MAX_PCI - k] = + /* Order addresses according to rev_scan value */ + io_port[MAX_INT_PARAM + (rev_scan ? (MAX_PCI - k) : (1 + k))] = (addr & PCI_BASE_ADDRESS_IO_MASK) + PCI_BASE_ADDRESS_0; } @@ -1842,6 +1890,23 @@ int eata2x_reset(Scsi_Cmnd *SCarg) { #endif /* new_eh_code */ +int eata2x_biosparam(Disk *disk, kdev_t dev, int *dkinfo) { + int size = disk->capacity; + + if (ext_tran || (scsicam_bios_param(disk, dev, dkinfo) < 0)) { + dkinfo[0] = 255; + dkinfo[1] = 63; + dkinfo[2] = size / (dkinfo[0] * dkinfo[1]); + } + +#if defined (DEBUG_GEOMETRY) + printk ("%s: biosparam, head=%d, sec=%d, cyl=%d.\n", driver_name, + dkinfo[0], dkinfo[1], dkinfo[2]); +#endif + + return FALSE; +} + static void sort(unsigned long sk[], unsigned int da[], unsigned int n, unsigned int rev) { unsigned int i, j, k, y; @@ -2020,7 +2085,7 @@ static inline void ihdlr(int irq, unsigned int j) { HD(j)->iocount); /* Check if this board is still busy */ - if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { + if (wait_on_busy(sh[j]->io_port, 20 * MAXLOOP)) { reg = inb(sh[j]->io_port + REG_STATUS); printk("%s: ihdlr, busy timeout error, irq %d, reg 0x%x, count %d.\n", BN(j), irq, reg, HD(j)->iocount); diff --git a/drivers/scsi/eata.h b/drivers/scsi/eata.h index e495b2d14343..f1641f42d469 100644 --- a/drivers/scsi/eata.h +++ b/drivers/scsi/eata.h @@ -14,8 +14,9 @@ int eata2x_abort(Scsi_Cmnd *); int eata2x_old_abort(Scsi_Cmnd *); int eata2x_reset(Scsi_Cmnd *); int eata2x_old_reset(Scsi_Cmnd *, unsigned int); +int eata2x_biosparam(Disk *, kdev_t, int *); -#define EATA_VERSION "4.31.00" +#define EATA_VERSION "4.33.00" #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) @@ -32,7 +33,7 @@ int eata2x_old_reset(Scsi_Cmnd *, unsigned int); eh_device_reset_handler: NULL, \ eh_bus_reset_handler: NULL, \ eh_host_reset_handler: eata2x_reset, \ - bios_param: scsicam_bios_param, \ + bios_param: eata2x_biosparam, \ this_id: 7, \ unchecked_isa_dma: 1, \ use_clustering: ENABLE_CLUSTERING, \ @@ -48,7 +49,7 @@ int eata2x_old_reset(Scsi_Cmnd *, unsigned int); queuecommand: eata2x_queuecommand, \ abort: eata2x_old_abort, \ reset: eata2x_old_reset, \ - bios_param: scsicam_bios_param, \ + bios_param: eata2x_biosparam, \ this_id: 7, \ unchecked_isa_dma: 1, \ use_clustering: ENABLE_CLUSTERING \ diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index e503dd8498ec..b8722ebaa5a7 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -284,6 +284,7 @@ static struct dev_info device_list[] = {"NRC","MBR-7.4","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"REGAL","CDC-4X","*", BLIST_MAX5LUN | BLIST_SINGLELUN}, {"NAKAMICH","MJ-4.8S","*", BLIST_FORCELUN | BLIST_SINGLELUN}, +{"NAKAMICH","MJ-5.16S","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER","CD-ROM DRM-600","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER","CD-ROM DRM-602X","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER","CD-ROM DRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN}, @@ -687,6 +688,7 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun, case TYPE_MOD: case TYPE_PROCESSOR: case TYPE_SCANNER: + case TYPE_MEDIUM_CHANGER: SDpnt->writeable = 1; break; case TYPE_WORM: diff --git a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c index 5dd63f0d3eca..cad88bc29a73 100644 --- a/drivers/scsi/u14-34f.c +++ b/drivers/scsi/u14-34f.c @@ -1,6 +1,18 @@ /* * u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters. * + * 26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111 + * Added command line option (et:[y|n]) to use the existing + * translation (returned by scsicam_bios_param) as disk geometry. + * The default is et:n, which uses the disk geometry jumpered + * on the board. + * The default value et:n is compatible with all previous revisions + * of this driver. + * + * 28 May 1998 Rev. 4.32 for linux 2.0.33 and 2.1.104 + * Increased busy timeout from 10 msec. to 200 msec. while + * processing interrupts. + * * 18 May 1998 Rev. 4.31 for linux 2.0.33 and 2.1.102 * Improved abort handling during the eh recovery process. * @@ -9,7 +21,7 @@ * abort and reset routines. * Added command line options (eh:[y|n]) to choose between * new_eh_code and the old scsi code. - * If linux verion >= 2.1.101 the default is eh:y, while the eh + * If linux version >= 2.1.101 the default is eh:y, while the eh * option is ignored for previous releases and the old scsi code * is used. * @@ -241,20 +253,23 @@ * * eh:y use new scsi code (linux 2.2 only); * eh:n use old scsi code; + * et:y use disk geometry returned by scsicam_bios_param; + * et:n use disk geometry jumpered on the board; * lc:y enables linked commands; * lc:n disables linked commands; * of:y enables old firmware support; * of:n disables old firmware support; * mq:xx set the max queue depth to the value xx (2 <= xx <= 8). * - * The default value is: "u14-34f=lc:n,of:n,mq:8". An example using the list - * of detection probes could be: "u14-34f=0x230,0x340,lc:y,of:n,mq:4,eh:n". + * The default value is: "u14-34f=lc:n,of:n,mq:8,et:n". + * An example using the list of detection probes could be: + * "u14-34f=0x230,0x340,lc:y,of:n,mq:4,eh:n,et:n". * * When loading as a module, parameters can be specified as well. * The above example would be (use 1 in place of y and 0 in place of n): * * modprobe u14-34f io_port=0x230,0x340 linked_comm=1 have_old_firmware=0 \ - * max_queue_depth=4 use_new_eh_code=0 + * max_queue_depth=4 use_new_eh_code=0 ext_tran=0 * * ---------------------------------------------------------------------------- * In this implementation, linked commands are designed to work with any DISK @@ -310,6 +325,7 @@ MODULE_PARM(have_old_firmware, "i"); MODULE_PARM(link_statistics, "i"); MODULE_PARM(max_queue_depth, "i"); MODULE_PARM(use_new_eh_code, "i"); +MODULE_PARM(ext_tran, "i"); MODULE_AUTHOR("Dario Ballabio"); #endif @@ -401,6 +417,7 @@ struct proc_dir_entry proc_scsi_u14_34f = { #undef DEBUG_RESET #undef DEBUG_GENERATE_ERRORS #undef DEBUG_GENERATE_ABORTS +#undef DEBUG_GEOMETRY #define MAX_ISA 3 #define MAX_VESA 1 @@ -552,6 +569,7 @@ static void flush_dev(Scsi_Device *, unsigned long, unsigned int, unsigned int); static int do_trace = FALSE; static int setup_done = FALSE; static int link_statistics = 0; +static int ext_tran = FALSE; #if defined(HAVE_OLD_UX4F_FIRMWARE) static int have_old_firmware = TRUE; @@ -872,9 +890,9 @@ __initfunc (static inline int port_detect \ if (j == 0) { printk("UltraStor 14F/34F: Copyright (C) 1994-1998 Dario Ballabio.\n"); - printk("%s config options -> of:%c, lc:%c, mq:%d, eh:%c.\n", + printk("%s config options -> of:%c, lc:%c, mq:%d, eh:%c, et:%c.\n", driver_name, YESNO(have_old_firmware), YESNO(linked_comm), - max_queue_depth, YESNO(use_new_eh_code)); + max_queue_depth, YESNO(use_new_eh_code), YESNO(ext_tran)); } printk("%s: %s 0x%03lx, BIOS 0x%05x, IRQ %u, %s, SG %d, MB %d.\n", @@ -918,6 +936,7 @@ __initfunc (void u14_34f_setup(char *str, int *ints)) { else if (!strncmp(cur, "mq:", 3)) max_queue_depth = val; else if (!strncmp(cur, "ls:", 3)) link_statistics = val; else if (!strncmp(cur, "eh:", 3)) use_new_eh_code = val; + else if (!strncmp(cur, "et:", 3)) ext_tran = val; if ((cur = strchr(cur, ','))) ++cur; } @@ -1551,6 +1570,18 @@ int u14_34f_biosparam(Disk *disk, kdev_t dev, int *dkinfo) { dkinfo[0] = HD(j)->heads; dkinfo[1] = HD(j)->sectors; dkinfo[2] = size / (HD(j)->heads * HD(j)->sectors); + + if (ext_tran && (scsicam_bios_param(disk, dev, dkinfo) < 0)) { + dkinfo[0] = 255; + dkinfo[1] = 63; + dkinfo[2] = size / (dkinfo[0] * dkinfo[1]); + } + +#if defined (DEBUG_GEOMETRY) + printk ("%s: biosparam, head=%d, sec=%d, cyl=%d.\n", driver_name, + dkinfo[0], dkinfo[1], dkinfo[2]); +#endif + return FALSE; } @@ -1733,7 +1764,7 @@ static inline void ihdlr(int irq, unsigned int j) { HD(j)->iocount); /* Check if this board is still busy */ - if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { + if (wait_on_busy(sh[j]->io_port, 20 * MAXLOOP)) { outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR); printk("%s: ihdlr, busy timeout error, irq %d, reg 0x%x, count %d.\n", BN(j), irq, reg, HD(j)->iocount); diff --git a/drivers/scsi/u14-34f.h b/drivers/scsi/u14-34f.h index 470296419488..943b8cbb3cc8 100644 --- a/drivers/scsi/u14-34f.h +++ b/drivers/scsi/u14-34f.h @@ -4,6 +4,7 @@ #ifndef _U14_34F_H #define _U14_34F_H +#include #include int u14_34f_detect(Scsi_Host_Template *); @@ -15,7 +16,7 @@ int u14_34f_reset(Scsi_Cmnd *); int u14_34f_old_reset(Scsi_Cmnd *, unsigned int); int u14_34f_biosparam(Disk *, kdev_t, int *); -#define U14_34F_VERSION "4.31.00" +#define U14_34F_VERSION "4.33.00" #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) diff --git a/drivers/sound/sb_common.c b/drivers/sound/sb_common.c index 828a6f96ff39..24d3d32336d5 100644 --- a/drivers/sound/sb_common.c +++ b/drivers/sound/sb_common.c @@ -1201,6 +1201,8 @@ probe_sbmpu (struct address_info *hw_config) } hw_config->name = "Sound Blaster 16"; hw_config->irq = -devc->irq; + hw_config->dma = -1; + hw_config->dma2 = -1; sb16_set_mpu_port(devc, hw_config); break; diff --git a/drivers/sound/sequencer.c b/drivers/sound/sequencer.c index 6e42e5431a31..9f6a3ae6cd71 100644 --- a/drivers/sound/sequencer.c +++ b/drivers/sound/sequencer.c @@ -1149,8 +1149,10 @@ sequencer_open (int dev, struct fileinfo *file) } if (!max_synthdev && !max_mididev) + { + sequencer_busy = 0; return -(ENXIO); - + } synth_open_mask = 0; for (i = 0; i < max_mididev; i++) diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index d7e2fc621c8b..c097011bb00f 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -587,7 +587,7 @@ int isofs_bmap(struct inode * inode,int block) int i; if (block<0) { - printk("_isofs_bmap: block<0"); + printk("_isofs_bmap: block<0\n"); return 0; } @@ -597,7 +597,8 @@ int isofs_bmap(struct inode * inode,int block) * If we are beyond the end of this file, don't give out any * blocks. */ - if( b_off > inode->i_size ) + if( (b_off > inode->i_size) || + ((b_off == inode->i_size) && (b_off & (PAGE_SIZE - 1))) ) { off_t max_legal_read_offset; @@ -614,7 +615,7 @@ int isofs_bmap(struct inode * inode,int block) if( b_off >= max_legal_read_offset ) { - printk("_isofs_bmap: block>= EOF(%d, %ld)", block, + printk("_isofs_bmap: block>= EOF(%d, %ld)\n", block, inode->i_size); } return 0; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index d5a414eccb99..2d24607bf7c3 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -344,6 +344,9 @@ static int run_nfsiod(void *dummy) current->session = 1; current->pgrp = 1; sprintf(current->comm, "nfsiod"); +#ifndef MODULE + current->blocked = ~0UL; +#endif ret = nfsiod(); MOD_DEC_USE_COUNT; return ret; diff --git a/fs/nfs/nfsiod.c b/fs/nfs/nfsiod.c index 167c5b5017b3..95b1350ed18d 100644 --- a/fs/nfs/nfsiod.c +++ b/fs/nfs/nfsiod.c @@ -99,10 +99,14 @@ nfsiod(void) /* Wait until user enqueues request */ dprintk("BIO: before: now %d nfsiod's active\n", active); dprintk("BIO: nfsiod %d waiting\n", current->pid); +#ifndef MODULE + current->signal = 0; +#endif interruptible_sleep_on(&req->rq_wait); - +#ifdef MODULE if (current->signal & ~current->blocked) break; +#endif if (!req->rq_rpcreq.rq_slot) continue; dprintk("BIO: nfsiod %d woken up; calling nfs_rpc_doio.\n", diff --git a/fs/proc/array.c b/fs/proc/array.c index 000cc5e6a46f..262fbf54365c 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -29,7 +29,7 @@ * Yves Arrouye : remove removal of trailing spaces in get_array. * * - * Alan Cox : security fixes. a + * Alan Cox : security fixes. */ #include diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index 5e936d7ffc04..fb374b80ef4b 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -205,13 +205,13 @@ extern struct timezone sys_tz; static int utc2local(int time) { - return time - sys_tz.tz_minuteswest * 60; + return time - sys_tz.tz_minuteswest * 60 + sys_tz.tz_dsttime * 3600; } static int local2utc(int time) { - return time + sys_tz.tz_minuteswest * 60; + return time + sys_tz.tz_minuteswest * 60 - sys_tz.tz_dsttime * 3600; } /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h index 7b851a1bd3c7..2bcacea0e091 100644 --- a/include/asm-i386/processor.h +++ b/include/asm-i386/processor.h @@ -27,6 +27,53 @@ extern char wp_works_ok; /* doesn't work on a 386 */ extern char hlt_works_ok; /* problems on some 486Dx4's and old 386's */ extern int have_cpuid; /* We have a CPUID */ +extern unsigned long cpu_hz; /* CPU clock frequency from time.c */ + +/* + * Detection of CPU model (CPUID). + */ +extern inline void cpuid(int op, int *eax, int *ebx, int *ecx, int *edx) +{ + __asm__("cpuid" + : "=a" (*eax), + "=b" (*ebx), + "=c" (*ecx), + "=d" (*edx) + : "a" (op) + : "cc"); +} + +/* + * Cyrix CPU register indexes (use special macros to access these) + */ +#define CX86_CCR2 0xc2 +#define CX86_CCR3 0xc3 +#define CX86_CCR4 0xe8 +#define CX86_CCR5 0xe9 +#define CX86_DIR0 0xfe +#define CX86_DIR1 0xff + +/* + * Cyrix CPU register access macros + */ + +extern inline unsigned char getCx86(unsigned char reg) +{ + unsigned char data; + + __asm__ __volatile__("movb %1,%%al\n\t" + "outb %%al,$0x22\n\t" + "inb $0x23,%%al" : "=a" (data) : "q" (reg)); + return data; +} + +extern inline void setCx86(unsigned char reg, unsigned char data) +{ + __asm__ __volatile__("outb %%al,$0x22\n\t" + "movb %1,%%al\n\t" + "outb %%al,$0x23" : : "a" (reg), "q" (data)); +} + /* * Bus types (default is ISA, but people can check others with these..) * MCA_bus hardcoded to 0 for now. diff --git a/include/asm-i386/smp.h b/include/asm-i386/smp.h index ff0caafe07da..ed8b06dccd85 100644 --- a/include/asm-i386/smp.h +++ b/include/asm-i386/smp.h @@ -160,6 +160,7 @@ struct cpuinfo_x86 char x86_mask; char x86_vendor_id[16]; int x86_capability; + int x86_ext_capability; int fdiv_bug; int have_cpuid; char wp_works_ok; diff --git a/include/linux/b1lli.h b/include/linux/b1lli.h index 401933c3f031..cc980ee1f001 100644 --- a/include/linux/b1lli.h +++ b/include/linux/b1lli.h @@ -1,11 +1,41 @@ /* - * $Id: b1lli.h,v 1.1 1997/03/04 21:27:32 calle Exp $ + * $Id: b1lli.h,v 1.1.2.9 1998/03/20 14:30:02 calle Exp $ * * ISDN lowlevel-module for AVM B1-card. * * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1lli.h,v $ + * Revision 1.1.2.9 1998/03/20 14:30:02 calle + * added cardnr to detect if you try to add same T1 to different io address. + * change number of nccis depending on number of channels. + * + * Revision 1.1.2.8 1998/03/04 17:32:33 calle + * Changes for T1. + * + * Revision 1.1.2.7 1998/02/27 15:38:29 calle + * T1 running with slow link. + * + * Revision 1.1.2.6 1998/02/24 17:57:36 calle + * changes for T1. + * + * Revision 1.1.2.5 1998/01/27 16:11:50 calle + * support for PCMCIA B1/M1/M2 ready. + * + * Revision 1.1.2.4 1998/01/26 14:51:56 calle + * interface change for pcmcia cards. + * + * Revision 1.1.2.3 1998/01/23 16:46:45 calle + * new functions for pcmcia cards. + * + * Revision 1.1.2.2 1997/11/26 16:57:26 calle + * more changes for B1/M1/T1. + * + * Revision 1.1.2.1 1997/11/26 10:47:01 calle + * prepared for M1 (Mobile) and T1 (PMX) cards. + * prepared to set configuration after load to support other D-channel + * protocols, point-to-point and leased lines. + * * Revision 1.1 1997/03/04 21:27:32 calle * First version in isdn4linux * @@ -32,10 +62,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,33 +86,47 @@ 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; + int cardnr; /* for HEMA/T1 */ +} avmb1_extcarddef; + +#define AVMB1_LOAD 0 /* load image to card */ +#define AVMB1_ADDCARD 1 /* add a new card */ +#define AVMB1_RESETCARD 2 /* reset a card */ +#define AVMB1_LOAD_AND_CONFIG 3 /* load image and config to card */ +#define AVMB1_ADDCARD_WITH_TYPE 4 /* add a new card, with cardtype */ +#define AVMB1_GET_CARDINFO 5 /* get cardtype */ +#define AVMB1_REMOVECARD 6 /* remove a card (usefull for T1) */ -#ifdef __KERNEL__ /* * card states for startup */ -#define CARD_NONE 0 +#define CARD_FREE 0 #define CARD_DETECTED 1 #define CARD_LOADING 2 #define CARD_INITSTATE 4 #define CARD_RUNNING 5 #define CARD_ACTIVE 6 -#define AVMB1_PORTLEN 0x1f +#ifdef __KERNEL__ + +#define AVMB1_PORTLEN 0x1f -#define AVM_MAXVERSION 8 -#define AVM_NBCHAN 2 +#define AVM_MAXVERSION 8 -#define AVM_NAPPS 30 -#define AVM_NPLCI 5 -#define AVM_NNCCI 6 +#define AVM_NAPPS 30 +#define AVM_NNCCI_PER_CHANNEL 4 /* * Main driver data @@ -81,6 +137,8 @@ typedef struct avmb1_card { int cnr; unsigned short port; unsigned irq; + int cardtype; + int cardnr; /* for T1-HEMA */ volatile unsigned short cardstate; int interrupt; int blocked; @@ -108,14 +166,18 @@ typedef struct avmb1_card { /* b1lli.c */ -int B1_detect(unsigned short base); +int B1_detect(unsigned short base, int cardtype); +int T1_detectandinit(unsigned short base, unsigned irq, int cardnr); void B1_reset(unsigned short base); +void T1_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_enable_irq(unsigned short base); +void B1_setinterrupt(unsigned short base, unsigned irq, int cardtype); unsigned char B1_disable_irq(unsigned short base); -int B1_valid_irq(unsigned irq); +void T1_disable_irq(unsigned short base); +int B1_valid_irq(unsigned irq, int cardtype); +int B1_valid_port(unsigned port, int cardtype); void B1_handle_interrupt(avmb1_card * card); void B1_send_init(unsigned short port, unsigned int napps, unsigned int nncci, unsigned int cardnr); @@ -133,8 +195,16 @@ 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/cyclades.h b/include/linux/cyclades.h index efed32b4db7e..4ede3d0b0ac7 100644 --- a/include/linux/cyclades.h +++ b/include/linux/cyclades.h @@ -1,14 +1,51 @@ -/* +/* $Revision: 2.4 $$Date: 1998/06/01 12:09:53 $ * linux/include/linux/cyclades.h * - * This file is maintained by Marcio Saito and + * This file is maintained by Ivan Passos , + * Marcio Saito and * Randolph Bentson . * * This file contains the general definitions for the cyclades.c driver *$Log: cyclades.h,v $ - * Revision 1.5 1995/11/13 21:13:31 bentson - * changes suggested by Michael Chastain - * to support use of this file in non-kernel applications + *Revision 2.4 1998/06/01 12:09:53 ivan + *removed closing_wait2 from cyclades_port structure; + * + *Revision 2.3 1998/03/16 18:01:12 ivan + *changes in the cyclades_port structure to get it closer to the + *standard serial port structure; + *added constants for new ioctls; + * + *Revision 2.2 1998/02/17 16:50:00 ivan + *changes in the cyclades_port structure (addition of shutdown_wait and + *chip_rev variables); + *added constants for new ioctls and for CD1400 rev. numbers. + * + *Revision 2.1 1997/10/24 16:03:00 ivan + *added rflow (which allows enabling the CD1400 special flow control + *feature) and rtsdtr_inv (which allows DTR/RTS pin inversion) to + *cyclades_port structure; + *added Alpha support + * + *Revision 2.0 1997/06/30 10:30:00 ivan + *added some new doorbell command constants related to IOCTLW and + *UART error signaling + * + *Revision 1.8 1997/06/03 15:30:00 ivan + *added constant ZFIRM_HLT + *added constant CyPCI_Ze_win ( = 2 * Cy_PCI_Zwin) + * + *Revision 1.7 1997/03/26 10:30:00 daniel + *new entries at the end of cyclades_port struct to reallocate + *variables illegally allocated within card memory. + * + *Revision 1.6 1996/09/09 18:35:30 bentson + *fold in changes for Cyclom-Z -- including structures for + *communicating with board as well modest changes to original + *structures to support new features. + * + *Revision 1.5 1995/11/13 21:13:31 bentson + *changes suggested by Michael Chastain + *to support use of this file in non-kernel applications * * */ @@ -34,17 +71,411 @@ struct cyclades_monitor { #define CYSETTIMEOUT 0x435907 #define CYGETDEFTIMEOUT 0x435908 #define CYSETDEFTIMEOUT 0x435909 +#define CYSETRFLOW 0x43590a +#define CYGETRFLOW 0x43590b +#define CYSETRTSDTR_INV 0x43590c +#define CYGETRTSDTR_INV 0x43590d +#define CYZPOLLCYCLE 0x43590e +#define CYGETCD1400VER 0x43590f +#define CYGETCARDINFO 0x435910 +#define CYSETWAIT 0x435911 +#define CYGETWAIT 0x435912 + +/*************** CYCLOM-Z ADDITIONS ***************/ + +#define CZIOC ('M' << 8) +#define CZ_NBOARDS (CZIOC|0xfa) +#define CZ_BOOT_START (CZIOC|0xfb) +#define CZ_BOOT_DATA (CZIOC|0xfc) +#define CZ_BOOT_END (CZIOC|0xfd) +#define CZ_TEST (CZIOC|0xfe) + +#define CZ_DEF_POLL (HZ/25) + +#define MAX_BOARD 4 /* Max number of boards */ +#define MAX_DEV 256 /* Max number of ports total */ +#define CYZ_MAX_SPEED 921600 + +#define CYZ_FIFO_SIZE 16 + +#define CYZ_BOOT_NWORDS 0x100 +struct CYZ_BOOT_CTRL { + unsigned short nboard; + int status[MAX_BOARD]; + int nchannel[MAX_BOARD]; + int fw_rev[MAX_BOARD]; + unsigned long offset; + unsigned long data[CYZ_BOOT_NWORDS]; +}; + + +#ifndef DP_WINDOW_SIZE +/* #include "cyclomz.h" */ +/****************** ****************** *******************/ +/* + * The data types defined below are used in all ZFIRM interface + * data structures. They accomodate differences between HW + * architectures and compilers. + */ + +#if defined(__alpha__) +typedef unsigned long ucdouble; /* 64 bits, unsigned */ +typedef unsigned int uclong; /* 32 bits, unsigned */ +#else +typedef unsigned long uclong; /* 32 bits, unsigned */ +#endif +typedef unsigned short ucshort; /* 16 bits, unsigned */ +typedef unsigned char ucchar; /* 8 bits, unsigned */ + +/* + * Memory Window Sizes + */ + +#define DP_WINDOW_SIZE (0x00080000) /* window size 512 Kb */ +#define ZE_DP_WINDOW_SIZE (0x00100000) /* window size 1 Mb (Ze and + 8Zo V.2 */ +#define CTRL_WINDOW_SIZE (0x00000080) /* runtime regs 128 bytes */ + +/* + * CUSTOM_REG - Cyclom-Z/PCI Custom Registers Set. The driver + * normally will access only interested on the fpga_id, fpga_version, + * start_cpu and stop_cpu. + */ + +struct CUSTOM_REG { + uclong fpga_id; /* FPGA Identification Register */ + uclong fpga_version; /* FPGA Version Number Register */ + uclong cpu_start; /* CPU start Register (write) */ + uclong cpu_stop; /* CPU stop Register (write) */ + uclong misc_reg; /* Miscelaneous Register */ + uclong idt_mode; /* IDT mode Register */ + uclong uart_irq_status; /* UART IRQ status Register */ + uclong clear_timer0_irq; /* Clear timer interrupt Register */ + uclong clear_timer1_irq; /* Clear timer interrupt Register */ + uclong clear_timer2_irq; /* Clear timer interrupt Register */ + uclong test_register; /* Test Register */ + uclong test_count; /* Test Count Register */ + uclong timer_select; /* Timer select register */ + uclong pr_uart_irq_status; /* Prioritized UART IRQ stat Reg */ + uclong ram_wait_state; /* RAM wait-state Register */ + uclong uart_wait_state; /* UART wait-state Register */ + uclong timer_wait_state; /* timer wait-state Register */ + uclong ack_wait_state; /* ACK wait State Register */ +}; + +/* + * RUNTIME_9060 - PLX PCI9060ES local configuration and shared runtime + * registers. This structure can be used to access the 9060 registers + * (memory mapped). + */ + +struct RUNTIME_9060 { + uclong loc_addr_range; /* 00h - Local Address Range */ + uclong loc_addr_base; /* 04h - Local Address Base */ + uclong loc_arbitr; /* 08h - Local Arbitration */ + uclong endian_descr; /* 0Ch - Big/Little Endian Descriptor */ + uclong loc_rom_range; /* 10h - Local ROM Range */ + uclong loc_rom_base; /* 14h - Local ROM Base */ + uclong loc_bus_descr; /* 18h - Local Bus descriptor */ + uclong loc_range_mst; /* 1Ch - Local Range for Master to PCI */ + uclong loc_base_mst; /* 20h - Local Base for Master PCI */ + uclong loc_range_io; /* 24h - Local Range for Master IO */ + uclong pci_base_mst; /* 28h - PCI Base for Master PCI */ + uclong pci_conf_io; /* 2Ch - PCI configuration for Master IO */ + uclong filler1; /* 30h */ + uclong filler2; /* 34h */ + uclong filler3; /* 38h */ + uclong filler4; /* 3Ch */ + uclong mail_box_0; /* 40h - Mail Box 0 */ + uclong mail_box_1; /* 44h - Mail Box 1 */ + uclong mail_box_2; /* 48h - Mail Box 2 */ + uclong mail_box_3; /* 4Ch - Mail Box 3 */ + uclong filler5; /* 50h */ + uclong filler6; /* 54h */ + uclong filler7; /* 58h */ + uclong filler8; /* 5Ch */ + uclong pci_doorbell; /* 60h - PCI to Local Doorbell */ + uclong loc_doorbell; /* 64h - Local to PCI Doorbell */ + uclong intr_ctrl_stat; /* 68h - Interrupt Control/Status */ + uclong init_ctrl; /* 6Ch - EEPROM control, Init Control, etc */ +}; + +/* Values for the Local Base Address re-map register */ + +#define WIN_RAM 0x00000001L /* set the sliding window to RAM */ +#define WIN_CREG 0x14000001L /* set the window to custom Registers */ + +/* Values timer select registers */ + +#define TIMER_BY_1M 0x00 /* clock divided by 1M */ +#define TIMER_BY_256K 0x01 /* clock divided by 256k */ +#define TIMER_BY_128K 0x02 /* clock divided by 128k */ +#define TIMER_BY_32K 0x03 /* clock divided by 32k */ + +/****************** ****************** *******************/ +#endif + +#ifndef ZFIRM_ID +/* #include "zfwint.h" */ +/****************** ****************** *******************/ +/* + * This file contains the definitions for interfacing with the + * Cyclom-Z ZFIRM Firmware. + */ + +/* General Constant definitions */ + +#define MAX_CHAN 64 /* max number of channels per board */ + +/* firmware id structure (set after boot) */ + +#define ID_ADDRESS 0x00000180L /* signature/pointer address */ +#define ZFIRM_ID 0x5557465AL /* ZFIRM/U signature */ +#define ZFIRM_HLT 0x59505B5CL /* ZFIRM needs external power supply */ +#define ZFIRM_RST 0x56040674L /* RST signal (due to FW reset) */ + +#define ZF_TINACT_DEF 1000 /* default inactivity timeout + (1000 ms) */ +#define ZF_TINACT ZF_TINACT_DEF + +struct FIRM_ID { + uclong signature; /* ZFIRM/U signature */ + uclong zfwctrl_addr; /* pointer to ZFW_CTRL structure */ +}; + +/* Op. System id */ + +#define C_OS_LINUX 0x00000030 /* generic Linux system */ + +/* channel op_mode */ + +#define C_CH_DISABLE 0x00000000 /* channel is disabled */ +#define C_CH_TXENABLE 0x00000001 /* channel Tx enabled */ +#define C_CH_RXENABLE 0x00000002 /* channel Rx enabled */ +#define C_CH_ENABLE 0x00000003 /* channel Tx/Rx enabled */ +#define C_CH_LOOPBACK 0x00000004 /* Loopback mode */ + +/* comm_parity - parity */ + +#define C_PR_NONE 0x00000000 /* None */ +#define C_PR_ODD 0x00000001 /* Odd */ +#define C_PR_EVEN 0x00000002 /* Even */ +#define C_PR_MARK 0x00000004 /* Mark */ +#define C_PR_SPACE 0x00000008 /* Space */ +#define C_PR_PARITY 0x000000ff + +#define C_PR_DISCARD 0x00000100 /* discard char with frame/par error */ +#define C_PR_IGNORE 0x00000200 /* ignore frame/par error */ + +/* comm_data_l - data length and stop bits */ + +#define C_DL_CS5 0x00000001 +#define C_DL_CS6 0x00000002 +#define C_DL_CS7 0x00000004 +#define C_DL_CS8 0x00000008 +#define C_DL_CS 0x0000000f +#define C_DL_1STOP 0x00000010 +#define C_DL_15STOP 0x00000020 +#define C_DL_2STOP 0x00000040 +#define C_DL_STOP 0x000000f0 + +/* interrupt enabling/status */ + +#define C_IN_DISABLE 0x00000000 /* zero, disable interrupts */ +#define C_IN_TXBEMPTY 0x00000001 /* tx buffer empty */ +#define C_IN_TXLOWWM 0x00000002 /* tx buffer below LWM */ +#define C_IN_RXHIWM 0x00000010 /* rx buffer above HWM */ +#define C_IN_RXNNDT 0x00000020 /* rx no new data timeout */ +#define C_IN_MDCD 0x00000100 /* modem DCD change */ +#define C_IN_MDSR 0x00000200 /* modem DSR change */ +#define C_IN_MRI 0x00000400 /* modem RI change */ +#define C_IN_MCTS 0x00000800 /* modem CTS change */ +#define C_IN_RXBRK 0x00001000 /* Break received */ +#define C_IN_PR_ERROR 0x00002000 /* parity error */ +#define C_IN_FR_ERROR 0x00004000 /* frame error */ +#define C_IN_OVR_ERROR 0x00008000 /* overrun error */ +#define C_IN_RXOFL 0x00010000 /* RX buffer overflow */ +#define C_IN_IOCTLW 0x00020000 /* I/O control w/ wait */ + +/* flow control */ + +#define C_FL_OXX 0x00000001 /* output Xon/Xoff flow control */ +#define C_FL_IXX 0x00000002 /* output Xon/Xoff flow control */ +#define C_FL_OIXANY 0x00000004 /* output Xon/Xoff (any xon) */ +#define C_FL_SWFLOW 0x0000000f + +/* flow status */ + +#define C_FS_TXIDLE 0x00000000 /* no Tx data in the buffer or UART */ +#define C_FS_SENDING 0x00000001 /* UART is sending data */ +#define C_FS_SWFLOW 0x00000002 /* Tx is stopped by received Xoff */ + +/* rs_control/rs_status RS-232 signals */ + +#define C_RS_DCD 0x00000100 /* CD */ +#define C_RS_DSR 0x00000200 /* DSR */ +#define C_RS_RI 0x00000400 /* RI */ +#define C_RS_CTS 0x00000800 /* CTS */ +#define C_RS_RTS 0x00000001 /* RTS */ +#define C_RS_DTR 0x00000004 /* DTR */ + +/* commands Host <-> Board */ + +#define C_CM_RESET 0x01 /* reset/flush buffers */ +#define C_CM_IOCTL 0x02 /* re-read CH_CTRL */ +#define C_CM_IOCTLW 0x03 /* re-read CH_CTRL, intr when done */ +#define C_CM_IOCTLM 0x04 /* RS-232 outputs change */ +#define C_CM_SENDXOFF 0x10 /* send Xoff */ +#define C_CM_SENDXON 0x11 /* send Xon */ +#define C_CM_CLFLOW 0x12 /* Clear flow control (resume) */ +#define C_CM_SENDBRK 0x41 /* send break */ +#define C_CM_INTBACK 0x42 /* Interrupt back */ +#define C_CM_SET_BREAK 0x43 /* Tx break on */ +#define C_CM_CLR_BREAK 0x44 /* Tx break off */ +#define C_CM_CMD_DONE 0x45 /* Previous command done */ +#define C_CM_TINACT 0x51 /* set inactivity detection */ +#define C_CM_IRQ_ENBL 0x52 /* enable generation of interrupts */ +#define C_CM_IRQ_DSBL 0x53 /* disable generation of interrupts */ +#define C_CM_ACK_ENBL 0x54 /* enable acknolowdged interrupt mode */ +#define C_CM_ACK_DSBL 0x55 /* disable acknolowdged intr mode */ +#define C_CM_FLUSH_RX 0x56 /* flushes Rx buffer */ +#define C_CM_FLUSH_TX 0x57 /* flushes Tx buffer */ + +#define C_CM_TXBEMPTY 0x60 /* Tx buffer is empty */ +#define C_CM_TXLOWWM 0x61 /* Tx buffer low water mark */ +#define C_CM_RXHIWM 0x62 /* Rx buffer high water mark */ +#define C_CM_RXNNDT 0x63 /* rx no new data timeout */ +#define C_CM_MDCD 0x70 /* modem DCD change */ +#define C_CM_MDSR 0x71 /* modem DSR change */ +#define C_CM_MRI 0x72 /* modem RI change */ +#define C_CM_MCTS 0x73 /* modem CTS change */ +#define C_CM_RXBRK 0x84 /* Break received */ +#define C_CM_PR_ERROR 0x85 /* Parity error */ +#define C_CM_FR_ERROR 0x86 /* Frame error */ +#define C_CM_OVR_ERROR 0x87 /* Overrun error */ +#define C_CM_RXOFL 0x88 /* RX buffer overflow */ +#define C_CM_CMDERROR 0x90 /* command error */ +#define C_CM_FATAL 0x91 /* fatal error */ +#define C_CM_HW_RESET 0x92 /* reset board */ + +/* + * CH_CTRL - This per port structure contains all parameters + * that control an specific port. It can be seen as the + * configuration registers of a "super-serial-controller". + */ + +struct CH_CTRL { + uclong op_mode; /* operation mode */ + uclong intr_enable; /* interrupt masking */ + uclong sw_flow; /* SW flow control */ + uclong flow_status; /* output flow status */ + uclong comm_baud; /* baud rate - numerically specified */ + uclong comm_parity; /* parity */ + uclong comm_data_l; /* data length/stop */ + uclong comm_flags; /* other flags */ + uclong hw_flow; /* HW flow control */ + uclong rs_control; /* RS-232 outputs */ + uclong rs_status; /* RS-232 inputs */ + uclong flow_xon; /* xon char */ + uclong flow_xoff; /* xoff char */ + uclong hw_overflow; /* hw overflow counter */ + uclong sw_overflow; /* sw overflow counter */ + uclong comm_error; /* frame/parity error counter */ +}; + + +/* + * BUF_CTRL - This per channel structure contains + * all Tx and Rx buffer control for a given channel. + */ + +struct BUF_CTRL { + uclong flag_dma; /* buffers are in Host memory */ + uclong tx_bufaddr; /* address of the tx buffer */ + uclong tx_bufsize; /* tx buffer size */ + uclong tx_threshold; /* tx low water mark */ + uclong tx_get; /* tail index tx buf */ + uclong tx_put; /* head index tx buf */ + uclong rx_bufaddr; /* address of the rx buffer */ + uclong rx_bufsize; /* rx buffer size */ + uclong rx_threshold; /* rx high water mark */ + uclong rx_get; /* tail index rx buf */ + uclong rx_put; /* head index rx buf */ + uclong filler[5]; /* filler to align structures */ +}; + +/* + * BOARD_CTRL - This per board structure contains all global + * control fields related to the board. + */ + +struct BOARD_CTRL { + + /* static info provided by the on-board CPU */ + uclong n_channel; /* number of channels */ + uclong fw_version; /* firmware version */ + + /* static info provided by the driver */ + uclong op_system; /* op_system id */ + uclong dr_version; /* driver version */ + + /* board control area */ + uclong inactivity; /* inactivity control */ + + /* host to FW commands */ + uclong hcmd_channel; /* channel number */ + uclong hcmd_param; /* pointer to parameters */ + + /* FW to Host commands */ + uclong fwcmd_channel; /* channel number */ + uclong fwcmd_param; /* pointer to parameters */ + + /* filler so the structures are aligned */ + uclong filler[7]; +}; + +/* + * ZFW_CTRL - This is the data structure that includes all other + * data structures used by the Firmware. + */ + +struct ZFW_CTRL { + struct BOARD_CTRL board_ctrl; + struct CH_CTRL ch_ctrl[MAX_CHAN]; + struct BUF_CTRL buf_ctrl[MAX_CHAN]; +}; + +/****************** ****************** *******************/ +#endif + + + #ifdef __KERNEL__ +/*************************************** + * Memory access functions/macros * + * (required to support Alpha systems) * + ***************************************/ + +#define cy_writeb(port,val) {writeb((ucchar)(val),(ulong)(port)); mb();} +#define cy_writew(port,val) {writew((ushort)(val),(ulong)(port)); mb();} +#define cy_writel(port,val) {writel((uclong)(val),(ulong)(port)); mb();} + +#define cy_readb(port) readb(port) +#define cy_readw(port) readw(port) +#define cy_readl(port) readl(port) + /* Per card data structure */ struct cyclades_card { - int base_addr; + long base_addr; + long ctl_addr; int irq; - int num_chips; /* 0 if card is absent */ + int num_chips; /* 0 if card absent, 1 if Z/PCI, else Y */ int first_line; /* minor number of first channel on card */ int bus_index; /* address shift - 0 for ISA, 1 for PCI */ + int inact_ctrl; /* FW Inactivity control - 0 disabled, 1 enabled */ }; struct cyclades_chip { @@ -62,23 +493,28 @@ struct cyclades_chip { struct cyclades_port { int magic; - int type; int card; int line; int flags; /* defined in tty.h */ + int type; /* UART type */ struct tty_struct *tty; int read_status_mask; + int ignore_status_mask; int timeout; int xmit_fifo_size; int cor1,cor2,cor3,cor4,cor5; int tbpr,tco,rbpr,rco; - int ignore_status_mask; + int baud; + int rflow; + int rtsdtr_inv; + int chip_rev; + int custom_divisor; + int x_char; /* to be pushed out ASAP */ int close_delay; - int IER; /* Interrupt Enable Register */ - int event; + unsigned short closing_wait; + unsigned long event; unsigned long last_active; int count; /* # of fd on device */ - int x_char; /* to be pushed out ASAP */ int x_break; int blocked_open; /* # of blocked opens */ long session; /* Session of opening process */ @@ -94,29 +530,49 @@ struct cyclades_port { struct termios callout_termios; struct wait_queue *open_wait; struct wait_queue *close_wait; + struct wait_queue *shutdown_wait; struct cyclades_monitor mon; + unsigned long jiffies[3]; + unsigned long rflush_count; }; /* * Events are used to schedule things to happen at timer-interrupt * time, instead of at cy interrupt time. */ -#define Cy_EVENT_READ_PROCESS 0 -#define Cy_EVENT_WRITE_WAKEUP 1 -#define Cy_EVENT_HANGUP 2 -#define Cy_EVENT_BREAK 3 -#define Cy_EVENT_OPEN_WAKEUP 4 +#define Cy_EVENT_READ_PROCESS 0 +#define Cy_EVENT_WRITE_WAKEUP 1 +#define Cy_EVENT_HANGUP 2 +#define Cy_EVENT_BREAK 3 +#define Cy_EVENT_OPEN_WAKEUP 4 +#define Cy_EVENT_SHUTDOWN_WAKEUP 5 + +#define CLOSING_WAIT_DELAY 60*HZ +#define CY_CLOSING_WAIT_NONE 65535 +#define CY_CLOSING_WAIT_INF 0 + +#define CyMAX_CHIPS_PER_CARD 8 +#define CyMAX_CHAR_FIFO 12 +#define CyPORTS_PER_CHIP 4 +#define CD1400_MAX_SPEED 115200 +#define CyISA_Ywin 0x2000 -#define CyMaxChipsPerCard 8 +#define CyPCI_Ywin 0x4000 +#define CyPCI_Zctl CTRL_WINDOW_SIZE +#define CyPCI_Zwin 0x80000 +#define CyPCI_Ze_win (2 * CyPCI_Zwin) /**** CD1400 registers ****/ -#define CyRegSize 0x0400 -#define Cy_HwReset 0x1400 -#define Cy_ClrIntr 0x1800 -#define Cy_EpldRev 0x1e00 +#define CD1400_REV_G 0x46 +#define CD1400_REV_J 0x48 + +#define CyRegSize 0x0400 +#define Cy_HwReset 0x1400 +#define Cy_ClrIntr 0x1800 +#define Cy_EpldRev 0x1e00 /* Global Registers */ @@ -152,6 +608,9 @@ struct cyclades_port { #define CyPPR (0x7E*2) #define CyCLOCK_20_1MS (0x27) #define CyCLOCK_25_1MS (0x31) +#define CyCLOCK_25_5MS (0xf4) +#define CyCLOCK_60_1MS (0x75) +#define CyCLOCK_60_2MS (0xea) /* Virtual Registers */ @@ -274,10 +733,6 @@ struct cyclades_port { #define CyTBPR (0x72*2) #define CyTCOR (0x76*2) -/* max number of chars in the FIFO */ - -#define CyMAX_CHAR_FIFO 12 - /***************************************************************************/ #endif /* __KERNEL__ */ diff --git a/include/linux/if_shaper.h b/include/linux/if_shaper.h new file mode 100644 index 000000000000..5c64f47a2fae --- /dev/null +++ b/include/linux/if_shaper.h @@ -0,0 +1,62 @@ +#ifndef __LINUX_SHAPER_H +#define __LINUX_SHAPER_H + +#ifdef __KERNEL__ + +#define SHAPER_QLEN 10 +/* + * This is a bit speed dependant (read it shouldnt be a constant!) + * + * 5 is about right for 28.8 upwards. Below that double for every + * halving of speed or so. - ie about 20 for 9600 baud. + */ +#define SHAPER_LATENCY (5*HZ) +#define SHAPER_MAXSLIP 2 +#define SHAPER_BURST (HZ/50) /* Good for >128K then */ + +struct shaper +{ + struct sk_buff_head sendq; + __u32 bitspersec; + __u32 bytespertick; + __u32 shapelatency; + __u32 shapeclock; + __u32 recovery; /* Time we can next clock a packet out on + an empty queue */ + char locked; + struct device *dev; + int (*hard_start_xmit) (struct sk_buff *skb, + struct device *dev); + int (*hard_header) (struct sk_buff *skb, + struct device *dev, + unsigned short type, + void *daddr, + void *saddr, + unsigned len); + int (*rebuild_header)(void *eth, struct device *dev, + unsigned long raddr, struct sk_buff *skb); + struct enet_statistics* (*get_stats)(struct device *dev); + struct wait_queue *wait_queue; + struct timer_list timer; +}; + +#endif + +#define SHAPER_SET_DEV 0x0001 +#define SHAPER_SET_SPEED 0x0002 +#define SHAPER_GET_DEV 0x0003 +#define SHAPER_GET_SPEED 0x0004 + +struct shaperconf +{ + __u16 ss_cmd; + union + { + char ssu_name[14]; + __u32 ssu_speed; + } ss_u; +#define ss_speed ss_u.ssu_speed +#define ss_name ss_u.ssu_name +}; + +#endif diff --git a/include/linux/isdn.h b/include/linux/isdn.h index dba8330295a3..b00b0b4d1393 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -1,4 +1,4 @@ -/* $Id: isdn.h,v 1.32 1997/08/21 09:49:46 fritz Exp $ +/* $Id: isdn.h,v 1.31.2.10 1998/06/07 13:48:30 fritz Exp $ * * Main header for the Linux ISDN subsystem (linklevel). * @@ -21,8 +21,48 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn.h,v $ - * Revision 1.32 1997/08/21 09:49:46 fritz - * Increased NET_DV + * Revision 1.31.2.10 1998/06/07 13:48:30 fritz + * ABC cleanup + * + * Revision 1.31.2.9 1998/06/02 12:12:49 detabc + * wegen einer einstweiliger verfuegung gegen DW ist zur zeit + * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar + * + * Revision 1.31.2.8 1998/05/06 08:30:44 detabc + * add Item to stop icmp-unreach (max. 6 times of dialwait delay) + * + * Revision 1.31.2.7 1998/04/26 19:51:40 detabc + * removed unused code + * + * Revision 1.31.2.6 1998/04/26 11:10:46 detabc + * add items for the abc_tx_queus and the abc_delayed_hangup + * only used if the abc-extension is enabled + * + * Revision 1.31.2.5 1998/04/18 17:39:45 detabc + * remove some unused abc-lines + * added defines und items for abc-secure callback (only used with abc-extenrsion) + * + * Revision 1.31.2.4 1998/04/08 21:42:25 keil + * Blocksize default 1024 + * + * Revision 1.31.2.3 1998/03/16 09:56:28 cal + * Merged in TimRu-patches. Still needs validation in conjunction with ABC-patches. + * + * Revision 1.31.2.2 1998/03/07 23:35:45 detabc + * added the abc-extension to the linux isdn-kernel + * for kernel-version 2.0.xx + * DO NOT USE FOR HIGHER KERNELS-VERSIONS + * all source-lines are switched with the define CONFIG_ISDN_WITH_ABC + * (make config and answer ABC-Ext. Support (Compress,TCP-Keepalive ...) with yes + * + * you need also a modified isdnctrl-source the switch on the + * features of the abc-extension + * + * please use carefully. more detail will be follow. + * thanks + * + * Revision 1.31.2.1 1997/08/21 15:57:04 fritz + * Synchronized 2.0.X branch with 2.0.31-pre7 * * Revision 1.31 1997/06/22 11:57:07 fritz * Added ability to adjust slave triggerlevel. @@ -182,6 +222,12 @@ #define IIOCGETCPS _IO('I',21) #define IIOCGETDVR _IO('I',22) +#define IIOCNETARU _IO('I',23) +#define IIOCNETDRU _IO('I',24) +#define IIOCNETGRU _IO('I',25) + +#define IIOCNETBUD _IO('I',26) + #define IIOCNETALN _IO('I',32) #define IIOCNETDLN _IO('I',33) @@ -254,6 +300,9 @@ typedef struct { int pppbind; /* ippp device for bindings */ int chargeint; /* Use fixed charge interval length */ int triggercps; /* BogoCPS needed for triggering slave */ + int dialtimeout; /* Dial-Timeout */ + int dialwait; /* Time to wait after failed dial */ + int stopped; /* Flag: Stopped */ } isdn_net_ioctl_cfg; #ifdef __KERNEL__ @@ -364,6 +413,9 @@ typedef struct { #define ISDN_NET_TMP 0x10 /* tmp interface until getting an IP */ #define ISDN_NET_DYNAMIC 0x20 /* this link is dynamically allocated */ #endif + +#define ISDN_NET_STOPPED 0x40 /* this interface is stopped */ + #define ISDN_NET_MAGIC 0x49344C02 /* for paranoia-checking */ /* Phone-list-element */ @@ -423,7 +475,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 */ + int triggercps; /* BogoCPS needed for triggering slave */ struct device *srobin; /* Ptr to Master device for slaves */ isdn_net_phone *phone[2]; /* List of remote-phonenumbers */ /* phone[0] = Incoming Numbers */ @@ -460,6 +512,11 @@ typedef struct isdn_net_local_s { struct device *, unsigned char *); int pppbind; /* ippp device for bindings */ + int dialtimeout; /* How long shall we try on dialing? (jiffies) */ + int dialwait; /* How long shall we wait after failed attempt? (jiffies) */ + ulong dialstarted; /* jiffies of first dialing-attempt */ + ulong dialwait_timer; /* jiffies of earliest next dialing-attempt */ + int huptimeout; /* How long will the connection be up? (seconds) */ } isdn_net_local; #ifdef CONFIG_ISDN_PPP @@ -502,7 +559,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 @@ -624,7 +682,7 @@ struct sqqueue { struct mpqueue { struct mpqueue *next; struct mpqueue *last; - long sqno; + long sqno; struct sk_buff *skb; int BEbyte; unsigned long time; @@ -663,8 +721,12 @@ struct ippp_struct { struct slcompress *slcomp; #endif unsigned long debug; - struct isdn_ppp_compressor *compressor,*link_compressor; + struct isdn_ppp_compressor *compressor, *link_compressor; void *decomp_stat,*comp_stat,*link_decomp_stat,*link_comp_stat; +#ifdef ISDN_SYNCPPP_READDRESS + unsigned long old_pa_addr; + unsigned long old_pa_dstaddr; +#endif }; #endif diff --git a/include/linux/isdnif.h b/include/linux/isdnif.h index 5b1f18a88213..24935ee42bd0 100644 --- a/include/linux/isdnif.h +++ b/include/linux/isdnif.h @@ -1,4 +1,4 @@ -/* $Id: isdnif.h,v 1.20 1997/05/27 15:18:06 fritz Exp $ +/* $Id: isdnif.h,v 1.20.2.1 1998/03/07 23:00:50 tsbogend Exp $ * * Linux ISDN subsystem * @@ -22,6 +22,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdnif.h,v $ + * Revision 1.20.2.1 1998/03/07 23:00:50 tsbogend + * added defines for Linux/Alpha 2.0.x with alpha-patches + * * Revision 1.20 1997/05/27 15:18:06 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -382,9 +385,15 @@ static inline unsigned long copy_to_user(void *to, const void *from, unsigned lo } #define GET_USER(x, addr) ( x = get_user(addr) ) +#ifdef __alpha__ /* needed for 2.0.x with alpha-patches */ +#define RWTYPE long +#define LSTYPE long +#define RWARG unsigned long +#else #define RWTYPE int #define LSTYPE int #define RWARG int +#endif #define LSARG off_t #else #include diff --git a/include/linux/serial.h b/include/linux/serial.h index 335c74eecb0d..63549d441c66 100644 --- a/include/linux/serial.h +++ b/include/linux/serial.h @@ -44,7 +44,8 @@ struct serial_struct { #define PORT_16550A 4 #define PORT_CIRRUS 5 #define PORT_16650 6 -#define PORT_MAX 6 +#define PORT_STARTECH 7 +#define PORT_MAX 7 /* * Definitions for async_struct (and serial_struct) flags field diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index aa38f2bf55b3..cf05129b98c6 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -112,6 +112,17 @@ struct sk_buff unsigned char *end; /* End pointer */ void (*destructor)(struct sk_buff *); /* Destruct function */ __u16 redirport; /* Redirect port */ + + /* + * Keep this at the end then we wont break stuff. + */ +#if defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE) + __u32 shapelatency; /* Latency on frame */ + __u32 shapeclock; /* Time it should go out */ + __u32 shapelen; /* Frame length in clocks */ + __u32 shapestamp; /* Stamp for shaper */ + __u16 shapepend; /* Pending */ +#endif }; #ifdef CONFIG_SKB_LARGE diff --git a/init/main.c b/init/main.c index 4bf6f9fd9958..5aa5bcda0b9b 100644 --- a/init/main.c +++ b/init/main.c @@ -169,6 +169,9 @@ extern void atari_scsi_setup (char *str, int *ints); extern void wd33c93_setup (char *str, int *ints); extern void gvp11_setup (char *str, int *ints); +#ifdef CONFIG_CYCLADES +extern void cy_setup(char *str, int *ints); +#endif #ifdef CONFIG_DIGI extern void pcxx_setup(char *str, int *ints); #endif @@ -446,6 +449,9 @@ struct kernel_param bootsetups[] = { #if defined(CONFIG_GVP11_SCSI) { "gvp11=", gvp11_setup }, #endif +#ifdef CONFIG_CYCLADES + { "cyclades=", cy_setup }, +#endif #ifdef CONFIG_DIGI { "digi=", pcxx_setup }, #endif diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 0a3658e1f20f..0789abba8862 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -310,6 +310,7 @@ struct symbol_table symbol_table = { X(sys_call_table), X(hard_reset_now), X(_ctype), + X(_ctmp), X(get_random_bytes), /* Signal interfaces */ @@ -368,6 +369,10 @@ struct symbol_table symbol_table = { X(get_write_access), X(put_write_access), +#ifdef CONFIG_PROC_FS + X(proc_dir_inode_operations), +#endif + /******************************************************** * Do not add anything below this line, * as the stacked modules depend on this! diff --git a/mm/mmap.c b/mm/mmap.c index 962e13d731f8..83a9f52e99b0 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -179,7 +179,7 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len, if (locks_verify_locked(file->f_inode)) return -EAGAIN; /* cevans -- whoops another append-only file flaw */ - if (IS_APPEND(file->f_inode) && (prot & PROT_WRITE)) + if (IS_APPEND(file->f_inode) && (file->f_mode & 2)) return -EACCES; /* fall through */ case MAP_PRIVATE: -- 2.39.5