From 6bbf087e7cc57ef1e671ef5c9cb2e75bed1104a4 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:26:52 -0500 Subject: [PATCH] Import 2.3.14pre1 --- CREDITS | 42 +- Documentation/Configure.help | 63 +- Documentation/isdn/CREDITS | 2 +- Documentation/isdn/HiSax.cert | 16 +- Documentation/isdn/INTERFACE | 132 +- Documentation/isdn/INTERFACE.fax | 163 + Documentation/isdn/README | 13 +- Documentation/isdn/README.HiSax | 36 +- Documentation/isdn/README.audio | 13 +- Documentation/isdn/README.avmb1 | 163 +- Documentation/isdn/README.diversion | 127 + Documentation/isdn/README.eicon | 6 +- Documentation/isdn/README.fax | 38 + Documentation/isdn/README.hfc-pci | 26 + Documentation/parport.txt | 3 +- Makefile | 8 +- arch/alpha/kernel/bios32.c | 1 - arch/alpha/kernel/core_apecs.c | 137 +- arch/alpha/kernel/core_cia.c | 176 +- arch/alpha/kernel/core_lca.c | 46 +- arch/alpha/kernel/core_mcpcia.c | 130 +- arch/alpha/kernel/core_polaris.c | 102 +- arch/alpha/kernel/core_pyxis.c | 121 +- arch/alpha/kernel/core_t2.c | 181 +- arch/alpha/kernel/core_tsunami.c | 156 +- arch/alpha/kernel/irq.c | 96 + arch/alpha/kernel/proto.h | 27 +- arch/alpha/kernel/setup.c | 10 +- arch/alpha/kernel/smp.c | 2 +- arch/alpha/kernel/sys_miata.c | 13 +- arch/alpha/kernel/sys_mikasa.c | 98 +- arch/alpha/kernel/sys_rx164.c | 7 +- arch/alpha/mm/fault.c | 1 + arch/i386/Makefile | 5 +- arch/i386/config.in | 2 + arch/i386/defconfig | 9 +- arch/i386/kernel/apm.c | 2 +- arch/i386/kernel/process.c | 66 +- arch/i386/kernel/setup.c | 53 +- arch/i386/vmlinux.lds | 75 - arch/ppc/kernel/smp.c | 1 - drivers/Makefile | 2 +- drivers/block/floppy.c | 48 +- drivers/block/genhd.c | 1428 +------ drivers/block/hd.c | 3 +- drivers/block/ide.c | 8 +- drivers/block/md.c | 2 +- drivers/block/raid1.c | 9 +- drivers/block/raid5.c | 1 - drivers/block/z2ram.c | 1 - drivers/char/adbmouse.c | 4 +- drivers/char/console.c | 6 +- drivers/char/defkeymap.c | 3 +- drivers/char/ftape/lowlevel/ftape-setup.c | 14 +- drivers/char/lp_intern.c | 364 -- drivers/char/lp_m68k.c | 556 --- drivers/char/mem.c | 7 +- drivers/isdn/Config.in | 32 +- drivers/isdn/Makefile | 14 +- drivers/isdn/avmb1/Makefile | 58 +- drivers/isdn/avmb1/avmcard.h | 559 +++ drivers/isdn/avmb1/b1.c | 688 ++++ drivers/isdn/avmb1/b1isa.c | 238 ++ drivers/isdn/avmb1/b1pci.c | 283 +- drivers/isdn/avmb1/b1pcmcia.c | 267 ++ drivers/isdn/avmb1/capi.c | 151 +- drivers/isdn/avmb1/capidev.h | 36 +- drivers/isdn/avmb1/capidrv.c | 430 +- drivers/isdn/avmb1/capilli.h | 110 + drivers/isdn/avmb1/capiutil.c | 26 +- drivers/isdn/avmb1/kcapi.c | 1553 +++++++ drivers/isdn/avmb1/t1isa.c | 545 +++ drivers/isdn/divert/Makefile | 21 + drivers/isdn/divert/divert_init.c | 105 + drivers/isdn/divert/divert_procfs.c | 456 +++ drivers/isdn/divert/isdn_divert.c | 902 +++++ drivers/isdn/divert/isdn_divert.h | 159 + drivers/isdn/eicon/eicon.h | 58 +- drivers/isdn/eicon/eicon_dsp.h | 119 +- drivers/isdn/eicon/eicon_idi.c | 505 ++- drivers/isdn/eicon/eicon_idi.h | 24 +- drivers/isdn/eicon/eicon_io.c | 28 +- drivers/isdn/eicon/eicon_isa.c | 12 +- drivers/isdn/eicon/eicon_mod.c | 356 +- drivers/isdn/eicon/eicon_pci.c | 32 +- drivers/isdn/hisax/Makefile | 60 +- drivers/isdn/hisax/amd7930.c | 14 +- drivers/isdn/hisax/arcofi.c | 143 +- drivers/isdn/hisax/arcofi.h | 19 +- drivers/isdn/hisax/asuscom.c | 56 +- drivers/isdn/hisax/avm_a1.c | 42 +- drivers/isdn/hisax/avm_a1p.c | 45 +- drivers/isdn/hisax/avm_pci.c | 83 +- drivers/isdn/hisax/bkm_a4t.c | 403 ++ drivers/isdn/hisax/bkm_a8.c | 529 +++ drivers/isdn/hisax/bkm_ax.h | 133 + drivers/isdn/hisax/callc.c | 1388 +++---- drivers/isdn/hisax/cert.c | 6 +- drivers/isdn/hisax/config.c | 531 ++- drivers/isdn/hisax/diva.c | 676 +++- drivers/isdn/hisax/elsa.c | 285 +- drivers/isdn/hisax/elsa_ser.c | 13 +- drivers/isdn/hisax/gazel.c | 751 ++++ drivers/isdn/hisax/hfc_2bds0.c | 40 +- drivers/isdn/hisax/hfc_2bs0.c | 22 +- drivers/isdn/hisax/hfc_pci.c | 1584 ++++++++ drivers/isdn/hisax/hfc_pci.h | 252 ++ drivers/isdn/hisax/hfcscard.c | 206 + drivers/isdn/hisax/hisax.h | 400 +- drivers/isdn/hisax/hscx.c | 19 +- drivers/isdn/hisax/hscx_irq.c | 11 +- drivers/isdn/hisax/isac.c | 263 +- drivers/isdn/hisax/isar.c | 272 +- drivers/isdn/hisax/isar.h | 95 +- drivers/isdn/hisax/isdnl1.c | 29 +- drivers/isdn/hisax/isdnl2.c | 1442 ++++--- drivers/isdn/hisax/isdnl3.c | 161 +- drivers/isdn/hisax/isdnl3.h | 20 +- drivers/isdn/hisax/isurf.c | 252 ++ drivers/isdn/hisax/ix1_micro.c | 42 +- drivers/isdn/hisax/jade.c | 326 ++ drivers/isdn/hisax/jade.h | 137 + drivers/isdn/hisax/jade_irq.c | 247 ++ drivers/isdn/hisax/l3_1tr6.c | 130 +- drivers/isdn/hisax/l3dss1.c | 2353 ++++++++--- drivers/isdn/hisax/l3dss1.h | 137 +- drivers/isdn/hisax/lmgr.c | 7 +- drivers/isdn/hisax/md5sums.asc | 34 +- drivers/isdn/hisax/mic.c | 42 +- drivers/isdn/hisax/netjet.c | 169 +- drivers/isdn/hisax/niccy.c | 134 +- drivers/isdn/hisax/s0box.c | 36 +- drivers/isdn/hisax/saphir.c | 326 ++ drivers/isdn/hisax/sedlbauer.c | 138 +- drivers/isdn/hisax/sportster.c | 27 +- drivers/isdn/hisax/tei.c | 39 +- drivers/isdn/hisax/teleint.c | 35 +- drivers/isdn/hisax/teles0.c | 46 +- drivers/isdn/hisax/teles3.c | 63 +- drivers/isdn/hisax/telespci.c | 94 +- drivers/isdn/icn/icn.c | 11 +- drivers/isdn/isdn_audio.c | 103 +- drivers/isdn/isdn_audio.h | 14 +- drivers/isdn/isdn_bsdcomp.c | 1 + drivers/isdn/isdn_budget.c | 206 - drivers/isdn/isdn_cards.c | 16 +- drivers/isdn/isdn_common.c | 252 +- drivers/isdn/isdn_common.h | 11 +- drivers/isdn/isdn_net.c | 50 +- drivers/isdn/isdn_ppp.c | 63 +- drivers/isdn/isdn_tty.c | 605 +-- drivers/isdn/isdn_tty.h | 85 +- drivers/isdn/isdn_ttyfax.c | 1201 ++++++ drivers/isdn/isdn_ttyfax.h | 33 + drivers/isdn/pcbit/drv.c | 2 +- drivers/isdn/pcbit/pcbit.h | 6 +- drivers/macintosh/mac_keyb.c | 1 - drivers/macintosh/macserial.c | 1 - drivers/misc/Config.in | 12 + drivers/misc/Makefile | 27 + drivers/misc/piix4_acpi.c | 214 + drivers/net/Makefile | 9 + drivers/net/cycx_drv.c | 3 +- drivers/net/cycx_main.c | 26 +- drivers/net/cycx_x25.c | 294 +- drivers/net/eepro100.c | 3 - drivers/net/hamradio/Config.in | 11 +- drivers/net/hamradio/Makefile | 32 +- drivers/net/sonic.h | 2 + drivers/parport/ieee1284_ops.c | 60 +- drivers/parport/parport_pc.c | 9 +- drivers/parport/procfs.c | 38 +- drivers/pci/Makefile | 2 +- drivers/pci/pci.c | 2 + drivers/scsi/BusLogic.c | 29 +- drivers/scsi/Makefile | 14 +- drivers/scsi/NCR53C9x.c | 2248 +++++------ drivers/scsi/NCR53C9x.h | 282 +- drivers/scsi/hosts.c | 14 + drivers/scsi/ncr53c8xx.c | 203 +- drivers/scsi/oktagon_esp.c | 599 +++ drivers/scsi/oktagon_esp.h | 57 + drivers/scsi/oktagon_io.S | 195 + drivers/scsi/pas16.c | 33 +- drivers/scsi/scsi.c | 15 + drivers/scsi/sun3x_esp.c | 290 ++ drivers/scsi/sun3x_esp.h | 41 + drivers/sound/Makefile | 8 +- drivers/sound/es1370.c | 52 +- drivers/sound/es1371.c | 73 +- drivers/sound/esssolo1.c | 331 +- drivers/sound/lowlevel/awe_compat-fbsd.h | 3 +- drivers/sound/sonicvibes.c | 67 +- drivers/usb/Makefile | 1 + drivers/usb/ohci.c | 31 +- drivers/usb/uhci.c | 39 +- drivers/usb/uhci.h | 21 + drivers/usb/usb.c | 108 +- drivers/usb/usb.h | 24 +- drivers/usb/usb_scsi.c | 2 +- drivers/usb/uss720.c | 15 +- drivers/video/Config.in | 2 +- drivers/video/S3triofb.c | 9 +- drivers/video/acornfb.c | 13 +- drivers/video/amifb.c | 24 +- drivers/video/atafb.c | 16 +- drivers/video/atyfb.c | 42 +- drivers/video/chipsfb.c | 5 +- drivers/video/clgenfb.c | 4485 +++++++++++++-------- drivers/video/clgenfb.h | 112 +- drivers/video/cyber2000fb.c | 24 +- drivers/video/cyberfb.c | 19 +- drivers/video/dnfb.c | 9 +- drivers/video/fbcon-vga-planes.c | 43 +- drivers/video/fbmem.c | 138 +- drivers/video/fm2fb.c | 16 +- drivers/video/font_6x11.c | 20 +- drivers/video/g364fb.c | 7 +- drivers/video/hpfb.c | 7 +- drivers/video/igafb.c | 25 +- drivers/video/imsttfb.c | 37 +- drivers/video/macfb.c | 12 +- drivers/video/matroxfb.c | 5 +- drivers/video/offb.c | 28 +- drivers/video/platinumfb.c | 18 +- drivers/video/pm2fb.c | 38 +- drivers/video/q40fb.c | 20 +- drivers/video/retz3fb.c | 28 +- drivers/video/sbusfb.c | 14 +- drivers/video/sgivwfb.c | 44 +- drivers/video/skeletonfb.c | 14 +- drivers/video/tgafb.c | 23 +- drivers/video/valkyriefb.c | 26 +- drivers/video/vesafb.c | 36 +- drivers/video/vfb.c | 23 +- drivers/video/vga.h | 368 ++ drivers/video/vga16fb.c | 19 +- drivers/video/virgefb.c | 27 +- fs/Config.in | 16 +- fs/Makefile | 4 +- fs/exec.c | 1 + fs/partitions/Config.in | 48 + fs/partitions/Makefile | 49 + fs/partitions/acorn.c | 454 +++ fs/partitions/acorn.h | 68 + fs/partitions/amiga.c | 119 + fs/partitions/amiga.h | 8 + fs/partitions/atari.c | 163 + fs/partitions/atari.h | 36 + fs/partitions/check.c | 323 ++ fs/partitions/check.h | 10 + fs/partitions/mac.c | 110 + fs/partitions/mac.h | 44 + fs/partitions/msdos.c | 494 +++ fs/partitions/msdos.h | 9 + fs/partitions/osf.c | 86 + fs/partitions/osf.h | 9 + fs/partitions/sgi.c | 86 + fs/partitions/sgi.h | 9 + fs/partitions/sun.c | 89 + fs/partitions/sun.h | 9 + fs/partitions/ultrix.c | 60 + fs/proc/array.c | 11 + include/asm-alpha/core_cia.h | 15 +- include/asm-alpha/core_pyxis.h | 14 +- include/asm-alpha/core_tsunami.h | 20 +- include/asm-alpha/smp.h | 15 +- include/linux/b1lli.h | 125 +- include/linux/b1pcmcia.h | 36 + include/linux/cyclomx.h | 14 +- include/linux/cycx_drv.h | 16 +- include/linux/fd.h | 2 +- include/linux/genhd.h | 14 - include/linux/isdn.h | 91 +- include/linux/isdn_budget.h | 62 - include/linux/isdn_compat.h | 108 + include/linux/isdn_divertif.h | 62 + include/linux/isdn_ppp.h | 10 +- include/linux/isdnif.h | 238 +- include/linux/kernelcapi.h | 50 +- include/linux/lp_intern.h | 22 - include/linux/lp_m68k.h | 135 - include/linux/lp_mfc.h | 14 - include/linux/mc6821.h | 51 + include/linux/parport.h | 3 +- include/linux/sysctl.h | 4 + kernel/ksyms.c | 2 + kernel/printk.c | 70 +- kernel/sysctl.c | 157 +- mm/filemap.c | 49 +- mm/mlock.c | 2 +- net/ipv4/ipconfig.c | 20 +- 292 files changed, 32817 insertions(+), 12313 deletions(-) create mode 100644 Documentation/isdn/INTERFACE.fax create mode 100644 Documentation/isdn/README.diversion create mode 100644 Documentation/isdn/README.fax create mode 100644 Documentation/isdn/README.hfc-pci delete mode 100644 arch/i386/vmlinux.lds delete mode 100644 drivers/char/lp_intern.c delete mode 100644 drivers/char/lp_m68k.c create mode 100644 drivers/isdn/avmb1/avmcard.h create mode 100644 drivers/isdn/avmb1/b1.c create mode 100644 drivers/isdn/avmb1/b1isa.c create mode 100644 drivers/isdn/avmb1/b1pcmcia.c create mode 100644 drivers/isdn/avmb1/capilli.h create mode 100644 drivers/isdn/avmb1/kcapi.c create mode 100644 drivers/isdn/avmb1/t1isa.c create mode 100644 drivers/isdn/divert/Makefile create mode 100644 drivers/isdn/divert/divert_init.c create mode 100644 drivers/isdn/divert/divert_procfs.c create mode 100644 drivers/isdn/divert/isdn_divert.c create mode 100644 drivers/isdn/divert/isdn_divert.h create mode 100644 drivers/isdn/hisax/bkm_a4t.c create mode 100644 drivers/isdn/hisax/bkm_a8.c create mode 100644 drivers/isdn/hisax/bkm_ax.h create mode 100644 drivers/isdn/hisax/gazel.c create mode 100644 drivers/isdn/hisax/hfc_pci.c create mode 100644 drivers/isdn/hisax/hfc_pci.h create mode 100644 drivers/isdn/hisax/hfcscard.c create mode 100644 drivers/isdn/hisax/isurf.c create mode 100644 drivers/isdn/hisax/jade.c create mode 100644 drivers/isdn/hisax/jade.h create mode 100644 drivers/isdn/hisax/jade_irq.c create mode 100644 drivers/isdn/hisax/saphir.c delete mode 100644 drivers/isdn/isdn_budget.c create mode 100644 drivers/isdn/isdn_ttyfax.c create mode 100644 drivers/isdn/isdn_ttyfax.h create mode 100644 drivers/misc/Config.in create mode 100644 drivers/misc/Makefile create mode 100644 drivers/misc/piix4_acpi.c create mode 100644 drivers/scsi/oktagon_esp.c create mode 100644 drivers/scsi/oktagon_esp.h create mode 100644 drivers/scsi/oktagon_io.S create mode 100644 drivers/scsi/sun3x_esp.c create mode 100644 drivers/scsi/sun3x_esp.h create mode 100644 drivers/video/vga.h create mode 100644 fs/partitions/Config.in create mode 100644 fs/partitions/Makefile create mode 100644 fs/partitions/acorn.c create mode 100644 fs/partitions/acorn.h create mode 100644 fs/partitions/amiga.c create mode 100644 fs/partitions/amiga.h create mode 100644 fs/partitions/atari.c create mode 100644 fs/partitions/atari.h create mode 100644 fs/partitions/check.c create mode 100644 fs/partitions/check.h create mode 100644 fs/partitions/mac.c create mode 100644 fs/partitions/mac.h create mode 100644 fs/partitions/msdos.c create mode 100644 fs/partitions/msdos.h create mode 100644 fs/partitions/osf.c create mode 100644 fs/partitions/osf.h create mode 100644 fs/partitions/sgi.c create mode 100644 fs/partitions/sgi.h create mode 100644 fs/partitions/sun.c create mode 100644 fs/partitions/sun.h create mode 100644 fs/partitions/ultrix.c create mode 100644 include/linux/b1pcmcia.h delete mode 100644 include/linux/isdn_budget.h create mode 100644 include/linux/isdn_compat.h create mode 100644 include/linux/isdn_divertif.h delete mode 100644 include/linux/lp_intern.h delete mode 100644 include/linux/lp_m68k.h delete mode 100644 include/linux/lp_mfc.h create mode 100644 include/linux/mc6821.h diff --git a/CREDITS b/CREDITS index e172503b20d9..6802d88ca602 100644 --- a/CREDITS +++ b/CREDITS @@ -176,9 +176,9 @@ S: Poland N: Carlos Henrique Bauer E: chbauer@acm.org -E: bauer@atlas.unisinos.tche.br -D: A test for detection of EPP-1.7 and ECP/EPP parports. +E: bauer@atlas.unisinos.br D: Some new sysctl entries for the parport driver. +D: New sysctl function for handling unsigned longs S: Universidade do Vale do Rio dos Sinos - UNISINOS S: DSI/IDASI S: Av. Unisinos, 950 @@ -987,10 +987,6 @@ S: Na Orechovce 7 S: 160 00 Praha 6 S: Czech Republic -N: Andreas S. Krebs -E: akrebs@altavista.net -D: CYPRESS CY82C693 chipset IDE, Digital's PC-Alpha 164SX boards - N: Niels Kristian Bech Jensen E: nkbj@image.dk W: http://www.image.dk/~nkbj @@ -1001,15 +997,6 @@ S: Dr. Holsts Vej 34, lejl. 164 S: DK-8230 Åbyhøj S: Denmark -N: Andrzej M. Krzysztofowicz -E: ankry@mif.pg.gda.pl -D: XT disk driver -D: Aladdin 1533/1543(C) chipset IDE -D: PIIX chipset IDE -S: ul. Matemblewska 1B/10 -S: 80-283 Gdansk -S: Poland - N: Michael K. Johnson E: johnsonm@redhat.com W: http://www.redhat.com/~johnsonm @@ -1024,6 +1011,29 @@ S: 201 Howell Street, Apartment 1C S: Chapel Hill, North Carolina 27514-4818 S: USA +N: Dave Jones +E: dave@powertweak.com +W: http://linux.powertweak.com +D: Centaur/IDT Winchip/Winchip 2 tweaks +D: Misc clean ups and other random hacking. +S: 40, Heol Edward Lewis, +S: Gelligaer, Hengoed, +S: Mid Glamorgan, CF82 8EJ, +S: Wales, United Kingdom + +N: Andreas S. Krebs +E: akrebs@altavista.net +D: CYPRESS CY82C693 chipset IDE, Digital's PC-Alpha 164SX boards + +N: Andrzej M. Krzysztofowicz +E: ankry@mif.pg.gda.pl +D: XT disk driver +D: Aladdin 1533/1543(C) chipset IDE +D: PIIX chipset IDE +S: ul. Matemblewska 1B/10 +S: 80-283 Gdansk +S: Poland + N: Bernhard Kaindl E: bkaindl@netway.at E: edv@bartelt.via.at @@ -1293,7 +1303,7 @@ E: Kai.Makisara@metla.fi D: SCSI Tape Driver N: Hamish Macdonald -E: hamish_macdonald@westendsys.com +E: hamishm@lucent.com D: Linux/68k port S: 32 Clydesdale Avenue S: Kanata, Ontario diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 35fdb50311bb..6c63d56dd17c 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -2311,7 +2311,7 @@ CONFIG_FBCON_MAC VGA characters/attributes support CONFIG_FBCON_VGA This is the low level frame buffer console driver for VGA text mode; - it is used if you said Y to "VGA chipset support (text only)" above. + it is used by frame buffer device drivers that support VGA text mode. Parallel-port support CONFIG_PARPORT @@ -10262,6 +10262,20 @@ CONFIG_ISDN_X25 connections. See Documentation/isdn/README.x25 for more information if you are thinking about using this. +ISDN diversion services support +CONFIG_ISDN_DIVERSION + This option allows you to use some supplementary diversion + services in conjunction with the HiSax driver on an EURO/DSS1 + line. Supported options are CD (call deflection), CFU (Call + forward unconditional), CFB (Call forward when busy) and CFNR + (call forward not reachable). + Additionally the actual CFU, CFB and CFNR state may be + interrogated. The use of CFU, CFB, CFNR and interrogation may + be limited to some countries. The keypad protocol is still not + implemented. + CD should work in all countries if this service has been sub- + scribed. + ICN 2B and 4B support CONFIG_ISDN_DRV_ICN This enables support for two kinds of ISDN-cards made by a German @@ -10488,6 +10502,30 @@ CONFIG_ISDN_DRV_SC you need to have access to a machine on the Internet that has a program like lynx or netscape). +Eicon.Diehl active card support +CONFIG_ISDN_DRV_EICON + Say Y here if you have an Eicon active ISDN card. In order to use + this card, additional firmware is necessary, which has to be loaded + into the card using the eiconctrl utility which is part of the latest + isdn4k-utils package. Please read the file + Documentation/isdn/README.eicon for more information. + +Eicon old-type card support +CONFIG_ISDN_DRV_EICON_ISA + Say Y here if you have an old-type Eicon active ISDN card. In order to + use this card, additional firmware is necessary, which has to be loaded + into the card using the eiconctrl utility which is part of the latest + isdn4k-utils package. Please read the file + Documentation/isdn/README.eicon for more information. + +Support AT-Fax Class 2 commands +CONFIG_ISDN_TTY_FAX + If you say Y here, the modem-emulator will support a subset of the + Fax Class 2 commands. Using a getty with fax-support + (mgetty+sendfax, hylafax), you will be able to use your Linux box + as an ISDN-fax-machine. This must be supported by the lowlevel driver + also. See Documentation/isdn/README.fax for more information. + AVM-B1 with CAPI2.0 support CONFIG_ISDN_DRV_AVMB1 This enables support for the AVM B1 ISDN networking cards. In @@ -11968,10 +12006,29 @@ CONFIG_USB_ACM mknod /dev/ttyACM2 c 166 2 mknod /dev/ttyACM3 c 166 3 +USB Printer Device Class support (Preliminary) +CONFIG_USB_PRINTER + This is a generic driver for USB printers. + USS720 parport driver CONFIG_USB_USS720 - This driver is for USB parallel port adapters that use the USS-720 - chip. + This driver is for USB parallel port adapters that use the + Lucent Technologies USS-720 chip. + + The chip has two modes: automatic mode and manual mode. + In automatic mode, it looks like a standard USB printer. Only + Printers may be connected to the USS-720 in this mode. + The generic USB printer driver (CONFIG_USB_PRINTER, above) + may be used in that mode. + + Manual mode is not limited to printers, any parallel port + device should work. This driver utilizes manual mode. + Note however that some operations are three orders of a magnitude + slower than on a PCI/ISA Parallel Port, so timing critical + applications might not work. + + Say Y or M if you own an USS-720 USB->Parport cable and + intend to connect anything other than a printer to it. USB /proc filesystem entry support (Preliminary) CONFIG_USB_PROC diff --git a/Documentation/isdn/CREDITS b/Documentation/isdn/CREDITS index 9faad9c601d1..31da77e3b1e4 100644 --- a/Documentation/isdn/CREDITS +++ b/Documentation/isdn/CREDITS @@ -24,7 +24,7 @@ Michael 'Ghandi' Herold (michael@abadonna.franken.de) Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) For his Sync-PPP-code. -Karsten Keil (isdn4@temic-ech.spacenet.de) +Karsten Keil (keil@isdn4linux.de) For adding 1TR6-support to the Teles-driver. For the HiSax-driver. diff --git a/Documentation/isdn/HiSax.cert b/Documentation/isdn/HiSax.cert index b1f8aaa53338..de440068f9ce 100644 --- a/Documentation/isdn/HiSax.cert +++ b/Documentation/isdn/HiSax.cert @@ -14,7 +14,8 @@ First: However, if you wish to modify the HiSax sources, please note the following: -HiSax has passed the ITU approval test suite with ELSA Quickstep ISDN cards. +HiSax has passed the ITU approval test suite with ELSA Quickstep ISDN cards +and Eicon Technology Diva 2.01 PCI card. The certification is only valid for the combination of the tested software version and the tested hardware. Any changes to the HiSax source code may therefore affect the certification. @@ -48,13 +49,14 @@ drivers/isdn/hisax/l3dss1.c drivers/isdn/hisax/l3_1tr6.c drivers/isdn/hisax/cert.c drivers/isdn/hisax/elsa.c +drivers/isdn/hisax/diva.c Please send any changes, bugfixes and patches to me rather than implementing them directly into the HiSax sources. This does not reduce your rights granted by the GNU General Public License. If you wish to change the sources, go ahead; but note that then the -certification is invalid even if you use ELSA Quickstep cards. +certification is invalid even if you use one of the approved cards. Here are the certification registration numbers for ELSA Quickstep cards: German D133361J CETECOM ICT Services GmbH 0682 @@ -68,9 +70,9 @@ keil@isdn4linux.de Version: 2.6.3i Charset: noconv -iQCVAwUBNj5OKDpxHvX/mS9tAQFHuQP/WeImlqCcDZ2d132yAvRBWFULlJoSf1P/ -c1lVTeaWvsSaY5Cu9hrKhXXhPzeEaitUbcUBPXdpzFWCA5CE902lnz7AhgRC+HF1 -0qiKgkZZyc/5HKasFymR1+IWSLw30GesP3Di/ZMR3NJi8SlY9PIjx7hnEMunGSRO -1ufPvfWWuu8= -=nGJk +iQCVAwUBN6xoKTpxHvX/mS9tAQF4DAP/efRWym6jvNOND1O9eaEFdP5fd2xKB3XD +Ifh6Iv0DvARcIuxXtEjT+z3FjjQk35eo/wX4C4tpRhYQYdgCxl+iv+5DzhVDpB95 +3QS9E5m0E1eIK3t8XiQTRgb+1JPCMYUThCrakYsX25o3ndGKyDipsCTfkyR38XwC +bUyTfcOYKAk= +=VKyL -----END PGP SIGNATURE----- diff --git a/Documentation/isdn/INTERFACE b/Documentation/isdn/INTERFACE index 213136b5549f..d8bf08ccc63c 100644 --- a/Documentation/isdn/INTERFACE +++ b/Documentation/isdn/INTERFACE @@ -1,4 +1,4 @@ -$Id: INTERFACE,v 1.11 1999/03/02 12:14:51 armin Exp $ +$Id: INTERFACE,v 1.13 1999/08/11 20:30:26 armin Exp $ Description of the Interface between Linklevel and Hardwarelevel of isdn4linux: @@ -216,7 +216,7 @@ Description of the Interface between Linklevel and Hardwarelevel Until now, the following commands are defined: -***CHANGEI1.34: The parameter "num" has been replaced by a union "para" containing +***CHANGEI1.34: The parameter "num" has been replaced by a union "parm" containing the old "num" and a new setup_type struct used for ISDN_CMD_DIAL and ISDN_STAT_ICALL callback. @@ -235,7 +235,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id. command = ISDN_CMD_IOCTL arg = Original ioctl-cmd - IIOCDRVCTL - para.num = first bytes filled with (unsigned long)arg + parm.num = first bytes filled with (unsigned long)arg Returnvalue: Depending on driver. @@ -251,10 +251,10 @@ Description of the Interface between Linklevel and Hardwarelevel command = ISDN_CMD_DIAL arg = channel-number locally to the driver. (starting with 0) - para.setup.phone = An ASCII-String containing the number to dial. - para.setup.eazmsn = An ASCII-Sting containing the own EAZ or MSN. - para.setup.si1 = The Service-Indicator. - para.setup.si2 = Additional Service-Indicator. + parm.setup.phone = An ASCII-String containing the number to dial. + parm.setup.eazmsn = An ASCII-Sting containing the own EAZ or MSN. + parm.setup.si1 = The Service-Indicator. + parm.setup.si2 = Additional Service-Indicator. If the Line has been designed as SPV (a special german feature, meaning semi-leased-line) the phone has to @@ -272,7 +272,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id. command = ISDN_CMD_ACCEPTD arg = channel-number locally to the driver. (starting with 0) - para = unused. + parm = unused. ISDN_CMD_ACCEPTB: @@ -283,7 +283,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id. command = ISDN_CMD_ACCEPTB arg = channel-number locally to the driver. (starting with 0) - para = unused. + parm = unused. ISDN_CMD_HANGUP: @@ -295,7 +295,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id. command = ISDN_CMD_HANGUP arg = channel-number locally to the driver. (starting with 0) - para = unused. + parm = unused. ISDN_CMD_CLREAZ: @@ -306,7 +306,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id. command = ISDN_CMD_CLREAZ arg = channel-number locally to the driver. (starting with 0) - para = unused. + parm = unused. ISDN_CMD_SETEAZ: @@ -317,7 +317,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id. command = ISDN_CMD_SETEAZ arg = channel-number locally to the driver. (starting with 0) - para.num = ASCII-String, containing the desired EAZ's/MSN's + parm.num = ASCII-String, containing the desired EAZ's/MSN's (comma-separated). If an empty String is given, the HL-driver should respond to ALL incoming calls, regardless of the destination-address. @@ -332,7 +332,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id. command = ISDN_CMD_GETEAZ arg = channel-number locally to the driver. (starting with 0) - para.num = ASCII-String, containing the current EAZ's/MSN's + parm.num = ASCII-String, containing the current EAZ's/MSN's ISDN_CMD_SETSIL: (currently unused) @@ -343,7 +343,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id. command = ISDN_CMD_SETSIL arg = channel-number locally to the driver. (starting with 0) - para.num = ASCII-String, containing the desired Service-Indicators. + parm.num = ASCII-String, containing the desired Service-Indicators. ISDN_CMD_GETSIL: (currently unused) @@ -354,7 +354,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id. command = ISDN_CMD_SETSIL arg = channel-number locally to the driver. (starting with 0) - para.num = ASCII-String, containing the current Service-Indicators. + parm.num = ASCII-String, containing the current Service-Indicators. ISDN_CMD_SETL2: @@ -369,7 +369,7 @@ Description of the Interface between Linklevel and Hardwarelevel arg = channel-number locally to the driver. (starting with 0) logical or'ed with (protocol-Id << 8) protocol-Id is one of the constants ISDN_PROTO_L2... - para = unused. + parm = unused. ISDN_CMD_GETL2: (currently unused) @@ -380,7 +380,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id. command = ISDN_CMD_GETL2 arg = channel-number locally to the driver. (starting with 0) - para = unused. + parm = unused. Returnvalue: current protocol-Id (one of the constants ISDN_L2_PROTO) @@ -397,7 +397,7 @@ Description of the Interface between Linklevel and Hardwarelevel arg = channel-number locally to the driver. (starting with 0) logical or'ed with (protocol-Id << 8) protocol-Id is one of the constants ISDN_PROTO_L3... - para = unused. + parm.fax = Pointer to T30_s fax struct. (fax usage only) ISDN_CMD_GETL2: (currently unused) @@ -408,7 +408,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id. command = ISDN_CMD_GETL3 arg = channel-number locally to the driver. (starting with 0) - para = unused. + parm = unused. Returnvalue: current protocol-Id (one of the constants ISDN_L3_PROTO) @@ -422,7 +422,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id. command = ISDN_CMD_LOCK arg = unused. - para = unused. + parm = unused. ISDN_CMD_UNLOCK: @@ -434,7 +434,19 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id. command = ISDN_CMD_UNLOCK arg = unused. - para = unused. + parm = unused. + + ISDN_CMD_FAXCMD: + + With this command the HL-driver receives a fax sub-command. + For details refer to INTERFACE.fax + + Parameter: + driver = driver-Id. + command = ISDN_CMD_FAXCMD + arg = channel-number locally to the driver. (starting with 0) + parm = unused. + 3. Description of the events to be signaled by the HL-driver to the LL. @@ -456,7 +468,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_STAVAIL arg = length of available data. - para = unused. + parm = unused. ISDN_STAT_ICALL: @@ -466,12 +478,12 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_ICALL arg = channel-number, locally to the driver. (starting with 0) - para.setup.phone = Callernumber. - para.setup.eazmsn = CalledNumber. - para.setup.si1 = Service Indicator. - para.setup.si2 = Additional Service Indicator. - para.setup.plan = octet 3 from Calling party number Information Element. - para.setup.screen = octet 3a from Calling party number Information Element. + parm.setup.phone = Callernumber. + parm.setup.eazmsn = CalledNumber. + parm.setup.si1 = Service Indicator. + parm.setup.si2 = Additional Service Indicator. + parm.setup.plan = octet 3 from Calling party number Information Element. + parm.setup.screen = octet 3a from Calling party number Information Element. Return: 0 = No device matching this call. @@ -497,7 +509,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_RUN arg = unused. - para = unused. + parm = unused. ISDN_STAT_STOP: @@ -508,7 +520,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_STOP arg = unused. - para = unused. + parm = unused. ISDN_STAT_DCONN: @@ -519,7 +531,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_DCONN arg = channel-number, locally to the driver. (starting with 0) - para = unused. + parm = unused. ISDN_STAT_BCONN: @@ -534,7 +546,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_BCONN arg = channel-number, locally to the driver. (starting with 0) - para.num = ASCII-String, containing type of connection (for analog + parm.num = ASCII-String, containing type of connection (for analog modem only). This will be appended to the CONNECT message e.g. 14400/V.32bis @@ -549,7 +561,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_DHUP arg = channel-number, locally to the driver. (starting with 0) - para = unused. + parm = unused. ISDN_STAT_BHUP: @@ -564,7 +576,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_BHUP arg = channel-number, locally to the driver. (starting with 0) - para = unused. + parm = unused. ISDN_STAT_CINF: @@ -575,7 +587,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_CINF arg = channel-number, locally to the driver. (starting with 0) - para.num = ASCII string containing charge-units (digits only). + parm.num = ASCII string containing charge-units (digits only). ISDN_STAT_LOAD: (currently unused) @@ -588,7 +600,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_UNLOAD arg = unused. - para = unused. + parm = unused. ISDN_STAT_BSENT: @@ -600,7 +612,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_BSENT arg = channel-number, locally to the driver. (starting with 0) - para.length = ***CHANGEI.1.21: New field. + parm.length = ***CHANGEI.1.21: New field. the driver has to set this to the original length of the skb at the time of receiving it from the linklevel. @@ -613,21 +625,21 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_NODCH arg = channel-number, locally to the driver. (starting with 0) - para = unused. + parm = unused. - ISDN_STAT_ADDCH: (currently unused) + ISDN_STAT_ADDCH: - This call is planned for HL-drivers, which are unable to check card-type + This call is for HL-drivers, which are unable to check card-type or numbers of supported channels before they have loaded any firmware - using ioctl. Those HL-driver simply set the channel-parameter to zero - or a minimum channel-number when registering, and later if they know + using ioctl. Those HL-driver simply set the channel-parameter to a + minimum channel-number when registering, and later if they know the real amount, perform this call, allocating additional channels. Parameter: driver = driver-Id command = ISDN_STAT_ADDCH - arg = to be defined. - para = to be defined. + arg = number of channels to be added. + parm = unused. ISDN_STAT_CAUSE: @@ -640,7 +652,7 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_NODCH arg = channel-number, locally to the driver. (starting with 0) - para.num = ASCII string containing CAUSE-message. + parm.num = ASCII string containing CAUSE-message. ISDN_STAT_L1ERR: @@ -653,5 +665,33 @@ Description of the Interface between Linklevel and Hardwarelevel driver = driver-Id command = ISDN_STAT_L1ERR arg = channel-number, locally to the driver. (starting with 0) - para.errcode= ISDN_STAT_L1ERR_SEND: Packet lost while sending. + parm.errcode= ISDN_STAT_L1ERR_SEND: Packet lost while sending. ISDN_STAT_L1ERR_RECV: Packet lost while receiving. + ISDN_STAT_DISCH: + + With this call, the HL-driver signals the LL to disable or enable the + use of supplied channel and driver. + The call may be used to reduce the available number of B-channels after + loading the driver. The LL has to ignore a disabled channel when searching + for free channels. The HL driver itself never delivers STAT callbacks for + disabled channels. + The LL returns a nonzero code if the operation was not successfull or the + selected channel is actually regarded as busy. + + Parameter: + driver = driver-Id + command = ISDN_STAT_DISCH + arg = channel-number, locally to the driver. (starting with 0) + parm.num[0] = 0 if channel shall be disabled, else enabled. + + ISDN_STAT_FAXIND: + + With this call the HL-driver signals a fax sub-command to the LL. + For details refer to INTERFACE.fax + + Parameter: + driver = driver-Id. + command = ISDN_STAT_FAXIND + arg = channel-number, locally to the driver. (starting with 0) + parm = unused. + diff --git a/Documentation/isdn/INTERFACE.fax b/Documentation/isdn/INTERFACE.fax new file mode 100644 index 000000000000..2c9659f0df01 --- /dev/null +++ b/Documentation/isdn/INTERFACE.fax @@ -0,0 +1,163 @@ +$Id: INTERFACE.fax,v 1.1 1999/08/11 20:30:28 armin Exp $ + + +Description of the fax-subinterface between linklevel and hardwarelevel of + isdn4linux. + + The communication between linklevel (LL) and harwarelevel (HL) for fax + is based on the struct T30_s (defined in isdnif.h). + This struct is allocated in the LL. + In order to use fax, the LL provides the pointer to this struct with the + command ISDN_CMD_SETL3 (parm.fax). This pointer expires in case of hangup + and when a new channel to a new connection is assigned. + + +Data handling: + In send-mode the HL-driver has to handle the codes and the bit-order + conversion by itself. + In receive-mode the LL-driver takes care of the bit-order conversion + (specified by +FBOR) + +Structure T30_s description: + + This structure stores the values (set by AT-commands), the remote- + capability-values and the command-codes between LL and HL. + + If the HL-driver receives ISDN_CMD_FAXCMD, all needed information + is in this struct set by the LL. + To signal information to the LL, the HL-driver has to set the + the parameters and use ISDN_STAT_FAXIND. + (Please refer to INTERFACE) + +Structure T30_s: + + All members are 8-bit unsigned (__u8) + + - resolution + - rate + - width + - length + - compression + - ecm + - binary + - scantime + - id[] + Local faxmachine's parameters, set by +FDIS, +FDCS, +FLID, ... + + - r_resolution + - r_rate + - r_width + - r_length + - r_compression + - r_ecm + - r_binary + - r_scantime + - r_id[] + Remote faxmachine's parameters. To be set by HL-driver. + + - phase + Defines the actual state of fax connection. Set by HL or LL + depending on progress and type of connection. + If the phase changes because of an AT command, the LL driver + changes this value. Otherwise the HL-driver takes care of it, but + only neccessary on call establishment (from IDLE to PHASE_A). + (one of the constants ISDN_FAX_PHASE_[IDLE,A,B,C,D,E]) + + - direction + Defines outgoing/send or incoming/receive connection. + (ISDN_TTY_FAX_CONN_[IN,OUT]) + + - code + Commands from LL to HL; possible constants : + ISDN_TTY_FAX_DR signals +FDR command to HL + + ISDN_TTY_FAX_DT signals +FDT command to HL + + ISDN_TTY_FAX_ET signals +FET command to HL + + + Other than that the "code" is set with the hangup-code value at + the end of connection for the +FHNG message. + + - r_code + Commands from HL to LL; possible constants : + ISDN_TTY_FAX_CFR output of +FCFR message. + + ISDN_TTY_FAX_RID output of remote ID set in r_id[] + (+FCSI/+FTSI on send/receive) + + ISDN_TTY_FAX_DCS output of +FDCS and CONNECT message, + switching to phase C. + + ISDN_TTY_FAX_ET signals end of data, + switching to phase D. + + ISDN_TTY_FAX_FCON signals the established, outgoing connection, + switching to phase B. + + ISDN_TTY_FAX_FCON_I signals the established, incoming connection, + switching to phase B. + + ISDN_TTY_FAX_DIS output of +FDIS message and values. + + ISDN_TTY_FAX_SENT signals that all data has been sent + and is acknowledged, + OK message will be sent. + + ISDN_TTY_FAX_PTS signals a msg-confirmation (page sent successful), + depending on fet value: + 0: output OK message (more pages follow) + 1: switching to phase B (next document) + + ISDN_TTY_FAX_TRAIN_OK output of +FDCS and OK message (for receive mode). + + ISDN_TTY_FAX_EOP signals end of data in receive mode, + switching to phase D. + + ISDN_TTY_FAX_HNG output of the +FHNG and value set by code and + OK message, switching to phase E. + + + - badlin + Value of +FBADLIN + + - badmul + Value of +FBADMUL + + - bor + Value of +FBOR + + - fet + Value of +FET command in send-mode. + Set by HL in receive-mode for +FET message. + + - pollid[] + ID-string, set by +FCIG + + - cq + Value of +FCQ + + - cr + Value of +FCR + + - ctcrty + Value of +FCTCRTY + + - minsp + Value of +FMINSP + + - phcto + Value of +FPHCTO + + - rel + Value of +FREL + + - nbc + Value of +FNBC (0,1) + (+FNBC is not a known class 2 fax command, I added this to change the + automatic "best capabilities" connection in the eicon HL-driver) + + +Armin +mac@melware.de + diff --git a/Documentation/isdn/README b/Documentation/isdn/README index 7b17183e069c..dcd14151eb9b 100644 --- a/Documentation/isdn/README +++ b/Documentation/isdn/README @@ -149,6 +149,8 @@ README for the ISDN-subsystem AT&X0 BTX-mode and T.70-mode off (default) AT&X1 BTX-mode on. (S13.1=1, S13.5=0 S14=0, S16=7, S18=7, S19=0) AT&X2 T.70-mode on. (S13.1=1, S13.5=1, S14=0, S16=7, S18=7, S19=0) + AT+Rx Resume a suspended call with CallID x (x = 1,2,3...) + AT+Sx Suspend a call with CallID x (x = 1,2,3...) For voice-mode commands refer to README.audio @@ -215,6 +217,9 @@ README for the ISDN-subsystem an incoming call happened (RING) and the remote party hung up before any local ATA was given. + Bit 7: 0 = Don't show display messages from net + 1 = Show display messages from net + (S12 Bit 1 must be 0 too) 14 0 Layer-2 protocol: 0 = X75/LAPB with I-frames 1 = X75/LAPB with UI-frames @@ -225,8 +230,11 @@ README for the ISDN-subsystem 8 = V.110, 19200 baud 9 = V.110, 38400 baud 10 = Analog Modem (only if hardware supports this) - 15 0 Layer-3 protocol: (at the moment always 0) + 11 = Fax G3 (only if hardware supports this) + 15 0 Layer-3 protocol: 0 = transparent + 1 = transparent with audio features (e.g. DSP) + 2 = Fax G3 (S14 has to be set to 11) 16 250 Send-Packet-size/16 17 8 Window-size (not yet implemented) 18 4 Bit coded register, Service-Octet-1 to accept, @@ -255,6 +263,9 @@ README for the ISDN-subsystem Set on incoming call (during RING) to octet 3a of calling party number IE (Screening info) See section 4.5.10 of ITU Q.931 + 23 0 Bit coded register: + Bit 0: 0 = Add CPN to RING message off + 1 = Add CPN to RING message on Last but not least a (at the moment fairly primitive) device to request the line-status (/dev/isdninfo) is made available. diff --git a/Documentation/isdn/README.HiSax b/Documentation/isdn/README.HiSax index 9f48e71e6ede..a9f9851beb08 100644 --- a/Documentation/isdn/README.HiSax +++ b/Documentation/isdn/README.HiSax @@ -42,6 +42,7 @@ 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 2.01 ISA and PCI 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) @@ -54,6 +55,13 @@ 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 +Siemens I-Surf +ACER P10 +HST Saphir +Berkom Telekom A4T +Scitel Quadro +Gazel ISDN cards +HFC-PCI based cards Note: PCF, PCF-Pro: up to now, only the ISDN part is supported PCC-8: not tested yet @@ -165,6 +173,14 @@ Card types: 27 AVM PnP (Fritz!PnP) irq, io (from isapnp setup) 27 AVM PCI (Fritz!PCI) no parameter 28 Sedlbauer Speed Fax+ irq, io (from isapnp setup) + 29 Siemens I-Surf irq, io, memory (from isapnp setup) + 30 ACER P10 irq, io (from isapnp setup) + 31 HST Saphir irq, io + 32 Telekom A4T none + 33 Scitel Quadro subcontroller (4*S0, subctrl 1...4) + 34 Gazel ISDN cards (ISA) irq,io + 34 Gazel ISDN cards (PCI) none + 35 HFC 2BDS0 PCI none At the moment IRQ sharing is only possible with PCI cards. Please make sure @@ -255,12 +271,19 @@ type 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) - 26 AVM A1 PCMCIA (Fritz!) irq, io (set with card manager) + 25 Teles S0Box pa=irq, pb=io (of the used lpt port) + 26 AVM A1 PCMCIA (Fritz!) pa=irq, pb=io (set with card manager) 27 AVM PnP (Fritz!PnP) ONLY WORKS AS A MODULE ! 27 AVM PCI (Fritz!PCI) no parameter 28 Sedlbauer Speed Fax+ ONLY WORKS AS A MODULE ! - + 29 Siemens I-Surf ONLY WORKS AS A MODULE ! + 30 ACER P10 ONLY WORKS AS A MODULE ! + 31 HST Saphir pa=irq, pb=io + 32 Telekom A4T no parameter + 33 Scitel Quadro subcontroller (4*S0, subctrl 1...4) + 34 Gazel ISDN cards (ISA) pa=irq, pb=io + 34 Gazel ISDN cards (PCI) no parameter + 35 HFC 2BDS0 PCI no parameter Running the driver ------------------ @@ -515,8 +538,7 @@ to e.g. the Internet: /sbin/hisaxctrl HiSax 5 1 Remarks: -a) If you have a CISCO don't forget to switch off the KEEP ALIVE option! -b) Use state of the art isdn4k-utils +a) Use state of the art isdn4k-utils Here an example script: #!/bin/sh @@ -568,7 +590,7 @@ case "$1" in /sbin/isdnctrl encap isdn0s cisco-h fi fi - /sbin/isdnctrl dialmode isdn0 auto + /sbin/isdnctrl dialmode isdn0 manual # configure tcp/ip /sbin/ifconfig isdn0 ${LOCAL_IP} pointopoint ${REMOTE_IP} /sbin/route add -host ${REMOTE_IP} isdn0 @@ -578,6 +600,7 @@ case "$1" in /sbin/hisaxctrl HiSax 5 1 if [ ${I4L_LEASED_128K} = "yes" ]; then # B-CHANNEL 2 + sleep 10; /* Wait for master */ /sbin/hisaxctrl HiSax 5 2 fi ;; @@ -599,4 +622,3 @@ case "$1" in exit 1 esac exit 0 - diff --git a/Documentation/isdn/README.audio b/Documentation/isdn/README.audio index 3708064017ab..8ebca19290d9 100644 --- a/Documentation/isdn/README.audio +++ b/Documentation/isdn/README.audio @@ -1,4 +1,4 @@ -$Id: README.audio,v 1.7 1998/07/26 18:45:34 armin Exp $ +$Id: README.audio,v 1.8 1999/07/11 17:17:29 armin Exp $ ISDN subsystem for Linux. Description of audio mode. @@ -58,6 +58,17 @@ The following commands are supported: AT+VSD=? Report possible parameters. AT+VSD? Show current parameters. + AT+VDD=x,y Set DTMF-detection parameters. + Only possible if online and during this connection. + Possible parameters: + x = 0 ... 15 sensitivity threshold level. + (default 0 , I4L soft-decode) + (1-15 soft-decode off, hardware on) + y = 0 ... 255 tone duration in units of 5ms. + Not for I4L soft decode (default 8, 40ms) + AT+VDD=? Report possible parameters. + AT+VDD? Show current parameters. + AT+VSM=x Select audio data format. Possible parameters: 2 = ADPCM-2 diff --git a/Documentation/isdn/README.avmb1 b/Documentation/isdn/README.avmb1 index 342532786841..34eee2125b2f 100644 --- a/Documentation/isdn/README.avmb1 +++ b/Documentation/isdn/README.avmb1 @@ -1,10 +1,22 @@ +Driver for active AVM Controller. + The driver provides a kernel capi2.0 Interface (kernelcapi) and on top of this a User-Level-CAPI2.0-interface (capi) and a driver to connect isdn4linux with CAPI2.0 (capidrv). +The lowlevel interface can be used to implement a CAPI2.0 +also for passive cards since July 1999. + +The author can be reached at calle@calle.in-berlin.de. +The command avmcapictrl is part of the isdn4k-utils. +t4-files can be found at ftp://ftp.avm.de/cardware/b1/linux/firmware -The Author can be reached at calle@calle.in-berlin.de -The command avmcapictrl is part of the isdn4linux-utils. -t4-files can be found at ftp.avm.de. +Currently supported cards: + B1 ISA (all versions) + B1 PCI + T1/T1B (HEMA card) + M1 + M2 + B1 PCMCIA Installing ---------- @@ -26,32 +38,145 @@ To use the card you need the t4-files to download the firmware. AVM GmbH provides several t4-files for the different D-channel protocols (b1.t4 for Euro-ISDN). Install these file in /lib/isdn. -If you do not compile the driver as modules, you have to add the -card(s) and load them after booting: - -avmcapictrl add 0x150 15 -avmcapictrl load /lib/isdn/b1.t4 1 - -if you configure as modules you have two possibilities: +if you configure as modules load the modules this way: insmod /lib/modules/current/misc/capiutil.o -insmod /lib/modules/current/misc/kernelcapi.o portbase=0x150 irq=15 +insmod /lib/modules/current/misc/b1.o +insmod /lib/modules/current/misc/kernelcapi.o insmod /lib/modules/current/misc/capidrv.o insmod /lib/modules/current/misc/capi.o -avmcapictrl load /lib/isdn/b1.t4 -or +if you have an B1-PCI card load the module b1pci.o +insmod /lib/modules/current/misc/b1pci.o +and load the firmware with +avmcapictrl load /lib/isdn/b1.t4 1 -insmod /lib/modules/current/misc/capiutil.o -insmod /lib/modules/current/misc/kernelcapi.o -insmod /lib/modules/current/misc/capidrv.o -insmod /lib/modules/current/misc/capi.o +if you have an B1-ISA card load the module b1isa.o +and add the card by calling avmcapictrl add 0x150 15 -avmcapictrl load /lib/isdn/b1.t4 +and load the firmware by calling +avmcapictrl load /lib/isdn/b1.t4 1 + +if you have an T1-ISA card load the module t1isa.o +and add the card by calling +avmcapictrl add 0x450 15 T1 0 +and load the firmware by calling +avmcapictrl load /lib/isdn/t1.t4 1 + +if you have an PCMCIA card (B1/M1/M2) load the module b1pcmcia.o +before you insert the card. + +Leased Lines with B1 +-------------------- +Init card and load firmware. +For an D64S use "FV: 1" as phone number +For an D64S2 use "FV: 1" and "FV: 2" for multilink +or "FV: 1,2" to use CAPI channel bundling. + +/proc-Interface +----------------- + +/proc/capi: + dr-xr-xr-x 2 root root 0 Jul 1 14:03 . + dr-xr-xr-x 82 root root 0 Jun 30 19:08 .. + -r--r--r-- 1 root root 0 Jul 1 14:03 applications + -r--r--r-- 1 root root 0 Jul 1 14:03 applstats + -r--r--r-- 1 root root 0 Jul 1 14:03 capi20 + -r--r--r-- 1 root root 0 Jul 1 14:03 capidrv + -r--r--r-- 1 root root 0 Jul 1 14:03 controller + -r--r--r-- 1 root root 0 Jul 1 14:03 contrstats + -r--r--r-- 1 root root 0 Jul 1 14:03 driver + -r--r--r-- 1 root root 0 Jul 1 14:03 ncci + -r--r--r-- 1 root root 0 Jul 1 14:03 users + +/proc/capi/applications: + applid level3cnt datablkcnt datablklen ncci-cnt recvqueuelen + level3cnt: capi_register parameter + datablkcnt: capi_register parameter + ncci-cnt: current number of nccis (connections) + recvqueuelen: number of messages on receive queue + for example: +1 -2 16 2048 1 0 +2 2 7 2048 1 0 + +/proc/capi/applstats: + applid recvctlmsg nrecvdatamsg nsentctlmsg nsentdatamsg + recvctlmsg: capi messages received without DATA_B3_IND + recvdatamsg: capi DATA_B3_IND received + sentctlmsg: capi messages sent without DATA_B3_REQ + sentdatamsg: capi DATA_B3_REQ sent + for example: +1 2057 1699 1721 1699 + +/proc/capi/capi20: statistics of capi.o (/dev/capi20) + minor nopen nrecvdropmsg nrecvctlmsg nrecvdatamsg sentctlmsg sentdatamsg + minor: minor device number of capi device + nopen: number of calls to devices open + nrecvdropmsg: capi messages dropped (messages in recvqueue in close) + nrecvctlmsg: capi messages received without DATA_B3_IND + nrecvdatamsg: capi DATA_B3_IND received + nsentctlmsg: capi messages sent without DATA_B3_REQ + nsentdatamsg: capi DATA_B3_REQ sent + + for example: +1 2 18 0 16 2 + +/proc/capi/capidrv: statistics of capidrv.o (capi messages) + nrecvctlmsg nrecvdatamsg sentctlmsg sentdatamsg + nrecvctlmsg: capi messages received without DATA_B3_IND + nrecvdatamsg: capi DATA_B3_IND received + nsentctlmsg: capi messages sent without DATA_B3_REQ + nsentdatamsg: capi DATA_B3_REQ sent + for example: +2780 2226 2256 2226 + +/proc/capi/controller: + controller drivername state cardname controllerinfo + for example: +1 b1pci running b1pci-e000 B1 3.07-01 0xe000 19 +2 t1isa running t1isa-450 B1 3.07-01 0x450 11 0 +3 b1pcmcia running m2-150 B1 3.07-01 0x150 5 + +/proc/capi/contrstats: + controller nrecvctlmsg nrecvdatamsg sentctlmsg sentdatamsg + nrecvctlmsg: capi messages received without DATA_B3_IND + nrecvdatamsg: capi DATA_B3_IND received + nsentctlmsg: capi messages sent without DATA_B3_REQ + nsentdatamsg: capi DATA_B3_REQ sent + for example: +1 2845 2272 2310 2274 +2 2 0 2 0 +3 2 0 2 0 + +/proc/capi/driver: + drivername ncontroller + for example: +b1pci 1 +t1isa 1 +b1pcmcia 1 +b1isa 0 + +/proc/capi/ncci: + apllid ncci winsize sendwindow + for example: +1 0x10101 8 0 + +/proc/capi/users: kernelmodules that use the kernelcapi. + name + for example: +capidrv +capi20 Questions --------- -Check out the FAQ (ftp.franken.de). +Check out the FAQ (ftp.franken.de) or subscribe to the +linux-avmb1@calle.in-berlin.de mailing list by sending +a mail to majordomo@calle.in-berlin.de with +subscribe linux-avmb1 +in the body. + +German documentaion and several scripts can be found at +ftp://ftp.avm.de/cardware/b1/linux/ Bugs ---- diff --git a/Documentation/isdn/README.diversion b/Documentation/isdn/README.diversion new file mode 100644 index 000000000000..8e1d7a01b161 --- /dev/null +++ b/Documentation/isdn/README.diversion @@ -0,0 +1,127 @@ +The isdn diversion services are a supporting module working together with +the isdn4linux and the HiSax module for passive cards. +Active cards, TAs and cards using a own or other driver than the HiSax +module need to be adapted to the HL<->LL interface described in a separate +document. The diversion services may be used with all cards supported by +the HiSax driver. +The diversion kernel interface and controlling tool divertctrl were written +by Werner Cornelius (werner@isdn4linux.de or werner@titro.de) under the +GNU Public License. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Table of contents +================= + +1. Features of the i4l diversion services + (Or what can the i4l diversion services do for me) + +2. Required hard- and software + +3. Compiling, installing and loading/unloading the module + Tracing calling and diversion information + +4. Tracing calling and diversion information + +5. Format of the divert device ASCII output + + +1. Features of the i4l diversion services + (Or what can the i4l diversion services do for me) + + The i4l diversion services offers call forwarding and logging normally + only supported by isdn phones. Incoming calls may be diverted + unconditionally (CFU), when not reachable (CFNR) or on busy condition + (CFB). + The diversions may be invoked statically in the providers exchange + as normally done by isdn phones. In this case all incoming calls + with a special (or all) service identifiers are forwarded if the + forwarding reason is met. Activated static services may also be + interrogated (queried). + The i4l diversion services additionally offers a dynamic version of + call forwarding which is not preprogrammed inside the providers exchange + but dynamically activated by i4l. + In this case all incoming calls are checked by rules that may be + compared to the mechanism of ipfwadm or ipchains. If a given rule matches + the checking process is finished and the rule matching will be applied + to the call. + The rules include primary and secondary service indentifiers, called + number and subaddress, callers number and subaddress and whether the rule + matches to all filtered calls or only those when all B-channel resources + are exhausted. + Actions that may be invoked by a rule are ignore, proceed, reject, + direct divert or delayed divert of a call. + All incoming calls matching a rule except the ignore rule a reported and + logged as ASCII via the proc filesystem (/proc/net/isdn/divert). If proceed + is selected the call will be held in a proceeding state (without ringing) + for a certain amount of time to let an external program or client decide + how to handle the call. + + +2. Required hard- and software + + For using the i4l diversion services the isdn line must be of a EURO/DSS1 + type. Additionally the i4l services only work together with the HiSax + driver for passive isdn cards. All HiSax supported cards may be used for + the diversion purposes. + The static diversion services require the provider having static services + CFU, CFNR, CFB activated on an MSN-line. The static services may not be + used on a point-to-point connection. Further the static services are only + available in some countries (for example germany). Countries requiring the + keypad protocol for activating static diversions (like the netherlands) are + not supported but may use the tty devices for this purpose. + The dynamic diversion servives may be used in all countries if the provider + enables the feature CF (call forwarding). This should work on both MSN- and + point-to-point lines. + To add and delete rules the additional divertctrl program is needed. This + program is part of the isdn4kutils package. + +3. Compiling, installing and loading/unloading the module + Tracing calling and diversion information + + + To compile the i4l code with diversion support you need to say yes to the + DSS1 diversion services when selecting the i4l options in the kernel + config (menuconfig or config). + After having properly activated a make modules and make modules_install all + required modules will be correctly installed in the needed modules dirs. + As the diversion services are currently not included in the scripts of most + standard distributions you will have to add a "insmod dss1_divert" after + having loaded the global isdn module. + The module can be loaded without any command line parameters. + If the module is actually loaded and active may be checked with a + "cat /proc/modules" or "ls /proc/net/isdn/divert". The divert file is + dynamically created by the diversion module and removed when the module is + unloaded. + + +4. Tracing calling and diversion information + + You also may put a "cat /proc/net/isdn/divert" in the background with the + output redirected to a file. Then all actions of the module are logged. + The divert file in the proc system may be opened more than once, so in + conjunction with inetd and a small remote client on other machines inside + your network incoming calls and reactions by the module may be shown on + every listening machine. + If a call is reported as proceeding an external program or client may + specify during a certain amount of time (normally 4 to 10 seconds) what + to do with that call. + To unload the module all open files to the device in the proc system must + be closed. Otherwise the module (and isdn.o) may not be unloaded. + +5. Format of the divert device ASCII output + + To be done later + diff --git a/Documentation/isdn/README.eicon b/Documentation/isdn/README.eicon index 22669282d538..d55cc14ad005 100644 --- a/Documentation/isdn/README.eicon +++ b/Documentation/isdn/README.eicon @@ -1,4 +1,4 @@ -$Id: README.eicon,v 1.3 1999/03/29 11:10:04 armin Exp $ +$Id: README.eicon,v 1.4 1999/07/11 17:17:30 armin Exp $ (c) 1999 Cytronics & Melware @@ -21,12 +21,8 @@ It is meant to be used with isdn4linux, an ISDN link-level module for Linux. along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -NOTE : Since the eicon driver is still experimental, this README file - may be incomplete and not up to date. -However, the driver should work under following conditions : - Supported Cards --------------- diff --git a/Documentation/isdn/README.fax b/Documentation/isdn/README.fax new file mode 100644 index 000000000000..897abd0ef538 --- /dev/null +++ b/Documentation/isdn/README.fax @@ -0,0 +1,38 @@ + +Fax with isdn4linux +=================== + +When enabled during kernel configuration, the tty emulator +of the ISDN subsystem is capable of the Fax Class 2 commands. + +This only makes sense under the following conditions : + +- You need the commands as dummy, because you are using + hylafax (with patch) for AVM capi. +- You want to use the fax capabillities of your isdn-card. + (supported cards are listed below) + + +NOTE: This implementation does *not* support fax with passive + ISDN-cards (known as softfax). The low-level driver of + the ISDN-card and/or the card itself must support this. + + +Supported ISDN-Cards +-------------------- + +Eicon DIVA Server BRI/PCI (will be ready soon) +Eicon DIVA Server PRI/PCI (will be ready soon) + + + +The command set is known as Class 2 (not Class 2.0) and +can be activated by AT+FCLASS=2 + + +The interface between the link-level-module and the hardware-level driver +is described in the files INTERFACE.fax and INTERFACE. + +Armin +mac@melware.de + diff --git a/Documentation/isdn/README.hfc-pci b/Documentation/isdn/README.hfc-pci new file mode 100644 index 000000000000..94e0d4352543 --- /dev/null +++ b/Documentation/isdn/README.hfc-pci @@ -0,0 +1,26 @@ +The driver for the HFC-PCI and HFC-PCI-A chips from CCD may be used +for many OEM cards using this chips. +Additionally the driver has a special feature which makes it possible +to read the echo-channel of the isdn bus. So all frames in both directions +may be logged. +When the echo logging feature is used the number of available B-channels +for a HFC-PCI card is reduced to 1. Of course this is only relevant to +the card, not to the isdn line. +To activate the echo mode the following ioctls must be entered: + +hisaxctrl 10 1 + +This reduces the available channels to 1. There must not be open connections +through this card when entering the command. +And then: + +hisaxctrl 12 1 + +This enables the echo mode. If Hex logging is activated the isdnctrlx +devices show a output with a line beginning of HEX: for the providers +exchange and ECHO: for isdn devices sending to the provider. + +Comments and reports to werner@isdn4linux.de or werner@titro.de . + + + diff --git a/Documentation/parport.txt b/Documentation/parport.txt index 854290f15449..152e3f3cfea7 100644 --- a/Documentation/parport.txt +++ b/Documentation/parport.txt @@ -139,8 +139,7 @@ spintime The number of microseconds to busy-loop while waiting peripherals. This is a port-wide setting, i.e. it applies to all devices on a particular port. -timeslice The number of jiffies (FIXME: this should be in - milliseconds or something) that a device driver is +timeslice The number of miliseconds that a device driver is allowed to keep a port claimed for. This is advisory, and driver can ignore it if it must. diff --git a/Makefile b/Makefile index 6cca8f5e0b91..fec7141b375c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 3 -SUBLEVEL = 13 +SUBLEVEL = 14 EXTRAVERSION = ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) @@ -111,6 +111,7 @@ FILESYSTEMS =fs/filesystems.a NETWORKS =net/network.a DRIVERS =drivers/block/block.a \ drivers/char/char.o \ + drivers/misc/misc.o \ drivers/parport/parport.a LIBS =$(TOPDIR)/lib/lib.a SUBDIRS =kernel drivers mm fs net ipc lib @@ -134,7 +135,7 @@ DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a endif ifeq ($(CONFIG_SOUND),y) -DRIVERS := $(DRIVERS) drivers/sound/sound.a +DRIVERS := $(DRIVERS) drivers/sound/sounddrivers.o endif ifdef CONFIG_PCI @@ -178,7 +179,7 @@ DRIVERS := $(DRIVERS) drivers/block/paride/paride.a endif ifdef CONFIG_HAMRADIO -DRIVERS := $(DRIVERS) drivers/net/hamradio/hamradio.a +DRIVERS := $(DRIVERS) drivers/net/hamradio/hamradio.o endif ifeq ($(CONFIG_TC),y) @@ -333,6 +334,7 @@ modules_install: if [ -f VIDEO_MODULES ]; then inst_mod VIDEO_MODULES video; fi; \ if [ -f FC4_MODULES ]; then inst_mod FC4_MODULES fc4; fi; \ if [ -f IRDA_MODULES ]; then inst_mod IRDA_MODULES net; fi; \ + if [ -f USB_MODULES ]; then inst_mod USB_MODULES usb; fi; \ \ ls *.o > $$MODLIB/.allmods; \ echo $$MODULES | tr ' ' '\n' | sort | comm -23 $$MODLIB/.allmods - > $$MODLIB/.misc; \ diff --git a/arch/alpha/kernel/bios32.c b/arch/alpha/kernel/bios32.c index 8fb4ab1a9f3a..42adc7ef3ead 100644 --- a/arch/alpha/kernel/bios32.c +++ b/arch/alpha/kernel/bios32.c @@ -522,7 +522,6 @@ layout_dev(struct pci_dev *dev) Cypress. */ if (dev->vendor == PCI_VENDOR_ID_CONTAQ && dev->device == PCI_DEVICE_ID_CONTAQ_82C693 - && dev->class >> 8 == PCI_CLASS_BRIDGE_ISA && idx < 2) { continue; } diff --git a/arch/alpha/kernel/core_apecs.c b/arch/alpha/kernel/core_apecs.c index fa23a4cf6ae3..b30cde2ae818 100644 --- a/arch/alpha/kernel/core_apecs.c +++ b/arch/alpha/kernel/core_apecs.c @@ -17,6 +17,7 @@ #include #include +#include #define __EXTERN_INLINE inline #include @@ -35,19 +36,16 @@ * BIOS32-style PCI interface: */ -#ifdef DEBUG -# define DBG(args) printk args +#define DEBUG_CONFIG 0 + +#if DEBUG_CONFIG +# define DBGC(args) printk args #else -# define DBG(args) +# define DBGC(args) #endif #define vuip volatile unsigned int * -volatile unsigned int apecs_mcheck_expected = 0; -volatile unsigned int apecs_mcheck_taken = 0; -static unsigned int apecs_jd, apecs_jd1, apecs_jd2; - - /* * Given a bus, device, and function number, compute resulting * configuration space address and setup the APECS_HAXR2 register @@ -96,9 +94,9 @@ mk_conf_addr(u8 bus, u8 device_fn, u8 where, unsigned long *pci_addr, { unsigned long addr; - DBG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x," - " pci_addr=0x%p, type1=0x%p)\n", - bus, device_fn, where, pci_addr, type1)); + DBGC(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x," + " pci_addr=0x%p, type1=0x%p)\n", + bus, device_fn, where, pci_addr, type1)); if (bus == 0) { int device = device_fn >> 3; @@ -106,8 +104,8 @@ mk_conf_addr(u8 bus, u8 device_fn, u8 where, unsigned long *pci_addr, /* type 0 configuration cycle: */ if (device > 20) { - DBG(("mk_conf_addr: device (%d) > 20, returning -1\n", - device)); + DBGC(("mk_conf_addr: device (%d) > 20, returning -1\n", + device)); return -1; } @@ -119,7 +117,7 @@ mk_conf_addr(u8 bus, u8 device_fn, u8 where, unsigned long *pci_addr, addr = (bus << 16) | (device_fn << 8) | (where); } *pci_addr = addr; - DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + DBGC(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); return 0; } @@ -132,25 +130,25 @@ conf_read(unsigned long addr, unsigned char type1) __save_and_cli(flags); /* avoid getting hit by machine check */ - DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); + DBGC(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); /* Reset status register to avoid losing errors. */ stat0 = *(vuip)APECS_IOC_DCSR; *(vuip)APECS_IOC_DCSR = stat0; mb(); - DBG(("conf_read: APECS DCSR was 0x%x\n", stat0)); + DBGC(("conf_read: APECS DCSR was 0x%x\n", stat0)); /* If Type1 access, must set HAE #2. */ if (type1) { haxr2 = *(vuip)APECS_IOC_HAXR2; mb(); *(vuip)APECS_IOC_HAXR2 = haxr2 | 1; - DBG(("conf_read: TYPE1 access\n")); + DBGC(("conf_read: TYPE1 access\n")); } draina(); - apecs_mcheck_expected = 1; - apecs_mcheck_taken = 0; + mcheck_expected(0) = 1; + mcheck_taken(0) = 0; mb(); /* Access configuration space. */ @@ -159,12 +157,12 @@ conf_read(unsigned long addr, unsigned char type1) asm volatile("ldl %0,%1; mb; mb" : "=r"(value) : "m"(*(vuip)addr) : "$9", "$10", "$11", "$12", "$13", "$14", "memory"); - if (apecs_mcheck_taken) { - apecs_mcheck_taken = 0; + if (mcheck_taken(0)) { + mcheck_taken(0) = 0; value = 0xffffffffU; mb(); } - apecs_mcheck_expected = 0; + mcheck_expected(0) = 0; mb(); #if 1 @@ -178,7 +176,7 @@ conf_read(unsigned long addr, unsigned char type1) /* Now look for any errors. */ stat0 = *(vuip)APECS_IOC_DCSR; - DBG(("conf_read: APECS DCSR after read 0x%x\n", stat0)); + DBGC(("conf_read: APECS DCSR after read 0x%x\n", stat0)); /* Is any error bit set? */ if (stat0 & 0xffe0U) { @@ -228,14 +226,14 @@ conf_write(unsigned long addr, unsigned int value, unsigned char type1) } draina(); - apecs_mcheck_expected = 1; + mcheck_expected(0) = 1; mb(); /* Access configuration space. */ *(vuip)addr = value; mb(); mb(); /* magic */ - apecs_mcheck_expected = 0; + mcheck_expected(0) = 0; mb(); #if 1 @@ -438,20 +436,21 @@ apecs_init_arch(unsigned long *mem_start, unsigned long *mem_end) *(vuip)APECS_IOC_HAXR2 = 0; mb(); } -int +void apecs_pci_clr_err(void) { - apecs_jd = *(vuip)APECS_IOC_DCSR; - if (apecs_jd & 0xffe0L) { - apecs_jd1 = *(vuip)APECS_IOC_SEAR; - *(vuip)APECS_IOC_DCSR = apecs_jd | 0xffe1L; - apecs_jd = *(vuip)APECS_IOC_DCSR; + unsigned int jd; + + jd = *(vuip)APECS_IOC_DCSR; + if (jd & 0xffe0L) { + *(vuip)APECS_IOC_SEAR; + *(vuip)APECS_IOC_DCSR = jd | 0xffe1L; mb(); + *(vuip)APECS_IOC_DCSR; } *(vuip)APECS_IOC_TBIA = (unsigned int)APECS_IOC_TBIA; - apecs_jd2 = *(vuip)APECS_IOC_TBIA; mb(); - return 0; + *(vuip)APECS_IOC_TBIA; } void @@ -461,8 +460,6 @@ apecs_machine_check(unsigned long vector, unsigned long la_ptr, struct el_common *mchk_header; struct el_apecs_procdata *mchk_procdata; struct el_apecs_sysdata_mcheck *mchk_sysdata; - unsigned long *ptr; - int i; mchk_header = (struct el_common *)la_ptr; @@ -473,66 +470,16 @@ apecs_machine_check(unsigned long vector, unsigned long la_ptr, mchk_sysdata = (struct el_apecs_sysdata_mcheck *) (la_ptr + mchk_header->sys_offset); -#ifdef DEBUG - printk("apecs_machine_check: vector=0x%lx la_ptr=0x%lx\n", - vector, la_ptr); - printk(" pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", - regs->pc, mchk_header->size, mchk_header->proc_offset, - mchk_header->sys_offset); - printk("apecs_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n", - apecs_mcheck_expected, mchk_sysdata->epic_dcsr, - mchk_sysdata->epic_pear); - ptr = (unsigned long *)la_ptr; - for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { - printk(" +%lx %lx %lx\n", i*sizeof(long), ptr[i], ptr[i+1]); - } -#endif - /* - * Check if machine check is due to a badaddr() and if so, - * ignore the machine check. - */ + /* Clear the error before any reporting. */ + mb(); + mb(); /* magic */ + draina(); + apecs_pci_clr_err(); + wrmces(0x7); /* reset machine check pending flag */ + mb(); - if (apecs_mcheck_expected - && (mchk_sysdata->epic_dcsr & 0x0c00UL)) { - apecs_mcheck_expected = 0; - apecs_mcheck_taken = 1; - mb(); - mb(); /* magic */ - apecs_pci_clr_err(); - wrmces(0x7); - mb(); - draina(); - DBG(("apecs_machine_check: EXPECTED\n")); - } - else if (vector == 0x620 || vector == 0x630) { - /* Disable correctable from now on. */ - wrmces(0x1f); - mb(); - draina(); - printk("apecs_machine_check: HW correctable (0x%lx)\n", - vector); - } - else { - printk(KERN_CRIT "APECS machine check:\n"); - printk(KERN_CRIT " vector=0x%lx la_ptr=0x%lx\n", - vector, la_ptr); - printk(KERN_CRIT - " pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", - regs->pc, mchk_header->size, mchk_header->proc_offset, - mchk_header->sys_offset); - printk(KERN_CRIT " expected %d DCSR 0x%lx PEAR 0x%lx\n", - apecs_mcheck_expected, mchk_sysdata->epic_dcsr, - mchk_sysdata->epic_pear); - - ptr = (unsigned long *)la_ptr; - for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { - printk(KERN_CRIT " +%lx %lx %lx\n", - i*sizeof(long), ptr[i], ptr[i+1]); - } -#if 0 - /* doesn't work with MILO */ - show_regs(regs); -#endif - } + process_mcheck_info(vector, la_ptr, regs, "APECS", + (mcheck_expected(0) + && (mchk_sysdata->epic_dcsr & 0x0c00UL))); } diff --git a/arch/alpha/kernel/core_cia.c b/arch/alpha/kernel/core_cia.c index eef31582638c..1c474944e4ce 100644 --- a/arch/alpha/kernel/core_cia.c +++ b/arch/alpha/kernel/core_cia.c @@ -30,31 +30,13 @@ * handle the system transaction. Another involves timing. Ho hum. */ -/* - * Machine check reasons. Defined according to PALcode sources - * (osf.h and platform.h). - */ -#define MCHK_K_TPERR 0x0080 -#define MCHK_K_TCPERR 0x0082 -#define MCHK_K_HERR 0x0084 -#define MCHK_K_ECC_C 0x0086 -#define MCHK_K_ECC_NC 0x0088 -#define MCHK_K_OS_BUGCHECK 0x008A -#define MCHK_K_PAL_BUGCHECK 0x0090 - /* * BIOS32-style PCI interface: */ -#define DEBUG_MCHECK 0 #define DEBUG_CONFIG 0 -/* #define DEBUG_DUMP_REGS */ +#define DEBUG_DUMP_REGS 0 -#if DEBUG_MCHECK -# define DBGM(args) printk args -#else -# define DBGM(args) -#endif #if DEBUG_CONFIG # define DBGC(args) printk args #else @@ -63,11 +45,6 @@ #define vuip volatile unsigned int * -static volatile unsigned int CIA_mcheck_expected = 0; -static volatile unsigned int CIA_mcheck_taken = 0; -static unsigned int CIA_jd; - - /* * Given a bus, device, and function number, compute resulting * configuration space address and setup the CIA_HAXR2 register @@ -173,20 +150,20 @@ conf_read(unsigned long addr, unsigned char type1) mb(); draina(); - CIA_mcheck_expected = 1; - CIA_mcheck_taken = 0; + mcheck_expected(0) = 1; + mcheck_taken(0) = 0; mb(); /* Access configuration space. */ value = *(vuip)addr; mb(); mb(); /* magic */ - if (CIA_mcheck_taken) { - CIA_mcheck_taken = 0; + if (mcheck_taken(0)) { + mcheck_taken(0) = 0; value = 0xffffffffU; mb(); } - CIA_mcheck_expected = 0; + mcheck_expected(0) = 0; mb(); #if 0 @@ -249,7 +226,7 @@ conf_write(unsigned long addr, unsigned int value, unsigned char type1) } draina(); - CIA_mcheck_expected = 1; + mcheck_expected(0) = 1; mb(); /* Access configuration space. */ @@ -257,7 +234,7 @@ conf_write(unsigned long addr, unsigned int value, unsigned char type1) mb(); mb(); /* magic */ - CIA_mcheck_expected = 0; + mcheck_expected(0) = 0; mb(); #if 0 @@ -395,7 +372,7 @@ cia_init_arch(unsigned long *mem_start, unsigned long *mem_end) { unsigned int cia_tmp; -#ifdef DEBUG_DUMP_REGS +#if DEBUG_DUMP_REGS { unsigned int temp; temp = *(vuip)CIA_IOC_CIA_REV; mb(); @@ -533,19 +510,26 @@ cia_init_arch(unsigned long *mem_start, unsigned long *mem_end) case 0: /* * Set up the PCI->physical memory translation windows. - * For now, windows 1,2 and 3 are disabled. In the future, + * For now, windows 2 and 3 are disabled. In the future, * we may want to use them to do scatter/gather DMA. * * Window 0 goes at 1 GB and is 1 GB large. + * Window 1 goes at 2 GB and is 1 GB large. */ - *(vuip)CIA_IOC_PCI_W0_BASE = 1U | (CIA_DMA_WIN_BASE_DEFAULT & 0xfff00000U); - *(vuip)CIA_IOC_PCI_W0_MASK = (CIA_DMA_WIN_SIZE_DEFAULT - 1) & 0xfff00000U; - *(vuip)CIA_IOC_PCI_T0_BASE = 0; + *(vuip)CIA_IOC_PCI_W0_BASE = CIA_DMA_WIN0_BASE_DEFAULT | 1U; + *(vuip)CIA_IOC_PCI_W0_MASK = (CIA_DMA_WIN0_SIZE_DEFAULT - 1) & + 0xfff00000U; + *(vuip)CIA_IOC_PCI_T0_BASE = CIA_DMA_WIN0_TRAN_DEFAULT >> 2; + + *(vuip)CIA_IOC_PCI_W1_BASE = CIA_DMA_WIN1_BASE_DEFAULT | 1U; + *(vuip)CIA_IOC_PCI_W1_MASK = (CIA_DMA_WIN1_SIZE_DEFAULT - 1) & + 0xfff00000U; + *(vuip)CIA_IOC_PCI_T1_BASE = CIA_DMA_WIN1_TRAN_DEFAULT >> 2; - *(vuip)CIA_IOC_PCI_W1_BASE = 0x0; *(vuip)CIA_IOC_PCI_W2_BASE = 0x0; *(vuip)CIA_IOC_PCI_W3_BASE = 0x0; + mb(); break; } @@ -593,128 +577,28 @@ cia_init_arch(unsigned long *mem_start, unsigned long *mem_end) } } -static int +static inline void cia_pci_clr_err(void) { - CIA_jd = *(vuip)CIA_IOC_CIA_ERR; - DBGM(("CIA_pci_clr_err: CIA ERR after read 0x%x\n", CIA_jd)); - *(vuip)CIA_IOC_CIA_ERR = CIA_jd; + unsigned int jd; + + jd = *(vuip)CIA_IOC_CIA_ERR; + *(vuip)CIA_IOC_CIA_ERR = jd; mb(); - return 0; + *(vuip)CIA_IOC_CIA_ERR; /* re-read to force write. */ } void cia_machine_check(unsigned long vector, unsigned long la_ptr, struct pt_regs * regs) { - struct el_common *mchk_header; - struct el_CIA_procdata *mchk_procdata; - struct el_CIA_sysdata_mcheck *mchk_sysdata; - unsigned long * ptr; - const char * reason; - char buf[128]; - long i; - - mchk_header = (struct el_common *)la_ptr; - - mchk_procdata = (struct el_CIA_procdata *) - (la_ptr + mchk_header->proc_offset); - - mchk_sysdata = (struct el_CIA_sysdata_mcheck *) - (la_ptr + mchk_header->sys_offset); - - DBGM(("cia_machine_check: vector=0x%lx la_ptr=0x%lx\n", - vector, la_ptr)); - DBGM((" pc=0x%lx size=0x%x procoffset=0x%x " - "sysoffset 0x%x\n", regs->pc, mchk_header->size, - mchk_header->proc_offset, mchk_header->sys_offset)); - DBGM(("cia_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n", - CIA_mcheck_expected, mchk_sysdata->epic_dcsr, - mchk_sysdata->epic_pear)); - -#if DEBUG_MCHECK - { - unsigned long *ptr; - int i; - - ptr = (unsigned long *)la_ptr; - for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { - printk(" +%lx %lx %lx\n", i*sizeof(long), - ptr[i], ptr[i+1]); - } - } -#endif - - /* - * Check if machine check is due to a badaddr() and if so, - * ignore the machine check. - */ - mb(); - mb(); /* magic */ - if (CIA_mcheck_expected) { - DBGM(("CIA machine check expected\n")); - CIA_mcheck_expected = 0; - CIA_mcheck_taken = 1; - mb(); - mb(); /* magic */ - draina(); - cia_pci_clr_err(); - wrmces(0x7); - mb(); - return; - } - - switch ((unsigned int) mchk_header->code) { - case MCHK_K_TPERR: reason = "tag parity error"; break; - case MCHK_K_TCPERR: reason = "tag control parity error"; break; - case MCHK_K_HERR: reason = "generic hard error"; break; - case MCHK_K_ECC_C: reason = "correctable ECC error"; break; - case MCHK_K_ECC_NC: reason = "uncorrectable ECC error"; break; - case MCHK_K_OS_BUGCHECK: reason = "OS-specific PAL bugcheck"; break; - case MCHK_K_PAL_BUGCHECK: reason = "callsys in kernel mode"; break; - case 0x96: reason = "i-cache read retryable error"; break; - case 0x98: reason = "processor detected hard error"; break; - - /* System specific (these are for Alcor, at least): */ - case 0x203: reason = "system detected uncorrectable ECC error"; break; - case 0x205: reason = "parity error detected by CIA"; break; - case 0x207: reason = "non-existent memory error"; break; - case 0x209: reason = "PCI SERR detected"; break; - case 0x20b: reason = "PCI data parity error detected"; break; - case 0x20d: reason = "PCI address parity error detected"; break; - case 0x20f: reason = "PCI master abort error"; break; - case 0x211: reason = "PCI target abort error"; break; - case 0x213: reason = "scatter/gather PTE invalid error"; break; - case 0x215: reason = "flash ROM write error"; break; - case 0x217: reason = "IOA timeout detected"; break; - case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break; - case 0x21b: reason = "EISA fail-safe timer timeout"; break; - case 0x21d: reason = "EISA bus time-out"; break; - case 0x21f: reason = "EISA software generated NMI"; break; - case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break; - default: - sprintf(buf, "reason for machine-check unknown (0x%x)", - (unsigned int) mchk_header->code); - reason = buf; - break; - } + /* Clear the error before any reporting. */ mb(); mb(); /* magic */ draina(); cia_pci_clr_err(); - wrmces(rdmces()); /* reset machine check pending flag */ + wrmces(rdmces()); /* reset machine check pending flag. */ mb(); - printk(KERN_CRIT "CIA machine check: %s%s\n", - reason, mchk_header->retry ? " (retryable)" : ""); - printk(KERN_CRIT " vector=0x%lx la_ptr=0x%lx pc=0x%lx\n", - vector, la_ptr, regs->pc); - - /* Dump the logout area to give all info. */ - - ptr = (unsigned long *)la_ptr; - for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { - printk(KERN_CRIT " +%8lx %016lx %016lx\n", - i*sizeof(long), ptr[i], ptr[i+1]); - } + process_mcheck_info(vector, la_ptr, regs, "CIA", mcheck_expected(0)); } diff --git a/arch/alpha/kernel/core_lca.c b/arch/alpha/kernel/core_lca.c index 8a529f27aa16..204d7acc6945 100644 --- a/arch/alpha/kernel/core_lca.c +++ b/arch/alpha/kernel/core_lca.c @@ -430,18 +430,18 @@ ioc_error (__u32 stat0, __u32 stat1) } void -lca_machine_check (unsigned long vector, unsigned long la, +lca_machine_check (unsigned long vector, unsigned long la_ptr, struct pt_regs *regs) { - unsigned long * ptr; const char * reason; union el_lca el; - char buf[128]; - long i; - printk(KERN_CRIT "lca: machine check (la=0x%lx,pc=0x%lx)\n", - la, regs->pc); - el.c = (struct el_common *) la; + el.c = (struct el_common *) la_ptr; + + wrmces(rdmces()); /* reset machine check pending flag */ + + printk(KERN_CRIT "LCA machine check: vector=%#lx pc=%#lx code=%#x\n", + vector, regs->pc, (unsigned int) el.c->code); /* * The first quadword after the common header always seems to @@ -450,13 +450,13 @@ lca_machine_check (unsigned long vector, unsigned long la, * logout frame, the upper 32 bits is the machine check * revision level, which we ignore for now. */ - switch (el.c->code & 0xffffffff) { + switch ((unsigned int) el.c->code) { case MCHK_K_TPERR: reason = "tag parity error"; break; case MCHK_K_TCPERR: reason = "tag control parity error"; break; case MCHK_K_HERR: reason = "access to non-existent memory"; break; case MCHK_K_ECC_C: reason = "correctable ECC error"; break; case MCHK_K_ECC_NC: reason = "non-correctable ECC error"; break; - case MCHK_K_CACKSOFT: reason = "MCHK_K_CACKSOFT"; break; /* what's this? */ + case MCHK_K_CACKSOFT: reason = "MCHK_K_CACKSOFT"; break; case MCHK_K_BUGCHECK: reason = "illegal exception in PAL mode"; break; case MCHK_K_OS_BUGCHECK: reason = "callsys in kernel mode"; break; case MCHK_K_DCPERR: reason = "d-cache parity error"; break; @@ -465,19 +465,13 @@ lca_machine_check (unsigned long vector, unsigned long la, case MCHK_K_SIO_IOCHK: reason = "SIO IOCHK occurred on ISA bus"; break; case MCHK_K_DCSR: reason = "MCHK_K_DCSR"; break; case MCHK_K_UNKNOWN: - default: - sprintf(buf, "reason for machine-check unknown (0x%lx)", - el.c->code & 0xffffffff); - reason = buf; - break; + default: reason = "unknown"; break; } - wrmces(rdmces()); /* reset machine check pending flag */ - switch (el.c->size) { case sizeof(struct el_lca_mcheck_short): printk(KERN_CRIT - " Reason: %s (short frame%s, dc_stat=%lx):\n", + " Reason: %s (short frame%s, dc_stat=%#lx):\n", reason, el.c->retry ? ", retryable" : "", el.s->dc_stat); if (el.s->esr & ESR_EAV) { @@ -492,9 +486,9 @@ lca_machine_check (unsigned long vector, unsigned long la, printk(KERN_CRIT " Reason: %s (long frame%s):\n", reason, el.c->retry ? ", retryable" : ""); printk(KERN_CRIT - " reason: %lx exc_addr: %lx dc_stat: %lx\n", + " reason: %#lx exc_addr: %#lx dc_stat: %#lx\n", el.l->pt[0], el.l->exc_addr, el.l->dc_stat); - printk(KERN_CRIT " car: %lx\n", el.l->car); + printk(KERN_CRIT " car: %#lx\n", el.l->car); if (el.l->esr & ESR_EAV) { mem_error(el.l->esr, el.l->ear); } @@ -508,12 +502,16 @@ lca_machine_check (unsigned long vector, unsigned long la, } /* Dump the logout area to give all info. */ - - ptr = (unsigned long *) la; - for (i = 0; i < el.c->size / sizeof(long); i += 2) { - printk(KERN_CRIT " +%8lx %016lx %016lx\n", - i*sizeof(long), ptr[i], ptr[i+1]); +#if DEBUG_MCHECK > 1 + { + unsigned long * ptr = (unsigned long *) la_ptr; + long i; + for (i = 0; i < el.c->size / sizeof(long); i += 2) { + printk(KERN_CRIT " +%8lx %016lx %016lx\n", + i*sizeof(long), ptr[i], ptr[i+1]); + } } +#endif } /* diff --git a/arch/alpha/kernel/core_mcpcia.c b/arch/alpha/kernel/core_mcpcia.c index 6e24dca07e83..035710d5760d 100644 --- a/arch/alpha/kernel/core_mcpcia.c +++ b/arch/alpha/kernel/core_mcpcia.c @@ -37,27 +37,14 @@ * BIOS32-style PCI interface: */ -#undef DEBUG_CFG +#define DEBUG_CFG 0 -#ifdef DEBUG_CFG +#if DEBUG_CFG # define DBG_CFG(args) printk args #else # define DBG_CFG(args) #endif - -#define DEBUG_MCHECK - -#ifdef DEBUG_MCHECK -# define DBG_MCK(args) printk args -#else -# define DBG_MCK(args) -#endif - -static volatile unsigned int MCPCIA_mcheck_expected[NR_CPUS]; -static volatile unsigned int MCPCIA_mcheck_taken[NR_CPUS]; -static unsigned int MCPCIA_jd[NR_CPUS]; - #define MCPCIA_MAX_HOSES 2 @@ -126,8 +113,9 @@ conf_read(unsigned long addr, unsigned char type1, mb(); draina(); - MCPCIA_mcheck_expected[cpu] = 1; - MCPCIA_mcheck_taken[cpu] = 0; + mcheck_expected(cpu) = 1; + mcheck_taken(cpu) = 0; + mcheck_hose(cpu) = hoseno; mb(); /* Access configuration space. */ @@ -135,12 +123,12 @@ conf_read(unsigned long addr, unsigned char type1, mb(); mb(); /* magic */ - if (MCPCIA_mcheck_taken[cpu]) { - MCPCIA_mcheck_taken[cpu] = 0; + if (mcheck_taken(cpu)) { + mcheck_taken(cpu) = 0; value = 0xffffffffU; mb(); } - MCPCIA_mcheck_expected[cpu] = 0; + mcheck_expected(cpu) = 0; mb(); DBG_CFG(("conf_read(): finished\n")); @@ -168,7 +156,8 @@ conf_write(unsigned long addr, unsigned int value, unsigned char type1, DBG_CFG(("conf_write: MCPCIA CAP_ERR(%d) was 0x%x\n", hoseno, stat0)); draina(); - MCPCIA_mcheck_expected[cpu] = 1; + mcheck_expected(cpu) = 1; + mcheck_hose(cpu) = hoseno; mb(); /* Access configuration space. */ @@ -176,7 +165,7 @@ conf_write(unsigned long addr, unsigned int value, unsigned char type1, mb(); mb(); /* magic */ temp = *(vuip)MCPCIA_CAP_ERR(hoseno); /* read to force the write */ - MCPCIA_mcheck_expected[cpu] = 0; + mcheck_expected(cpu) = 0; mb(); DBG_CFG(("conf_write(): finished\n")); @@ -328,8 +317,8 @@ mcpcia_init_arch(unsigned long *mem_start, unsigned long *mem_end) mb(); mb(); draina(); - MCPCIA_mcheck_expected[cpu] = 1; - MCPCIA_mcheck_taken[cpu] = 0; + mcheck_expected(cpu) = 1; + mcheck_taken(cpu) = 0; mb(); /* Access the bus revision word. */ @@ -337,12 +326,12 @@ mcpcia_init_arch(unsigned long *mem_start, unsigned long *mem_end) mb(); mb(); /* magic */ - if (MCPCIA_mcheck_taken[cpu]) { - MCPCIA_mcheck_taken[cpu] = 0; + if (mcheck_taken(cpu)) { + mcheck_taken(cpu) = 0; pci_rev = 0xffffffff; mb(); } - MCPCIA_mcheck_expected[cpu] = 0; + mcheck_expected(cpu) = 0; mb(); #if 0 @@ -554,19 +543,13 @@ mcpcia_init_arch(unsigned long *mem_start, unsigned long *mem_end) } } -static int -mcpcia_pci_clr_err(int h) +static void +mcpcia_pci_clr_err(int hose) { - unsigned int cpu = smp_processor_id(); - - MCPCIA_jd[cpu] = *(vuip)MCPCIA_CAP_ERR(h); -#if 0 - DBG_MCK(("MCPCIA_pci_clr_err: MCPCIA CAP_ERR(%d) after read 0x%x\n", - h, MCPCIA_jd[cpu])); -#endif - *(vuip)MCPCIA_CAP_ERR(h) = 0xffffffff; mb(); /* clear them all */ - MCPCIA_jd[cpu] = *(vuip)MCPCIA_CAP_ERR(h); - return 0; + *(vuip)MCPCIA_CAP_ERR(hose); + *(vuip)MCPCIA_CAP_ERR(hose) = 0xffffffff; /* Clear them all. */ + mb(); + *(vuip)MCPCIA_CAP_ERR(hose); /* Re-read for force write. */ } static void @@ -642,70 +625,33 @@ mcpcia_print_uncorrectable(struct el_MCPCIA_uncorrected_frame_mcheck *logout) } void -mcpcia_machine_check(unsigned long type, unsigned long la_ptr, +mcpcia_machine_check(unsigned long vector, unsigned long la_ptr, struct pt_regs * regs) { -#if 0 - printk("mcpcia machine check ignored\n") ; -#else struct el_common *mchk_header; struct el_MCPCIA_uncorrected_frame_mcheck *mchk_logout; unsigned int cpu = smp_processor_id(); - int h = 0; mchk_header = (struct el_common *)la_ptr; mchk_logout = (struct el_MCPCIA_uncorrected_frame_mcheck *)la_ptr; -#if 0 - DBG_MCK(("mcpcia_machine_check: type=0x%lx la_ptr=0x%lx\n", - type, la_ptr)); - DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", - regs->pc, mchk_header->size, mchk_header->proc_offset, - mchk_header->sys_offset)); -#endif - /* - * Check if machine check is due to a badaddr() and if so, - * ignore the machine check. - */ mb(); mb(); /* magic */ - if (MCPCIA_mcheck_expected[cpu]) { -#if 0 - DBG_MCK(("MCPCIA machine check expected\n")); -#endif - MCPCIA_mcheck_expected[cpu] = 0; - MCPCIA_mcheck_taken[cpu] = 1; - mb(); - mb(); /* magic */ - draina(); - mcpcia_pci_clr_err(h); - wrmces(0x7); - mb(); + draina(); + if (mcheck_expected(cpu)) { + mcpcia_pci_clr_err(mcheck_hose(cpu)); + } else { + /* FIXME: how do we figure out which hose the error was on? */ + mcpcia_pci_clr_err(0); + mcpcia_pci_clr_err(1); } -#if 1 - else { - printk("MCPCIA machine check NOT expected on CPU %d\n", cpu); - DBG_MCK(("mcpcia_machine_check: type=0x%lx pc=0x%lx" - " code=0x%lx\n", - type, regs->pc, mchk_header->code)); - - MCPCIA_mcheck_expected[cpu] = 0; - MCPCIA_mcheck_taken[cpu] = 1; - mb(); - mb(); /* magic */ - draina(); - mcpcia_pci_clr_err(h); - wrmces(0x7); - mb(); -#ifdef DEBUG_MCHECK_DUMP - if (type == 0x620) - printk("MCPCIA machine check: system CORRECTABLE!\n"); - else if (type == 0x630) - printk("MCPCIA machine check: processor CORRECTABLE!\n"); - else -#endif /* DEBUG_MCHECK_DUMP */ - mcpcia_print_uncorrectable(mchk_logout); + wrmces(0x7); + mb(); + + process_mcheck_info(vector, la_ptr, regs, "MCPCIA", + mcheck_expected(cpu)); + + if (vector != 0x620 && vector != 0x630) { + mcpcia_print_uncorrectable(mchk_logout); } -#endif -#endif } diff --git a/arch/alpha/kernel/core_polaris.c b/arch/alpha/kernel/core_polaris.c index b9945402f4ca..8f07dc2da510 100644 --- a/arch/alpha/kernel/core_polaris.c +++ b/arch/alpha/kernel/core_polaris.c @@ -26,23 +26,14 @@ * BIOS32-style PCI interface: */ -#ifdef DEBUG_CONFIG +#define DEBUG_CONFIG 0 + +#if DEBUG_CONFIG # define DBG_CFG(args) printk args #else # define DBG_CFG(args) #endif -#define DEBUG_MCHECK -#ifdef DEBUG_MCHECK -# define DBG_MCK(args) printk args -/* #define DEBUG_MCHECK_DUMP */ -#else -# define DBG_MCK(args) -#endif - -static volatile unsigned int POLARIS_mcheck_expected = 0; -static volatile unsigned int POLARIS_mcheck_taken = 0; -static volatile unsigned short POLARIS_jd = 0; /* * Given a bus, device, and function number, compute resulting @@ -195,81 +186,28 @@ polaris_init_arch(unsigned long *mem_start, unsigned long *mem_end) #endif } -int polaris_pci_clr_err(void) +static inline void +polaris_pci_clr_err(void) { - POLARIS_jd = *((vusp)POLARIS_W_STATUS); - DBG_MCK(("POLARIS_pci_clr_err: POLARIS_W_STATUS after read 0x%x\n", - POLARIS_jd)); + *(vusp)POLARIS_W_STATUS; /* Write 1's to settable bits to clear errors */ - *((vusp)POLARIS_W_STATUS) = 0x7800; mb(); - POLARIS_jd = *((vusp)POLARIS_W_STATUS); - return 0; + *(vusp)POLARIS_W_STATUS = 0x7800; + mb(); + *(vusp)POLARIS_W_STATUS; } -void polaris_machine_check(unsigned long vector, unsigned long la_ptr, - struct pt_regs * regs) +void +polaris_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs * regs) { - struct el_common *mchk_header; - struct el_POLARIS_sysdata_mcheck *mchk_sysdata; - - mchk_header = (struct el_common *)la_ptr; - - mchk_sysdata = - (struct el_POLARIS_sysdata_mcheck *)(la_ptr+mchk_header->sys_offset); - -#if 0 - DBG_MCK(("polaris_machine_check: vector=0x%lx la_ptr=0x%lx\n", - vector, la_ptr)); - DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", - regs->pc, mchk_header->size, mchk_header->proc_offset, - mchk_header->sys_offset)); - DBG_MCK(("polaris_machine_check: expected %d status 0x%lx\n", - POLARIS_mcheck_expected, mchk_sysdata->psc_status)); -#endif -#ifdef DEBUG_MCHECK_DUMP - { - unsigned long *ptr; - int i; - - ptr = (unsigned long *)la_ptr; - for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { - printk(" +%lx %lx %lx\n", i*sizeof(long), ptr[i], ptr[i+1]); - } - } -#endif /* DEBUG_MCHECK_DUMP */ - /* - * Check if machine check is due to a badaddr() and if so, - * ignore the machine check. - */ + /* Clear the error before any reporting. */ mb(); mb(); - if (POLARIS_mcheck_expected) { - DBG_MCK(("POLARIS machine check expected\n")); - POLARIS_mcheck_expected = 0; - POLARIS_mcheck_taken = 1; - mb(); - mb(); - draina(); - polaris_pci_clr_err(); - wrmces(0x7); - mb(); - } -#if 1 - else { - printk("POLARIS machine check NOT expected\n") ; - DBG_MCK(("polaris_machine_check: vector=0x%lx la_ptr=0x%lx\n", - vector, la_ptr)); - DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", - regs->pc, mchk_header->size, mchk_header->proc_offset, - mchk_header->sys_offset)); - POLARIS_mcheck_expected = 0; - POLARIS_mcheck_taken = 1; - mb(); - mb(); - draina(); - polaris_pci_clr_err(); - wrmces(0x7); - mb(); - } -#endif + draina(); + polaris_pci_clr_err(); + wrmces(0x7); + mb(); + + process_mcheck_info(vector, la_ptr, regs, "POLARIS", + mcheck_expected(0)); } diff --git a/arch/alpha/kernel/core_pyxis.c b/arch/alpha/kernel/core_pyxis.c index 52bca054db2b..9b204dd4b386 100644 --- a/arch/alpha/kernel/core_pyxis.c +++ b/arch/alpha/kernel/core_pyxis.c @@ -32,7 +32,6 @@ */ #define DEBUG_CONFIG 0 -#define DEBUG_MCHECK 0 #if DEBUG_CONFIG # define DBG_CNF(args) printk args @@ -40,18 +39,6 @@ # define DBG_CNF(args) #endif -#if DEBUG_MCHECK -# define DBG_MCK(args) printk args -# define DEBUG_MCHECK_DUMP -#else -# define DBG_MCK(args) -#endif - - -static volatile unsigned int PYXIS_mcheck_expected = 0; -static volatile unsigned int PYXIS_mcheck_taken = 0; -static unsigned int PYXIS_jd; - /* * Given a bus, device, and function number, compute resulting @@ -132,8 +119,8 @@ conf_read(unsigned long addr, unsigned char type1) mb(); draina(); - PYXIS_mcheck_expected = 1; - PYXIS_mcheck_taken = 0; + mcheck_expected(0) = 1; + mcheck_taken(0) = 0; mb(); /* Access configuration space. */ @@ -141,12 +128,12 @@ conf_read(unsigned long addr, unsigned char type1) mb(); mb(); /* magic */ - if (PYXIS_mcheck_taken) { - PYXIS_mcheck_taken = 0; + if (mcheck_taken(0)) { + mcheck_taken(0) = 0; value = 0xffffffffU; mb(); } - PYXIS_mcheck_expected = 0; + mcheck_expected(0) = 0; mb(); /* If Type1 access, must reset IOC CFG so normal IO space ops work. */ @@ -186,15 +173,15 @@ conf_write(unsigned long addr, unsigned int value, unsigned char type1) mb(); draina(); - PYXIS_mcheck_expected = 1; - PYXIS_mcheck_taken = 0; + mcheck_expected(0) = 1; + mcheck_taken(0) = 0; mb(); /* Access configuration space. */ *(vuip)addr = value; mb(); temp = *(vuip)addr; /* read back to force the write */ - PYXIS_mcheck_expected = 0; + mcheck_expected(0) = 0; mb(); /* If Type1 access, must reset IOC CFG so normal IO space ops work. */ @@ -432,14 +419,13 @@ pyxis_native_window_setup(void) * Window 1 goes at 3 GB and is 1 GB large. */ - *(vuip)PYXIS_W0_BASE = PYXIS_DMA_WIN_BASE_DEFAULT | 1UL; - *(vuip)PYXIS_W0_MASK = (PYXIS_DMA_WIN_SIZE_DEFAULT - 1) & 0xfff00000U; - *(vuip)PYXIS_T0_BASE = 0; + *(vuip)PYXIS_W0_BASE = PYXIS_DMA_WIN0_BASE_DEFAULT | 1U; + *(vuip)PYXIS_W0_MASK = (PYXIS_DMA_WIN0_SIZE_DEFAULT - 1) & 0xfff00000U; + *(vuip)PYXIS_T0_BASE = PYXIS_DMA_WIN0_TRAN_DEFAULT >> 2; - *(vuip)PYXIS_W1_BASE = (PYXIS_DMA_WIN_BASE_DEFAULT + - PYXIS_DMA_WIN_SIZE_DEFAULT) | 1U; - *(vuip)PYXIS_W1_MASK = (PYXIS_DMA_WIN_SIZE_DEFAULT - 1) & 0xfff00000U; - *(vuip)PYXIS_T1_BASE = PYXIS_DMA_WIN_SIZE_DEFAULT; + *(vuip)PYXIS_W1_BASE = PYXIS_DMA_WIN1_BASE_DEFAULT | 1U; + *(vuip)PYXIS_W1_MASK = (PYXIS_DMA_WIN1_SIZE_DEFAULT - 1) & 0xfff00000U; + *(vuip)PYXIS_T1_BASE = PYXIS_DMA_WIN1_TRAN_DEFAULT >> 2; *(vuip)PYXIS_W2_BASE = 0x0; *(vuip)PYXIS_W3_BASE = 0x0; @@ -530,83 +516,26 @@ pyxis_init_arch(unsigned long *mem_start, unsigned long *mem_end) pyxis_finish_init_arch(); } -static int +static inline void pyxis_pci_clr_err(void) { - PYXIS_jd = *(vuip)PYXIS_ERR; - DBG_MCK(("PYXIS_pci_clr_err: PYXIS ERR after read 0x%x\n", PYXIS_jd)); - *(vuip)PYXIS_ERR = 0x0180; mb(); - PYXIS_jd = *(vuip)PYXIS_ERR; /* re-read to force write */ - return 0; + *(vuip)PYXIS_ERR; + *(vuip)PYXIS_ERR = 0x0180; + mb(); + *(vuip)PYXIS_ERR; /* re-read to force write */ } void pyxis_machine_check(unsigned long vector, unsigned long la_ptr, struct pt_regs * regs) { - struct el_common *mchk_header; - struct el_PYXIS_sysdata_mcheck *mchk_sysdata; - - mchk_header = (struct el_common *)la_ptr; - - mchk_sysdata = (struct el_PYXIS_sysdata_mcheck *) - (la_ptr + mchk_header->sys_offset); - -#if 0 - DBG_MCK(("pyxis_machine_check: vector=0x%lx la_ptr=0x%lx\n", - vector, la_ptr)); - DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", - regs->pc, mchk_header->size, mchk_header->proc_offset, - mchk_header->sys_offset)); - DBG_MCK(("pyxis_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n", - PYXIS_mcheck_expected, mchk_sysdata->epic_dcsr, - mchk_sysdata->epic_pear)); -#endif - - /* - * Check if machine check is due to a badaddr() and if so, - * ignore the machine check. - */ + /* Clear the error before reporting anything. */ mb(); mb(); /* magic */ - if (PYXIS_mcheck_expected) { - DBG_MCK(("PYXIS machine check expected\n")); - PYXIS_mcheck_expected = 0; - PYXIS_mcheck_taken = 1; - mb(); - mb(); /* magic */ - draina(); - pyxis_pci_clr_err(); - wrmces(0x7); - mb(); - } - else { - printk("PYXIS machine check NOT expected\n") ; - DBG_MCK(("pyxis_machine_check: vector=0x%lx la_ptr=0x%lx\n", - vector, la_ptr)); - DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x" - " sysoffset 0x%x\n", - regs->pc, mchk_header->size, mchk_header->proc_offset, - mchk_header->sys_offset)); - PYXIS_mcheck_expected = 0; - PYXIS_mcheck_taken = 1; - mb(); - mb(); /* magic */ - draina(); - pyxis_pci_clr_err(); - wrmces(0x7); - mb(); - -#ifdef DEBUG_MCHECK_DUMP - { - unsigned long *ptr = (unsigned long *)la_ptr;; - long n = mchk_header->size / (2*sizeof(long)); + draina(); + pyxis_pci_clr_err(); + wrmces(0x7); + mb(); - do - printk(" +%lx %lx %lx\n", i*sizeof(long), - ptr[i], ptr[i+1]); - while (--i); - } -#endif - } + process_mcheck_info(vector, la_ptr, regs, "PYXIS", mcheck_expected(0)); } diff --git a/arch/alpha/kernel/core_t2.c b/arch/alpha/kernel/core_t2.c index c7ec2b6ee94f..d0dd399c83f1 100644 --- a/arch/alpha/kernel/core_t2.c +++ b/arch/alpha/kernel/core_t2.c @@ -32,37 +32,18 @@ * handle the system transaction. Another involves timing. Ho hum. */ -/* - * Machine check reasons. Defined according to PALcode sources - * (osf.h and platform.h). - */ -#define MCHK_K_TPERR 0x0080 -#define MCHK_K_TCPERR 0x0082 -#define MCHK_K_HERR 0x0084 -#define MCHK_K_ECC_C 0x0086 -#define MCHK_K_ECC_NC 0x0088 -#define MCHK_K_OS_BUGCHECK 0x008A -#define MCHK_K_PAL_BUGCHECK 0x0090 - /* * BIOS32-style PCI interface: */ -#ifdef DEBUG_CONF +#define DEBUG_CONFIG 0 + +#if DEBUG_CONFIG # define DBG(args) printk args #else # define DBG(args) #endif -#ifdef DEBUG_MCHECK -# define DBGMC(args) printk args -#else -# define DBGMC(args) -#endif - -static volatile unsigned int T2_mcheck_expected[NR_CPUS]; -static volatile unsigned int T2_mcheck_taken[NR_CPUS]; - /* * Given a bus, device, and function number, compute resulting @@ -173,8 +154,8 @@ conf_read(unsigned long addr, unsigned char type1) mb(); draina(); - T2_mcheck_expected[cpu] = 1; - T2_mcheck_taken[cpu] = 0; + mcheck_expected(cpu) = 1; + mcheck_taken(cpu) = 0; mb(); /* Access configuration space. */ @@ -182,12 +163,12 @@ conf_read(unsigned long addr, unsigned char type1) mb(); mb(); /* magic */ - if (T2_mcheck_taken[cpu]) { - T2_mcheck_taken[cpu] = 0; + if (mcheck_taken(cpu)) { + mcheck_taken(cpu) = 0; value = 0xffffffffU; mb(); } - T2_mcheck_expected[cpu] = 0; + mcheck_expected(cpu) = 0; mb(); /* If Type1 access, must reset T2 CFG so normal IO space ops work. */ @@ -233,7 +214,7 @@ conf_write(unsigned long addr, unsigned int value, unsigned char type1) mb(); draina(); - T2_mcheck_expected[cpu] = 1; + mcheck_expected(cpu) = 1; mb(); /* Access configuration space. */ @@ -241,7 +222,7 @@ conf_write(unsigned long addr, unsigned int value, unsigned char type1) mb(); mb(); /* magic */ - T2_mcheck_expected[cpu] = 0; + mcheck_expected(cpu) = 0; mb(); /* If Type1 access, must reset T2 CFG so normal IO space ops work. */ @@ -355,8 +336,8 @@ t2_init_arch(unsigned long *mem_start, unsigned long *mem_end) unsigned int i; for (i = 0; i < NR_CPUS; i++) { - T2_mcheck_expected[i] = 0; - T2_mcheck_taken[i] = 0; + mcheck_expected(i) = 0; + mcheck_taken(i) = 0; } #if 0 @@ -481,27 +462,22 @@ t2_init_arch(unsigned long *mem_start, unsigned long *mem_end) #define SIC_SEIC (1UL << 33) /* System Event Clear */ -static int -t2_clear_errors(void) +static void +t2_clear_errors(int cpu) { - unsigned int cpu = smp_processor_id(); - static struct sable_cpu_csr *cpu_regs = NULL; - - switch (cpu) - { - case 0: cpu_regs = (struct sable_cpu_csr *)T2_CPU0_BASE; break; - case 1: cpu_regs = (struct sable_cpu_csr *)T2_CPU1_BASE; break; - case 2: cpu_regs = (struct sable_cpu_csr *)T2_CPU2_BASE; break; - case 3: cpu_regs = (struct sable_cpu_csr *)T2_CPU3_BASE; break; - } - - DBGMC(("???????? t2_clear_errors\n")); - + struct sable_cpu_csr *cpu_regs; + + cpu_regs = (struct sable_cpu_csr *)T2_CPU0_BASE; + if (cpu == 1) + cpu_regs = (struct sable_cpu_csr *)T2_CPU1_BASE; + if (cpu == 2) + cpu_regs = (struct sable_cpu_csr *)T2_CPU2_BASE; + if (cpu == 3) + cpu_regs = (struct sable_cpu_csr *)T2_CPU3_BASE; + cpu_regs->sic &= ~SIC_SEIC; - /* - * clear CPU errors - */ + /* Clear CPU errors. */ cpu_regs->bcce |= cpu_regs->bcce; cpu_regs->cbe |= cpu_regs->cbe; cpu_regs->bcue |= cpu_regs->bcue; @@ -512,116 +488,21 @@ t2_clear_errors(void) mb(); mb(); /* magic */ - return 0; } void t2_machine_check(unsigned long vector, unsigned long la_ptr, struct pt_regs * regs) { - struct el_t2_logout_header *mchk_header; - struct el_t2_procdata_mcheck *mchk_procdata; - struct el_t2_sysdata_mcheck *mchk_sysdata; - unsigned long * ptr; - const char * reason; - char buf[128]; - long i; - unsigned int cpu = smp_processor_id(); - - DBGMC(("t2_machine_check: vector=0x%lx la_ptr=0x%lx\n", - vector, la_ptr)); - - mchk_header = (struct el_t2_logout_header *)la_ptr; - - DBGMC(("t2_machine_check: susoffset=0x%lx procoffset=0x%lx\n", - mchk_header->elfl_sysoffset, mchk_header->elfl_procoffset)); - - mchk_sysdata = (struct el_t2_sysdata_mcheck *) - (la_ptr + mchk_header->elfl_sysoffset); - mchk_procdata = (struct el_t2_procdata_mcheck *) - (la_ptr + mchk_header->elfl_procoffset - sizeof(unsigned long)*32); - - DBGMC((" pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", - regs->pc, mchk_header->elfl_size, mchk_header->elfl_procoffset, - mchk_header->elfl_sysoffset)); - DBGMC(("t2_machine_check: expected %d\n", T2_mcheck_expected[cpu])); - -#ifdef DEBUG_DUMP - { - unsigned long *ptr; - int i; - - ptr = (unsigned long *)la_ptr; - for (i = 0; i < mchk_header->elfl_size/sizeof(long); i += 2) { - printk(" +%lx %lx %lx\n", i*sizeof(long), - ptr[i], ptr[i+1]); - } - } -#endif /* DEBUG_DUMP */ + int cpu = smp_processor_id(); - /* - * Check if machine check is due to a badaddr() and if so, - * ignore the machine check. - */ + /* Clear the error before any reporting. */ mb(); mb(); /* magic */ - if (T2_mcheck_expected[cpu]) { - DBGMC(("T2 machine check expected\n")); - T2_mcheck_taken[cpu] = 1; - t2_clear_errors(); - T2_mcheck_expected[cpu] = 0; - mb(); - mb(); /* magic */ - wrmces(rdmces()|1);/* ??? */ - draina(); - return; - } - - switch ((unsigned int) mchk_header->elfl_error_type) { - case MCHK_K_TPERR: reason = "tag parity error"; break; - case MCHK_K_TCPERR: reason = "tag control parity error"; break; - case MCHK_K_HERR: reason = "generic hard error"; break; - case MCHK_K_ECC_C: reason = "correctable ECC error"; break; - case MCHK_K_ECC_NC: reason = "uncorrectable ECC error"; break; - case MCHK_K_OS_BUGCHECK: reason = "OS-specific PAL bugcheck"; break; - case MCHK_K_PAL_BUGCHECK: reason = "callsys in kernel mode"; break; - case 0x96: reason = "i-cache read retryable error"; break; - case 0x98: reason = "processor detected hard error"; break; - - /* System specific (these are for Alcor, at least): */ - case 0x203: reason = "system detected uncorrectable ECC error"; break; - case 0x205: reason = "parity error detected by T2"; break; - case 0x207: reason = "non-existent memory error"; break; - case 0x209: reason = "PCI SERR detected"; break; - case 0x20b: reason = "PCI data parity error detected"; break; - case 0x20d: reason = "PCI address parity error detected"; break; - case 0x20f: reason = "PCI master abort error"; break; - case 0x211: reason = "PCI target abort error"; break; - case 0x213: reason = "scatter/gather PTE invalid error"; break; - case 0x215: reason = "flash ROM write error"; break; - case 0x217: reason = "IOA timeout detected"; break; - case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break; - case 0x21b: reason = "EISA fail-safe timer timeout"; break; - case 0x21d: reason = "EISA bus time-out"; break; - case 0x21f: reason = "EISA software generated NMI"; break; - case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break; - default: - sprintf(buf, "reason for machine-check unknown (0x%x)", - (unsigned int) mchk_header->elfl_error_type); - reason = buf; - break; - } - wrmces(rdmces()|1); /* reset machine check pending flag */ + draina(); + t2_clear_errors(cpu); + wrmces(rdmces()|1); /* ??? */ mb(); - printk(KERN_CRIT " T2 machine check: %s%s\n", - reason, mchk_header->elfl_retry ? " (retryable)" : ""); - - /* Dump the logout area to give all info. */ - - ptr = (unsigned long *)la_ptr; - for (i = 0; i < mchk_header->elfl_size / sizeof(long); i += 2) { - printk(KERN_CRIT " +%8lx %016lx %016lx\n", - i*sizeof(long), ptr[i], ptr[i+1]); - } + process_mcheck_info(vector, la_ptr, regs, "T2", mcheck_expected(cpu)); } diff --git a/arch/alpha/kernel/core_tsunami.c b/arch/alpha/kernel/core_tsunami.c index efcce56d5557..7b93a0dab3a2 100644 --- a/arch/alpha/kernel/core_tsunami.c +++ b/arch/alpha/kernel/core_tsunami.c @@ -17,6 +17,7 @@ #include #include #include +#include #define __EXTERN_INLINE inline #include @@ -26,6 +27,8 @@ #include "proto.h" #include "bios32.h" +int TSUNAMI_bootcpu; + /* * NOTE: Herein lie back-to-back mb instructions. They are magic. * One plausible explanation is that the I/O controller does not properly @@ -36,24 +39,15 @@ * BIOS32-style PCI interface: */ -#ifdef DEBUG_CONFIG +#define DEBUG_MCHECK 0 /* 0 = minimal, 1 = debug, 2 = debug+dump. */ +#define DEBUG_CONFIG 0 + +#if DEBUG_CONFIG # define DBG_CFG(args) printk args #else # define DBG_CFG(args) #endif -#define DEBUG_MCHECK -#ifdef DEBUG_MCHECK -# define DBG_MCK(args) printk args -#define DEBUG_MCHECK_DUMP -#else -# define DBG_MCK(args) -#endif - -static volatile unsigned int TSUNAMI_mcheck_expected[NR_CPUS]; -static volatile unsigned int TSUNAMI_mcheck_taken[NR_CPUS]; -static unsigned int TSUNAMI_jd[NR_CPUS]; -int TSUNAMI_bootcpu; /* * Given a bus, device, and function number, compute resulting @@ -207,13 +201,14 @@ tsunami_probe_read(volatile unsigned long *vaddr) int cpu = smp_processor_id(); int s = swpipl(6); /* Block everything but machine checks. */ - TSUNAMI_mcheck_taken[cpu] = 0; - TSUNAMI_mcheck_expected[cpu] = 1; + mcheck_taken(cpu) = 0; + mcheck_expected(cpu) = 1; + mb(); dont_care = *vaddr; draina(); - TSUNAMI_mcheck_expected[cpu] = 0; - probe_result = !TSUNAMI_mcheck_taken[cpu]; - TSUNAMI_mcheck_taken[cpu] = 0; + mcheck_expected(cpu) = 0; + probe_result = !mcheck_taken(cpu); + mcheck_taken(cpu) = 0; setipl(s); printk("dont_care == 0x%lx\n", dont_care); @@ -302,20 +297,18 @@ tsunami_init_one_pchip(tsunami_pchip *pchip, int index, * we may want to use them to do scatter/gather DMA. * * Window 0 goes at 1 GB and is 1 GB large, mapping to 0. + * Window 1 goes at 2 GB and is 1 GB large, mapping to 1GB. */ - pchip->wsba[0].csr = 1L | (TSUNAMI_DMA_WIN_BASE_DEFAULT & 0xfff00000U); - pchip->wsm[0].csr = (TSUNAMI_DMA_WIN_SIZE_DEFAULT - 1) & 0xfff00000UL; - pchip->tba[0].csr = 0; - -#if 0 - pchip->wsba[1].csr = 0; -#else - /* make the second window at 2Gb for 1Gb mapping to 1Gb */ - pchip->wsba[1].csr = 1L | ((0x80000000U) & 0xfff00000U); - pchip->wsm[1].csr = (0x40000000UL - 1) & 0xfff00000UL; - pchip->tba[1].csr = 0x40000000; -#endif + pchip->wsba[0].csr = TSUNAMI_DMA_WIN0_BASE_DEFAULT | 1UL; + pchip->wsm[0].csr = (TSUNAMI_DMA_WIN0_SIZE_DEFAULT - 1) & + 0xfff00000UL; + pchip->tba[0].csr = TSUNAMI_DMA_WIN0_TRAN_DEFAULT; + + pchip->wsba[1].csr = TSUNAMI_DMA_WIN1_BASE_DEFAULT | 1UL; + pchip->wsm[1].csr = (TSUNAMI_DMA_WIN1_SIZE_DEFAULT - 1) & + 0xfff00000UL; + pchip->tba[1].csr = TSUNAMI_DMA_WIN1_TRAN_DEFAULT; pchip->wsba[2].csr = 0; pchip->wsba[3].csr = 0; @@ -364,107 +357,48 @@ tsunami_init_arch(unsigned long *mem_start, unsigned long *mem_end) /* Align memory to cache line; we'll be allocating from it. */ *mem_start = (*mem_start | 31) + 1; - /* Find how many hoses we have, and initialize them. */ + TSUNAMI_bootcpu = __hard_smp_processor_id(); + + /* Find how many hoses we have, and initialize them. TSUNAMI + and TYPHOON can have 2, but might only have 1 (DS10). */ tsunami_init_one_pchip(TSUNAMI_pchip0, 0, mem_start); - /* must change this for TYPHOON which may have 4 */ if (TSUNAMI_cchip->csc.csr & 1L<<14) tsunami_init_one_pchip(TSUNAMI_pchip1, 1, mem_start); } static inline void -tsunami_pci_clr_err_1(tsunami_pchip *pchip, int cpu) +tsunami_pci_clr_err_1(tsunami_pchip *pchip) { - TSUNAMI_jd[cpu] = pchip->perror.csr; - DBG_MCK(("TSUNAMI_pci_clr_err: PERROR after read 0x%x\n", - TSUNAMI_jd[cpu])); + unsigned int jd; + + jd = pchip->perror.csr; pchip->perror.csr = 0x040; mb(); - TSUNAMI_jd[cpu] = pchip->perror.csr; + jd = pchip->perror.csr; } -static int +static inline void tsunami_pci_clr_err(void) { - int cpu = smp_processor_id(); - tsunami_pci_clr_err_1(TSUNAMI_pchip0, cpu); - /* must change this for TYPHOON which may have 4 */ + tsunami_pci_clr_err_1(TSUNAMI_pchip0); + + /* TSUNAMI and TYPHOON can have 2, but might only have 1 (DS10) */ if (TSUNAMI_cchip->csc.csr & 1L<<14) - tsunami_pci_clr_err_1(TSUNAMI_pchip1, cpu); - return 0; + tsunami_pci_clr_err_1(TSUNAMI_pchip1); } void tsunami_machine_check(unsigned long vector, unsigned long la_ptr, struct pt_regs * regs) { -#if 0 - printk("TSUNAMI machine check ignored\n") ; -#else - struct el_common *mchk_header; - struct el_TSUNAMI_sysdata_mcheck *mchk_sysdata; - unsigned int cpu = smp_processor_id(); - - mb(); - mchk_header = (struct el_common *)la_ptr; - - mchk_sysdata = (struct el_TSUNAMI_sysdata_mcheck *) - (la_ptr + mchk_header->sys_offset); - -#if 0 - DBG_MCK(("tsunami_machine_check: vector=0x%lx la_ptr=0x%lx\n", - vector, la_ptr)); - DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", - regs->pc, mchk_header->size, mchk_header->proc_offset, - mchk_header->sys_offset)); - DBG_MCK(("tsunami_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n", - TSUNAMI_mcheck_expected[cpu], mchk_sysdata->epic_dcsr, - mchk_sysdata->epic_pear)); -#endif -#ifdef DEBUG_MCHECK_DUMP - { - unsigned long *ptr; - int i; - - ptr = (unsigned long *)la_ptr; - for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { - printk(" +%lx %lx %lx\n", i*sizeof(long), ptr[i], ptr[i+1]); - } - } -#endif /* DEBUG_MCHECK_DUMP */ - /* - * Check if machine check is due to a badaddr() and if so, - * ignore the machine check. - */ + /* Clear error before any reporting. */ mb(); mb(); /* magic */ - if (TSUNAMI_mcheck_expected[cpu]) { - DBG_MCK(("TSUNAMI machine check expected\n")); - TSUNAMI_mcheck_expected[cpu] = 0; - TSUNAMI_mcheck_taken[cpu] = 1; - mb(); - mb(); /* magic */ - draina(); - tsunami_pci_clr_err(); - wrmces(0x7); - mb(); - } -#if 1 - else { - printk("TSUNAMI machine check NOT expected\n") ; - DBG_MCK(("tsunami_machine_check: vector=0x%lx la_ptr=0x%lx\n", - vector, la_ptr)); - DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", - regs->pc, mchk_header->size, mchk_header->proc_offset, - mchk_header->sys_offset)); - TSUNAMI_mcheck_expected[cpu] = 0; - TSUNAMI_mcheck_taken[cpu] = 1; - mb(); - mb(); /* magic */ - draina(); - tsunami_pci_clr_err(); - wrmces(0x7); - mb(); - } -#endif -#endif + draina(); + tsunami_pci_clr_err(); + wrmces(0x7); + mb(); + + process_mcheck_info(vector, la_ptr, regs, "TSUNAMI", + mcheck_expected(smp_processor_id())); } diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c index cd05c023e2fa..bfd7e5fc3b40 100644 --- a/arch/alpha/kernel/irq.c +++ b/arch/alpha/kernel/irq.c @@ -904,3 +904,99 @@ init_IRQ(void) wrent(entInt, 0); alpha_mv.init_irq(); } + + +/* + */ +#define MCHK_K_TPERR 0x0080 +#define MCHK_K_TCPERR 0x0082 +#define MCHK_K_HERR 0x0084 +#define MCHK_K_ECC_C 0x0086 +#define MCHK_K_ECC_NC 0x0088 +#define MCHK_K_OS_BUGCHECK 0x008A +#define MCHK_K_PAL_BUGCHECK 0x0090 + +#ifndef __SMP__ +struct mcheck_info __mcheck_info; +#endif + +void +process_mcheck_info(unsigned long vector, unsigned long la_ptr, + struct pt_regs *regs, const char *machine, + int expected) +{ + struct el_common *mchk_header; + const char *reason; + + /* + * See if the machine check is due to a badaddr() and if so, + * ignore it. + */ + +#if DEBUG_MCHECK > 0 + printk(KERN_CRIT "%s machine check %s\n", machine, + expected ? "expected." : "NOT expected!!!"); +#endif + + if (expected) { + int cpu = smp_processor_id(); + mcheck_expected(cpu) = 0; + mcheck_taken(cpu) = 1; + return; + } + + mchk_header = (struct el_common *)la_ptr; + + printk(KERN_CRIT "%s machine check: vector=0x%lx pc=0x%lx code=0x%lx\n", + machine, vector, regs->pc, mchk_header->code); + + switch ((unsigned int) mchk_header->code) { + /* Machine check reasons. Defined according to PALcode sources. */ + case 0x80: reason = "tag parity error"; break; + case 0x82: reason = "tag control parity error"; break; + case 0x84: reason = "generic hard error"; break; + case 0x86: reason = "correctable ECC error"; break; + case 0x88: reason = "uncorrectable ECC error"; break; + case 0x8A: reason = "OS-specific PAL bugcheck"; break; + case 0x90: reason = "callsys in kernel mode"; break; + case 0x96: reason = "i-cache read retryable error"; break; + case 0x98: reason = "processor detected hard error"; break; + + /* System specific (these are for Alcor, at least): */ + case 0x203: reason = "system detected uncorrectable ECC error"; break; + case 0x204: reason = "SIO SERR occurred on PCI bus"; break; + case 0x205: reason = "parity error detected by CIA"; break; + case 0x206: reason = "SIO IOCHK occurred on ISA bus"; break; + case 0x207: reason = "non-existent memory error"; break; + case 0x208: reason = "MCHK_K_DCSR"; break; + case 0x209: reason = "PCI SERR detected"; break; + case 0x20b: reason = "PCI data parity error detected"; break; + case 0x20d: reason = "PCI address parity error detected"; break; + case 0x20f: reason = "PCI master abort error"; break; + case 0x211: reason = "PCI target abort error"; break; + case 0x213: reason = "scatter/gather PTE invalid error"; break; + case 0x215: reason = "flash ROM write error"; break; + case 0x217: reason = "IOA timeout detected"; break; + case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break; + case 0x21b: reason = "EISA fail-safe timer timeout"; break; + case 0x21d: reason = "EISA bus time-out"; break; + case 0x21f: reason = "EISA software generated NMI"; break; + case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break; + default: reason = "unknown"; break; + } + + printk(KERN_CRIT "machine check type: %s%s\n", + reason, mchk_header->retry ? " (retryable)" : ""); + +#if DEBUG_MCHECK > 1 + { + /* Dump the logout area to give all info. */ + unsigned long *ptr = (unsigned long *)la_ptr; + long i; + for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { + printk(KERN_CRIT " +%8lx %016lx %016lx\n", + i*sizeof(long), ptr[i], ptr[i+1]); + } + } +#endif +} diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h index 7f72ca2f01eb..51c72f31a4be 100644 --- a/arch/alpha/kernel/proto.h +++ b/arch/alpha/kernel/proto.h @@ -25,7 +25,7 @@ extern void apecs_init_arch(unsigned long *, unsigned long *); extern volatile unsigned int apecs_mcheck_expected; extern volatile unsigned int apecs_mcheck_taken; -extern int apecs_pci_clr_err(void); +extern void apecs_pci_clr_err(void); extern void apecs_machine_check(u64, u64, struct pt_regs *); /* core_cia.c */ @@ -202,3 +202,28 @@ extern int ptrace_cancel_bpt (struct task_struct *child); /* ../mm/init.c */ void srm_paging_stop(void); + +/* irq.h */ + +#ifdef __SMP__ +#define mcheck_expected(cpu) (cpu_data[cpu].mcheck_expected) +#define mcheck_taken(cpu) (cpu_data[cpu].mcheck_taken) +#define mcheck_hose(cpu) (cpu_data[cpu].mcheck_hose) +#else +extern struct mcheck_info +{ + unsigned char expected __attribute__((aligned(8))); + unsigned char taken; + unsigned char hose; +} __mcheck_info; + +#define mcheck_expected(cpu) (__mcheck_info.expected) +#define mcheck_taken(cpu) (__mcheck_info.taken) +#define mcheck_hose(cpu) (__mcheck_info.hose) +#endif + +#define DEBUG_MCHECK 0 /* 0 = minimal, 1 = debug, 2 = debug+dump. */ + +extern void process_mcheck_info(unsigned long vector, unsigned long la_ptr, + struct pt_regs *regs, const char *machine, + int expected); diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index 6c13fea3f9b7..21fe3fec0f64 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -224,9 +224,15 @@ setup_arch(char **cmdline_p, unsigned long * memory_start_p, alpha_using_srm = strncmp((const char *)hwrpb->ssn, "MILO", 4) != 0; #endif - printk("Booting on %s%s%s using machine vector %s\n", + printk("Booting " +#ifdef CONFIG_ALPHA_GENERIC + "GENERIC " +#endif + "on %s%s%s using machine vector %s from %s\n", type_name, (*var_name ? " variation " : ""), - var_name, alpha_mv.vector_name); + var_name, alpha_mv.vector_name, + (alpha_using_srm ? "SRM" : "MILO")); + printk("Command line: %s\n", command_line); /* diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index cd733f605baa..cc8ea63c9f27 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/arch/alpha/kernel/sys_miata.c b/arch/alpha/kernel/sys_miata.c index f9c4b64c5b4e..31363006ac3c 100644 --- a/arch/alpha/kernel/sys_miata.c +++ b/arch/alpha/kernel/sys_miata.c @@ -271,6 +271,17 @@ miata_pci_fixup(void) es1888_init(); } +static void +miata_kill_arch (int mode, char *reboot_cmd) +{ + /* Who said DEC engineers have no sense of humor? ;-) */ + if (alpha_using_srm) { + *(vuip) PYXIS_RESET = 0x0000dead; + mb(); + } + generic_kill_arch(mode, reboot_cmd); +} + /* * The System Vector @@ -295,6 +306,6 @@ struct alpha_machine_vector miata_mv __initmv = { init_irq: miata_init_irq, init_pit: generic_init_pit, pci_fixup: miata_pci_fixup, - kill_arch: generic_kill_arch, + kill_arch: miata_kill_arch, }; ALIAS_MV(miata) diff --git a/arch/alpha/kernel/sys_mikasa.c b/arch/alpha/kernel/sys_mikasa.c index 9952c311fd48..61b03f0e4fc4 100644 --- a/arch/alpha/kernel/sys_mikasa.c +++ b/arch/alpha/kernel/sys_mikasa.c @@ -151,92 +151,34 @@ mikasa_primo_pci_fixup(void) common_pci_fixup(mikasa_map_irq, common_swizzle); } +#if defined(CONFIG_ALPHA_GENERIC) || !defined(CONFIG_ALPHA_PRIMO) static void -mikasa_machine_check(unsigned long vector, unsigned long la_ptr, - struct pt_regs * regs) +mikasa_apecs_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs * regs) { #define MCHK_NO_DEVSEL 0x205L #define MCHK_NO_TABT 0x204L struct el_common *mchk_header; - struct el_apecs_procdata *mchk_procdata; - struct el_apecs_mikasa_sysdata_mcheck *mchk_sysdata; - unsigned long *ptr; - int i; + unsigned int code; mchk_header = (struct el_common *)la_ptr; - mchk_procdata = (struct el_apecs_procdata *) - (la_ptr + mchk_header->proc_offset - - sizeof(mchk_procdata->paltemp)); - - mchk_sysdata = (struct el_apecs_mikasa_sysdata_mcheck *) - (la_ptr + mchk_header->sys_offset); - -#ifdef DEBUG - printk("mikasa_machine_check: vector=0x%lx la_ptr=0x%lx\n", - vector, la_ptr); - printk(" pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", - regs->pc, mchk_header->size, mchk_header->proc_offset, - mchk_header->sys_offset); - printk("mikasa_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n", - apecs_mcheck_expected, mchk_sysdata->epic_dcsr, - mchk_sysdata->epic_pear); - ptr = (unsigned long *)la_ptr; - for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { - printk(" +%lx %lx %lx\n", i*sizeof(long), ptr[i], ptr[i+1]); - } -#endif - - /* - * Check if machine check is due to a badaddr() and if so, - * ignore the machine check. - */ - - if (apecs_mcheck_expected - && ((unsigned int)mchk_header->code == MCHK_NO_DEVSEL - || (unsigned int)mchk_header->code == MCHK_NO_TABT)) { - apecs_mcheck_expected = 0; - apecs_mcheck_taken = 1; - mb(); - mb(); /* magic */ - apecs_pci_clr_err(); - wrmces(0x7); - mb(); - draina(); - } - else if (vector == 0x620 || vector == 0x630) { - /* Disable correctable from now on. */ - wrmces(0x1f); - mb(); - draina(); - printk("mikasa_machine_check: HW correctable (0x%lx)\n", - vector); - } - else { - printk(KERN_CRIT "APECS machine check:\n"); - printk(KERN_CRIT " vector=0x%lx la_ptr=0x%lx\n", - vector, la_ptr); - printk(KERN_CRIT - " pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", - regs->pc, mchk_header->size, mchk_header->proc_offset, - mchk_header->sys_offset); - printk(KERN_CRIT " expected %d DCSR 0x%lx PEAR 0x%lx\n", - apecs_mcheck_expected, mchk_sysdata->epic_dcsr, - mchk_sysdata->epic_pear); - - ptr = (unsigned long *)la_ptr; - for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { - printk(KERN_CRIT " +%lx %lx %lx\n", - i*sizeof(long), ptr[i], ptr[i+1]); - } -#if 0 - /* doesn't work with MILO */ - show_regs(regs); -#endif - } + /* Clear the error before any reporting. */ + mb(); + mb(); /* magic */ + draina(); + apecs_pci_clr_err(); + wrmces(0x7); + mb(); + + code = mchk_header->code; + process_mcheck_info(vector, la_ptr, regs, "MIKASA APECS", + (mcheck_expected(0) + && (code == MCHK_NO_DEVSEL + || code == MCHK_NO_TABT))); } - +#endif /* * The System Vector @@ -249,7 +191,7 @@ struct alpha_machine_vector mikasa_mv __initmv = { DO_DEFAULT_RTC, DO_APECS_IO, DO_APECS_BUS, - machine_check: mikasa_machine_check, + machine_check: mikasa_apecs_machine_check, max_dma_address: ALPHA_MAX_DMA_ADDRESS, nr_irqs: 32, @@ -274,7 +216,7 @@ struct alpha_machine_vector mikasa_primo_mv __initmv = { DO_DEFAULT_RTC, DO_CIA_IO, DO_CIA_BUS, - machine_check: mikasa_machine_check, + machine_check: cia_machine_check, max_dma_address: ALPHA_MAX_DMA_ADDRESS, nr_irqs: 32, diff --git a/arch/alpha/kernel/sys_rx164.c b/arch/alpha/kernel/sys_rx164.c index 5d2cf52888d6..eae999f41512 100644 --- a/arch/alpha/kernel/sys_rx164.c +++ b/arch/alpha/kernel/sys_rx164.c @@ -45,10 +45,10 @@ rx164_update_irq_hw(unsigned long irq, unsigned long mask, int unmask_p) outb(mask, 0x21); /* ISA PIC1 */ } +#if 0 static void rx164_srm_update_irq_hw(unsigned long irq, unsigned long mask, int unmask_p) { -#if 0 if (irq >= 16) { if (unmask_p) cserve_ena(irq - 16); @@ -59,8 +59,8 @@ rx164_srm_update_irq_hw(unsigned long irq, unsigned long mask, int unmask_p) outb(mask >> 8, 0xA1); /* ISA PIC2 */ else outb(mask, 0x21); /* ISA PIC1 */ -#endif } +#endif static void rx164_isa_device_interrupt(unsigned long vector, struct pt_regs * regs) @@ -107,7 +107,8 @@ rx164_device_interrupt(unsigned long vector, struct pt_regs *regs) printk("PLD 0x%lx\n", pld); #endif - if (pld & 0xffffffff00000000UL) pld &= 0x00000000ffffffffUL; + if (pld & 0xffffffff00000000UL) + pld &= 0x00000000ffffffffUL; /* * Now for every possible bit set, work through them and call diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c index 5c3399f0615b..dc15db08b021 100644 --- a/arch/alpha/mm/fault.c +++ b/arch/alpha/mm/fault.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/arch/i386/Makefile b/arch/i386/Makefile index d8c7f5e11b68..e13adc08990f 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -35,8 +35,8 @@ AFLAGS := $(AFLAGS) -DCPU=386 endif ifdef CONFIG_M486 -CFLAGS := $(CFLAGS) -m486 -DCPU=486 -CFLAGS += $(shell if $(CC) -march=i486 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i486"; fi) +CFLAGS := $(CFLAGS) -DCPU=486 +CFLAGS += $(shell if $(CC) -march=i486 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i486"; else echo "-m486"; fi) AFLAGS := $(AFLAGS) -DCPU=486 endif @@ -111,6 +111,7 @@ archclean: @$(MAKEBOOT) clean archmrproper: + rm -f arch/i386/vmlinux.lds archdep: @$(MAKEBOOT) dep diff --git a/arch/i386/config.in b/arch/i386/config.in index 792dbd5ffefd..66eb470a7088 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -167,6 +167,8 @@ source drivers/char/Config.in source drivers/usb/Config.in +source drivers/misc/Config.in + source fs/Config.in if [ "$CONFIG_VT" = "y" ]; then diff --git a/arch/i386/defconfig b/arch/i386/defconfig index 16161e449483..f8494ca16d95 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -330,6 +330,10 @@ CONFIG_82C710_MOUSE=y # # CONFIG_USB is not set +# +# Misc devices +# + # # Filesystems # @@ -366,10 +370,9 @@ CONFIG_LOCKD=y # # Partition Types # -# CONFIG_BSD_DISKLABEL is not set -# CONFIG_MAC_PARTITION is not set +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y # CONFIG_SMD_DISKLABEL is not set -# CONFIG_SOLARIS_X86_PARTITION is not set # CONFIG_SGI_DISKLABEL is not set # CONFIG_NLS is not set diff --git a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c index 994ea1d9b08c..e84d581c1616 100644 --- a/arch/i386/kernel/apm.c +++ b/arch/i386/kernel/apm.c @@ -288,7 +288,7 @@ extern void apm_unregister_callback(int (*)(apm_event_t)); /* * Local variables */ -static asmlinkage struct { +static struct { unsigned long offset; unsigned short segment; } apm_bios_entry; diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index ef428c763086..58e8d5163d3a 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -63,53 +63,10 @@ void enable_hlt(void) hlt_counter--; } -static void default_idle(void) -{ - while (1) { - while (!current->need_resched) { - if (!current_cpu_data.hlt_works_ok) - continue; - if (hlt_counter) - continue; - asm volatile("sti ; hlt" : : : "memory"); - } - schedule(); - check_pgt_cache(); - } -} - -void (*idle)(void) = default_idle; - -#if 1 - -#include - -static int __init piix4_idle_init(void) -{ - /* This is the PIIX4 ACPI device */ - struct pci_dev *dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL); - - if (dev) { - u32 base; - - printk("Found PIIX4 ACPI device\n"); - pci_read_config_dword(dev, 0x40, &base); - printk(" Base address %04x\n", base); -#ifdef __SMP__ - /* - * We can't really do idle things with multiple CPU's, I'm - * afraid. We'd need a per-CPU ACPI device. - */ - if (smp_num_cpus > 1) - return 0; -#endif - } - return 0; -} - -__initcall(piix4_idle_init); - -#endif +/* + * Powermanagement idle function, if any.. + */ +void (*acpi_idle)(void) = NULL; /* * The idle thread. There's no useful work to be @@ -123,7 +80,20 @@ void cpu_idle(void) init_idle(); current->priority = 0; current->counter = -100; - idle(); + + while (1) { + while (!current->need_resched) { + if (!current_cpu_data.hlt_works_ok) + continue; + if (hlt_counter) + continue; + asm volatile("sti ; hlt" : : : "memory"); + } + schedule(); + check_pgt_cache(); + if (acpi_idle) + acpi_idle(); + } } /* diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 0ac20e60e721..a94fa0a160e6 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -14,6 +14,9 @@ * Bart Hartgers , May 1999. * * Intel Mobile Pentium II detection fix. Sean Gilley, June 1999. + * + * IDT Winchip tweaks, misc clean ups. + * Dave Jones , August 1999 */ /* @@ -818,7 +821,6 @@ __initfunc(void identify_cpu(struct cpuinfo_x86 *c)) p = "Celeron (Dixon)"; } } - } if (p) { @@ -868,22 +870,25 @@ __initfunc(void print_cpu_info(struct cpuinfo_x86 *c)) printk("%s", c->x86_model_id); if (c->x86_mask || c->cpuid_level>=0) - printk(" stepping %02x", c->x86_mask); + printk(" stepping %02x\n", c->x86_mask); - if(c->x86_vendor == X86_VENDOR_CENTAUR) - { + if(c->x86_vendor == X86_VENDOR_CENTAUR) { u32 hv,lv; rdmsr(0x107, lv, hv); - printk("\nCentaur FSR was 0x%X ",lv); - lv|=(1<<8); - lv|=(1<<7); + printk("Centaur FSR was 0x%X ",lv); + lv|=(1<<1 | 1<<2 | 1<<7); /* lv|=(1<<6); - may help too if the board can cope */ - printk("now 0x%X", lv); + printk("now 0x%X\n", lv); wrmsr(0x107, lv, hv); /* Emulate MTRRs using Centaur's MCR. */ - c->x86_capability |= X86_FEATURE_MTRR; + c->x86_capability |= X86_FEATURE_MTRR; + + /* Set 3DNow! on Winchip 2 and above. */ + if (c->x86_model >=8) + c->x86_capability |= X86_FEATURE_AMD3D; + + c->x86_capability |=X86_FEATURE_CX8; } - printk("\n"); } /* @@ -918,7 +923,7 @@ int get_cpuinfo(char * buffer) c->x86 + '0', c->x86_model, c->x86_model_id[0] ? c->x86_model_id : "unknown"); - + if (c->x86_mask || c->cpuid_level >= 0) p += sprintf(p, "stepping\t: %d\n", c->x86_mask); else @@ -934,14 +939,20 @@ int get_cpuinfo(char * buffer) p += sprintf(p, "cache size\t: %d KB\n", c->x86_cache_size); /* Modify the capabilities according to chip type */ - if (c->x86_vendor == X86_VENDOR_CYRIX) { + switch (c->x86_vendor) { + + case X86_VENDOR_CYRIX: x86_cap_flags[24] = "cxmmx"; - } else if (c->x86_vendor == X86_VENDOR_AMD) { - x86_cap_flags[16] = "fcmov"; - x86_cap_flags[31] = "3dnow"; + break; + + case X86_VENDOR_AMD: if (c->x86 == 5 && c->x86_model == 6) x86_cap_flags[10] = "sep"; - } else if (c->x86_vendor == X86_VENDOR_INTEL) { + x86_cap_flags[16] = "fcmov"; + x86_cap_flags[31] = "3dnow"; + break; + + case X86_VENDOR_INTEL: x86_cap_flags[6] = "pae"; x86_cap_flags[9] = "apic"; x86_cap_flags[14] = "mca"; @@ -949,6 +960,16 @@ int get_cpuinfo(char * buffer) x86_cap_flags[17] = "pse36"; x86_cap_flags[18] = "psn"; x86_cap_flags[24] = "osfxsr"; + break; + + case X86_VENDOR_CENTAUR: + if (c->x86_model >=8) /* Only Winchip2 and above */ + x86_cap_flags[31] = "3dnow"; + break; + + default: + /* Unknown CPU manufacturer. Transmeta ? :-) */ + break; } sep_bug = c->x86_vendor == X86_VENDOR_INTEL && diff --git a/arch/i386/vmlinux.lds b/arch/i386/vmlinux.lds deleted file mode 100644 index 088441e5f51b..000000000000 --- a/arch/i386/vmlinux.lds +++ /dev/null @@ -1,75 +0,0 @@ -/* ld script to make i386 Linux kernel - * Written by Martin Mares ; - */ -OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") -OUTPUT_ARCH(i386) -ENTRY(_start) -SECTIONS -{ - . = 0xC0000000 + 0x100000; - _text = .; /* Text and read-only data */ - .text : { - *(.text) - *(.fixup) - *(.gnu.warning) - } = 0x9090 - .text.lock : { *(.text.lock) } /* out-of-line lock text */ - .rodata : { *(.rodata) } - .kstrtab : { *(.kstrtab) } - - . = ALIGN(16); /* Exception table */ - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; - - __start___ksymtab = .; /* Kernel symbol table */ - __ksymtab : { *(__ksymtab) } - __stop___ksymtab = .; - - _etext = .; /* End of text section */ - - .data : { /* Data */ - *(.data) - CONSTRUCTORS - } - - _edata = .; /* End of data section */ - - . = ALIGN(8192); /* init_task */ - .data.init_task : { *(.data.init_task) } - - . = ALIGN(4096); /* Init code and data */ - __init_begin = .; - .text.init : { *(.text.init) } - .data.init : { *(.data.init) } - . = ALIGN(16); - __setup_start = .; - .setup.init : { *(.setup.init) } - __setup_end = .; - __initcall_start = .; - .initcall.init : { *(.initcall.init) } - __initcall_end = .; - . = ALIGN(4096); - __init_end = .; - - . = ALIGN(4096); - .data.page_aligned : { *(.data.idt) } - - . = ALIGN(32); - .data.cacheline_aligned : { *(.data.cacheline_aligned) } - - __bss_start = .; /* BSS */ - .bss : { - *(.bss) - } - _end = . ; - - /* Stabs debugging sections. */ - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - .comment 0 : { *(.comment) } -} diff --git a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c index d44ac47a9109..3439bdb2e6f4 100644 --- a/arch/ppc/kernel/smp.c +++ b/arch/ppc/kernel/smp.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include diff --git a/drivers/Makefile b/drivers/Makefile index f9f067ad9c3a..74af9e7cb005 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -7,7 +7,7 @@ # # Note 2! The CFLAGS definitions are now in the main makefile. -SUB_DIRS := block char net parport sound +SUB_DIRS := block char net parport sound misc MOD_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) pci sgi scsi sbus cdrom isdn pnp i2o \ macintosh video dio zorro fc4 usb \ diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 0b5a805eac9c..0116527dc1f1 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -106,6 +106,12 @@ * 1998/09/20 -- David Weinehall -- Added slow-down code for buggy PS/2-drives. */ +/* + * 1999/08/13 -- Paul Slootman -- floppy stopped working on Alpha after 24 + * days, 6 hours, 32 minutes and 32 seconds (i.e. MAXINT jiffies; ints were + * being used to store jiffies, which are unsigned longs). + */ + #define FLOPPY_SANITY_CHECK #undef FLOPPY_SILENT_DCL_CLEAR @@ -606,10 +612,10 @@ static void is_alive(const char *message) #define OLOGSIZE 20 static void (*lasthandler)(void) = NULL; -static int interruptjiffies=0; -static int resultjiffies=0; +static unsigned long interruptjiffies=0; +static unsigned long resultjiffies=0; static int resultsize=0; -static int lastredo=0; +static unsigned long lastredo=0; static struct output_log { unsigned char data; @@ -629,7 +635,7 @@ static void reschedule_timeout(int drive, const char *message, int marg) drive = current_drive; del_timer(&fd_timeout); if (drive < 0 || drive > N_DRIVE) { - fd_timeout.expires = jiffies + 20*HZ; + fd_timeout.expires = jiffies + 20UL*HZ; drive=0; } else fd_timeout.expires = jiffies + UDP->timeout; @@ -712,7 +718,7 @@ static int disk_change(int drive) #ifdef DCL_DEBUG if (UDP->flags & FD_DEBUG){ DPRINT("checking disk change line for drive %d\n",drive); - DPRINT("jiffies=%ld\n", jiffies); + DPRINT("jiffies=%lu\n", jiffies); DPRINT("disk change line=%x\n",fd_inb(FD_DIR)&0x80); DPRINT("flags=%lx\n",UDRS->flags); } @@ -1006,7 +1012,7 @@ static void main_command_interrupt(void) } /* waits for a delay (spinup or select) to pass */ -static int wait_for_completion(int delay, timeout_fn function) +static int wait_for_completion(unsigned long delay, timeout_fn function) { if (FDCS->reset){ reset_fdc(); /* do the reset during sleep to win time @@ -1277,7 +1283,7 @@ static int fdc_configure(void) static void fdc_specify(void) { unsigned char spec1, spec2; - int srt, hlt, hut; + unsigned long srt, hlt, hut; unsigned long dtr = NOMINAL_DTR; unsigned long scale_dtr = NOMINAL_DTR; int hlt_max_code = 0x7f; @@ -1367,7 +1373,7 @@ static int fdc_dtr(void) * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies) */ FDCS->dtr = raw_cmd->rate & 3; - return(wait_for_completion(jiffies+2*HZ/100, + return(wait_for_completion(jiffies+2UL*HZ/100, (timeout_fn) floppy_ready)); } /* fdc_dtr */ @@ -1463,7 +1469,8 @@ static int interpret_errors(void) */ static void setup_rw_floppy(void) { - int i,ready_date,r, flags,dflags; + int i,r, flags,dflags; + unsigned long ready_date; timeout_fn function; flags = raw_cmd->flags; @@ -1536,7 +1543,7 @@ static void seek_interrupt(void) #ifdef DCL_DEBUG if (DP->flags & FD_DEBUG){ DPRINT("clearing NEWCHANGE flag because of effective seek\n"); - DPRINT("jiffies=%ld\n", jiffies); + DPRINT("jiffies=%lu\n", jiffies); } #endif CLEARF(FD_DISK_NEWCHANGE); /* effective seek */ @@ -1826,20 +1833,20 @@ static void show_floppy(void) printk("\n"); printk("floppy driver state\n"); printk("-------------------\n"); - printk("now=%ld last interrupt=%d last called handler=%p\n", - jiffies, interruptjiffies, lasthandler); + printk("now=%lu last interrupt=%lu diff=%lu last called handler=%p\n", + jiffies, interruptjiffies, jiffies-interruptjiffies, lasthandler); #ifdef FLOPPY_SANITY_CHECK printk("timeout_message=%s\n", timeout_message); printk("last output bytes:\n"); for (i=0; i < OLOGSIZE; i++) - printk("%2x %2x %ld\n", + printk("%2x %2x %lu\n", output_log[(i+output_log_pos) % OLOGSIZE].data, output_log[(i+output_log_pos) % OLOGSIZE].status, output_log[(i+output_log_pos) % OLOGSIZE].jiffies); - printk("last result at %d\n", resultjiffies); - printk("last redo_fd_request at %d\n", lastredo); + printk("last result at %lu\n", resultjiffies); + printk("last redo_fd_request at %lu\n", lastredo); for (i=0; icheckfreq < jiffies - UDRS->last_checked){ + if (UDP->checkfreq < (int)(jiffies - UDRS->last_checked)) { lock_fdc(drive,0); poll_drive(0,0); process_fd_request(); @@ -4365,7 +4372,6 @@ char *floppy=NULL; static void __init parse_floppy_cfg_string(char *cfg) { char *ptr; - int ints[11]; while(*cfg) { for(ptr = cfg;*cfg && *cfg != ' ' && *cfg != '\t'; cfg++); diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index 801e239025cd..1814a20b4c04 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -4,1404 +4,27 @@ * * Copyright (C) 1991-1998 Linus Torvalds * - * - * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug - * in the early extended-partition checks and added DM partitions - * - * Support for DiskManager v6.0x added by Mark Lord, - * with information provided by OnTrack. This now works for linux fdisk - * and LILO, as well as loadlin and bootln. Note that disks other than - * /dev/hda *must* have a "DOS" type 0x51 partition in the first slot (hda1). - * - * More flexible handling of extended partitions - aeb, 950831 - * - * Check partition table on IDE disks for common CHS translations - * - * Added needed MAJORS for new pairs, {hdi,hdj}, {hdk,hdl} + * Moved partition checking code to fs/partitions* - Russell King + * (linux@arm.uk.linux.org) */ #include #include #include #include -#include -#include #include #include -#include -#include - -/* - * Many architectures don't like unaligned accesses, which is - * frequently the case with the nr_sects and start_sect partition - * table entries. - */ -#include - -#define SYS_IND(p) (get_unaligned(&p->sys_ind)) -#define NR_SECTS(p) ({ __typeof__(p->nr_sects) __a = \ - get_unaligned(&p->nr_sects); \ - le32_to_cpu(__a); \ - }) - -#define START_SECT(p) ({ __typeof__(p->start_sect) __a = \ - get_unaligned(&p->start_sect); \ - le32_to_cpu(__a); \ - }) - -struct gendisk *gendisk_head = NULL; - -static int current_minor = 0; -extern int *blk_size[]; -extern void rd_load(void); -extern void initrd_load(void); - +extern int parport_init(void); extern int chr_dev_init(void); extern int blk_dev_init(void); extern int scsi_dev_init(void); extern int net_dev_init(void); -extern int i2o_init(void); - -#ifdef CONFIG_PPC -extern void note_bootable_part(kdev_t dev, int part); -#endif - -/* - * disk_name() is used by genhd.c and blkpg.c. - * It formats the devicename of the indicated disk into - * the supplied buffer (of size at least 32), and returns - * a pointer to that same buffer (for convenience). - */ -char *disk_name (struct gendisk *hd, int minor, char *buf) -{ - unsigned int part; - const char *maj = hd->major_name; - int unit = (minor >> hd->minor_shift) + 'a'; - - /* - * IDE devices use multiple major numbers, but the drives - * are named as: {hda,hdb}, {hdc,hdd}, {hde,hdf}, {hdg,hdh}.. - * This requires special handling here. - */ - switch (hd->major) { - case IDE9_MAJOR: - unit += 2; - case IDE8_MAJOR: - unit += 2; - case IDE7_MAJOR: - unit += 2; - case IDE6_MAJOR: - unit += 2; - case IDE5_MAJOR: - unit += 2; - case IDE4_MAJOR: - unit += 2; - case IDE3_MAJOR: - unit += 2; - case IDE2_MAJOR: - unit += 2; - case IDE1_MAJOR: - unit += 2; - case IDE0_MAJOR: - maj = "hd"; - break; - } - part = minor & ((1 << hd->minor_shift) - 1); - if (hd->major >= SCSI_DISK1_MAJOR && hd->major <= SCSI_DISK7_MAJOR) { - unit = unit + (hd->major - SCSI_DISK1_MAJOR + 1) * 16; - if (unit > 'z') { - unit -= 'z' + 1; - sprintf(buf, "sd%c%c", 'a' + unit / 26, 'a' + unit % 26); - if (part) - sprintf(buf + 4, "%d", part); - return buf; - } - } - if (hd->major >= COMPAQ_SMART2_MAJOR && hd->major <= COMPAQ_SMART2_MAJOR+7) { - int ctlr = hd->major - COMPAQ_SMART2_MAJOR; - int disk = minor >> hd->minor_shift; - int part = minor & (( 1 << hd->minor_shift) - 1); - if (part == 0) - sprintf(buf, "%s/c%dd%d", maj, ctlr, disk); - else - sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, disk, part); - return buf; - } - if (part) - sprintf(buf, "%s%c%d", maj, unit, part); - else - sprintf(buf, "%s%c", maj, unit); - return buf; -} - -static void add_partition (struct gendisk *hd, int minor, int start, int size) -{ - char buf[40]; - hd->part[minor].start_sect = start; - hd->part[minor].nr_sects = size; - if (hd->major >= COMPAQ_SMART2_MAJOR+0 && hd->major <= COMPAQ_SMART2_MAJOR+7) - printk(" p%d", (minor & ((1 << hd->minor_shift) - 1))); - else - printk(" %s", disk_name(hd, minor, buf)); -} - -static inline int is_extended_partition(struct partition *p) -{ - return (SYS_IND(p) == DOS_EXTENDED_PARTITION || - SYS_IND(p) == WIN98_EXTENDED_PARTITION || - SYS_IND(p) == LINUX_EXTENDED_PARTITION); -} - -int get_hardsect_size(kdev_t dev) -{ - if (hardsect_size[MAJOR(dev)] != NULL) - return hardsect_size[MAJOR(dev)][MINOR(dev)]; - else - return 512; -} - -static unsigned int get_ptable_blocksize(kdev_t dev) -{ - int ret = 1024; - - /* - * See whether the low-level driver has given us a minumum blocksize. - * If so, check to see whether it is larger than the default of 1024. - */ - if (!blksize_size[MAJOR(dev)]) - { - return ret; - } - - /* - * Check for certain special power of two sizes that we allow. - * With anything larger than 1024, we must force the blocksize up to - * the natural blocksize for the device so that we don't have to try - * and read partial sectors. Anything smaller should be just fine. - */ - - switch( blksize_size[MAJOR(dev)][MINOR(dev)] ) - { - case 2048: - ret = 2048; - break; - case 4096: - ret = 4096; - break; - case 8192: - ret = 8192; - break; - case 1024: - case 512: - case 256: - case 0: - /* - * These are all OK. - */ - break; - default: - panic("Strange blocksize for partition table\n"); - } - - return ret; - -} - -#ifdef CONFIG_MSDOS_PARTITION -/* - * Create devices for each logical partition in an extended partition. - * The logical partitions form a linked list, with each entry being - * a partition table with two entries. The first entry - * is the real data partition (with a start relative to the partition - * table start). The second is a pointer to the next logical partition - * (with a start relative to the entire extended partition). - * We do not create a Linux partition for the partition tables, but - * only for the actual data partitions. - */ - -#define MSDOS_LABEL_MAGIC 0xAA55 - -static void extended_partition(struct gendisk *hd, kdev_t dev) -{ - struct buffer_head *bh; - struct partition *p; - unsigned long first_sector, first_size, this_sector, this_size; - int mask = (1 << hd->minor_shift) - 1; - int sector_size = get_hardsect_size(dev) / 512; - int i; - - first_sector = hd->part[MINOR(dev)].start_sect; - first_size = hd->part[MINOR(dev)].nr_sects; - this_sector = first_sector; - - while (1) { - if ((current_minor & mask) == 0) - return; - if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) - return; - - if ((*(__u16 *) (bh->b_data+510)) != cpu_to_le16(MSDOS_LABEL_MAGIC)) - goto done; - - p = (struct partition *) (0x1BE + bh->b_data); - - this_size = hd->part[MINOR(dev)].nr_sects; - - /* - * Usually, the first entry is the real data partition, - * the 2nd entry is the next extended partition, or empty, - * and the 3rd and 4th entries are unused. - * However, DRDOS sometimes has the extended partition as - * the first entry (when the data partition is empty), - * and OS/2 seems to use all four entries. - */ - - /* - * First process the data partition(s) - */ - for (i=0; i<4; i++, p++) { - if (!NR_SECTS(p) || is_extended_partition(p)) - continue; - - /* Check the 3rd and 4th entries - - these sometimes contain random garbage */ - if (i >= 2 - && START_SECT(p) + NR_SECTS(p) > this_size - && (this_sector + START_SECT(p) < first_sector || - this_sector + START_SECT(p) + NR_SECTS(p) > - first_sector + first_size)) - continue; - - add_partition(hd, current_minor, this_sector+START_SECT(p)*sector_size, NR_SECTS(p)*sector_size); - current_minor++; - if ((current_minor & mask) == 0) - goto done; - } - /* - * Next, process the (first) extended partition, if present. - * (So far, there seems to be no reason to make - * extended_partition() recursive and allow a tree - * of extended partitions.) - * It should be a link to the next logical partition. - * Create a minor for this just long enough to get the next - * partition table. The minor will be reused for the next - * data partition. - */ - p -= 4; - for (i=0; i<4; i++, p++) - if(NR_SECTS(p) && is_extended_partition(p)) - break; - if (i == 4) - goto done; /* nothing left to do */ - - hd->part[current_minor].nr_sects = NR_SECTS(p) * sector_size; /* JSt */ - hd->part[current_minor].start_sect = first_sector + START_SECT(p) * sector_size; - this_sector = first_sector + START_SECT(p) * sector_size; - dev = MKDEV(hd->major, current_minor); - - /* Use bforget(), as we have changed the disk geometry */ - bforget(bh); - } -done: - bforget(bh); -} - -#ifdef CONFIG_SOLARIS_X86_PARTITION -static void -solaris_x86_partition(struct gendisk *hd, kdev_t dev, long offset) { - - struct buffer_head *bh; - struct solaris_x86_vtoc *v; - struct solaris_x86_slice *s; - int i; - - if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) - return; - v = (struct solaris_x86_vtoc *)(bh->b_data + 512); - if(v->v_sanity != SOLARIS_X86_VTOC_SANE) { - brelse(bh); - return; - } - printk(" v_version != 1) { - printk(" cannot handle version %ld vtoc>", v->v_version); - brelse(bh); - return; - } - for(i=0; iv_slice[i]; - - if (s->s_size == 0) - continue; - printk(" [s%d]", i); - /* solaris partitions are relative to current MS-DOS - * one but add_partition starts relative to sector - * zero of the disk. Therefore, must add the offset - * of the current partition */ - add_partition(hd, current_minor, s->s_start+offset, s->s_size); - current_minor++; - } - brelse(bh); - printk(" >"); -} -#endif - -#ifdef CONFIG_BSD_DISKLABEL -static void check_and_add_bsd_partition(struct gendisk *hd, - struct bsd_partition *bsd_p, kdev_t dev) -{ - struct hd_struct *lin_p; - /* check relative position of partitions. */ - for (lin_p = hd->part + 1 + MINOR(dev); - lin_p - hd->part - MINOR(dev) < current_minor; lin_p++) { - /* no relationship -> try again */ - if (lin_p->start_sect + lin_p->nr_sects <= bsd_p->p_offset - || lin_p->start_sect >= bsd_p->p_offset + bsd_p->p_size) - continue; - /* equal -> no need to add */ - if (lin_p->start_sect == bsd_p->p_offset && - lin_p->nr_sects == bsd_p->p_size) - return; - /* bsd living within dos partition */ - if (lin_p->start_sect <= bsd_p->p_offset && lin_p->start_sect - + lin_p->nr_sects >= bsd_p->p_offset + bsd_p->p_size) { -#ifdef DEBUG_BSD_DISKLABEL - printk("w: %d %ld+%ld,%d+%d", - lin_p - hd->part, - lin_p->start_sect, lin_p->nr_sects, - bsd_p->p_offset, bsd_p->p_size); -#endif - break; - } - /* ouch: bsd and linux overlap. Don't even try for that partition */ -#ifdef DEBUG_BSD_DISKLABEL - printk("???: %d %ld+%ld,%d+%d", - lin_p - hd->part, lin_p->start_sect, lin_p->nr_sects, - bsd_p->p_offset, bsd_p->p_size); -#endif - printk("???"); - return; - } /* if the bsd partition is not currently known to linux, we end - * up here - */ - add_partition(hd, current_minor, bsd_p->p_offset, bsd_p->p_size); - current_minor++; -} -/* - * Create devices for BSD partitions listed in a disklabel, under a - * dos-like partition. See extended_partition() for more information. - */ -static void bsd_disklabel_partition(struct gendisk *hd, kdev_t dev, - int max_partitions) -{ - struct buffer_head *bh; - struct bsd_disklabel *l; - struct bsd_partition *p; - int mask = (1 << hd->minor_shift) - 1; - - if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) - return; - l = (struct bsd_disklabel *) (bh->b_data+512); - if (l->d_magic != BSD_DISKMAGIC) { - brelse(bh); - return; - } - - if (l->d_npartitions < max_partitions) - max_partitions = l->d_npartitions; - for (p = l->d_partitions; p - l->d_partitions < max_partitions; p++) { - if ((current_minor & mask) >= (4 + hd->max_p)) - break; - - if (p->p_fstype != BSD_FS_UNUSED) - check_and_add_bsd_partition(hd, p, dev); - } - - /* Use bforget(), as we have changed the disk setup */ - bforget(bh); - -} -#endif - -#ifdef CONFIG_UNIXWARE_DISKLABEL -/* - * Create devices for Unixware partitions listed in a disklabel, under a - * dos-like partition. See extended_partition() for more information. - */ -static void unixware_partition(struct gendisk *hd, kdev_t dev) -{ - struct buffer_head *bh; - struct unixware_disklabel *l; - struct unixware_slice *p; - int mask = (1 << hd->minor_shift) - 1; - - if (!(bh = bread(dev, 14, get_ptable_blocksize(dev)))) - return; - l = (struct unixware_disklabel *) (bh->b_data+512); - if (le32_to_cpu(l->d_magic) != UNIXWARE_DISKMAGIC || - le32_to_cpu(l->vtoc.v_magic) != UNIXWARE_DISKMAGIC2) { - brelse(bh); - return; - } - printk(" vtoc.v_slice[1]; - /* I omit the 0th slice as it is the same as whole disk. */ - while (p - &l->vtoc.v_slice[0] < UNIXWARE_NUMSLICE) { - if ((current_minor & mask) == 0) - break; - - if (p->s_label != UNIXWARE_FS_UNUSED) { - add_partition(hd, current_minor, START_SECT(p), NR_SECTS(p)); - current_minor++; - } - p++; - } - /* Use bforget, as we have changed the disk setup */ - bforget(bh); - printk(" >"); -} -#endif - -static int msdos_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector) -{ - int i, minor = current_minor; - struct buffer_head *bh; - struct partition *p; - unsigned char *data; - int mask = (1 << hd->minor_shift) - 1; - int sector_size = get_hardsect_size(dev) / 512; -#ifdef CONFIG_BSD_DISKLABEL - /* no bsd disklabel as a default */ - kdev_t bsd_kdev = 0; - int bsd_maxpart = BSD_MAXPARTITIONS; -#endif -#ifdef CONFIG_BLK_DEV_IDE - int tested_for_xlate = 0; - -read_mbr: -#endif - if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) { - printk(" unable to read partition table\n"); - return -1; - } - data = bh->b_data; -#ifdef CONFIG_BLK_DEV_IDE -check_table: -#endif - /* Use bforget(), because we have potentially changed the disk geometry */ - if (*(unsigned short *) (0x1fe + data) != cpu_to_le16(MSDOS_LABEL_MAGIC)) { - bforget(bh); - return 0; - } - p = (struct partition *) (0x1be + data); - -#ifdef CONFIG_BLK_DEV_IDE - if (!tested_for_xlate++) { /* Do this only once per disk */ - /* - * Look for various forms of IDE disk geometry translation - */ - extern int ide_xlate_1024(kdev_t, int, const char *); - unsigned int sig = le16_to_cpu(*(unsigned short *)(data + 2)); - if (SYS_IND(p) == EZD_PARTITION) { - /* - * The remainder of the disk must be accessed using - * a translated geometry that reduces the number of - * apparent cylinders to less than 1024 if possible. - * - * ide_xlate_1024() will take care of the necessary - * adjustments to fool fdisk/LILO and partition check. - */ - if (ide_xlate_1024(dev, -1, " [EZD]")) { - data += 512; - goto check_table; - } - } else if (SYS_IND(p) == DM6_PARTITION) { - - /* - * Everything on the disk is offset by 63 sectors, - * including a "new" MBR with its own partition table, - * and the remainder of the disk must be accessed using - * a translated geometry that reduces the number of - * apparent cylinders to less than 1024 if possible. - * - * ide_xlate_1024() will take care of the necessary - * adjustments to fool fdisk/LILO and partition check. - */ - if (ide_xlate_1024(dev, 1, " [DM6:DDO]")) { - bforget(bh); - goto read_mbr; /* start over with new MBR */ - } - } else if (sig <= 0x1ae && - *(unsigned short *)(data + sig) == cpu_to_le16(0x55AA) && - (1 & *(unsigned char *)(data + sig + 2))) { - /* DM6 signature in MBR, courtesy of OnTrack */ - (void) ide_xlate_1024 (dev, 0, " [DM6:MBR]"); - } else if (SYS_IND(p) == DM6_AUX1PARTITION || SYS_IND(p) == DM6_AUX3PARTITION) { - /* - * DM6 on other than the first (boot) drive - */ - (void) ide_xlate_1024(dev, 0, " [DM6:AUX]"); - } else { - /* - * Examine the partition table for common translations. - * This is useful for drives in situations where the - * translated geometry is unavailable from the BIOS. - */ - for (i = 0; i < 4; i++) { - struct partition *q = &p[i]; - if (NR_SECTS(q) - && (q->sector & 63) == 1 - && (q->end_sector & 63) == 63) { - unsigned int heads = q->end_head + 1; - if (heads == 32 || heads == 64 || - heads == 128 || heads == 240 || - heads == 255) { - (void) ide_xlate_1024(dev, heads, " [PTBL]"); - break; - } - } - } - } - } -#endif /* CONFIG_BLK_DEV_IDE */ - - current_minor += 4; /* first "extra" minor (for extended partitions) */ - for (i=1 ; i<=4 ; minor++,i++,p++) { - if (!NR_SECTS(p)) - continue; - add_partition(hd, minor, first_sector+START_SECT(p)*sector_size, NR_SECTS(p)*sector_size); - if (is_extended_partition(p)) { - printk(" <"); - /* - * If we are rereading the partition table, we need - * to set the size of the partition so that we will - * be able to bread the block containing the extended - * partition info. - */ - hd->sizes[minor] = hd->part[minor].nr_sects - >> (BLOCK_SIZE_BITS - 9); - extended_partition(hd, MKDEV(hd->major, minor)); - printk(" >"); - /* prevent someone doing mkfs or mkswap on an - extended partition, but leave room for LILO */ - if (hd->part[minor].nr_sects > 2) - hd->part[minor].nr_sects = 2; - } -#ifdef CONFIG_BSD_DISKLABEL - /* tag first disklabel for late recognition */ - if (SYS_IND(p) == BSD_PARTITION || SYS_IND(p) == NETBSD_PARTITION) { - printk("!"); - if (!bsd_kdev) - bsd_kdev = MKDEV(hd->major, minor); - } else if (SYS_IND(p) == OPENBSD_PARTITION) { - printk("!"); - if (!bsd_kdev) { - bsd_kdev = MKDEV(hd->major, minor); - bsd_maxpart = OPENBSD_MAXPARTITIONS; - } - } -#endif -#ifdef CONFIG_UNIXWARE_DISKLABEL - if (SYS_IND(p) == UNIXWARE_PARTITION) - unixware_partition(hd, MKDEV(hd->major, minor)); -#endif -#ifdef CONFIG_SOLARIS_X86_PARTITION - - /* james@bpgc.com: Solaris has a nasty indicator: 0x82 - * which also means linux swap. For that reason, all - * of the prints are done inside the - * solaris_x86_partition routine */ - - if(SYS_IND(p) == SOLARIS_X86_PARTITION) { - solaris_x86_partition(hd, MKDEV(hd->major, minor), - first_sector+START_SECT(p)); - } -#endif - } -#ifdef CONFIG_BSD_DISKLABEL - if (bsd_kdev) { - printk(" <"); - bsd_disklabel_partition(hd, bsd_kdev, bsd_maxpart); - printk(" >"); - } -#endif - /* - * Check for old-style Disk Manager partition table - */ - if (*(unsigned short *) (data+0xfc) == cpu_to_le16(MSDOS_LABEL_MAGIC)) { - p = (struct partition *) (0x1be + data); - for (i = 4 ; i < 16 ; i++, current_minor++) { - p--; - if ((current_minor & mask) == 0) - break; - if (!(START_SECT(p) && NR_SECTS(p))) - continue; - add_partition(hd, current_minor, START_SECT(p), NR_SECTS(p)); - } - } - printk("\n"); - bforget(bh); - return 1; -} - -#endif /* CONFIG_MSDOS_PARTITION */ - -#ifdef CONFIG_OSF_PARTITION - -static int osf_partition(struct gendisk *hd, unsigned int dev, unsigned long first_sector) -{ - int i; - int mask = (1 << hd->minor_shift) - 1; - struct buffer_head *bh; - struct disklabel { - u32 d_magic; - u16 d_type,d_subtype; - u8 d_typename[16]; - u8 d_packname[16]; - u32 d_secsize; - u32 d_nsectors; - u32 d_ntracks; - u32 d_ncylinders; - u32 d_secpercyl; - u32 d_secprtunit; - u16 d_sparespertrack; - u16 d_sparespercyl; - u32 d_acylinders; - u16 d_rpm, d_interleave, d_trackskew, d_cylskew; - u32 d_headswitch, d_trkseek, d_flags; - u32 d_drivedata[5]; - u32 d_spare[5]; - u32 d_magic2; - u16 d_checksum; - u16 d_npartitions; - u32 d_bbsize, d_sbsize; - struct d_partition { - u32 p_size; - u32 p_offset; - u32 p_fsize; - u8 p_fstype; - u8 p_frag; - u16 p_cpg; - } d_partitions[8]; - } * label; - struct d_partition * partition; -#define DISKLABELMAGIC (0x82564557UL) - - if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) { - printk("unable to read partition table\n"); - return -1; - } - label = (struct disklabel *) (bh->b_data+64); - partition = label->d_partitions; - if (label->d_magic != DISKLABELMAGIC) { - brelse(bh); - return 0; - } - if (label->d_magic2 != DISKLABELMAGIC) { - brelse(bh); - return 0; - } - for (i = 0 ; i < label->d_npartitions; i++, partition++) { - if ((current_minor & mask) == 0) - break; - if (partition->p_size) - add_partition(hd, current_minor, - first_sector+partition->p_offset, - partition->p_size); - current_minor++; - } - printk("\n"); - brelse(bh); - return 1; -} - -#endif /* CONFIG_OSF_PARTITION */ - -#ifdef CONFIG_SUN_PARTITION - -static int sun_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector) -{ - int i, csum; - unsigned short *ush; - struct buffer_head *bh; - struct sun_disklabel { - unsigned char info[128]; /* Informative text string */ - unsigned char spare[292]; /* Boot information etc. */ - unsigned short rspeed; /* Disk rotational speed */ - unsigned short pcylcount; /* Physical cylinder count */ - unsigned short sparecyl; /* extra sects per cylinder */ - unsigned char spare2[4]; /* More magic... */ - unsigned short ilfact; /* Interleave factor */ - unsigned short ncyl; /* Data cylinder count */ - unsigned short nacyl; /* Alt. cylinder count */ - unsigned short ntrks; /* Tracks per cylinder */ - unsigned short nsect; /* Sectors per track */ - unsigned char spare3[4]; /* Even more magic... */ - struct sun_partition { - __u32 start_cylinder; - __u32 num_sectors; - } partitions[8]; - unsigned short magic; /* Magic number */ - unsigned short csum; /* Label xor'd checksum */ - } * label; - struct sun_partition *p; - unsigned long spc; -#define SUN_LABEL_MAGIC 0xDABE - - if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) { - printk("Dev %s: unable to read partition table\n", - kdevname(dev)); - return -1; - } - label = (struct sun_disklabel *) bh->b_data; - p = label->partitions; - if (be16_to_cpu(label->magic) != SUN_LABEL_MAGIC) { - printk("Dev %s Sun disklabel: bad magic %04x\n", - kdevname(dev), be16_to_cpu(label->magic)); - brelse(bh); - return 0; - } - /* Look at the checksum */ - ush = ((unsigned short *) (label+1)) - 1; - for(csum = 0; ush >= ((unsigned short *) label);) - csum ^= *ush--; - if(csum) { - printk("Dev %s Sun disklabel: Csum bad, label corrupted\n", - kdevname(dev)); - brelse(bh); - return 0; - } - /* All Sun disks have 8 partition entries */ - spc = be16_to_cpu(label->ntrks) * be16_to_cpu(label->nsect); - for(i=0; i < 8; i++, p++) { - unsigned long st_sector; - int num_sectors; +extern void console_map_init(void); +extern int soc_probe(void); - st_sector = first_sector + be32_to_cpu(p->start_cylinder) * spc; - num_sectors = be32_to_cpu(p->num_sectors); - if (num_sectors) - add_partition(hd, current_minor, st_sector, num_sectors); - current_minor++; - } - printk("\n"); - brelse(bh); - return 1; -} - -#endif /* CONFIG_SUN_PARTITION */ - -#ifdef CONFIG_SGI_PARTITION - -static int sgi_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector) -{ - int i, csum, magic; - unsigned int *ui, start, blocks, cs; - struct buffer_head *bh; - struct sgi_disklabel { - int magic_mushroom; /* Big fat spliff... */ - short root_part_num; /* Root partition number */ - short swap_part_num; /* Swap partition number */ - char boot_file[16]; /* Name of boot file for ARCS */ - unsigned char _unused0[48]; /* Device parameter useless crapola.. */ - struct sgi_volume { - char name[8]; /* Name of volume */ - int block_num; /* Logical block number */ - int num_bytes; /* How big, in bytes */ - } volume[15]; - struct sgi_partition { - int num_blocks; /* Size in logical blocks */ - int first_block; /* First logical block */ - int type; /* Type of this partition */ - } partitions[16]; - int csum; /* Disk label checksum */ - int _unused1; /* Padding */ - } *label; - struct sgi_partition *p; -#define SGI_LABEL_MAGIC 0x0be5a941 - - if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) { - printk("Dev %s: unable to read partition table\n", kdevname(dev)); - return -1; - } - label = (struct sgi_disklabel *) bh->b_data; - p = &label->partitions[0]; - magic = label->magic_mushroom; - if(be32_to_cpu(magic) != SGI_LABEL_MAGIC) { - printk("Dev %s SGI disklabel: bad magic %08x\n", - kdevname(dev), magic); - brelse(bh); - return 0; - } - ui = ((unsigned int *) (label + 1)) - 1; - for(csum = 0; ui >= ((unsigned int *) label);) { - cs = *ui--; - csum += be32_to_cpu(cs); - } - if(csum) { - printk("Dev %s SGI disklabel: csum bad, label corrupted\n", - kdevname(dev)); - brelse(bh); - return 0; - } - /* All SGI disk labels have 16 partitions, disks under Linux only - * have 15 minor's. Luckily there are always a few zero length - * partitions which we don't care about so we never overflow the - * current_minor. - */ - for(i = 0; i < 16; i++, p++) { - blocks = be32_to_cpu(p->num_blocks); - start = be32_to_cpu(p->first_block); - if(!blocks) - continue; - add_partition(hd, current_minor, start, blocks); - current_minor++; - } - printk("\n"); - brelse(bh); - return 1; -} - -#endif - -#ifdef CONFIG_AMIGA_PARTITION -#include - -static __inline__ u32 -checksum_block(u32 *m, int size) -{ - u32 sum = 0; - - while (size--) - sum += htonl(*m++); - return sum; -} - -static int -amiga_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector) -{ - struct buffer_head *bh; - struct RigidDiskBlock *rdb; - struct PartitionBlock *pb; - int start_sect; - int nr_sects; - int blk; - int part, res; - int old_blocksize; - int blocksize; - - old_blocksize = get_ptable_blocksize(dev); - blocksize = get_hardsect_size(dev); - - set_blocksize(dev,blocksize); - res = 0; - - for (blk = 0; blk < RDB_ALLOCATION_LIMIT; blk++) { - if(!(bh = bread(dev,blk,blocksize))) { - printk("Dev %s: unable to read RDB block %d\n", - kdevname(dev),blk); - goto rdb_done; - } - if (*(u32 *)bh->b_data == htonl(IDNAME_RIGIDDISK)) { - rdb = (struct RigidDiskBlock *)bh->b_data; - if (checksum_block((u32 *)bh->b_data,htonl(rdb->rdb_SummedLongs) & 0x7F)) { - /* Try again with 0xdc..0xdf zeroed, Windows might have - * trashed it. - */ - *(u32 *)(&bh->b_data[0xdc]) = 0; - if (checksum_block((u32 *)bh->b_data, - htonl(rdb->rdb_SummedLongs) & 0x7F)) { - brelse(bh); - printk("Dev %s: RDB in block %d has bad checksum\n", - kdevname(dev),blk); - continue; - } - printk("Warning: Trashed word at 0xd0 in block %d " - "ignored in checksum calculation\n",blk); - } - printk(" RDSK"); - blk = htonl(rdb->rdb_PartitionList); - brelse(bh); - for (part = 1; blk > 0 && part <= 16; part++) { - if (!(bh = bread(dev,blk,blocksize))) { - printk("Dev %s: unable to read partition block %d\n", - kdevname(dev),blk); - goto rdb_done; - } - pb = (struct PartitionBlock *)bh->b_data; - blk = htonl(pb->pb_Next); - if (pb->pb_ID == htonl(IDNAME_PARTITION) && checksum_block( - (u32 *)pb,htonl(pb->pb_SummedLongs) & 0x7F) == 0 ) { - - /* Tell Kernel about it */ - - if (!(nr_sects = (htonl(pb->pb_Environment[10]) + 1 - - htonl(pb->pb_Environment[9])) * - htonl(pb->pb_Environment[3]) * - htonl(pb->pb_Environment[5]))) { - brelse(bh); - continue; - } - start_sect = htonl(pb->pb_Environment[9]) * - htonl(pb->pb_Environment[3]) * - htonl(pb->pb_Environment[5]); - add_partition(hd,current_minor,start_sect,nr_sects); - current_minor++; - res = 1; - } - brelse(bh); - } - printk("\n"); - } - else - brelse(bh); - } - -rdb_done: - set_blocksize(dev,old_blocksize); - return res; -} -#endif /* CONFIG_AMIGA_PARTITION */ - -#ifdef CONFIG_MAC_PARTITION -#include - -/* - * Code to understand MacOS partition tables. - */ - -#define MAC_PARTITION_MAGIC 0x504d - -/* type field value for A/UX or other Unix partitions */ -#define APPLE_AUX_TYPE "Apple_UNIX_SVR2" - -struct mac_partition { - __u16 signature; /* expected to be MAC_PARTITION_MAGIC */ - __u16 res1; - __u32 map_count; /* # blocks in partition map */ - __u32 start_block; /* absolute starting block # of partition */ - __u32 block_count; /* number of blocks in partition */ - char name[32]; /* partition name */ - char type[32]; /* string type description */ - __u32 data_start; /* rel block # of first data block */ - __u32 data_count; /* number of data blocks */ - __u32 status; /* partition status bits */ - __u32 boot_start; - __u32 boot_size; - __u32 boot_load; - __u32 boot_load2; - __u32 boot_entry; - __u32 boot_entry2; - __u32 boot_cksum; - char processor[16]; /* identifies ISA of boot */ - /* there is more stuff after this that we don't need */ -}; - -#define MAC_STATUS_BOOTABLE 8 /* partition is bootable */ - -#define MAC_DRIVER_MAGIC 0x4552 - -/* Driver descriptor structure, in block 0 */ -struct mac_driver_desc { - __u16 signature; /* expected to be MAC_DRIVER_MAGIC */ - __u16 block_size; - __u32 block_count; - /* ... more stuff */ -}; - -static int mac_partition(struct gendisk *hd, kdev_t dev, unsigned long fsec) +void __init device_init(void) { - struct buffer_head *bh; - int blk, blocks_in_map; - int dev_bsize, dev_pos, pos; - unsigned secsize; -#ifdef CONFIG_PPC - int first_bootable = 1; -#endif - struct mac_partition *part; - struct mac_driver_desc *md; - - dev_bsize = get_ptable_blocksize(dev); - dev_pos = 0; - /* Get 0th block and look at the first partition map entry. */ - if ((bh = bread(dev, 0, dev_bsize)) == 0) { - printk("%s: error reading partition table\n", - kdevname(dev)); - return -1; - } - md = (struct mac_driver_desc *) bh->b_data; - if (be16_to_cpu(md->signature) != MAC_DRIVER_MAGIC) { - brelse(bh); - return 0; - } - secsize = be16_to_cpu(md->block_size); - if (secsize >= dev_bsize) { - brelse(bh); - dev_pos = secsize; - if ((bh = bread(dev, secsize/dev_bsize, dev_bsize)) == 0) { - printk("%s: error reading partition table\n", - kdevname(dev)); - return -1; - } - } - part = (struct mac_partition *) (bh->b_data + secsize - dev_pos); - if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC) { - brelse(bh); - return 0; /* not a MacOS disk */ - } - blocks_in_map = be32_to_cpu(part->map_count); - for (blk = 1; blk <= blocks_in_map; ++blk) { - pos = blk * secsize; - if (pos >= dev_pos + dev_bsize) { - brelse(bh); - dev_pos = pos; - if ((bh = bread(dev, pos/dev_bsize, dev_bsize)) == 0) { - printk("%s: error reading partition table\n", - kdevname(dev)); - return -1; - } - } - part = (struct mac_partition *) (bh->b_data + pos - dev_pos); - if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC) - break; - blocks_in_map = be32_to_cpu(part->map_count); - add_partition(hd, current_minor, - fsec + be32_to_cpu(part->start_block) * (secsize/512), - be32_to_cpu(part->block_count) * (secsize/512)); - -#ifdef CONFIG_PPC - /* - * If this is the first bootable partition, tell the - * setup code, in case it wants to make this the root. - */ - if ( (_machine == _MACH_Pmac) && first_bootable - && (be32_to_cpu(part->status) & MAC_STATUS_BOOTABLE) - && strcasecmp(part->processor, "powerpc") == 0) { - note_bootable_part(dev, blk); - first_bootable = 0; - } -#endif /* CONFIG_PPC */ - - ++current_minor; - } - brelse(bh); - printk("\n"); - return 1; -} - -#endif /* CONFIG_MAC_PARTITION */ - -#ifdef CONFIG_ATARI_PARTITION -#include - -/* ++guenther: this should be settable by the user ("make config")?. - */ -#define ICD_PARTS - -static int atari_partition (struct gendisk *hd, kdev_t dev, - unsigned long first_sector) -{ - int minor = current_minor, m_lim = current_minor + hd->max_p; - struct buffer_head *bh; - struct rootsector *rs; - struct partition_info *pi; - ulong extensect; - unsigned int psum; - int i; -#ifdef ICD_PARTS - int part_fmt = 0; /* 0:unknown, 1:AHDI, 2:ICD/Supra */ -#endif - - bh = bread (dev, 0, get_ptable_blocksize(dev)); - if (!bh) { - printk (" unable to read block 0 (partition table)\n"); - return -1; - } - - /* Verify this is an Atari rootsector: */ - psum=0; - for (i=0;i<256;i++) { - psum+=ntohs(((__u16 *) (bh->b_data))[i]); - } - if ((psum & 0xFFFF) != 0x1234) { - brelse(bh); - return 0; - } - - rs = (struct rootsector *) bh->b_data; - pi = &rs->part[0]; - printk (" AHDI"); - for (; pi < &rs->part[4] && minor < m_lim; minor++, pi++) - { - if (pi->flg & 1) - /* active partition */ - { - if (memcmp (pi->id, "XGM", 3) == 0) - /* extension partition */ - { - struct rootsector *xrs; - struct buffer_head *xbh; - ulong partsect; - -#ifdef ICD_PARTS - part_fmt = 1; -#endif - printk(" XGM<"); - partsect = extensect = ntohl(pi->st); - while (1) - { - xbh = bread (dev, partsect / 2, get_ptable_blocksize(dev)); - if (!xbh) - { - printk (" block %ld read failed\n", partsect); - brelse(bh); - return 0; - } - if (partsect & 1) - xrs = (struct rootsector *) &xbh->b_data[512]; - else - xrs = (struct rootsector *) &xbh->b_data[0]; - - /* ++roman: sanity check: bit 0 of flg field must be set */ - if (!(xrs->part[0].flg & 1)) { - printk( "\nFirst sub-partition in extended partition is not valid!\n" ); - break; - } - - add_partition(hd, minor, partsect + ntohl(xrs->part[0].st), - ntohl(xrs->part[0].siz)); - - if (!(xrs->part[1].flg & 1)) { - /* end of linked partition list */ - brelse( xbh ); - break; - } - if (memcmp( xrs->part[1].id, "XGM", 3 ) != 0) { - printk( "\nID of extended partition is not XGM!\n" ); - brelse( xbh ); - break; - } - - partsect = ntohl(xrs->part[1].st) + extensect; - brelse (xbh); - minor++; - if (minor >= m_lim) { - printk( "\nMaximum number of partitions reached!\n" ); - break; - } - } - printk(" >"); - } - else - { - /* we don't care about other id's */ - add_partition (hd, minor, ntohl(pi->st), ntohl(pi->siz)); - } - } - } -#ifdef ICD_PARTS - if ( part_fmt!=1 ) /* no extended partitions -> test ICD-format */ - { - pi = &rs->icdpart[0]; - /* sanity check: no ICD format if first partition invalid */ - if (memcmp (pi->id, "GEM", 3) == 0 || - memcmp (pi->id, "BGM", 3) == 0 || - memcmp (pi->id, "LNX", 3) == 0 || - memcmp (pi->id, "SWP", 3) == 0 || - memcmp (pi->id, "RAW", 3) == 0 ) - { - printk(" ICD<"); - for (; pi < &rs->icdpart[8] && minor < m_lim; minor++, pi++) - { - /* accept only GEM,BGM,RAW,LNX,SWP partitions */ - if (pi->flg & 1 && - (memcmp (pi->id, "GEM", 3) == 0 || - memcmp (pi->id, "BGM", 3) == 0 || - memcmp (pi->id, "LNX", 3) == 0 || - memcmp (pi->id, "SWP", 3) == 0 || - memcmp (pi->id, "RAW", 3) == 0) ) - { - part_fmt = 2; - add_partition (hd, minor, ntohl(pi->st), ntohl(pi->siz)); - } - } - printk(" >"); - } - } -#endif - brelse (bh); - - printk ("\n"); - - return 1; -} -#endif /* CONFIG_ATARI_PARTITION */ - -#ifdef CONFIG_ULTRIX_PARTITION - -static int ultrix_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector) -{ - int i, minor = current_minor; - struct buffer_head *bh; - struct ultrix_disklabel { - s32 pt_magic; /* magic no. indicating part. info exits */ - s32 pt_valid; /* set by driver if pt is current */ - struct pt_info { - s32 pi_nblocks; /* no. of sectors */ - u32 pi_blkoff; /* block offset for start */ - } pt_part[8]; - } *label; - -#define PT_MAGIC 0x032957 /* Partition magic number */ -#define PT_VALID 1 /* Indicates if struct is valid */ - -#define SBLOCK ((unsigned long)((16384 - sizeof(struct ultrix_disklabel)) \ - /get_ptable_blocksize(dev))) - - bh = bread (dev, SBLOCK, get_ptable_blocksize(dev)); - if (!bh) { - printk (" unable to read block 0x%lx\n", SBLOCK); - return -1; - } - - label = (struct ultrix_disklabel *)(bh->b_data - + get_ptable_blocksize(dev) - - sizeof(struct ultrix_disklabel)); - - if (label->pt_magic == PT_MAGIC && label->pt_valid == PT_VALID) { - for (i=0; i<8; i++, minor++) - if (label->pt_part[i].pi_nblocks) - add_partition(hd, minor, - label->pt_part[i].pi_blkoff, - label->pt_part[i].pi_nblocks); - brelse(bh); - printk ("\n"); - return 1; - } else { - brelse(bh); - return 0; - } -} - -#endif /* CONFIG_ULTRIX_PARTITION */ - -static void check_partition(struct gendisk *hd, kdev_t dev) -{ - static int first_time = 1; - unsigned long first_sector; - char buf[40]; - - if (first_time) - printk(KERN_INFO "Partition check:\n"); - first_time = 0; - first_sector = hd->part[MINOR(dev)].start_sect; - - /* - * This is a kludge to allow the partition check to be - * skipped for specific drives (e.g. IDE CD-ROM drives) - */ - if ((int)first_sector == -1) { - hd->part[MINOR(dev)].start_sect = 0; - return; - } - - printk(KERN_INFO " %s:", disk_name(hd, MINOR(dev), buf)); -#ifdef CONFIG_MSDOS_PARTITION - if (msdos_partition(hd, dev, first_sector)) - return; -#endif -#ifdef CONFIG_OSF_PARTITION - if (osf_partition(hd, dev, first_sector)) - return; -#endif -#ifdef CONFIG_SUN_PARTITION - if(sun_partition(hd, dev, first_sector)) - return; -#endif -#ifdef CONFIG_AMIGA_PARTITION - if(amiga_partition(hd, dev, first_sector)) - return; -#endif -#ifdef CONFIG_ATARI_PARTITION - if(atari_partition(hd, dev, first_sector)) - return; -#endif -#ifdef CONFIG_MAC_PARTITION - if (mac_partition(hd, dev, first_sector)) - return; -#endif -#ifdef CONFIG_SGI_PARTITION - if(sgi_partition(hd, dev, first_sector)) - return; -#endif -#ifdef CONFIG_ULTRIX_PARTITION - if(ultrix_partition(hd, dev, first_sector)) - return; -#endif - printk(" unknown partition table\n"); -} - -/* This function is used to re-read partition tables for removable disks. - Much of the cleanup from the old partition tables should have already been - done */ - -/* This function will re-read the partition tables for a given device, -and set things back up again. There are some important caveats, -however. You must ensure that no one is using the device, and no one -can start using the device while this function is being executed. */ - -void resetup_one_dev(struct gendisk *dev, int drive) -{ - int i; - int first_minor = drive << dev->minor_shift; - int end_minor = first_minor + dev->max_p; - - blk_size[dev->major] = NULL; - current_minor = 1 + first_minor; - check_partition(dev, MKDEV(dev->major, first_minor)); - - /* - * We need to set the sizes array before we will be able to access - * any of the partitions on this device. - */ - if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */ - for (i = first_minor; i < end_minor; i++) - dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9); - blk_size[dev->major] = dev->sizes; - } -} - -static inline void setup_dev(struct gendisk *dev) -{ - int i, drive; - int end_minor = dev->max_nr * dev->max_p; - - blk_size[dev->major] = NULL; - for (i = 0; i < end_minor; i++) { - dev->part[i].start_sect = 0; - dev->part[i].nr_sects = 0; - dev->sizes[i] = 0; - } - dev->init(dev); - for (drive = 0; drive < dev->nr_real; drive++) - resetup_one_dev(dev, drive); -} - -static int __init device_setup(void) -{ - extern void console_map_init(void); - extern void cpqarray_init(void); -#ifdef CONFIG_PARPORT - extern int parport_init(void) __init; -#endif -#ifdef CONFIG_MD_BOOT - extern void md_setup_drive(void) __init; -#endif -#ifdef CONFIG_FC4_SOC - extern int soc_probe(void); -#endif - struct gendisk *p; - #ifdef CONFIG_PARPORT parport_init(); #endif @@ -1431,43 +54,4 @@ static int __init device_setup(void) #ifdef CONFIG_VT console_map_init(); #endif - - for (p = gendisk_head ; p ; p=p->next) - setup_dev(p); - -#ifdef CONFIG_BLK_DEV_RAM -#ifdef CONFIG_BLK_DEV_INITRD - if (initrd_start && mount_initrd) initrd_load(); - else -#endif - rd_load(); -#endif -#ifdef CONFIG_MD_BOOT - md_setup_drive(); -#endif - return 0; } - -__initcall(device_setup); - -#ifdef CONFIG_PROC_FS -int get_partition_list(char * page) -{ - struct gendisk *p; - char buf[40]; - int n, len; - - len = sprintf(page, "major minor #blocks name\n\n"); - for (p = gendisk_head; p; p = p->next) { - for (n=0; n < (p->nr_real << p->minor_shift); n++) { - if (p->part[n].nr_sects && len < PAGE_SIZE - 80) { - len += sprintf(page+len, - "%4d %4d %10d %s\n", - p->major, n, p->sizes[n], - disk_name(p, n, buf)); - } - } - } - return len; -} -#endif diff --git a/drivers/block/hd.c b/drivers/block/hd.c index 389bf168eb89..8e995be1b822 100644 --- a/drivers/block/hd.c +++ b/drivers/block/hd.c @@ -27,7 +27,6 @@ /* Uncomment the following if you want verbose error reports. */ /* #define VERBOSE_ERRORS */ -#include #include #include #include @@ -52,7 +51,9 @@ #ifdef __arm__ #undef HD_IRQ +#endif #include +#ifdef __arm__ #define HD_IRQ IRQ_HARDDISK #endif diff --git a/drivers/block/ide.c b/drivers/block/ide.c index 7b0df6e31de4..31556ede4f67 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -434,8 +434,8 @@ void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount) void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) { ++bytecount; -#ifdef CONFIG_ATARI - if (MACH_IS_ATARI) { +#if defined(CONFIG_ATARI) || defined(CONFIG_Q40) + if (MACH_IS_ATARI || MACH_IS_Q40) { /* Atari has a byte-swapped IDE interface */ insw_swapw(IDE_DATA_REG, buffer, bytecount / 2); return; @@ -449,8 +449,8 @@ void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) { ++bytecount; -#ifdef CONFIG_ATARI - if (MACH_IS_ATARI) { +#if defined(CONFIG_ATARI) || defined(CONFIG_Q40) + if (MACH_IS_ATARI || MACH_IS_Q40) { /* Atari has a byte-swapped IDE interface */ outsw_swapw(IDE_DATA_REG, buffer, bytecount / 2); return; diff --git a/drivers/block/md.c b/drivers/block/md.c index 66023e933b39..69ff1813b795 100644 --- a/drivers/block/md.c +++ b/drivers/block/md.c @@ -740,7 +740,7 @@ int md_make_request (int minor, int rw, struct buffer_head * bh) if (buffer_locked(bh)) return 0; set_bit(BH_Lock, &bh->b_state); - if (rw == WRITE || rw == WRITEA) { + if (rw == WRITE) { if (!buffer_dirty(bh)) { bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state)); return 0; diff --git a/drivers/block/raid1.c b/drivers/block/raid1.c index 7efb7840012b..6671f83e8db6 100644 --- a/drivers/block/raid1.c +++ b/drivers/block/raid1.c @@ -159,7 +159,7 @@ void raid1_end_request (struct buffer_head *bh, int uptodate) } /* - * WRITE or WRITEA. + * WRITE. */ PRINTK(("raid1_end_request(), write branch.\n")); @@ -215,15 +215,14 @@ raid1_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh) memset (r1_bh, 0, sizeof (struct raid1_bh)); /* - * make_request() can abort the operation when READA or WRITEA are being + * make_request() can abort the operation when READA is being * used and no empty request is available. * * Currently, just replace the command with READ/WRITE. */ if (rw == READA) rw = READ; - if (rw == WRITEA) rw = WRITE; - if (rw == WRITE || rw == WRITEA) + if (rw == WRITE) mark_buffer_clean(bh); /* Too early ? */ /* @@ -269,7 +268,7 @@ raid1_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh) } /* - * WRITE or WRITEA. + * WRITE. */ PRINTK(("raid1_make_request(n=%d), write branch.\n",n)); diff --git a/drivers/block/raid5.c b/drivers/block/raid5.c index 3e60f92ce4de..b41a720a5058 100644 --- a/drivers/block/raid5.c +++ b/drivers/block/raid5.c @@ -1206,7 +1206,6 @@ static int raid5_make_request (struct md_dev *mddev, int rw, struct buffer_head struct stripe_head *sh; if (rw == READA) rw = READ; - if (rw == WRITEA) rw = WRITE; new_sector = raid5_compute_sector(bh->b_rsector, raid_disks, data_disks, &dd_idx, &pd_idx, raid_conf); diff --git a/drivers/block/z2ram.c b/drivers/block/z2ram.c index cf142b094150..f75150e4a4f5 100644 --- a/drivers/block/z2ram.c +++ b/drivers/block/z2ram.c @@ -27,7 +27,6 @@ #define MAJOR_NR Z2RAM_MAJOR -#include #include #include #include diff --git a/drivers/char/adbmouse.c b/drivers/char/adbmouse.c index 83f806c4c3fa..df4784f71cb9 100644 --- a/drivers/char/adbmouse.c +++ b/drivers/char/adbmouse.c @@ -40,7 +40,7 @@ #ifdef __powerpc__ #include #endif -#ifdef __mc68000__ +#if defined(__mc68000__) || defined(MODULE) #include #endif @@ -186,8 +186,6 @@ __initfunc(void adb_mouse_setup(char *str, int *ints)) } #ifdef MODULE -#include - int init_module(void) { return adb_mouse_init(); diff --git a/drivers/char/console.c b/drivers/char/console.c index db165915c3e3..f003c61de752 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -1984,7 +1984,7 @@ void vt_console_print(struct console *co, const char * b, unsigned count) static unsigned long printing = 0; const ushort *start; ushort cnt = 0; - ushort myx = x; + ushort myx; /* console busy or not yet initialized */ if (!printable || test_and_set_bit(0, &printing)) @@ -1993,6 +1993,10 @@ void vt_console_print(struct console *co, const char * b, unsigned count) if (kmsg_redirect && vc_cons_allocated(kmsg_redirect - 1)) currcons = kmsg_redirect - 1; + /* read `x' only after setting currecons properly (otherwise + the `x' macro will read the x of the foreground console). */ + myx = x; + if (!vc_cons_allocated(currcons)) { /* impossible */ printk("vt_console_print: tty %d not allocated ??\n", currcons+1); diff --git a/drivers/char/defkeymap.c b/drivers/char/defkeymap.c index d8ac176c251d..42fae3568711 100644 --- a/drivers/char/defkeymap.c +++ b/drivers/char/defkeymap.c @@ -233,6 +233,7 @@ struct kbdiacr accent_table[MAX_DIACR] = { {'A', 'A', '\305'}, {'a', 'a', '\345'}, {'A', 'E', '\306'}, {'a', 'e', '\346'}, {',', 'C', '\307'}, {',', 'c', '\347'}, + {'\'', 'C', '\307'}, {'\'', 'c', '\347'}, {'`', 'E', '\310'}, {'`', 'e', '\350'}, {'\'', 'E', '\311'}, {'\'', 'e', '\351'}, {'^', 'E', '\312'}, {'^', 'e', '\352'}, @@ -259,4 +260,4 @@ struct kbdiacr accent_table[MAX_DIACR] = { {'s', 'z', '\337'}, {'i', 'j', '\377'}, }; -unsigned int accent_table_size = 68; +unsigned int accent_table_size = 70; diff --git a/drivers/char/ftape/lowlevel/ftape-setup.c b/drivers/char/ftape/lowlevel/ftape-setup.c index 01c231bb0c4f..1c590fde3fb7 100644 --- a/drivers/char/ftape/lowlevel/ftape-setup.c +++ b/drivers/char/ftape/lowlevel/ftape-setup.c @@ -60,12 +60,15 @@ static struct param_table { { "mach2", &ft_mach2, CONFIG_FT_MACH2, 0, 1} }; -void __init ftape_setup(char *str, int *ints) +static int __init ftape_setup(char *str) { int i; int param; + int ints[2]; + TRACE_FUN(ft_t_flow); + str = get_options(str, ARRAY_SIZE(ints), ints); if (str) { for (i=0; i < NR_ITEMS(config_params); i++) { if (strcmp(str,config_params[i].name) == 0){ @@ -81,13 +84,13 @@ void __init ftape_setup(char *str, int *ints) config_params[i].name, config_params[i].min, config_params[i].max); - TRACE_EXIT; + goto out; } if(config_params[i].var) { TRACE(ft_t_info, "%s=%d", str, param); *config_params[i].var = param; } - TRACE_EXIT; + goto out; } } } @@ -101,5 +104,8 @@ void __init ftape_setup(char *str, int *ints) } else { TRACE(ft_t_err, "botched ftape option"); } - TRACE_EXIT; + out: + TRACE_EXIT 1; } + +__setup("ftape=", ftape_setup); diff --git a/drivers/char/lp_intern.c b/drivers/char/lp_intern.c deleted file mode 100644 index b65d51118925..000000000000 --- a/drivers/char/lp_intern.c +++ /dev/null @@ -1,364 +0,0 @@ - -/* - * split into mid and low-level for better support of different hardware - * by Joerg Dorchain (dorchain@mpi-sb.mpg.de) - * - * Amiga printer device by Michael Rausch (linux@uni-koblenz.de); - * Atari support added by Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de); - * based upon work from - * - * Copyright (C) 1992 by Jim Weigand and Linus Torvalds - * Copyright (C) 1992,1993 by Michael K. Johnson - * - Thanks much to Gunter Windau for pointing out to me where the error - * checking ought to be. - * Copyright (C) 1993 by Nigel Gamble (added interrupt code) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_AMIGA -#include -#include -#endif -#ifdef CONFIG_ATARI -#include -#include -#include -#endif -#ifdef CONFIG_MVME16x -#include -#endif -#ifdef CONFIG_BVME6000 -#include -#endif -#include - -static int minor = -1; -MODULE_PARM(minor,"i"); - -static void lp_int_out(int, int); -static int lp_int_busy(int); -static int lp_int_pout(int); -static int lp_int_online(int); - - -static void -lp_int_out (int c, int dev) -{ - switch (m68k_machtype) - { -#ifdef CONFIG_AMIGA - case MACH_AMIGA: - { - int wait = 0; - while (wait != lp_table[dev]->wait) wait++; - ciaa.prb = c; - } - break; -#endif -#ifdef CONFIG_ATARI - case MACH_ATARI: - { - int wait = 0; - sound_ym.rd_data_reg_sel = 15; - sound_ym.wd_data = c; - sound_ym.rd_data_reg_sel = 14; - while (wait != lp_table[dev]->wait) wait++; - sound_ym.wd_data = sound_ym.rd_data_reg_sel & ~(1 << 5); - while (wait) wait--; - sound_ym.wd_data = sound_ym.rd_data_reg_sel | (1 << 5); - break; - } -#endif -#ifdef CONFIG_MVME16x - case MACH_MVME16x: - { - int wait = 0; - while (wait != lp_table[dev]->wait) wait++; - mvmelp.data = c; - break; - } -#endif -#ifdef CONFIG_BVME6000 - case MACH_BVME6000: - { - int wait = 0; - while (wait != lp_table[dev]->wait) wait++; - bvmepit.padr = c; - bvmepit.pacr |= 0x02; - break; - } -#endif - } -} - -static int -lp_int_busy (int dev) -{ - switch (m68k_machtype) - { -#ifdef CONFIG_AMIGA - case MACH_AMIGA: - return ciab.pra & 1; -#endif -#ifdef CONFIG_ATARI - case MACH_ATARI: - return mfp.par_dt_reg & 1; -#endif -#ifdef CONFIG_MVME16x - case MACH_MVME16x: - return mvmelp.isr & 1; -#endif -#ifdef CONFIG_BVME6000 - case MACH_BVME6000: - return 0 /* !(bvmepit.psr & 0x40) */ ; -#endif - default: - return 0; - } -} - -static int -lp_int_pout (int dev) -{ - switch (m68k_machtype) - { -#ifdef CONFIG_AMIGA - case MACH_AMIGA: - return ciab.pra & 2; -#endif -#ifdef CONFIG_ATARI - case MACH_ATARI: - return 0; -#endif -#ifdef CONFIG_MVME16x - case MACH_MVME16x: - return mvmelp.isr & 2; -#endif -#ifdef CONFIG_BVME6000 - case MACH_BVME6000: - return 0; -#endif - default: - return 0; - } -} - -static int -lp_int_online (int dev) -{ - switch (m68k_machtype) - { -#ifdef CONFIG_AMIGA - case MACH_AMIGA: - return ciab.pra & 4; -#endif -#ifdef CONFIG_ATARI - case MACH_ATARI: - return !(mfp.par_dt_reg & 1); -#endif -#ifdef CONFIG_MVME16x - case MACH_MVME16x: - return mvmelp.isr & 4; -#endif -#ifdef CONFIG_BVME6000 - case MACH_BVME6000: - return 1; -#endif - default: - return 0; - } -} - -static void lp_int_interrupt(int irq, void *data, struct pt_regs *fp) -{ -#ifdef CONFIG_MVME16x - if (MACH_IS_MVME16x) - mvmelp.ack_icr |= 0x08; -#endif -#ifdef CONFIG_BVME6000 - if (MACH_IS_BVME6000) - bvmepit.pacr &= ~0x02; -#endif - lp_interrupt(minor); -} - -static int lp_int_open(int dev) -{ - MOD_INC_USE_COUNT; - return 0; -} - -static void lp_int_release(int dev) -{ - MOD_DEC_USE_COUNT; -} - -static struct lp_struct tab = { - "Builtin parallel port", /* name */ - 0, /* irq */ - lp_int_out, - lp_int_busy, - lp_int_pout, - lp_int_online, - 0, - NULL, /* ioctl */ - lp_int_open, - lp_int_release, - LP_EXIST, - LP_INIT_CHAR, - LP_INIT_TIME, - LP_INIT_WAIT, - NULL, - NULL, -}; - -int __init lp_internal_init(void) -{ -#ifdef CONFIG_AMIGA - if (MACH_IS_AMIGA && AMIGAHW_PRESENT(AMI_PARALLEL)) - { - ciaa.ddrb = 0xff; - ciab.ddra &= 0xf8; - if (lp_irq) - tab.irq = request_irq(IRQ_AMIGA_CIAA_FLG, lp_int_interrupt, - 0, "builtin printer port", lp_int_interrupt); - tab.base = (void *) &ciaa.prb; /* dummy, not used */ - tab.type = LP_AMIGA; - } -#endif -#ifdef CONFIG_ATARI - if (MACH_IS_ATARI) - { - unsigned long flags; - - save_flags(flags); - cli(); - sound_ym.rd_data_reg_sel = 7; - sound_ym.wd_data = (sound_ym.rd_data_reg_sel & 0x3f) | 0xc0; - restore_flags(flags); - if (lp_irq) - tab.irq = request_irq(IRQ_MFP_BUSY, lp_int_interrupt, - IRQ_TYPE_SLOW, "builtin printer port", lp_int_interrupt); - tab.base = (void *) &sound_ym.wd_data; /* dummy, not used */ - tab.type = LP_ATARI; - } -#endif -#ifdef CONFIG_MAC - if (MACH_IS_MAC) - return -ENODEV; -#endif -#ifdef CONFIG_MVME16x - if (MACH_IS_MVME16x) - { - unsigned long flags; - - if (!(mvme16x_config & MVME16x_CONFIG_GOT_LP)) - return -ENODEV; - - save_flags(flags); - cli(); - mvmelp.ack_icr = 0x08; - mvmelp.flt_icr = 0x08; - mvmelp.sel_icr = 0x08; - mvmelp.pe_icr = 0x08; - mvmelp.bsy_icr = 0x08; - mvmelp.cr = 0x10; - mvmelp.ack_icr = 0xd9; /* Int on trailing edge of ACK */ - restore_flags(flags); - - if (lp_irq) - tab.irq = request_irq(MVME167_IRQ_PRN, lp_int_interrupt, - 0, "builtin printer port", lp_int_interrupt); - tab.base = (void *)&mvmelp; /* dummy, not used */ - tab.type = LP_MVME167; - } -#endif -#ifdef CONFIG_BVME6000 - if (MACH_IS_BVME6000) - { - unsigned long flags; - - save_flags(flags); - cli(); - bvmepit.pgcr = 0x0f; - bvmepit.psrr = 0x18; - bvmepit.paddr = 0xff; - bvmepit.pcdr = (bvmepit.pcdr & 0xfc) | 0x02; - bvmepit.pcddr |= 0x03; - bvmepit.pacr = 0x78; - bvmepit.pbcr = 0x00; - bvmepit.pivr = BVME_IRQ_PRN; - bvmepit.pgcr = 0x1f; - restore_flags(flags); - - if (lp_irq) - tab.irq = request_irq(BVME_IRQ_PRN, lp_int_interrupt, - 0, "builtin printer port", lp_int_interrupt); - tab.base = (void *)&bvmepit; /* dummy, not used */ - tab.type = LP_BVME6000; - } -#endif - - - if ((minor = register_parallel(&tab, minor)) < 0) { - printk("builtin lp init: cant get a minor\n"); - if (lp_irq) { -#ifdef CONFIG_AMIGA - if (MACH_IS_AMIGA) - free_irq(IRQ_AMIGA_CIAA_FLG, lp_int_interrupt); -#endif -#ifdef CONFIG_ATARI - if (MACH_IS_ATARI) - free_irq(IRQ_MFP_BUSY, lp_int_interrupt); -#endif -#ifdef CONFIG_MVME16x - if (MACH_IS_MVME16x) - free_irq(MVME167_IRQ_PRN, lp_int_interrupt); -#endif -#ifdef CONFIG_BVME6000 - if (MACH_IS_BVME6000) - free_irq(BVME_IRQ_PRN, lp_int_interrupt); -#endif - } - return -ENODEV; - } - - return 0; -} - -#ifdef MODULE -int init_module(void) -{ -return lp_internal_init(); -} - -void cleanup_module(void) -{ -if (lp_irq) { -#ifdef CONFIG_AMIGA - if (MACH_IS_AMIGA) - free_irq(IRQ_AMIGA_CIAA_FLG, lp_int_interrupt); -#endif -#ifdef CONFIG_ATARI - if (MACH_IS_ATARI) - free_irq(IRQ_MFP_BUSY, lp_int_interrupt); -#endif -#ifdef CONFIG_MVME16x - if (MACH_IS_MVME16x) - free_irq(MVME167_IRQ_PRN, lp_int_interrupt); -#endif -#ifdef CONFIG_BVME6000 - if (MACH_IS_BVME6000) - free_irq(BVME_IRQ_PRN, lp_int_interrupt); -#endif -} -unregister_parallel(minor); -} -#endif diff --git a/drivers/char/lp_m68k.c b/drivers/char/lp_m68k.c deleted file mode 100644 index b16de36c51c3..000000000000 --- a/drivers/char/lp_m68k.c +++ /dev/null @@ -1,556 +0,0 @@ -/* - * split in two parts for better support of different hardware - * by Joerg Dorchain (dorchain@mpi-sb.mpg.de) - * - * Amiga printer device by Michael Rausch (linux@uni-koblenz.de); - * Atari support added by Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de); - * based upon work from - * - * Copyright (C) 1992 by Jim Weigand and Linus Torvalds - * Copyright (C) 1992,1993 by Michael K. Johnson - * - Thanks much to Gunter Windau for pointing out to me where the error - * checking ought to be. - * Copyright (C) 1993 by Nigel Gamble (added interrupt code) - */ - -/* 01/17/95: Matthias Welwarsky (dg8y@rs11.hrz.th-darmstadt.de) - * lp_write(): rewritten from scratch - * lp_interrupt(): fixed cli()/sti()-bug - * - * 95/05/28: Andreas Schwab (schwab@issan.informatik.uni-dortmund.de) - * lp_write() fixed to make it work again. - * 95/08/18: Andreas Schwab - * lp_write_interrupt: fix race condition - * - * * CAUTION, please do check! * - * - * on 68000-based machines sti() must NEVER appear in interrupt driven - * code. The 68k-CPU has a priority-based interrupt scheme. while an interrupt - * with a certain priority is executed, all requests with lower or same - * priority get locked out. executing the sti()-macro allows ANY interrupt - * to be served. this really causes BIG trouble! - * to protect an interrupt driven routine against being interrupted - * (if absolutely needed!) one should use save_flags();cli()/restore_flags()! - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_AMIGA -#ifdef CONFIG_MULTIFACE_III_LP -#include -#endif -#endif - -#include -#include -#include -#include - -#include -#include -#include - - -/* - * why bother around with the pio driver when the interrupt works; - * so, for "security" reasons only, it's configurable here. - * saves some bytes, at least ... - */ -#define FORCE_POLLING 0 -#define FORCE_INTERRUPT 1 -/* - * PREFER_INTERRUPT doesn't make much sense on m68k. - * it is preserved here in case of joining with the i386 driver - * -#define PREFER_INTERRUPT 2 - */ - -#define WHICH_DRIVER FORCE_INTERRUPT - -struct lp_struct *lp_table[MAX_LP] = {NULL,}; - -static int max_lp; /* the real number of devices */ - -/* - * All my debugging code assumes that you debug with only one printer at - * a time. RWWH - */ - -#define LP_DEBUG -#undef LP_DEBUG - - -#if WHICH_DRIVER != FORCE_INTERRUPT -#ifdef LP_DEBUG -static int lp_max_count = 1; -#endif - -static int lp_char_polled(char lpchar, int dev) -{ - unsigned long count = 0; - - do { - count ++; - if (current->need_resched) - schedule(); - } while (lp_table[dev]->lp_is_busy(dev) && count < lp_table[dev]->chars); - - if (count == lp_table[dev]->chars) { - return 0; - /* we timed out, and the character was /not/ printed */ - } -#ifdef LP_DEBUG - if (count > lp_max_count) { - printk("lp success after %d counts.\n",count); - lp_max_count = count; - } -#endif - lp_table[dev]->lp_out(lpchar, dev); - return 1; -} -#endif - - -#ifdef LP_DEBUG -unsigned int lp_total_chars = 0; -unsigned int lp_last_call = 0; -#endif - - -#if WHICH_DRIVER != FORCE_POLLING -static __inline__ int lp_char_interrupt(char lpchar, int dev) -{ - if (!lp_table[dev]->lp_is_busy(dev)) { - lp_table[dev]->lp_out(lpchar,dev); - return 1; - } - return 0; -} - -static int lp_error; - -void lp_interrupt(int dev) -{ - if (dev >= 0 && dev < MAX_LP && lp_table[dev]->do_print) - { - if (lp_table[dev]->copy_size) - { - unsigned long flags; - save_flags(flags); - cli(); - if (lp_char_interrupt(lp_table[dev]->lp_buffer[lp_table[dev]->bytes_written], dev)) { - --lp_table[dev]->copy_size; - ++lp_table[dev]->bytes_written; - restore_flags(flags); - } - else - { - lp_table[dev]->do_print = 0; - restore_flags(flags); - lp_error = 1; - wake_up_interruptible(&lp_table[dev]->lp_wait_q); - } - } - else - { - lp_table[dev]->do_print = 0; - lp_error = 0; - wake_up_interruptible(&lp_table[dev]->lp_wait_q); - } - - } -} - -#if WHICH_DRIVER == FORCE_INTERRUPT -static ssize_t lp_write(struct file *file, const char *buf, - size_t count, loff_t *ppos) -#else -static ssize_t lp_write_interrupt(struct file *file, const char *buf, - size_t count, loff_t *ppos) -#endif -{ - struct inode *inode = file->f_dentry->d_inode; - unsigned long total_bytes_written = 0; - unsigned int flags; - long timeout; - int rc; - int dev = MINOR(inode->i_rdev); - - do { - lp_table[dev]->do_print = 0; /* disable lp_interrupt() */ - lp_table[dev]->bytes_written = 0; /* init buffer read-pointer */ - lp_error = 0; - lp_table[dev]->copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE); - if (copy_from_user(lp_table[dev]->lp_buffer, buf, - lp_table[dev]->copy_size)) - return -EFAULT; - while (lp_table[dev]->copy_size) { - save_flags(flags); - cli(); /* no interrupts now */ - lp_table[dev]->do_print = 1; /* enable lp_interrupt() */ - if (lp_char_interrupt(lp_table[dev]->lp_buffer[lp_table[dev]->bytes_written], dev)) { - ++lp_table[dev]->bytes_written; - --lp_table[dev]->copy_size; - lp_error = 0; - } else { /* something went wrong */ - lp_table[dev]->do_print = 0; /* disable lp_interrupt() */ - lp_error = 1; /* printer caused error */ - } - if (lp_error) { - - /* something blocked printing, so we don't want to sleep too long, - in case we have to rekick the interrupt */ - - timeout = LP_TIMEOUT_POLLED; - } else { - timeout = LP_TIMEOUT_INTERRUPT; - } - - interruptible_sleep_on_timeout(&lp_table[dev]->lp_wait_q, timeout); - restore_flags(flags); - - /* we're up again and running. we first disable lp_interrupt(), then - check what happened meanwhile */ - - lp_table[dev]->do_print = 0; - rc = total_bytes_written + lp_table[dev]->bytes_written; - - if (signal_pending(current)) { - if (rc == 0) - rc = -EINTR; - return rc; - } - if (lp_error) { - - /* an error has occurred, maybe in lp_interrupt(). - figure out the type of error, exit on request or if nothing has - been printed at all. */ - - if (lp_table[dev]->lp_has_pout(dev)) { - printk(KERN_NOTICE "lp%d: paper-out\n",dev); - if (!rc) rc = -ENOSPC; - } else if (!lp_table[dev]->lp_is_online(dev)) { - printk(KERN_NOTICE "lp%d: off-line\n",dev); - if (!rc) rc = -EIO; - } else if (lp_table[dev]->lp_is_busy(dev)) { - printk(KERN_NOTICE "lp%d: on fire\n",dev); - if (!rc) rc = -EIO; - } - if (lp_table[dev]->flags & LP_ABORT) - return rc; - } - /* check if our buffer was completely printed, if not, most likely - an unsolved error blocks the printer. As we can`t do anything - against, we start all over again. Else we set the read-pointer - of the buffer and count the printed characters */ - - if (!lp_table[dev]->copy_size) { - total_bytes_written += lp_table[dev]->bytes_written; - buf += lp_table[dev]->bytes_written; - count -= lp_table[dev]->bytes_written; - } - } - } while (count > 0); - return total_bytes_written; -} -#else -void (*lp_interrupt)() = NULL; -#endif - -#if WHICH_DRIVER != FORCE_INTERRUPT -#if WHICH_DRIVER == FORCE_POLLING -static ssize_t lp_write(struct file *file, const char *buf, - size_t count, loff_t *ppos) -#else -static ssize_t lp_write_polled(struct file *file, const char *buf, - size_t count, loff_t *ppos) -#endif -{ - struct inode *inode = file->f_dentry->d_inode; - char *temp = buf; - int dev = MINOR(inode->i_rdev); - -#ifdef LP_DEBUG - if (time_after(jiffies, lp_last_call + lp_table[dev]->time)) { - lp_total_chars = 0; - lp_max_count = 1; - } - lp_last_call = jiffies; -#endif - - temp = buf; - while (count > 0) { - int c; - if (get_user(c, temp)) - return -EFAULT; - if (lp_char_polled(c, dev)) { - /* only update counting vars if character was printed */ - count--; temp++; -#ifdef LP_DEBUG - lp_total_chars++; -#endif - } else { /* if printer timed out */ - unsigned long timeout = LP_TIMEOUT_POLLED; - int error = 0; - if (lp_table[dev]->lp_has_pout(dev)) { - printk(KERN_NOTICE "lp%d: out of paper\n",dev); - if (lp_table[dev]->flags & LP_ABORT) - error = -ENOSPC; - } else if (!lp_table[dev]->lp_is_online(dev)) { - printk(KERN_NOTICE "lp%d: off-line\n",dev); - if (lp_table[dev]->flags & LP_ABORT) - error = -EIO; - } else - /* not offline or out of paper. on fire? */ - if (lp_table[dev]->lp_is_busy(dev)) { - printk(KERN_NOTICE "lp%d: on fire\n",dev); - if (lp_table[dev]->flags & LP_ABORT) - error = -EIO; - } - else - timeout = lp_table[dev]->time; - - /* check for signals before going to sleep */ - if (error == 0 && signal_pending(current)) - error = -EINTR; - if (error) { - if (temp != buf) - return temp-buf; - else - return error; - } - -#ifdef LP_DEBUG - printk("lp sleeping at %d characters for %d jiffies\n", - lp_total_chars, timeout); - lp_total_chars = 0; -#endif - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(timeout); - } - } - return temp - buf; -} -#endif - -unsigned int lp_irq = 0; - -#if WHICH_DRIVER == PREFER_INTERRUPT -static ssize_t lp_write(struct file *file, const char *buf, size_t count, - loff_t *ppos) -{ - if (lp_irq) - return lp_write_interrupt(file, buf, count, ppos); - else - return lp_write_polled(file, buf, count, ppos); -} -#endif - -static long long lp_lseek(struct file * file, long long offset, int origin) -{ - return -ESPIPE; -} - -static int lp_open(struct inode *inode, struct file *file) -{ - int dev = MINOR(inode->i_rdev); - int ret; - - MOD_INC_USE_COUNT; - - ret = -ENODEV; - if (dev >= MAX_LP) - goto out_err; - - if (!lp_table[dev]) { - char modname[30]; - - sprintf(modname, "char-major-%d-%d", LP_MAJOR, dev); - request_module(modname); - } - if (!lp_table[dev]) - goto out_err; - if (!(lp_table[dev]->flags & LP_EXIST)) - goto out_err; - ret = -EBUSY; - if (lp_table[dev]->flags & LP_BUSY) - goto out_err; - - lp_table[dev]->flags |= LP_BUSY; - - ret = lp_table[dev]->lp_open(dev); - if (ret != 0) { - lp_table[dev]->flags &= ~LP_BUSY; - goto out_err; - } - return ret; - -out_err: - MOD_DEC_USE_COUNT; - return ret; -} - -static int lp_release(struct inode *inode, struct file *file) -{ - int dev =MINOR(inode->i_rdev); - - lp_table[dev]->flags &= ~LP_BUSY; - lp_table[dev]->lp_release(dev); - MOD_DEC_USE_COUNT; - return 0; -} - - -static int lp_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - unsigned int minor = MINOR(inode->i_rdev); - int retval = -ENODEV; - -#ifdef LP_DEBUG - printk("lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg); -#endif - if (minor >= max_lp) - goto out; - if (!(lp_table[minor]->flags & LP_EXIST)) - goto out; - retval = 0; - switch (cmd) { - case LPTIME: - lp_table[minor]->time = arg; - break; - case LPCHAR: - lp_table[minor]->chars = arg; - break; - case LPABORT: - if (arg) - lp_table[minor]->flags |= LP_ABORT; - else - lp_table[minor]->flags &= ~LP_ABORT; - break; - case LPWAIT: - lp_table[minor]->wait = arg; - break; - case LPSETIRQ: - case LPGETIRQ: - retval = lp_irq; - break; - default: - retval = -EINVAL; - if (lp_table[minor]->lp_ioctl) - retval = lp_table[minor]->lp_ioctl(minor, cmd, arg); - } -out: - return retval; -} - - -static struct file_operations lp_fops = { - lp_lseek, - NULL, /* lp_read */ - lp_write, - NULL, /* lp_readdir */ - NULL, /* lp_poll */ - lp_ioctl, - NULL, /* lp_mmap */ - lp_open, - NULL, /* flush */ - lp_release -}; - -EXPORT_SYMBOL(lp_table); -EXPORT_SYMBOL(lp_irq); -EXPORT_SYMBOL(lp_interrupt); -EXPORT_SYMBOL(register_parallel); -EXPORT_SYMBOL(unregister_parallel); - -int __init lp_m68k_init(void) -{ - extern char m68k_debug_device[]; - - if (!strcmp( m68k_debug_device, "par" )) - return -EBUSY; - - if (register_chrdev(LP_MAJOR,"lp", &lp_fops)) { - printk(KERN_ERR "unable to get major %d for line printer\n", LP_MAJOR); - return -ENXIO; - } - -#if WHICH_DRIVER == FORCE_POLLING - lp_irq = 0; - printk(KERN_INFO "lp_init: lp using polling driver\n"); -#else - - lp_irq = 1; - printk(KERN_INFO "lp_init: lp using interrupt driver\n"); -#endif - -#ifndef MODULE - lp_internal_init(); -#ifdef CONFIG_MULTIFACE_III_LP - lp_mfc_init(); -#endif -#endif - return 0; -} - -/* - * Currently we do not accept any lp-parameters, but that may change. - */ -void __init lp_setup(char *str, int *ints) -{ -} - -#ifdef MODULE -int init_module(void) -{ - return lp_m68k_init(); -} - -void cleanup_module(void) -{ - unregister_chrdev(LP_MAJOR, "lp"); -} -#endif - -/* - * (un-)register for hardware drivers - * tab is an inititalised lp_struct, dev the desired minor - * if dev < 0, let the driver choose the first free minor - * if successful return the minor, else -1 - */ -int register_parallel(struct lp_struct *tab, int dev) -{ -if (dev < 0) { - dev = 0; - while ((dev < MAX_LP) && (lp_table[dev] != NULL)) - dev++; -} -if (dev > MAX_LP) - return -1; -if (lp_table[dev] != NULL) - return -1; -lp_table[dev] = tab; -printk(KERN_INFO "lp%d: %s at 0x%08lx\n", dev, tab->name, (long)tab->base); -return dev; -} - -#ifdef CONFIG_MODULES -void unregister_parallel(int dev) -{ -if ((dev < 0) || (dev > MAX_LP) || (lp_table[dev] == NULL)) - printk(KERN_ERR "WARNING: unregister_parallel for non-existant device ignored!\n"); -else - lp_table[dev] = NULL; -} -#endif diff --git a/drivers/char/mem.c b/drivers/char/mem.c index b47f0b647377..abda28741752 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -465,17 +465,12 @@ static int open_port(struct inode * inode, struct file * filp) return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; } -static int open_mem(struct inode * inode, struct file * filp) -{ - return (capable(CAP_SYS_RAWIO) - || !(filp->f_mode & FMODE_WRITE)) ? 0 : -EPERM; -} - #define mmap_kmem mmap_mem #define zero_lseek null_lseek #define full_lseek null_lseek #define write_zero write_null #define read_full read_zero +#define open_mem open_port #define open_kmem open_mem static struct file_operations mem_fops = { diff --git a/drivers/isdn/Config.in b/drivers/isdn/Config.in index 52e3acbdb74a..5ad3772819c9 100644 --- a/drivers/isdn/Config.in +++ b/drivers/isdn/Config.in @@ -9,6 +9,10 @@ if [ "$CONFIG_INET" != "n" ]; then fi fi bool 'Support audio via ISDN' CONFIG_ISDN_AUDIO +if [ "$CONFIG_ISDN_AUDIO" != "n" ]; then + bool 'Support AT-Fax Class 2 commands' CONFIG_ISDN_TTY_FAX +fi +bool 'Support isdn diversion services' CONFIG_ISDN_DIVERSION if [ "$CONFIG_X25" != "n" ]; then bool 'X.25 PLP on top of ISDN (EXPERIMENTAL)' CONFIG_ISDN_X25 fi @@ -26,7 +30,6 @@ if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then bool 'HiSax Support for german 1TR6' CONFIG_HISAX_1TR6 bool 'HiSax Support for Teles 16.0/8.0' CONFIG_HISAX_16_0 bool 'HiSax Support for Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3 - bool 'HiSax Support for Teles 16.3c' CONFIG_HISAX_TELES3C bool 'HiSax Support for Teles PCI' CONFIG_HISAX_TELESPCI bool 'HiSax Support for Teles S0Box' CONFIG_HISAX_S0BOX bool 'HiSax Support for AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1 @@ -37,24 +40,39 @@ if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then bool 'HiSax Support for Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA bool 'HiSax Support for ASUSCOM cards' CONFIG_HISAX_ASUSCOM bool 'HiSax Support for TELEINT cards' CONFIG_HISAX_TELEINT - bool 'HiSax Support for Sedlbauer speed card/win/star/fax' CONFIG_HISAX_SEDLBAUER + bool 'HiSax Support for HFC-S based cards' CONFIG_HISAX_HFCS + bool 'HiSax Support for Sedlbauer cards' CONFIG_HISAX_SEDLBAUER bool 'HiSax Support for USR Sportster internal TA' CONFIG_HISAX_SPORTSTER bool 'HiSax Support for MIC card' CONFIG_HISAX_MIC bool 'HiSax Support for NETjet card' CONFIG_HISAX_NETJET bool 'HiSax Support for Niccy PnP/PCI card' CONFIG_HISAX_NICCY + bool 'HiSax Support for Siemens I-Surf card' CONFIG_HISAX_ISURF + bool 'HiSax Support for HST Saphir card' CONFIG_HISAX_HSTSAPHIR + bool 'HiSax Support for Telekom A4T card' CONFIG_HISAX_BKM_A4T + bool 'HiSax Support for Scitel Quadro card' CONFIG_HISAX_SCT_QUADRO + bool 'HiSax Support for Gazel cards' CONFIG_HISAX_GAZEL if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then - if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then - bool 'HiSax Support for Am7930' CONFIG_HISAX_AMD7930 - fi + bool 'HiSax Support for HFC PCI-Bus cards (EXPERIMENTAL)' CONFIG_HISAX_HFC_PCI +# bool 'HiSax Support for TESTEMULATOR (EXPERIMENTAL)' CONFIG_HISAX_TESTEMU + if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then + bool 'HiSax Support for Am7930' CONFIG_HISAX_AMD7930 + fi fi fi if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then dep_tristate 'Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN dep_tristate 'IBM Active 2000 support (EXPERIMENTAL)' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN - dep_tristate 'Eicon.Diehl active card support (EXPERIMENTAL)' CONFIG_ISDN_DRV_EICON $CONFIG_ISDN fi -dep_tristate 'AVM-B1 with CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN +dep_tristate 'Eicon.Diehl active card support' CONFIG_ISDN_DRV_EICON $CONFIG_ISDN +if [ "$CONFIG_ISDN_DRV_EICON" != "n" ]; then + bool 'Eicon S,SX,SCOM,Quadro,S2M support' CONFIG_ISDN_DRV_EICON_ISA +fi +dep_tristate 'AVM CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN if [ "$CONFIG_ISDN_DRV_AVMB1" != "n" ]; then + bool 'AVM B1 ISA support' CONFIG_ISDN_DRV_AVMB1_B1ISA + bool 'AVM B1 PCI support' CONFIG_ISDN_DRV_AVMB1_B1PCI + bool 'AVM T1/T1B ISA support' CONFIG_ISDN_DRV_AVMB1_T1ISA + bool 'AVM B1/M1/M2 PCMCIA support' CONFIG_ISDN_DRV_AVMB1_B1PCMCIA bool 'Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON fi diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile index 36a1f5bb763c..04be19f9cd69 100644 --- a/drivers/isdn/Makefile +++ b/drivers/isdn/Makefile @@ -1,6 +1,6 @@ SUB_DIRS := MOD_SUB_DIRS := -ALL_SUB_DIRS := icn pcbit hisax avmb1 act2000 eicon +ALL_SUB_DIRS := icn pcbit hisax avmb1 act2000 eicon divert L_OBJS := LX_OBJS := @@ -24,6 +24,9 @@ ifeq ($(CONFIG_ISDN),y) endif ifdef CONFIG_ISDN_AUDIO L_OBJS += isdn_audio.o + ifdef CONFIG_ISDN_TTY_FAX + L_OBJS += isdn_ttyfax.o + endif endif else ifeq ($(CONFIG_ISDN),m) @@ -41,10 +44,19 @@ else endif ifdef CONFIG_ISDN_AUDIO O_OBJS += isdn_audio.o + ifdef CONFIG_ISDN_TTY_FAX + O_OBJS += isdn_ttyfax.o + endif endif endif endif +ifeq ($(CONFIG_ISDN_DIVERSION),y) + ifeq ($(CONFIG_MODULES),y) + MOD_SUB_DIRS += divert + endif +endif + ifeq ($(CONFIG_ISDN_DRV_HISAX),y) L_OBJS += hisax/hisax.o SUB_DIRS += hisax diff --git a/drivers/isdn/avmb1/Makefile b/drivers/isdn/avmb1/Makefile index cce4af1310a7..9f73ea2e3699 100644 --- a/drivers/isdn/avmb1/Makefile +++ b/drivers/isdn/avmb1/Makefile @@ -1,5 +1,5 @@ # -# $Id: Makefile,v 1.4 1997/03/30 17:10:40 calle Exp $ +# $Id: Makefile,v 1.6 1999/07/20 06:41:44 calle Exp $ # # Makefile for the CAPI and AVM-B1 device drivers. # @@ -11,6 +11,26 @@ # parent makes.. # # $Log: Makefile,v $ +# Revision 1.6 1999/07/20 06:41:44 calle +# Bugfix: After the redesign of the AVM B1 driver, the driver didn't even +# compile, if not selected as modules. +# +# Revision 1.5 1999/07/01 15:26:20 calle +# complete new version (I love it): +# + new hardware independed "capi_driver" interface that will make it easy to: +# - support other controllers with CAPI-2.0 (i.e. USB Controller) +# - write a CAPI-2.0 for the passive cards +# - support serial link CAPI-2.0 boxes. +# + wrote "capi_driver" for all supported cards. +# + "capi_driver" (supported cards) now have to be configured with +# make menuconfig, in the past all supported cards where included +# at once. +# + new and better informations in /proc/capi/ +# + new ioctl to switch trace of capi messages per controller +# using "avmcapictrl trace [contr] on|off|...." +# + complete testcircle with all supported cards and also the +# PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. +# # Revision 1.4 1997/03/30 17:10:40 calle # added support for AVM-B1-PCI card. # @@ -56,20 +76,38 @@ L_TARGET := # used for .a targets (from L and LX objects) ifeq ($(CONFIG_ISDN_DRV_AVMB1),y) O_TARGET += avmb1.o - O_OBJS += capi.o b1lli.o - OX_OBJS += capiutil.o b1capi.o capidrv.o - ifdef CONFIG_PCI - OX_OBJS += b1pci.o + OX_OBJS += kcapi.o + O_OBJS += capi.o + ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA + O_OBJS += b1isa.o + endif + ifdef CONFIG_ISDN_DRV_AVMB1_B1PCI + O_OBJS += b1pci.o + endif + ifdef CONFIG_ISDN_DRV_AVMB1_T1ISA + O_OBJS += t1isa.o + endif + ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA + OX_OBJS += b1pcmcia.o endif + OX_OBJS += capiutil.o capidrv.o b1.o else ifeq ($(CONFIG_ISDN_DRV_AVMB1),m) O_TARGET += kernelcapi.o - O_OBJS += b1lli.o - OX_OBJS += b1capi.o + OX_OBJS += kcapi.o M_OBJS += capi.o kernelcapi.o - MX_OBJS += capiutil.o capidrv.o - ifdef CONFIG_PCI - MX_OBJS += b1pci.o + ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA + M_OBJS += b1isa.o + endif + ifdef CONFIG_ISDN_DRV_AVMB1_B1PCI + M_OBJS += b1pci.o + endif + ifdef CONFIG_ISDN_DRV_AVMB1_T1ISA + M_OBJS += t1isa.o + endif + MX_OBJS += capiutil.o capidrv.o b1.o + ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA + MX_OBJS += b1pcmcia.o endif endif endif diff --git a/drivers/isdn/avmb1/avmcard.h b/drivers/isdn/avmb1/avmcard.h new file mode 100644 index 000000000000..e94c5637cdec --- /dev/null +++ b/drivers/isdn/avmb1/avmcard.h @@ -0,0 +1,559 @@ +/* + * $Id: avmcard.h,v 1.4 1999/08/04 10:10:08 calle Exp $ + * + * Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: avmcard.h,v $ + * Revision 1.4 1999/08/04 10:10:08 calle + * Bugfix: corrected /proc functions, added structure for new AVM cards. + * + * Revision 1.3 1999/07/23 08:41:47 calle + * prepared for new AVM cards. + * + * Revision 1.2 1999/07/05 15:09:45 calle + * - renamed "appl_release" to "appl_released". + * - version und profile data now cleared on controller reset + * - extended /proc interface, to allow driver and controller specific + * informations to include by driver hackers. + * + * Revision 1.1 1999/07/01 15:26:22 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + */ + +#ifndef _AVMCARD_H_ +#define _AVMCARD_H_ + +#define AVMB1_PORTLEN 0x1f +#define AVM_MAXVERSION 8 +#define AVM_NAPPS 30 +#define AVM_NCCI_PER_CHANNEL 4 + +/* + * Versions + */ + +#define VER_DRIVER 0 +#define VER_CARDTYPE 1 +#define VER_HWID 2 +#define VER_SERIAL 3 +#define VER_OPTION 4 +#define VER_PROTO 5 +#define VER_PROFILE 6 +#define VER_CAPI 7 + +enum avmcardtype { + avm_b1isa, + avm_b1pci, + avm_b1pcmcia, + avm_m1, + avm_m2, + avm_t1isa, + avm_t1pci, + avm_c4 +}; + +typedef struct avmcard_dmainfo { + __u32 recvlen; + __u8 recvbuf[128+2048]; + struct sk_buff_head send_queue; + __u8 sendbuf[128+2048]; +} avmcard_dmainfo; + +typedef struct avmcard { + char name[32]; + unsigned int port; + unsigned irq; + unsigned long membase; + enum avmcardtype cardtype; + int cardnr; /* for t1isa */ + + int versionlen; + char versionbuf[1024]; + char *version[AVM_MAXVERSION]; + + char cardname[32]; + + char infobuf[128]; /* for function procinfo */ + char msgbuf[128]; /* capimsg msg part */ + char databuf[2048]; /* capimsg data part */ + + int interrupt; + + void *mbase; + __u32 csr; + avmcard_dmainfo *dma; + + struct capi_ctr *ctrl; +} avmcard; + +extern int b1_irq_table[16]; + +/* + * LLI Messages to the ISDN-ControllerISDN Controller + */ + +#define SEND_POLL 0x72 /* + * after load <- RECEIVE_POLL + */ +#define SEND_INIT 0x11 /* + * first message <- RECEIVE_INIT + * int32 NumApplications int32 + * NumNCCIs int32 BoardNumber + */ +#define SEND_REGISTER 0x12 /* + * register an application int32 + * ApplIDId int32 NumMessages + * int32 NumB3Connections int32 + * NumB3Blocks int32 B3Size + * + * AnzB3Connection != 0 && + * AnzB3Blocks >= 1 && B3Size >= 1 + */ +#define SEND_RELEASE 0x14 /* + * deregister an application int32 + * ApplID + */ +#define SEND_MESSAGE 0x15 /* + * send capi-message int32 length + * capi-data ... + */ +#define SEND_DATA_B3_REQ 0x13 /* + * send capi-data-message int32 + * MsgLength capi-data ... int32 + * B3Length data .... + */ + +#define SEND_CONFIG 0x21 /* + */ + +#define SEND_POLLACK 0x73 /* T1 Watchdog */ + +/* + * LLI Messages from the ISDN-ControllerISDN Controller + */ + +#define RECEIVE_POLL 0x32 /* + * <- after SEND_POLL + */ +#define RECEIVE_INIT 0x27 /* + * <- after SEND_INIT int32 length + * byte total length b1struct board + * driver revision b1struct card + * type b1struct reserved b1struct + * serial number b1struct driver + * capability b1struct d-channel + * protocol b1struct CAPI-2.0 + * profile b1struct capi version + */ +#define RECEIVE_MESSAGE 0x21 /* + * <- after SEND_MESSAGE int32 + * AppllID int32 Length capi-data + * .... + */ +#define RECEIVE_DATA_B3_IND 0x22 /* + * received data int32 AppllID + * int32 Length capi-data ... + * int32 B3Length data ... + */ +#define RECEIVE_START 0x23 /* + * Handshake + */ +#define RECEIVE_STOP 0x24 /* + * Handshake + */ +#define RECEIVE_NEW_NCCI 0x25 /* + * int32 AppllID int32 NCCI int32 + * WindowSize + */ +#define RECEIVE_FREE_NCCI 0x26 /* + * int32 AppllID int32 NCCI + */ +#define RECEIVE_RELEASE 0x26 /* + * int32 AppllID int32 0xffffffff + */ +#define RECEIVE_TASK_READY 0x31 /* + * int32 tasknr + * int32 Length Taskname ... + */ +#define RECEIVE_DEBUGMSG 0x71 /* + * int32 Length message + * + */ +#define RECEIVE_POLLDWORD 0x75 /* t1pci in dword mode */ + +#define WRITE_REGISTER 0x00 +#define READ_REGISTER 0x01 + +/* + * port offsets + */ + +#define B1_READ 0x00 +#define B1_WRITE 0x01 +#define B1_INSTAT 0x02 +#define B1_OUTSTAT 0x03 +#define B1_RESET 0x10 +#define B1_ANALYSE 0x04 + + +#define B1_STAT0(cardtype) ((cardtype) == avm_m1 ? 0x81200000l : 0x80A00000l) +#define B1_STAT1(cardtype) (0x80E00000l) + +/* ---------------------------------------------------------------- */ + +static inline unsigned char b1outp(unsigned int base, + unsigned short offset, + unsigned char value) +{ + outb(value, base + offset); + return inb(base + B1_ANALYSE); +} + + +static inline int b1_rx_full(unsigned int base) +{ + return inb(base + B1_INSTAT) & 0x1; +} + +static inline unsigned char b1_get_byte(unsigned int base) +{ + unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */ + while (!b1_rx_full(base) && time_before(jiffies, stop)); + 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 int 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 int base) +{ + return inb(base + B1_OUTSTAT) & 0x1; +} + +static inline void b1_put_byte(unsigned int base, unsigned char val) +{ + while (!b1_tx_empty(base)); + b1outp(base, B1_WRITE, val); +} + +static inline int b1_save_put_byte(unsigned int base, unsigned char val) +{ + unsigned long stop = jiffies + 2 * HZ; + while (!b1_tx_empty(base) && time_before(jiffies,stop)); + if (!b1_tx_empty(base)) return -1; + b1outp(base, B1_WRITE, val); + return 0; +} + +static inline void b1_put_word(unsigned int 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 int base, + unsigned char *dp) +{ + unsigned int len, i; + + len = i = b1_get_word(base); + while (i-- > 0) *dp++ = b1_get_byte(base); + return len; +} + +static inline void b1_put_slice(unsigned int base, + unsigned char *dp, unsigned int len) +{ + unsigned i = len; + b1_put_word(base, i); + while (i-- > 0) + b1_put_byte(base, *dp++); +} + +static void b1_wr_reg(unsigned int 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 int base, + unsigned int reg) +{ + b1_put_byte(base, READ_REGISTER); + b1_put_word(base, reg); + return b1_get_word(base); + +} + +static inline void b1_reset(unsigned int base) +{ + b1outp(base, B1_RESET, 0); + udelay(55 * 2 * 1000); /* 2 TIC's */ + + b1outp(base, B1_RESET, 1); + udelay(55 * 2 * 1000); /* 2 TIC's */ + + b1outp(base, B1_RESET, 0); + udelay(55 * 2 * 1000); /* 2 TIC's */ +} + +static inline unsigned char b1_disable_irq(unsigned int base) +{ + return b1outp(base, B1_INSTAT, 0x00); +} + +/* ---------------------------------------------------------------- */ + +static inline void b1_set_test_bit(unsigned int base, + enum avmcardtype cardtype, + int onoff) +{ + b1_wr_reg(base, B1_STAT0(cardtype), onoff ? 0x21 : 0x20); +} + +static inline int b1_get_test_bit(unsigned int base, + enum avmcardtype cardtype) +{ + return (b1_rd_reg(base, B1_STAT0(cardtype)) & 0x01) != 0; +} + +/* ---------------------------------------------------------------- */ + +#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 + +static inline void t1outp(unsigned int base, + unsigned short offset, + unsigned char value) +{ + outb(value, base + offset); +} + +static inline unsigned char t1inp(unsigned int base, + unsigned short offset) +{ + return inb(base + offset); +} + +static inline int t1_isfastlink(unsigned int base) +{ + return (inb(base + T1_IDENT) & ~0x82) == 1; +} + +static inline unsigned char t1_fifostatus(unsigned int base) +{ + return inb(base + T1_FIFOSTAT); +} + +static inline unsigned int t1_get_slice(unsigned int base, + unsigned char *dp) +{ + unsigned int len, i; +#ifdef FASTLINK_DEBUG + unsigned wcnt = 0, bcnt = 0; +#endif + + len = i = b1_get_word(base); + if (t1_isfastlink(base)) { + int status; + while (i > 0) { + status = t1_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 t1_put_slice(unsigned int base, + unsigned char *dp, unsigned int len) +{ + unsigned i = len; + b1_put_word(base, i); + if (t1_isfastlink(base)) { + int status; + while (i > 0) { + status = t1_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 inline void t1_disable_irq(unsigned int base) +{ + t1outp(base, T1_IRQMASTER, 0x00); +} + +static inline void t1_reset(unsigned int 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); +} + +static inline void b1_setinterrupt(unsigned int base, unsigned irq, + enum avmcardtype cardtype) +{ + switch (cardtype) { + case avm_t1isa: + t1outp(base, B1_INSTAT, 0x00); + t1outp(base, B1_INSTAT, 0x02); + t1outp(base, T1_IRQMASTER, 0x08); + break; + case avm_b1isa: + b1outp(base, B1_INSTAT, 0x00); + b1outp(base, B1_RESET, b1_irq_table[irq]); + b1outp(base, B1_INSTAT, 0x02); + break; + default: + case avm_m1: + case avm_m2: + case avm_b1pci: + b1outp(base, B1_INSTAT, 0x00); + b1outp(base, B1_RESET, 0xf0); + b1outp(base, B1_INSTAT, 0x02); + break; + case avm_c4: + case avm_t1pci: + b1outp(base, B1_RESET, 0xf0); + break; + } +} + +int b1_detect(unsigned int base, enum avmcardtype cardtype); +int b1_load_t4file(unsigned int base, capiloaddatapart * t4file); +int b1_load_config(unsigned int base, capiloaddatapart * config); +int b1_loaded(unsigned int base); +int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data); +void b1_reset_ctr(struct capi_ctr *ctrl); +void b1_register_appl(struct capi_ctr *ctrl, __u16 appl, + capi_register_params *rp); +void b1_release_appl(struct capi_ctr *ctrl, __u16 appl); +void b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb); +void b1_parse_version(avmcard *card); +void b1_handle_interrupt(avmcard * card); + +int b1ctl_read_proc(char *page, char **start, off_t off, + int count, int *eof, struct capi_ctr *ctrl); + + +#endif /* _AVMCARD_H_ */ diff --git a/drivers/isdn/avmb1/b1.c b/drivers/isdn/avmb1/b1.c new file mode 100644 index 000000000000..620a2aa6e336 --- /dev/null +++ b/drivers/isdn/avmb1/b1.c @@ -0,0 +1,688 @@ +/* + * $Id: b1.c,v 1.7 1999/08/04 10:10:09 calle Exp $ + * + * Common module for AVM B1 cards. + * + * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: b1.c,v $ + * Revision 1.7 1999/08/04 10:10:09 calle + * Bugfix: corrected /proc functions, added structure for new AVM cards. + * + * Revision 1.6 1999/07/23 08:51:04 calle + * small fix and typo in checkin before. + * + * Revision 1.5 1999/07/23 08:41:48 calle + * prepared for new AVM cards. + * + * Revision 1.4 1999/07/09 15:05:38 keil + * compat.h is now isdn_compat.h + * + * Revision 1.3 1999/07/06 07:41:59 calle + * - changes in /proc interface + * - check and changed calls to [dev_]kfree_skb and [dev_]alloc_skb. + * + * Revision 1.2 1999/07/05 15:09:47 calle + * - renamed "appl_release" to "appl_released". + * - version und profile data now cleared on controller reset + * - extended /proc interface, to allow driver and controller specific + * informations to include by driver hackers. + * + * Revision 1.1 1999/07/01 15:26:23 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "capilli.h" +#include "avmcard.h" +#include "capicmd.h" +#include "capiutil.h" + +static char *revision = "$Revision: 1.7 $"; + +/* ------------------------------------------------------------- */ + +MODULE_AUTHOR("Carsten Paeth "); + +/* ------------------------------------------------------------- */ + +int b1_irq_table[16] = +{0, + 0, + 0, + 192, /* irq 3 */ + 32, /* irq 4 */ + 160, /* irq 5 */ + 96, /* irq 6 */ + 224, /* irq 7 */ + 0, + 64, /* irq 9 */ + 80, /* irq 10 */ + 208, /* irq 11 */ + 48, /* irq 12 */ + 0, + 0, + 112, /* irq 15 */ +}; + +/* ------------------------------------------------------------- */ + +int b1_detect(unsigned int base, enum avmcardtype cardtype) +{ + int onoff, i; + + /* + * Statusregister 0000 00xx + */ + if ((inb(base + B1_INSTAT) & 0xfc) + || (inb(base + B1_OUTSTAT) & 0xfc)) + return 1; + /* + * Statusregister 0000 001x + */ + b1outp(base, B1_INSTAT, 0x2); /* enable irq */ + /* b1outp(base, B1_OUTSTAT, 0x2); */ + if ((inb(base + B1_INSTAT) & 0xfe) != 0x2 + /* || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2 */) + return 2; + /* + * Statusregister 0000 000x + */ + b1outp(base, B1_INSTAT, 0x0); /* disable irq */ + b1outp(base, B1_OUTSTAT, 0x0); + if ((inb(base + B1_INSTAT) & 0xfe) + || (inb(base + B1_OUTSTAT) & 0xfe)) + return 3; + + 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; + } + + if (cardtype == avm_m1) + return 0; + + if ((b1_rd_reg(base, B1_STAT1(cardtype)) & 0x0f) != 0x01) + return 5; + + return 0; +} + +int b1_load_t4file(unsigned int base, capiloaddatapart * t4file) +{ + unsigned char buf[256]; + unsigned char *dp; + int i, left, retval; + + dp = t4file->data; + left = t4file->len; + while (left > sizeof(buf)) { + if (t4file->user) { + retval = copy_from_user(buf, dp, sizeof(buf)); + if (retval) + return -EFAULT; + } else { + memcpy(buf, dp, sizeof(buf)); + } + for (i = 0; i < sizeof(buf); i++) + if (b1_save_put_byte(base, buf[i]) < 0) { + printk(KERN_ERR "b1_load_t4file: corrupted t4 file ?\n"); + return -EIO; + } + left -= sizeof(buf); + dp += sizeof(buf); + } + if (left) { + if (t4file->user) { + retval = copy_from_user(buf, dp, left); + if (retval) + return -EFAULT; + } else { + memcpy(buf, dp, left); + } + for (i = 0; i < left; i++) + if (b1_save_put_byte(base, buf[i]) < 0) { + printk(KERN_ERR "b1_load_t4file: corrupted t4 file ?\n"); + return -EIO; + } + } + return 0; +} + +int b1_load_config(unsigned int base, capiloaddatapart * config) +{ + 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)) { + if (config->user) { + retval = copy_from_user(buf, dp, sizeof(buf)); + if (retval) + return -EFAULT; + } else { + memcpy(buf, dp, 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++]); + } + } + left -= sizeof(buf); + dp += sizeof(buf); + } + if (left) { + if (config->user) { + retval = copy_from_user(buf, dp, left); + if (retval) + return -EFAULT; + } else { + memcpy(buf, dp, 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); + } + } + } + return 0; +} + +int b1_loaded(unsigned int base) +{ + unsigned long stop; + unsigned char ans; + unsigned long tout = 2; + + for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { + if (b1_tx_empty(base)) + break; + } + if (!b1_tx_empty(base)) { + printk(KERN_ERR "b1_loaded: tx err, corrupted t4 file ?\n"); + return 0; + } + b1_put_byte(base, SEND_POLL); + for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { + if (b1_rx_full(base)) { + if ((ans = b1_get_byte(base)) == RECEIVE_POLL) { + return 1; + } + printk(KERN_ERR "b1_loaded: got 0x%x, firmware not running\n", ans); + return 0; + } + } + printk(KERN_ERR "b1_loaded: firmware not running\n"); + return 0; +} + +/* ------------------------------------------------------------- */ + +int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + unsigned long flags; + int retval; + + b1_reset(port); + + if ((retval = b1_load_t4file(port, &data->firmware))) { + b1_reset(port); + printk(KERN_ERR "%s: failed to load t4file!!\n", + card->name); + return retval; + } + + b1_disable_irq(port); + + if (data->configuration.len > 0 && data->configuration.data) { + if ((retval = b1_load_config(port, &data->configuration))) { + b1_reset(port); + printk(KERN_ERR "%s: failed to load config!!\n", + card->name); + return retval; + } + } + + if (!b1_loaded(port)) { + printk(KERN_ERR "%s: failed to load t4file.\n", card->name); + return -EIO; + } + + save_flags(flags); + cli(); + b1_setinterrupt(port, card->irq, card->cardtype); + b1_put_byte(port, SEND_INIT); + b1_put_word(port, AVM_NAPPS); + b1_put_word(port, AVM_NCCI_PER_CHANNEL*2); + b1_put_word(port, ctrl->cnr - 1); + restore_flags(flags); + + return 0; +} + +void b1_reset_ctr(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + + b1_reset(port); + b1_reset(port); + + memset(card->version, 0, sizeof(card->version)); + ctrl->reseted(ctrl); +} + +void b1_register_appl(struct capi_ctr *ctrl, + __u16 appl, + capi_register_params *rp) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + unsigned long flags; + int nconn, want = rp->level3cnt; + + if (want > 0) nconn = want; + else nconn = ctrl->profile.nbchannel * -want; + if (nconn == 0) nconn = ctrl->profile.nbchannel; + + save_flags(flags); + cli(); + b1_put_byte(port, SEND_REGISTER); + b1_put_word(port, appl); + b1_put_word(port, 1024 * (nconn+1)); + b1_put_word(port, nconn); + b1_put_word(port, rp->datablkcnt); + b1_put_word(port, rp->datablklen); + restore_flags(flags); + + ctrl->appl_registered(ctrl, appl); +} + +void b1_release_appl(struct capi_ctr *ctrl, __u16 appl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + unsigned long flags; + + save_flags(flags); + cli(); + b1_put_byte(port, SEND_RELEASE); + b1_put_word(port, appl); + restore_flags(flags); +} + +void b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + unsigned long flags; + __u16 len = CAPIMSG_LEN(skb->data); + __u8 cmd = CAPIMSG_COMMAND(skb->data); + __u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data); + + save_flags(flags); + cli(); + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + __u16 dlen = CAPIMSG_DATALEN(skb->data); + b1_put_byte(port, SEND_DATA_B3_REQ); + b1_put_slice(port, skb->data, len); + b1_put_slice(port, skb->data + len, dlen); + } else { + b1_put_byte(port, SEND_MESSAGE); + b1_put_slice(port, skb->data, len); + } + restore_flags(flags); + dev_kfree_skb(skb); +} + +/* ------------------------------------------------------------- */ + +void b1_parse_version(avmcard *card) +{ + struct capi_ctr *ctrl = card->ctrl; + capi_profile *profp; + __u8 *dversion; + __u8 flag; + int i, j; + + for (j = 0; j < AVM_MAXVERSION; j++) + card->version[j] = "\0\0" + 1; + for (i = 0, j = 0; + j < AVM_MAXVERSION && i < card->versionlen; + j++, i += card->versionbuf[i] + 1) + card->version[j] = &card->versionbuf[i + 1]; + + strncpy(ctrl->serial, card->version[VER_SERIAL], CAPI_SERIAL_LEN); + memcpy(&ctrl->profile, card->version[VER_PROFILE],sizeof(capi_profile)); + strncpy(ctrl->manu, "AVM GmbH", CAPI_MANUFACTURER_LEN); + dversion = card->version[VER_DRIVER]; + ctrl->version.majorversion = 2; + ctrl->version.minorversion = 0; + ctrl->version.majormanuversion = (((dversion[0] - '0') & 0xf) << 4); + ctrl->version.majormanuversion |= ((dversion[2] - '0') & 0xf); + ctrl->version.minormanuversion = (dversion[3] - '0') << 4; + ctrl->version.minormanuversion |= + (dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf); + + profp = &ctrl->profile; + + flag = ((__u8 *)(profp->manu))[1]; + switch (flag) { + case 0: if (card->version[VER_CARDTYPE]) + strcpy(card->cardname, card->version[VER_CARDTYPE]); + else strcpy(card->cardname, "B1"); + break; + case 3: strcpy(card->cardname,"PCMCIA B"); break; + case 4: strcpy(card->cardname,"PCMCIA M1"); break; + case 5: strcpy(card->cardname,"PCMCIA M2"); break; + case 6: strcpy(card->cardname,"B1 V3.0"); break; + case 7: strcpy(card->cardname,"B1 PCI"); break; + default: sprintf(card->cardname, "AVM?%u", (unsigned int)flag); break; + } + printk(KERN_NOTICE "%s: card %d \"%s\" ready.\n", + card->name, ctrl->cnr, card->cardname); + + flag = ((__u8 *)(profp->manu))[3]; + if (flag) + printk(KERN_NOTICE "b1capi: card %d Protocol:%s%s%s%s%s%s%s\n", + ctrl->cnr, + (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 "%s: card %d Linetype:%s%s%s%s\n", + card->name, + ctrl->cnr, + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); +} + +/* ------------------------------------------------------------- */ + +void b1_handle_interrupt(avmcard * card) +{ + struct capi_ctr *ctrl = card->ctrl; + unsigned char b1cmd; + struct sk_buff *skb; + + unsigned ApplId; + unsigned MsgLen; + unsigned DataB3Len; + unsigned NCCI; + unsigned WindowSize; + + if (!b1_rx_full(card->port)) + return; + + b1cmd = b1_get_byte(card->port); + + switch (b1cmd) { + + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = b1_get_slice(card->port, card->msgbuf); + DataB3Len = b1_get_slice(card->port, card->databuf); + + if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len); + CAPIMSG_SETDATA(skb->data, skb->data + MsgLen); + ctrl->handle_capimsg(ctrl, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = b1_get_slice(card->port, card->msgbuf); + if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + ctrl->handle_capimsg(ctrl, ApplId, skb); + } + break; + + case RECEIVE_NEW_NCCI: + + ApplId = b1_get_word(card->port); + NCCI = b1_get_word(card->port); + WindowSize = b1_get_word(card->port); + + ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize); + + break; + + case RECEIVE_FREE_NCCI: + + ApplId = b1_get_word(card->port); + NCCI = b1_get_word(card->port); + + if (NCCI != 0xffffffff) + ctrl->free_ncci(ctrl, ApplId, NCCI); + else ctrl->appl_released(ctrl, ApplId); + break; + + case RECEIVE_START: + /* b1_put_byte(card->port, SEND_POLLACK); */ + ctrl->resume_output(ctrl); + break; + + case RECEIVE_STOP: + ctrl->suspend_output(ctrl); + break; + + case RECEIVE_INIT: + + card->versionlen = b1_get_slice(card->port, card->versionbuf); + b1_parse_version(card); + printk(KERN_INFO "%s: %s-card (%s) now active\n", + card->name, + card->version[VER_CARDTYPE], + card->version[VER_DRIVER]); + ctrl->ready(ctrl); + break; + + case RECEIVE_TASK_READY: + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = b1_get_slice(card->port, card->msgbuf); + card->msgbuf[MsgLen--] = 0; + while ( MsgLen >= 0 + && ( card->msgbuf[MsgLen] == '\n' + || card->msgbuf[MsgLen] == '\r')) + card->msgbuf[MsgLen--] = 0; + printk(KERN_INFO "%s: task %d \"%s\" ready.\n", + card->name, ApplId, card->msgbuf); + break; + + case RECEIVE_DEBUGMSG: + MsgLen = b1_get_slice(card->port, card->msgbuf); + card->msgbuf[MsgLen--] = 0; + while ( MsgLen >= 0 + && ( card->msgbuf[MsgLen] == '\n' + || card->msgbuf[MsgLen] == '\r')) + card->msgbuf[MsgLen--] = 0; + printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); + break; + + case 0xff: + printk(KERN_ERR "%s: card removed ?\n", card->name); + return; + default: + printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n", + card->name, b1cmd); + return; + } +} + +/* ------------------------------------------------------------- */ +int b1ctl_read_proc(char *page, char **start, off_t off, + int count, int *eof, struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + __u8 flag; + int len = 0; + char *s; + + len += sprintf(page+len, "%-16s %s\n", "name", card->name); + len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port); + len += sprintf(page+len, "%-16s %d\n", "irq", card->irq); + switch (card->cardtype) { + case avm_b1isa: s = "B1 ISA"; break; + case avm_b1pci: s = "B1 PCI"; break; + case avm_b1pcmcia: s = "B1 PCMCIA"; break; + case avm_m1: s = "M1"; break; + case avm_m2: s = "M2"; break; + case avm_t1isa: s = "T1 ISA (HEMA)"; break; + case avm_t1pci: s = "T1 PCI"; break; + case avm_c4: s = "C4"; break; + default: s = "???"; break; + } + len += sprintf(page+len, "%-16s %s\n", "type", s); + if (card->cardtype == avm_t1isa) + len += sprintf(page+len, "%-16s %d\n", "cardnr", card->cardnr); + if ((s = card->version[VER_DRIVER]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_driver", s); + if ((s = card->version[VER_CARDTYPE]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s); + if ((s = card->version[VER_SERIAL]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_serial", s); + + if (card->cardtype != avm_m1) { + flag = ((__u8 *)(ctrl->profile.manu))[3]; + if (flag) + len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n", + "protocol", + (flag & 0x01) ? " DSS1" : "", + (flag & 0x02) ? " CT1" : "", + (flag & 0x04) ? " VN3" : "", + (flag & 0x08) ? " NI1" : "", + (flag & 0x10) ? " AUSTEL" : "", + (flag & 0x20) ? " ESS" : "", + (flag & 0x40) ? " 1TR6" : "" + ); + } + if (card->cardtype != avm_m1) { + flag = ((__u8 *)(ctrl->profile.manu))[5]; + if (flag) + len += sprintf(page+len, "%-16s%s%s%s%s\n", + "linetype", + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); + } + len += sprintf(page+len, "%-16s %s\n", "cardname", card->cardname); + + if (off+count >= len) + *eof = 1; + if (len < off) + return 0; + *start = page + off; + return ((count < len-off) ? count : len-off); +} + +/* ------------------------------------------------------------- */ + +EXPORT_SYMBOL(b1_irq_table); + +EXPORT_SYMBOL(b1_detect); +EXPORT_SYMBOL(b1_load_t4file); +EXPORT_SYMBOL(b1_load_config); +EXPORT_SYMBOL(b1_loaded); +EXPORT_SYMBOL(b1_load_firmware); +EXPORT_SYMBOL(b1_reset_ctr); +EXPORT_SYMBOL(b1_register_appl); +EXPORT_SYMBOL(b1_release_appl); +EXPORT_SYMBOL(b1_send_message); + +EXPORT_SYMBOL(b1_parse_version); +EXPORT_SYMBOL(b1_handle_interrupt); + +EXPORT_SYMBOL(b1ctl_read_proc); + +#ifdef MODULE +#define b1_init init_module +void cleanup_module(void); +#endif + +int b1_init(void) +{ + char *p; + char rev[10]; + + if ((p = strchr(revision, ':'))) { + strncpy(rev, p + 1, sizeof(rev)); + p = strchr(rev, '$'); + *p = 0; + } else + strcpy(rev, "1.0"); + + printk(KERN_INFO "b1: revision %s\n", rev); + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ +} +#endif diff --git a/drivers/isdn/avmb1/b1isa.c b/drivers/isdn/avmb1/b1isa.c new file mode 100644 index 000000000000..9b4c47d43671 --- /dev/null +++ b/drivers/isdn/avmb1/b1isa.c @@ -0,0 +1,238 @@ +/* + * $Id: b1isa.c,v 1.3 1999/07/09 15:05:40 keil Exp $ + * + * Module for AVM B1 ISA-card. + * + * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: b1isa.c,v $ + * Revision 1.3 1999/07/09 15:05:40 keil + * compat.h is now isdn_compat.h + * + * Revision 1.2 1999/07/05 15:09:49 calle + * - renamed "appl_release" to "appl_released". + * - version und profile data now cleared on controller reset + * - extended /proc interface, to allow driver and controller specific + * informations to include by driver hackers. + * + * Revision 1.1 1999/07/01 15:26:27 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "capicmd.h" +#include "capiutil.h" +#include "capilli.h" +#include "avmcard.h" + +static char *revision = "$Revision: 1.3 $"; + +/* ------------------------------------------------------------- */ + +MODULE_AUTHOR("Carsten Paeth "); + +/* ------------------------------------------------------------- */ + +static struct capi_driver_interface *di; + +/* ------------------------------------------------------------- */ + +static void b1isa_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmcard *card; + + card = (avmcard *) devptr; + + if (!card) { + printk(KERN_WARNING "b1_interrupt: wrong device\n"); + return; + } + if (card->interrupt) { + printk(KERN_ERR "b1_interrupt: reentering interrupt hander (%s)\n", card->name); + return; + } + + card->interrupt = 1; + + b1_handle_interrupt(card); + + card->interrupt = 0; +} +/* ------------------------------------------------------------- */ + +static void b1isa_remove_ctr(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + + b1_reset(port); + b1_reset(port); + + di->detach_ctr(ctrl); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + kfree(card); + + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------- */ + +static int b1isa_add_card(struct capi_driver *driver, struct capicardparams *p) +{ + avmcard *card; + int retval; + + card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); + + if (!card) { + printk(KERN_WARNING "b1isa: no memory.\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(avmcard)); + sprintf(card->name, "b1isa-%x", p->port); + card->port = p->port; + card->irq = p->irq; + card->cardtype = avm_b1isa; + + if (check_region(card->port, AVMB1_PORTLEN)) { + printk(KERN_WARNING + "b1isa: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + if (b1_irq_table[card->irq & 0xf] == 0) { + printk(KERN_WARNING "b1isa: irq %d not valid.\n", card->irq); + kfree(card); + return -EINVAL; + } + if ( card->port != 0x150 && card->port != 0x250 + && card->port != 0x300 && card->port != 0x340) { + printk(KERN_WARNING "b1isa: illegal port 0x%x.\n", card->port); + kfree(card); + return -EINVAL; + } + b1_reset(card->port); + if ((retval = b1_detect(card->port, card->cardtype)) != 0) { + printk(KERN_NOTICE "b1isa: NO card at 0x%x (%d)\n", + card->port, retval); + kfree(card); + return -EIO; + } + b1_reset(card->port); + + request_region(p->port, AVMB1_PORTLEN, card->name); + + retval = request_irq(card->irq, b1isa_interrupt, 0, card->name, card); + if (retval) { + printk(KERN_ERR "b1isa: unable to get IRQ %d.\n", card->irq); + release_region(card->port, AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + + card->ctrl = di->attach_ctr(driver, card->name, card); + if (!card->ctrl) { + printk(KERN_ERR "b1isa: attach controller failed.\n"); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + + MOD_INC_USE_COUNT; + return 0; +} + +static char *b1isa_procinfo(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + if (!card) + return ""; + sprintf(card->infobuf, "%s %s 0x%x %d", + card->cardname[0] ? card->cardname : "-", + card->version[VER_DRIVER] ? card->version[VER_DRIVER] : "-", + card->port, card->irq + ); + return card->infobuf; +} + +/* ------------------------------------------------------------- */ + +static struct capi_driver b1isa_driver = { + "b1isa", + "0.0", + b1_load_firmware, + b1_reset_ctr, + b1isa_remove_ctr, + b1_register_appl, + b1_release_appl, + b1_send_message, + + b1isa_procinfo, + b1ctl_read_proc, + 0, /* use standard driver_read_proc */ + + b1isa_add_card, +}; + +#ifdef MODULE +#define b1isa_init init_module +void cleanup_module(void); +#endif + +int b1isa_init(void) +{ + struct capi_driver *driver = &b1isa_driver; + char *p; + + if ((p = strchr(revision, ':'))) { + strncpy(driver->revision, p + 1, sizeof(driver->revision)); + p = strchr(driver->revision, '$'); + *p = 0; + } + + printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision); + + di = attach_capi_driver(driver); + + if (!di) { + printk(KERN_ERR "%s: failed to attach capi_driver\n", + driver->name); + return -EIO; + } + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + detach_capi_driver(&b1isa_driver); +} +#endif diff --git a/drivers/isdn/avmb1/b1pci.c b/drivers/isdn/avmb1/b1pci.c index 112ddbb81f98..8c0006a78bf5 100644 --- a/drivers/isdn/avmb1/b1pci.c +++ b/drivers/isdn/avmb1/b1pci.c @@ -1,59 +1,65 @@ /* - * $Id: b1pci.c,v 1.9 1999/04/15 19:49:32 calle Exp $ + * $Id: b1pci.c,v 1.16 1999/08/11 21:01:07 keil Exp $ * * Module for AVM B1 PCI-card. * - * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) + * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1pci.c,v $ - * Revision 1.9 1999/04/15 19:49:32 calle - * fix fuer die B1-PCI. Jetzt geht z.B. auch IRQ 17 ... + * Revision 1.16 1999/08/11 21:01:07 keil + * new PCI codefix * - * Revision 1.8 1998/06/17 19:51:16 he - * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay()) - * brute force fix to avoid Ugh's in isdn_tty_write() - * cleaned up some dead code + * Revision 1.15 1999/08/10 16:02:27 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. * - * Revision 1.7 1998/03/29 16:06:02 calle - * changes from 2.0 tree merged. + * Revision 1.14 1999/07/09 15:05:41 keil + * compat.h is now isdn_compat.h * - * 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.13 1999/07/05 15:09:50 calle + * - renamed "appl_release" to "appl_released". + * - version und profile data now cleared on controller reset + * - extended /proc interface, to allow driver and controller specific + * informations to include by driver hackers. * - * Revision 1.6 1998/02/25 09:15:36 fritz - * apply Martin's pci driver patch to isdn drivers (vgerCVS) + * Revision 1.12 1999/07/01 15:26:29 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. * - * Revision 1.5 1998/01/31 11:14:43 calle - * merged changes to 2.0 tree, prepare 2.1.82 to work. - * - * Revision 1.4 1997/12/10 20:00:50 calle - * get changes from 2.0 version - * - * Revision 1.3 1997/10/01 09:21:14 fritz - * Removed old compatibility stuff for 2.0.X kernels. - * From now on, this code is for 2.1.X ONLY! - * Old stuff is still in the separate branch. - * - * Revision 1.2 1997/05/18 09:24:13 calle - * added verbose disconnect reason reporting to avmb1. - * some fixes in capi20 interface. - * changed info messages for B1-PCI - * - * Revision 1.1 1997/03/30 17:10:42 calle - * added support for AVM-B1-PCI card. * */ #include -#include #include #include -#include #include -#include "compat.h" +#include +#include +#include +#include +#include #include -#include +#include +#include +#include "capicmd.h" +#include "capiutil.h" +#include "capilli.h" +#include "avmcard.h" + +static char *revision = "$Revision: 1.16 $"; + +/* ------------------------------------------------------------- */ #ifndef PCI_VENDOR_ID_AVM #define PCI_VENDOR_ID_AVM 0x1244 @@ -63,65 +69,213 @@ #define PCI_DEVICE_ID_AVM_B1 0x700 #endif -static char *revision = "$Revision: 1.9 $"; - /* ------------------------------------------------------------- */ MODULE_AUTHOR("Carsten Paeth "); /* ------------------------------------------------------------- */ +static struct capi_driver_interface *di; + +/* ------------------------------------------------------------- */ + +static void b1pci_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmcard *card; + + card = (avmcard *) devptr; + + if (!card) { + printk(KERN_WARNING "b1_interrupt: wrong device\n"); + return; + } + if (card->interrupt) { + printk(KERN_ERR "b1_interrupt: reentering interrupt hander (%s)\n", card->name); + return; + } + + card->interrupt = 1; + + b1_handle_interrupt(card); + + card->interrupt = 0; +} +/* ------------------------------------------------------------- */ + +static void b1pci_remove_ctr(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + + b1_reset(port); + b1_reset(port); + + di->detach_ctr(ctrl); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + ctrl->driverdata = 0; + kfree(card); + + MOD_DEC_USE_COUNT; +} + /* ------------------------------------------------------------- */ -/* -------- Init & Cleanup ------------------------------------- */ + +static char *b1pci_procinfo(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + if (!card) + return ""; + sprintf(card->infobuf, "%s %s 0x%x %d", + card->cardname[0] ? card->cardname : "-", + card->version[VER_DRIVER] ? card->version[VER_DRIVER] : "-", + card->port, card->irq + ); + return card->infobuf; +} + /* ------------------------------------------------------------- */ -/* - * init / exit functions - */ +static int b1pci_add_card(struct capi_driver *driver, struct capicardparams *p) +{ + avmcard *card; + int retval; + + card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); + + if (!card) { + printk(KERN_WARNING "b1pci: no memory.\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(avmcard)); + sprintf(card->name, "b1pci-%x", p->port); + card->port = p->port; + card->irq = p->irq; + card->cardtype = avm_b1pci; + + if (check_region(card->port, AVMB1_PORTLEN)) { + printk(KERN_WARNING + "b1pci: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + b1_reset(card->port); + if ((retval = b1_detect(card->port, card->cardtype)) != 0) { + printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n", + card->port, retval); + kfree(card); + return -EIO; + } + b1_reset(card->port); + + request_region(p->port, AVMB1_PORTLEN, card->name); + + retval = request_irq(card->irq, b1pci_interrupt, 0, card->name, card); + if (retval) { + printk(KERN_ERR "b1pci: unable to get IRQ %d.\n", card->irq); + release_region(card->port, AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + + card->ctrl = di->attach_ctr(driver, card->name, card); + if (!card->ctrl) { + printk(KERN_ERR "b1pci: attach controller failed.\n"); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + + MOD_INC_USE_COUNT; + + return 0; +} + +/* ------------------------------------------------------------- */ + +static struct capi_driver b1pci_driver = { + "b1pci", + "0.0", + b1_load_firmware, + b1_reset_ctr, + b1pci_remove_ctr, + b1_register_appl, + b1_release_appl, + b1_send_message, + + b1pci_procinfo, + b1ctl_read_proc, + 0, /* use standard driver_read_proc */ + + 0, /* no add_card function */ +}; #ifdef MODULE #define b1pci_init init_module +void cleanup_module(void); #endif +static int ncards = 0; + int b1pci_init(void) { - char *p; - char rev[10]; - int rc; + struct capi_driver *driver = &b1pci_driver; struct pci_dev *dev = NULL; + char *p; + int retval; if ((p = strchr(revision, ':'))) { - strcpy(rev, p + 1); - p = strchr(rev, '$'); + strncpy(driver->revision, p + 1, sizeof(driver->revision)); + p = strchr(driver->revision, '$'); *p = 0; - } else - strcpy(rev, " ??? "); + } + + printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision); + di = attach_capi_driver(driver); + + if (!di) { + printk(KERN_ERR "%s: failed to attach capi_driver\n", + driver->name); + return -EIO; + } #ifdef CONFIG_PCI if (!pci_present()) { - printk(KERN_ERR "b1pci: no PCI bus present\n"); + printk(KERN_ERR "%s: no PCI bus present\n", driver->name); + detach_capi_driver(driver); return -EIO; } - printk(KERN_INFO "b1pci: revision %s\n", rev); - while ((dev = pci_find_device(PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_B1, dev))) { - unsigned int ioaddr = dev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK; - unsigned int irq = dev->irq; + struct capicardparams param; + + param.port = get_pcibase(dev, 1) & PCI_BASE_ADDRESS_IO_MASK; + param.irq = dev->irq; printk(KERN_INFO - "b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n", - ioaddr, irq); - if ((rc = avmb1_probecard(ioaddr, irq, AVM_CARDTYPE_B1PCI)) != 0) { + "%s: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n", + driver->name, param.port, param.irq); + retval = b1pci_add_card(driver, ¶m); + if (retval != 0) { printk(KERN_ERR - "b1pci: no AVM-B1 at i/o %#x, irq %d detected\n", - ioaddr, irq); - return rc; + "%s: no AVM-B1 at i/o %#x, irq %d detected\n", + driver->name, param.port, param.irq); +#ifdef MODULE + cleanup_module(); +#endif + return retval; } - if ((rc = avmb1_addcard(ioaddr, irq, AVM_CARDTYPE_B1PCI)) < 0) - return rc; + ncards++; } - return 0; + if (ncards) { + printk(KERN_INFO "%s: %d B1-PCI card(s) detected\n", + driver->name, ncards); + return 0; + } + printk(KERN_ERR "%s: NO B1-PCI card detected\n", driver->name); + return -ESRCH; #else printk(KERN_ERR "b1pci: kernel not compiled with PCI.\n"); return -EIO; @@ -131,5 +285,6 @@ int b1pci_init(void) #ifdef MODULE void cleanup_module(void) { + detach_capi_driver(&b1pci_driver); } #endif diff --git a/drivers/isdn/avmb1/b1pcmcia.c b/drivers/isdn/avmb1/b1pcmcia.c new file mode 100644 index 000000000000..ff7629cb0f71 --- /dev/null +++ b/drivers/isdn/avmb1/b1pcmcia.c @@ -0,0 +1,267 @@ +/* + * $Id: b1pcmcia.c,v 1.3 1999/07/09 15:05:41 keil Exp $ + * + * Module for AVM B1/M1/M2 PCMCIA-card. + * + * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: b1pcmcia.c,v $ + * Revision 1.3 1999/07/09 15:05:41 keil + * compat.h is now isdn_compat.h + * + * Revision 1.2 1999/07/05 15:09:51 calle + * - renamed "appl_release" to "appl_released". + * - version und profile data now cleared on controller reset + * - extended /proc interface, to allow driver and controller specific + * informations to include by driver hackers. + * + * Revision 1.1 1999/07/01 15:26:30 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "capicmd.h" +#include "capiutil.h" +#include "capilli.h" +#include "avmcard.h" + +static char *revision = "$Revision: 1.3 $"; + +/* ------------------------------------------------------------- */ + +MODULE_AUTHOR("Carsten Paeth "); + +/* ------------------------------------------------------------- */ + +static struct capi_driver_interface *di; + +/* ------------------------------------------------------------- */ + +static void b1pcmcia_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmcard *card; + + card = (avmcard *) devptr; + + if (!card) { + printk(KERN_WARNING "b1_interrupt: wrong device\n"); + return; + } + if (card->interrupt) { + printk(KERN_ERR "b1_interrupt: reentering interrupt hander (%s)\n", card->name); + return; + } + + card->interrupt = 1; + + b1_handle_interrupt(card); + + card->interrupt = 0; +} +/* ------------------------------------------------------------- */ + +static void b1pcmcia_remove_ctr(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + + b1_reset(port); + b1_reset(port); + + di->detach_ctr(ctrl); + free_irq(card->irq, card); + /* io addrsses managent by CardServices + * release_region(card->port, AVMB1_PORTLEN); + */ + kfree(card); + + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------- */ + +static int b1pcmcia_add_card(struct capi_driver *driver, + unsigned int port, + unsigned irq, + enum avmcardtype cardtype) +{ + avmcard *card; + int retval; + + card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); + + if (!card) { + printk(KERN_WARNING "b1pcmcia: no memory.\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(avmcard)); + switch (cardtype) { + case avm_m1: sprintf(card->name, "m1-%x", port); break; + case avm_m2: sprintf(card->name, "m2-%x", port); break; + default: sprintf(card->name, "b1pcmcia-%x", port); break; + } + card->port = port; + card->irq = irq; + card->cardtype = cardtype; + + b1_reset(card->port); + if ((retval = b1_detect(card->port, card->cardtype)) != 0) { + printk(KERN_NOTICE "b1pcmcia: NO card at 0x%x (%d)\n", + card->port, retval); + kfree(card); + return -EIO; + } + b1_reset(card->port); + + retval = request_irq(card->irq, b1pcmcia_interrupt, 0, card->name, card); + if (retval) { + printk(KERN_ERR "b1pcmcia: unable to get IRQ %d.\n", card->irq); + kfree(card); + return -EBUSY; + } + + card->ctrl = di->attach_ctr(driver, card->name, card); + if (!card->ctrl) { + printk(KERN_ERR "b1pcmcia: attach controller failed.\n"); + free_irq(card->irq, card); + kfree(card); + return -EBUSY; + } + + MOD_INC_USE_COUNT; + return card->ctrl->cnr; +} + +/* ------------------------------------------------------------- */ + +static char *b1pcmcia_procinfo(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + if (!card) + return ""; + sprintf(card->infobuf, "%s %s 0x%x %d", + card->cardname[0] ? card->cardname : "-", + card->version[VER_DRIVER] ? card->version[VER_DRIVER] : "-", + card->port, card->irq + ); + return card->infobuf; +} + +/* ------------------------------------------------------------- */ + +static struct capi_driver b1pcmcia_driver = { + "b1pcmcia", + "0.0", + b1_load_firmware, + b1_reset_ctr, + b1pcmcia_remove_ctr, + b1_register_appl, + b1_release_appl, + b1_send_message, + + b1pcmcia_procinfo, + b1ctl_read_proc, + 0, /* use standard driver_read_proc */ + + 0, +}; + +/* ------------------------------------------------------------- */ + +int b1pcmcia_addcard_b1(unsigned int port, unsigned irq) +{ + return b1pcmcia_add_card(&b1pcmcia_driver, port, irq, avm_b1pcmcia); +} + +int b1pcmcia_addcard_m1(unsigned int port, unsigned irq) +{ + return b1pcmcia_add_card(&b1pcmcia_driver, port, irq, avm_m1); +} + +int b1pcmcia_addcard_m2(unsigned int port, unsigned irq) +{ + return b1pcmcia_add_card(&b1pcmcia_driver, port, irq, avm_m2); +} + +int b1pcmcia_delcard(unsigned int port, unsigned irq) +{ + struct capi_ctr *ctrl; + avmcard *card; + + for (ctrl = b1pcmcia_driver.controller; ctrl; ctrl = ctrl->next) { + card = (avmcard *)(ctrl->driverdata); + if (card->port == port && card->irq == irq) { + b1pcmcia_remove_ctr(ctrl); + return 0; + } + } + return -ESRCH; +} + +EXPORT_SYMBOL(b1pcmcia_addcard_b1); +EXPORT_SYMBOL(b1pcmcia_addcard_m1); +EXPORT_SYMBOL(b1pcmcia_addcard_m2); +EXPORT_SYMBOL(b1pcmcia_delcard); + +/* ------------------------------------------------------------- */ + +#ifdef MODULE +#define b1pcmcia_init init_module +void cleanup_module(void); +#endif + +int b1pcmcia_init(void) +{ + struct capi_driver *driver = &b1pcmcia_driver; + char *p; + + if ((p = strchr(revision, ':'))) { + strncpy(driver->revision, p + 1, sizeof(driver->revision)); + p = strchr(driver->revision, '$'); + *p = 0; + } + + printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision); + + di = attach_capi_driver(driver); + + if (!di) { + printk(KERN_ERR "%s: failed to attach capi_driver\n", + driver->name); + return -EIO; + } + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + detach_capi_driver(&b1pcmcia_driver); +} +#endif diff --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c index 69ed317f3a7b..c8cab69193e0 100644 --- a/drivers/isdn/avmb1/capi.c +++ b/drivers/isdn/avmb1/capi.c @@ -1,11 +1,43 @@ /* - * $Id: capi.c,v 1.13 1998/08/28 04:32:25 calle Exp $ + * $Id: capi.c,v 1.19 1999/07/09 15:05:42 keil Exp $ * * CAPI 2.0 Interface for Linux * * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capi.c,v $ + * Revision 1.19 1999/07/09 15:05:42 keil + * compat.h is now isdn_compat.h + * + * Revision 1.18 1999/07/06 07:42:01 calle + * - changes in /proc interface + * - check and changed calls to [dev_]kfree_skb and [dev_]alloc_skb. + * + * Revision 1.17 1999/07/01 15:26:30 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + * Revision 1.16 1999/07/01 08:22:57 keil + * compatibility macros now in + * + * Revision 1.15 1999/06/21 15:24:11 calle + * extend information in /proc. + * + * Revision 1.14 1999/06/10 16:51:03 calle + * Bugfix: open/release of control device was not handled correct. + * * Revision 1.13 1998/08/28 04:32:25 calle * Added patch send by Michael.Mueller4@post.rwth-aachen.de, to get AVM B1 * driver running with 2.1.118. @@ -80,11 +112,12 @@ #include #include #include +#include #include #include #include -#include "compat.h" +#include #include "capiutil.h" #include "capicmd.h" #include "capidev.h" @@ -178,7 +211,10 @@ static ssize_t capi_read(struct file *file, char *buf, } copied = skb->len; - + if (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3 + && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) + cdev->nrecvdatapkt++; + else cdev->nrecvctlpkt++; kfree_skb(skb); return copied; @@ -207,7 +243,7 @@ static ssize_t capi_write(struct file *file, const char *buf, skb = alloc_skb(count, GFP_USER); if ((retval = copy_from_user(skb_put(skb, count), buf, count))) { - dev_kfree_skb(skb); + kfree_skb(skb); return retval; } cmd = CAPIMSG_COMMAND(skb->data); @@ -216,11 +252,11 @@ static ssize_t capi_write(struct file *file, const char *buf, if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) { __u16 dlen = CAPIMSG_DATALEN(skb->data); if (mlen + dlen != count) { - dev_kfree_skb(skb); + kfree_skb(skb); return -EINVAL; } } else if (mlen != count) { - dev_kfree_skb(skb); + kfree_skb(skb); return -EINVAL; } CAPIMSG_SETAPPID(skb->data, cdev->applid); @@ -228,9 +264,12 @@ static ssize_t capi_write(struct file *file, const char *buf, cdev->errcode = (*capifuncs->capi_put_message) (cdev->applid, skb); if (cdev->errcode) { - dev_kfree_skb(skb); + kfree_skb(skb); return -EIO; } + if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) + cdev->nsentdatapkt++; + else cdev->nsentctlpkt++; return count; } @@ -426,16 +465,13 @@ static int capi_open(struct inode *inode, struct file *file) capidevs[minor].is_open = 1; skb_queue_head_init(&capidevs[minor].recv_queue); MOD_INC_USE_COUNT; + capidevs[minor].nopen++; } else { - - if (!capidevs[minor].is_open) { - capidevs[minor].is_open = 1; - MOD_INC_USE_COUNT; - } + capidevs[minor].is_open++; + MOD_INC_USE_COUNT; } - return 0; } @@ -460,10 +496,13 @@ capi_release(struct inode *inode, struct file *file) cdev->is_registered = 0; cdev->applid = 0; - while ((skb = skb_dequeue(&cdev->recv_queue)) != 0) + while ((skb = skb_dequeue(&cdev->recv_queue)) != 0) { kfree_skb(skb); + } + cdev->is_open = 0; + } else { + cdev->is_open--; } - cdev->is_open = 0; MOD_DEC_USE_COUNT; return 0; @@ -487,7 +526,82 @@ static struct file_operations capi_fops = NULL, /* capi_fasync */ }; +/* -------- /proc functions ----------------------------------- */ +/* + * /proc/capi/capi20: + * minor opencount nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt + */ +static int proc_capidev_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capidev *cp; + int i; + int len = 0; + off_t begin = 0; + + for (i=0; i < CAPI_MAXMINOR; i++) { + cp = &capidevs[i+1]; + if (cp->nopen == 0) continue; + len += sprintf(page+len, "%d %lu %lu %lu %lu %lu\n", + i+1, + cp->nopen, + cp->nrecvctlpkt, + cp->nrecvdatapkt, + cp->nsentctlpkt, + cp->nsentdatapkt); + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } +endloop: + if (i >= CAPI_MAXMINOR) + *eof = 1; + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +static struct procfsentries { + char *name; + mode_t mode; + int (*read_proc)(char *page, char **start, off_t off, + int count, int *eof, void *data); + struct proc_dir_entry *procent; +} procfsentries[] = { + /* { "capi", S_IFDIR, 0 }, */ + { "capi/capi20", 0 , proc_capidev_read_proc }, +}; + +static void proc_init(void) +{ + int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]); + int i; + + for (i=0; i < nelem; i++) { + struct procfsentries *p = procfsentries + i; + p->procent = create_proc_entry(p->name, p->mode, 0); + if (p->procent) p->procent->read_proc = p->read_proc; + } +} + +static void proc_exit(void) +{ + int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]); + int i; + + for (i=nelem-1; i >= 0; i--) { + struct procfsentries *p = procfsentries + i; + if (p->procent) { + remove_proc_entry(p->name, 0); + p->procent = 0; + } + } +} /* -------- init function and module interface ---------------------- */ #ifdef MODULE @@ -501,12 +615,12 @@ static struct capi_interface_user cuser = { int capi_init(void) { -#if LINUX_VERSION_CODE >= 131841 +#ifdef COMPAT_HAS_NEW_WAITQ int j; #endif memset(capidevs, 0, sizeof(capidevs)); -#if LINUX_VERSION_CODE >= 131841 +#ifdef COMPAT_HAS_NEW_WAITQ for ( j = 0; j < CAPI_MAXMINOR+1; j++ ) { init_waitqueue_head(&capidevs[j].recv_wait); } @@ -522,13 +636,14 @@ int capi_init(void) unregister_chrdev(capi_major, "capi20"); return -EIO; } - + (void)proc_init(); return 0; } #ifdef MODULE void cleanup_module(void) { + (void)proc_exit(); unregister_chrdev(capi_major, "capi20"); (void) detach_capi_interface(&cuser); } diff --git a/drivers/isdn/avmb1/capidev.h b/drivers/isdn/avmb1/capidev.h index bd57255b674c..c66554a2979e 100644 --- a/drivers/isdn/avmb1/capidev.h +++ b/drivers/isdn/avmb1/capidev.h @@ -1,11 +1,33 @@ /* - * $Id: capidev.h,v 1.1 1997/03/04 21:50:30 calle Exp $ + * $Id: capidev.h,v 1.4 1999/07/01 15:26:32 calle Exp $ * * CAPI 2.0 Interface for Linux * * (c) Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capidev.h,v $ + * Revision 1.4 1999/07/01 15:26:32 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + * Revision 1.3 1999/07/01 08:22:58 keil + * compatibility macros now in + * + * Revision 1.2 1999/06/21 15:24:13 calle + * extend information in /proc. + * * Revision 1.1 1997/03/04 21:50:30 calle * Frirst version in isdn4linux * @@ -22,12 +44,18 @@ struct capidev { int is_registered; __u16 applid; struct sk_buff_head recv_queue; -#if LINUX_VERSION_CODE < 131841 - struct wait_queue *recv_wait; -#else +#ifdef COMPAT_HAS_NEW_WAITQ wait_queue_head_t recv_wait; +#else + struct wait_queue *recv_wait; #endif __u16 errcode; + /* Statistic */ + unsigned long nopen; + unsigned long nrecvctlpkt; + unsigned long nrecvdatapkt; + unsigned long nsentctlpkt; + unsigned long nsentdatapkt; }; #define CAPI_MAXMINOR CAPI_MAXAPPL diff --git a/drivers/isdn/avmb1/capidrv.c b/drivers/isdn/avmb1/capidrv.c index 80a2eccdb2c6..fb83e52a6e06 100644 --- a/drivers/isdn/avmb1/capidrv.c +++ b/drivers/isdn/avmb1/capidrv.c @@ -1,11 +1,67 @@ /* - * $Id: capidrv.c,v 1.13 1998/06/26 15:12:55 fritz Exp $ + * $Id: capidrv.c,v 1.26 1999/08/06 07:41:16 calle Exp $ * * ISDN4Linux Driver, using capi20 interface (kernelcapi) * * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capidrv.c,v $ + * Revision 1.26 1999/08/06 07:41:16 calle + * Added the "vbox patch". if (si1 == 1) si2 = 0; + * + * Revision 1.25 1999/08/04 10:10:11 calle + * Bugfix: corrected /proc functions, added structure for new AVM cards. + * + * Revision 1.24 1999/07/20 06:48:02 calle + * Bugfix: firmware version check for D2 trace was too restrictiv. + * + * Revision 1.23 1999/07/09 15:05:44 keil + * compat.h is now isdn_compat.h + * + * Revision 1.22 1999/07/06 07:24:14 calle + * Bugfix: call to kfree_skb in capidrv_signal was too early, + * thanks to Lars Heete . + * + * Revision 1.21 1999/07/01 15:26:34 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + * Revision 1.20 1999/07/01 08:22:59 keil + * compatibility macros now in + * + * Revision 1.19 1999/06/29 16:16:54 calle + * Let ISDN_CMD_UNLOAD work with open isdn devices without crash again. + * Also right unlocking (ISDN_CMD_UNLOCK) is done now. + * isdnlog should check returncode of read(2) calls. + * + * Revision 1.18 1999/06/21 15:24:15 calle + * extend information in /proc. + * + * Revision 1.17 1999/06/10 16:53:55 calle + * Removing of module b1pci will now remove card from lower level. + * + * Revision 1.16 1999/05/31 11:50:33 calle + * Bugfix: In if_sendbuf, skb_push'ed DATA_B3 header was not skb_pull'ed + * on failure, result in data block with DATA_B3 header transmitted + * + * Revision 1.15 1999/05/25 21:26:16 calle + * Include CAPI-Channelallocation (leased lines) from the 2.0 tree. + * + * Revision 1.14 1999/05/22 07:55:06 calle + * Added *V110* to AVM B1 driver. + * * Revision 1.13 1998/06/26 15:12:55 fritz * Added handling of STAT_ICALL with incomplete CPN. * Added AT&L for ttyI emulator. @@ -96,15 +152,18 @@ #include #include #include +#include #include #include +#include +#include -#include "compat.h" +#include #include "capiutil.h" #include "capicmd.h" #include "capidrv.h" -static char *revision = "$Revision: 1.13 $"; +static char *revision = "$Revision: 1.26 $"; int debugmode = 0; MODULE_AUTHOR("Carsten Paeth "); @@ -160,6 +219,7 @@ struct capidrv_contr { __u16 msgid; /* to identfy CONNECT_CONF */ int chan; int state; + int leasedline; struct capidrv_ncci { struct capidrv_ncci *next; struct capidrv_plci *plcip; @@ -194,6 +254,12 @@ struct capidrv_data { __u16 appid; int ncontr; struct capidrv_contr *contr_list; + + /* statistic */ + unsigned long nrecvctlpkt; + unsigned long nrecvdatapkt; + unsigned long nsentctlpkt; + unsigned long nsentdatapkt; }; typedef struct capidrv_plci capidrv_plci; @@ -224,6 +290,12 @@ static inline __u32 b1prot(int l2, int l3) return 0; case ISDN_PROTO_L2_TRANS: return 1; + case ISDN_PROTO_L2_V11096: + case ISDN_PROTO_L2_V11019: + case ISDN_PROTO_L2_V11038: + return 2; + case ISDN_PROTO_L2_FAX: + return 4; } } @@ -237,7 +309,12 @@ static inline __u32 b2prot(int l2, int l3) return 0; case ISDN_PROTO_L2_HDLC: case ISDN_PROTO_L2_TRANS: + case ISDN_PROTO_L2_V11096: + case ISDN_PROTO_L2_V11019: + case ISDN_PROTO_L2_V11038: return 1; + case ISDN_PROTO_L2_FAX: + return 4; } } @@ -249,8 +326,45 @@ static inline __u32 b3prot(int l2, int l3) case ISDN_PROTO_L2_X75BUI: case ISDN_PROTO_L2_HDLC: case ISDN_PROTO_L2_TRANS: + case ISDN_PROTO_L2_V11096: + case ISDN_PROTO_L2_V11019: + case ISDN_PROTO_L2_V11038: default: return 0; + case ISDN_PROTO_L2_FAX: + return 4; + } +} + +static _cstruct b1config_sync_v110(__u16 rate) +{ + /* CAPI-Spec "B1 Configuration" */ + static unsigned char buf[9]; + buf[0] = 8; /* len */ + /* maximum bitrate */ + buf[1] = rate & 0xff; buf[2] = (rate >> 8) & 0xff; + buf[3] = buf[4] = 0; /* reserved, bits per character */ + buf[5] = buf[6] = 0; /* reserved, parity */ + buf[7] = buf[9] = 0; /* reserved, stop bits */ + return buf; +} + +static _cstruct b1config(int l2, int l3) +{ + switch (l2) { + case ISDN_PROTO_L2_X75I: + case ISDN_PROTO_L2_X75UI: + case ISDN_PROTO_L2_X75BUI: + case ISDN_PROTO_L2_HDLC: + case ISDN_PROTO_L2_TRANS: + default: + return 0; + case ISDN_PROTO_L2_V11096: + return b1config_sync_v110(9600); + case ISDN_PROTO_L2_V11019: + return b1config_sync_v110(19200); + case ISDN_PROTO_L2_V11038: + return b1config_sync_v110(38400); } } @@ -522,9 +636,10 @@ static void send_message(capidrv_contr * card, _cmsg * cmsg) size_t len; capi_cmsg2message(cmsg, cmsg->buf); len = CAPIMSG_LEN(cmsg->buf); - skb = dev_alloc_skb(len); + skb = alloc_skb(len, GFP_ATOMIC); memcpy(skb_put(skb, len), cmsg->buf, len); (*capifuncs->capi_put_message) (global.appid, skb); + global.nsentctlpkt++; } /* -------- state machine -------------------------------------------- */ @@ -917,6 +1032,13 @@ static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg) cmd.parm.setup.si2, cmd.parm.setup.eazmsn); + if (cmd.parm.setup.si1 == 1 && cmd.parm.setup.si2 != 0) { + printk(KERN_INFO "capidrv-%d: patching si2=%d to 0 for VBOX\n", + card->contrnr, + cmd.parm.setup.si2); + cmd.parm.setup.si2 = 0; + } + switch (card->interface.statcallb(&cmd)) { case 0: case 3: @@ -1353,6 +1475,7 @@ static void handle_data(_cmsg * cmsg, struct sk_buff *skb) printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrController & 0x7f); + kfree_skb(skb); return; } if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) { @@ -1377,13 +1500,14 @@ 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) + if (debugmode > 2) 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) { handle_data(&s_cmsg, skb); + global.nrecvdatapkt++; continue; } if ((s_cmsg.adr.adrController & 0xffffff00) == 0) @@ -1392,8 +1516,13 @@ static void capidrv_signal(__u16 applid, __u32 dummy) handle_plci(&s_cmsg); else handle_ncci(&s_cmsg); - + /* + * data of skb used in s_cmsg, + * free data when s_cmsg is not used again + * thanks to Lars Heete + */ kfree_skb(skb); + global.nrecvctlpkt++; } } @@ -1459,6 +1588,11 @@ static _cmsg cmdcmsg; static int capidrv_ioctl(isdn_ctrl * c, capidrv_contr * card) { switch (c->arg) { + case 1: + debugmode = (int)(*((unsigned int *)c->parm.num)); + printk(KERN_DEBUG "capidrv-%d: debugmode=%d\n", + card->contrnr, debugmode); + return 0; default: printk(KERN_DEBUG "capidrv-%d: capidrv_ioctl(%ld) called ??\n", card->contrnr, c->arg); @@ -1467,11 +1601,103 @@ static int capidrv_ioctl(isdn_ctrl * c, capidrv_contr * card) return -EINVAL; } +/* + * Handle leased lines (CAPI-Bundling) + */ + +struct internal_bchannelinfo { + unsigned short channelalloc; + unsigned short operation; + unsigned char cmask[31]; +}; + +static int decodeFVteln(char *teln, unsigned long *bmaskp, int *activep) +{ + unsigned long bmask = 0; + int active = !0; + char *s; + int i; + + if (strncmp(teln, "FV:", 3) != 0) + return 1; + s = teln + 3; + while (*s && *s == ' ') s++; + if (!*s) return -2; + if (*s == 'p' || *s == 'P') { + active = 0; + s++; + } + if (*s == 'a' || *s == 'A') { + active = !0; + s++; + } + while (*s) { + int digit1 = 0; + int digit2 = 0; + if (!isdigit(*s)) return -3; + while (isdigit(*s)) { digit1 = digit1*10 + (*s - '0'); s++; } + if (digit1 <= 0 && digit1 > 30) return -4; + if (*s == 0 || *s == ',' || *s == ' ') { + bmask |= (1 << digit1); + digit1 = 0; + if (*s) s++; + continue; + } + if (*s != '-') return -5; + s++; + if (!isdigit(*s)) return -3; + while (isdigit(*s)) { digit2 = digit2*10 + (*s - '0'); s++; } + if (digit2 <= 0 && digit2 > 30) return -4; + if (*s == 0 || *s == ',' || *s == ' ') { + if (digit1 > digit2) + for (i = digit2; i <= digit1 ; i++) + bmask |= (1 << i); + else + for (i = digit1; i <= digit2 ; i++) + bmask |= (1 << i); + digit1 = digit2 = 0; + if (*s) s++; + continue; + } + return -6; + } + if (activep) *activep = active; + if (bmaskp) *bmaskp = bmask; + return 0; +} + +static int FVteln2capi20(char *teln, __u8 AdditionalInfo[1+2+2+31]) +{ + unsigned long bmask; + int active; + int rc, i; + + rc = decodeFVteln(teln, &bmask, &active); + if (rc) return rc; + /* Length */ + AdditionalInfo[0] = 2+2+31; + /* Channel: 3 => use channel allocation */ + AdditionalInfo[1] = 3; AdditionalInfo[2] = 0; + /* Operation: 0 => DTE mode, 1 => DCE mode */ + if (active) { + AdditionalInfo[3] = 0; AdditionalInfo[4] = 0; + } else { + AdditionalInfo[3] = 1; AdditionalInfo[4] = 0; + } + /* Channel mask array */ + AdditionalInfo[5] = 0; /* no D-Channel */ + for (i=1; i <= 30; i++) + AdditionalInfo[5+i] = (bmask & (1 << i)) ? 0xff : 0; + return 0; +} + static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) { isdn_ctrl cmd; struct capidrv_bchan *bchan; struct capidrv_plci *plcip; + __u8 AdditionalInfo[1+2+2+31]; + int rc, isleasedline = 0; if (c->command == ISDN_CMD_IOCTL) return capidrv_ioctl(c, card); @@ -1508,15 +1734,25 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) strncpy(bchan->num, c->parm.setup.phone, sizeof(bchan->num)); strncpy(bchan->mynum, c->parm.setup.eazmsn, sizeof(bchan->mynum)); - - calling[0] = strlen(bchan->mynum) + 2; - calling[1] = 0; - calling[2] = 0x80; - strncpy(calling + 3, bchan->mynum, ISDN_MSNLEN); - - called[0] = strlen(bchan->num) + 1; - called[1] = 0x80; - strncpy(called + 2, bchan->num, ISDN_MSNLEN); + rc = FVteln2capi20(bchan->num, AdditionalInfo); + isleasedline = (rc == 0); + if (rc < 0) + printk(KERN_ERR "capidrv-%d: WARNING: illegal leased linedefinition \"%s\"\n", card->contrnr, bchan->num); + + if (isleasedline) { + calling[0] = 0; + called[0] = 0; + if (debugmode) + printk(KERN_DEBUG "capidrv-%d: connecting leased line\n", card->contrnr); + } else { + calling[0] = strlen(bchan->mynum) + 2; + calling[1] = 0; + calling[2] = 0x80; + strncpy(calling + 3, bchan->mynum, ISDN_MSNLEN); + called[0] = strlen(bchan->num) + 1; + called[1] = 0x80; + strncpy(called + 2, bchan->num, ISDN_MSNLEN); + } capi_fill_CONNECT_REQ(&cmdcmsg, global.appid, @@ -1530,13 +1766,14 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) b1prot(bchan->l2, bchan->l3), /* B1protocol */ b2prot(bchan->l2, bchan->l3), /* B2protocol */ b3prot(bchan->l2, bchan->l3), /* B3protocol */ - 0, /* B1configuration */ + b1config(bchan->l2, bchan->l3), /* B1configuration */ 0, /* B2configuration */ 0, /* B3configuration */ 0, /* BC */ 0, /* LLC */ 0, /* HLC */ - 0, /* BChannelinformation */ + /* BChannelinformation */ + isleasedline ? AdditionalInfo : 0, 0, /* Keypadfacility */ 0, /* Useruserdata */ 0 /* Facilitydataarray */ @@ -1549,6 +1786,7 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) return -1; } plcip->msgid = cmdcmsg.Messagenumber; + plcip->leasedline = isleasedline; plci_change_state(card, plcip, EV_PLCI_CONNECT_REQ); send_message(card, &cmdcmsg); return 0; @@ -1570,7 +1808,7 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) b1prot(bchan->l2, bchan->l3), /* B1protocol */ b2prot(bchan->l2, bchan->l3), /* B2protocol */ b3prot(bchan->l2, bchan->l3), /* B3protocol */ - 0, /* B1configuration */ + b1config(bchan->l2, bchan->l3), /* B1configuration */ 0, /* B2configuration */ 0, /* B3configuration */ 0, /* ConnectedNumber */ @@ -1752,6 +1990,9 @@ static int if_sendbuf(int id, int channel, int doack, struct sk_buff *skb) card->contrnr, id); return 0; } + if (debugmode > 1) + printk(KERN_DEBUG "capidrv-%d: sendbuf len=%d skb=%p doack=%d\n", + card->contrnr, len, skb, doack); bchan = &card->bchans[channel % card->nbchan]; nccip = bchan->nccip; if (!nccip || nccip->state != ST_NCCI_ACTIVE) { @@ -1774,23 +2015,23 @@ static int if_sendbuf(int id, int channel, int doack, struct sk_buff *skb) capi_cmsg2message(&sendcmsg, sendcmsg.buf); msglen = CAPIMSG_LEN(sendcmsg.buf); if (skb_headroom(skb) < msglen) { - struct sk_buff *nskb = dev_alloc_skb(msglen + skb->len); + struct sk_buff *nskb = skb_realloc_headroom(skb, msglen); if (!nskb) { printk(KERN_ERR "capidrv-%d: if_sendbuf: no memory\n", card->contrnr); (void)capidrv_del_ack(nccip, datahandle); return 0; } -#if 0 - printk(KERN_DEBUG "capidrv-%d: only %d bytes headroom\n", - card->contrnr, skb_headroom(skb)); +#if 1 + printk(KERN_DEBUG "capidrv-%d: only %d bytes headroom, need %d\n", + card->contrnr, skb_headroom(skb), msglen); #endif - memcpy(skb_put(nskb, msglen), sendcmsg.buf, msglen); - memcpy(skb_put(nskb, skb->len), skb->data, skb->len); + memcpy(skb_push(nskb, msglen), sendcmsg.buf, msglen); errcode = (*capifuncs->capi_put_message) (global.appid, nskb); if (errcode == CAPI_NOERROR) { dev_kfree_skb(skb); nccip->datahandle++; + global.nsentdatapkt++; return len; } (void)capidrv_del_ack(nccip, datahandle); @@ -1801,8 +2042,10 @@ static int if_sendbuf(int id, int channel, int doack, struct sk_buff *skb) errcode = (*capifuncs->capi_put_message) (global.appid, skb); if (errcode == CAPI_NOERROR) { nccip->datahandle++; + global.nsentdatapkt++; return len; } + skb_pull(skb, msglen); (void)capidrv_del_ack(nccip, datahandle); return errcode == CAPI_SENDQUEUEFULL ? 0 : -1; } @@ -1847,8 +2090,8 @@ static void enable_dchannel_trace(capidrv_contr *card) return; } if (strstr(manufacturer, "AVM") == 0) { - printk(KERN_ERR "%s: not from AVM, no d-channel trace possible\n", - card->name); + printk(KERN_ERR "%s: not from AVM, no d-channel trace possible (%s)\n", + card->name, manufacturer); return; } errcode = (*capifuncs->capi_get_version)(contr, &version); @@ -1862,7 +2105,7 @@ static void enable_dchannel_trace(capidrv_contr *card) avmversion[1] |= (version.minormanuversion >> 4) & 0x0f; avmversion[2] |= version.minormanuversion & 0x0f; - if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 6)) { + 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++, @@ -1884,6 +2127,51 @@ static void enable_dchannel_trace(capidrv_contr *card) send_message(card, &cmdcmsg); } +static void disable_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 (%s)\n", + card->name, manufacturer); + 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 disabled\n", card->name); + } else { + printk(KERN_INFO "%s: D3 trace disabled\n", card->name); + } + capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid, + card->msgid++, + contr, + 0x214D5641, /* ManuID */ + 0, /* Class */ + 1, /* Function */ + (_cstruct)"\004\000\000\000\000"); + send_message(card, &cmdcmsg); +} + static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) { capidrv_contr *card; @@ -1920,6 +2208,13 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L2_TRANS | ISDN_FEATURE_L3_TRANS | + ISDN_FEATURE_L2_V11096 | + ISDN_FEATURE_L2_V11019 | + ISDN_FEATURE_L2_V11038 | +#if 0 + ISDN_FEATURE_L2_FAX | + ISDN_FEATURE_L3_FAX | +#endif ISDN_FEATURE_P_UNKNOWN; card->interface.hl_hdrlen = 22; /* len of DATA_B3_REQ */ strncpy(card->interface.id, id, sizeof(card->interface.id) - 1); @@ -1964,8 +2259,7 @@ 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); + enable_dchannel_trace(card); return 0; } @@ -1985,6 +2279,15 @@ static int capidrv_delcontr(__u16 contr) return -1; } card = *pp; + + if (debugmode) + printk(KERN_DEBUG "capidrv-%d: id=%d unloading\n", + card->contrnr, card->myid); + + cmd.command = ISDN_STAT_UNLOAD; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + *pp = (*pp)->next; global.ncontr--; @@ -1998,10 +2301,6 @@ static int capidrv_delcontr(__u16 contr) } kfree(card->bchans); - cmd.command = ISDN_STAT_UNLOAD; - cmd.driver = card->myid; - card->interface.statcallb(&cmd); - printk(KERN_INFO "%s: now down.\n", card->name); kfree(card); @@ -2012,16 +2311,78 @@ static int capidrv_delcontr(__u16 contr) static void lower_callback(unsigned int cmd, __u16 contr, void *data) { + switch (cmd) { case KCI_CONTRUP: + printk(KERN_INFO "capidrv: controller %hu up\n", contr); (void) capidrv_addcontr(contr, (capi_profile *) data); break; case KCI_CONTRDOWN: + printk(KERN_INFO "capidrv: controller %hu down\n", contr); (void) capidrv_delcontr(contr); break; } } +/* + * /proc/capi/capidrv: + * nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt + */ +static int proc_capidrv_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + + len += sprintf(page+len, "%lu %lu %lu %lu\n", + global.nrecvctlpkt, + global.nrecvdatapkt, + global.nsentctlpkt, + global.nsentdatapkt); + if (off+count >= len) + *eof = 1; + if (len < off) + return 0; + *start = page + off; + return ((count < len-off) ? count : len-off); +} + +static struct procfsentries { + char *name; + mode_t mode; + int (*read_proc)(char *page, char **start, off_t off, + int count, int *eof, void *data); + struct proc_dir_entry *procent; +} procfsentries[] = { + /* { "capi", S_IFDIR, 0 }, */ + { "capi/capidrv", 0 , proc_capidrv_read_proc }, +}; + +static void proc_init(void) +{ + int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]); + int i; + + for (i=0; i < nelem; i++) { + struct procfsentries *p = procfsentries + i; + p->procent = create_proc_entry(p->name, p->mode, 0); + if (p->procent) p->procent->read_proc = p->read_proc; + } +} + +static void proc_exit(void) +{ + int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]); + int i; + + for (i=nelem-1; i >= 0; i--) { + struct procfsentries *p = procfsentries + i; + if (p->procent) { + remove_proc_entry(p->name, 0); + p->procent = 0; + } + } +} + static struct capi_interface_user cuser = { "capidrv", lower_callback @@ -2077,6 +2438,7 @@ int capidrv_init(void) continue; (void) capidrv_addcontr(contr, &profile); } + proc_init(); return 0; } @@ -2098,11 +2460,13 @@ void cleanup_module(void) for (card = global.contr_list; card; card = next) { next = card->next; + disable_dchannel_trace(card); capidrv_delcontr(card->contrnr); } (void) (*capifuncs->capi_release) (global.appid); detach_capi_interface(&cuser); + proc_exit(); printk(KERN_NOTICE "capidrv: Rev%s: unloaded\n", rev); } diff --git a/drivers/isdn/avmb1/capilli.h b/drivers/isdn/avmb1/capilli.h new file mode 100644 index 000000000000..da64a183b673 --- /dev/null +++ b/drivers/isdn/avmb1/capilli.h @@ -0,0 +1,110 @@ +/* + * $Id: capilli.h,v 1.4 1999/07/23 08:51:05 calle Exp $ + * + * Kernel CAPI 2.0 Driver Interface for Linux + * + * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) + * + */ +#ifndef __CAPILLI_H__ +#define __CAPILLI_H__ + +typedef struct capiloaddatapart { + int user; /* data in userspace ? */ + int len; + unsigned char *data; +} capiloaddatapart; + +typedef struct capiloaddata { + capiloaddatapart firmware; + capiloaddatapart configuration; +} capiloaddata; + +typedef struct capicardparams { + unsigned int port; + unsigned irq; + int cardtype; + int cardnr; + unsigned int membase; +} capicardparams; + +struct capi_driver; + +struct capi_ctr { + struct capi_ctr *next; /* next ctr of same driver */ + struct capi_driver *driver; + int cnr; /* controller number */ + char name[32]; /* name of controller */ + volatile unsigned short cardstate; /* controller state */ + volatile int blocked; /* output blocked */ + int traceflag; /* capi trace */ + + void *driverdata; /* driver specific */ + + /* filled before calling ready callback */ + __u8 manu[CAPI_MANUFACTURER_LEN]; /* CAPI_GET_MANUFACTURER */ + capi_version version; /* CAPI_GET_VERSION */ + capi_profile profile; /* CAPI_GET_PROFILE */ + __u8 serial[CAPI_SERIAL_LEN]; /* CAPI_GET_SERIAL */ + + /* functions */ + void (*ready)(struct capi_ctr * card); + void (*reseted)(struct capi_ctr * card); + void (*suspend_output)(struct capi_ctr * card); + void (*resume_output)(struct capi_ctr * card); + void (*handle_capimsg)(struct capi_ctr * card, + __u16 appl, struct sk_buff *skb); + void (*appl_registered)(struct capi_ctr * card, __u16 appl); + void (*appl_released)(struct capi_ctr * card, __u16 appl); + + void (*new_ncci)(struct capi_ctr * card, + __u16 appl, __u32 ncci, __u32 winsize); + void (*free_ncci)(struct capi_ctr * card, __u16 appl, __u32 ncci); + + /* management information for kcapi */ + + unsigned long nrecvctlpkt; + unsigned long nrecvdatapkt; + unsigned long nsentctlpkt; + unsigned long nsentdatapkt; + + struct proc_dir_entry *procent; + char procfn[128]; +}; + +struct capi_driver_interface { + struct capi_ctr *(*attach_ctr)(struct capi_driver *driver, char *name, void *data); + int (*detach_ctr)(struct capi_ctr *); +}; + +struct capi_driver { + char name[32]; /* driver name */ + char revision[32]; + int (*load_firmware)(struct capi_ctr *, capiloaddata *); + void (*reset_ctr)(struct capi_ctr *); + void (*remove_ctr)(struct capi_ctr *); + void (*register_appl)(struct capi_ctr *, __u16 appl, + capi_register_params *); + void (*release_appl)(struct capi_ctr *, __u16 appl); + void (*send_message)(struct capi_ctr *, struct sk_buff *skb); + + char *(*procinfo)(struct capi_ctr *); + int (*ctr_read_proc)(char *page, char **start, off_t off, + int count, int *eof, struct capi_ctr *card); + int (*driver_read_proc)(char *page, char **start, off_t off, + int count, int *eof, struct capi_driver *driver); + + int (*add_card)(struct capi_driver *driver, capicardparams *data); + + /* intitialized by kcapi */ + struct capi_ctr *controller; /* list of controllers */ + struct capi_driver *next; + int ncontroller; + struct proc_dir_entry *procent; + char procfn[128]; +}; + +struct capi_driver_interface *attach_capi_driver(struct capi_driver *driver); +void detach_capi_driver(struct capi_driver *driver); + +#endif /* __CAPILLI_H__ */ diff --git a/drivers/isdn/avmb1/capiutil.c b/drivers/isdn/avmb1/capiutil.c index 8eb7d3ae15d9..7f1a9f1adfa5 100644 --- a/drivers/isdn/avmb1/capiutil.c +++ b/drivers/isdn/avmb1/capiutil.c @@ -1,5 +1,5 @@ /* - * $Id: capiutil.c,v 1.6 1997/11/04 06:12:12 calle Exp $ + * $Id: capiutil.c,v 1.9 1999/07/09 15:05:46 keil Exp $ * * CAPI 2.0 convert capi message to capi message struct * @@ -7,6 +7,28 @@ * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capiutil.c,v $ + * Revision 1.9 1999/07/09 15:05:46 keil + * compat.h is now isdn_compat.h + * + * Revision 1.8 1999/07/01 15:26:37 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + * Revision 1.7 1999/07/01 08:23:01 keil + * compatibility macros now in + * * Revision 1.6 1997/11/04 06:12:12 calle * capi.c: new read/write in file_ops since 2.1.60 * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware. @@ -48,7 +70,7 @@ #include #include -#include "compat.h" +#include #include "capiutil.h" /* from CAPI2.0 DDK AVM Berlin GmbH */ diff --git a/drivers/isdn/avmb1/kcapi.c b/drivers/isdn/avmb1/kcapi.c new file mode 100644 index 000000000000..fc58f12415cb --- /dev/null +++ b/drivers/isdn/avmb1/kcapi.c @@ -0,0 +1,1553 @@ +/* + * $Id: kcapi.c,v 1.6 1999/07/20 06:41:49 calle Exp $ + * + * Kernel CAPI 2.0 Module + * + * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: kcapi.c,v $ + * Revision 1.6 1999/07/20 06:41:49 calle + * Bugfix: After the redesign of the AVM B1 driver, the driver didn't even + * compile, if not selected as modules. + * + * Revision 1.5 1999/07/09 15:05:48 keil + * compat.h is now isdn_compat.h + * + * Revision 1.4 1999/07/08 14:15:17 calle + * Forgot to count down ncards in drivercb_detach_ctr. + * + * Revision 1.3 1999/07/06 07:42:02 calle + * - changes in /proc interface + * - check and changed calls to [dev_]kfree_skb and [dev_]alloc_skb. + * + * Revision 1.2 1999/07/05 15:09:52 calle + * - renamed "appl_release" to "appl_released". + * - version und profile data now cleared on controller reset + * - extended /proc interface, to allow driver and controller specific + * informations to include by driver hackers. + * + * Revision 1.1 1999/07/01 15:26:42 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + */ +#define CONFIG_AVMB1_COMPAT + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "capicmd.h" +#include "capiutil.h" +#include "capilli.h" +#ifdef CONFIG_AVMB1_COMPAT +#include +#endif + +static char *revision = "$Revision: 1.6 $"; + +/* ------------------------------------------------------------- */ + +#define CARD_FREE 0 +#define CARD_DETECTED 1 +#define CARD_LOADING 2 +#define CARD_RUNNING 3 + +/* ------------------------------------------------------------- */ + +int showcapimsgs = 0; + +MODULE_AUTHOR("Carsten Paeth "); +MODULE_PARM(showcapimsgs, "0-4i"); + +/* ------------------------------------------------------------- */ + +struct msgidqueue { + struct msgidqueue *next; + __u16 msgid; +}; + +struct capi_ncci { + struct capi_ncci *next; + __u16 applid; + __u32 ncci; + __u32 winsize; + int nmsg; + struct msgidqueue *msgidqueue; + struct msgidqueue *msgidlast; + struct msgidqueue *msgidfree; + struct msgidqueue msgidpool[CAPI_MAXDATAWINDOW]; +}; + +struct capi_appl { + __u16 applid; + capi_register_params rparam; + int releasing; + __u32 param; + void (*signal) (__u16 applid, __u32 param); + struct sk_buff_head recv_queue; + int nncci; + struct capi_ncci *nccilist; + + unsigned long nrecvctlpkt; + unsigned long nrecvdatapkt; + unsigned long nsentctlpkt; + unsigned long nsentdatapkt; +}; + +/* ------------------------------------------------------------- */ + +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"; + +#define APPL(a) (&applications[(a)-1]) +#define VALID_APPLID(a) ((a) && (a) <= CAPI_MAXAPPL && APPL(a)->applid == a) +#define APPL_IS_FREE(a) (APPL(a)->applid == 0) +#define APPL_MARK_FREE(a) do{ APPL(a)->applid=0; MOD_DEC_USE_COUNT; }while(0); +#define APPL_MARK_USED(a) do{ APPL(a)->applid=(a); MOD_INC_USE_COUNT; }while(0); + +#define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f) + +#define VALID_CARD(c) ((c) > 0 && (c) <= CAPI_MAXCONTR) +#define CARD(c) (&cards[(c)-1]) +#define CARDNR(cp) (((cp)-cards)+1) + +static struct capi_appl applications[CAPI_MAXAPPL]; +static struct capi_ctr cards[CAPI_MAXCONTR]; +static int ncards = 0; +static struct sk_buff_head recv_queue; +static struct capi_interface_user *capi_users = 0; +static struct capi_driver *drivers; +#ifdef CONFIG_AVMB1_COMPAT +static struct capi_driver *b1isa_driver; +static struct capi_driver *t1isa_driver; +#endif +static long notify_up_set = 0; +static long notify_down_set = 0; + +static struct tq_struct tq_state_notify; +static struct tq_struct tq_recv_notify; + +/* -------- util functions ------------------------------------ */ + +static char *cardstate2str(unsigned short cardstate) +{ + switch (cardstate) { + default: + case CARD_FREE: return "free"; + case CARD_DETECTED: return "detected"; + case CARD_LOADING: return "loading"; + case CARD_RUNNING: return "running"; + } +} + +static inline int capi_cmd_valid(__u8 cmd) +{ + switch (cmd) { + case CAPI_ALERT: + case CAPI_CONNECT: + case CAPI_CONNECT_ACTIVE: + case CAPI_CONNECT_B3_ACTIVE: + case CAPI_CONNECT_B3: + case CAPI_CONNECT_B3_T90_ACTIVE: + case CAPI_DATA_B3: + case CAPI_DISCONNECT_B3: + case CAPI_DISCONNECT: + case CAPI_FACILITY: + case CAPI_INFO: + case CAPI_LISTEN: + case CAPI_MANUFACTURER: + case CAPI_RESET_B3: + case CAPI_SELECT_B_PROTOCOL: + return 1; + } + return 0; +} + +static inline int capi_subcmd_valid(__u8 subcmd) +{ + switch (subcmd) { + case CAPI_REQ: + case CAPI_CONF: + case CAPI_IND: + case CAPI_RESP: + return 1; + } + return 0; +} + +/* -------- /proc functions ----------------------------------- */ +/* + * /proc/capi/applications: + * applid l3cnt dblkcnt dblklen #ncci recvqueuelen + */ +static int proc_applications_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capi_appl *ap; + int i; + int len = 0; + off_t begin = 0; + + for (i=0; i < CAPI_MAXAPPL; i++) { + ap = &applications[i]; + if (ap->applid == 0) continue; + len += sprintf(page+len, "%u %d %d %d %d %d\n", + ap->applid, + ap->rparam.level3cnt, + ap->rparam.datablkcnt, + ap->rparam.datablklen, + ap->nncci, + skb_queue_len(&ap->recv_queue)); + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } +endloop: + if (i >= CAPI_MAXAPPL) + *eof = 1; + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * /proc/capi/ncci: + * applid ncci winsize nblk + */ +static int proc_ncci_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capi_appl *ap; + struct capi_ncci *np; + int i; + int len = 0; + off_t begin = 0; + + for (i=0; i < CAPI_MAXAPPL; i++) { + ap = &applications[i]; + if (ap->applid == 0) continue; + for (np = ap->nccilist; np; np = np->next) { + len += sprintf(page+len, "%d 0x%x %d %d\n", + np->applid, + np->ncci, + np->winsize, + np->nmsg); + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } + } +endloop: + if (i >= CAPI_MAXAPPL) + *eof = 1; + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * /proc/capi/driver: + * driver ncontroller + */ +static int proc_driver_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capi_driver *driver; + int len = 0; + off_t begin = 0; + + for (driver = drivers; driver; driver = driver->next) { + len += sprintf(page+len, "%-32s %d %s\n", + driver->name, + driver->ncontroller, + driver->revision); + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } +endloop: + if (!driver) + *eof = 1; + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * /proc/capi/users: + * name + */ +static int proc_users_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capi_interface_user *cp; + int len = 0; + off_t begin = 0; + + for (cp = capi_users; cp ; cp = cp->next) { + len += sprintf(page+len, "%s\n", cp->name); + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } +endloop: + if (cp == 0) + *eof = 1; + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * /proc/capi/controller: + * cnr driver cardstate name driverinfo + */ +static int proc_controller_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capi_ctr *cp; + int i; + int len = 0; + off_t begin = 0; + + for (i=0; i < CAPI_MAXCONTR; i++) { + cp = &cards[i]; + if (cp->cardstate == CARD_FREE) continue; + len += sprintf(page+len, "%d %-10s %-8s %-16s %s\n", + cp->cnr, cp->driver->name, + cardstate2str(cp->cardstate), + cp->name, + cp->driver->procinfo ? cp->driver->procinfo(cp) : "" + ); + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } +endloop: + if (i >= CAPI_MAXCONTR) + *eof = 1; + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * /proc/capi/applstats: + * applid nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt + */ +static int proc_applstats_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capi_appl *ap; + int i; + int len = 0; + off_t begin = 0; + + for (i=0; i < CAPI_MAXAPPL; i++) { + ap = &applications[i]; + if (ap->applid == 0) continue; + len += sprintf(page+len, "%u %lu %lu %lu %lu\n", + ap->applid, + ap->nrecvctlpkt, + ap->nrecvdatapkt, + ap->nsentctlpkt, + ap->nsentdatapkt); + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } +endloop: + if (i >= CAPI_MAXAPPL) + *eof = 1; + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * /proc/capi/contrstats: + * cnr nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt + */ +static int proc_contrstats_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capi_ctr *cp; + int i; + int len = 0; + off_t begin = 0; + + for (i=0; i < CAPI_MAXCONTR; i++) { + cp = &cards[i]; + if (cp->cardstate == CARD_FREE) continue; + len += sprintf(page+len, "%d %lu %lu %lu %lu\n", + cp->cnr, + cp->nrecvctlpkt, + cp->nrecvdatapkt, + cp->nsentctlpkt, + cp->nsentdatapkt); + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } +endloop: + if (i >= CAPI_MAXCONTR) + *eof = 1; + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +static struct procfsentries { + char *name; + mode_t mode; + int (*read_proc)(char *page, char **start, off_t off, + int count, int *eof, void *data); + struct proc_dir_entry *procent; +} procfsentries[] = { + { "capi", S_IFDIR, 0 }, + { "capi/applications", 0 , proc_applications_read_proc }, + { "capi/ncci", 0 , proc_ncci_read_proc }, + { "capi/driver", 0 , proc_driver_read_proc }, + { "capi/users", 0 , proc_users_read_proc }, + { "capi/controller", 0 , proc_controller_read_proc }, + { "capi/applstats", 0 , proc_applstats_read_proc }, + { "capi/contrstats", 0 , proc_contrstats_read_proc }, + { "capi/drivers", S_IFDIR, 0 }, + { "capi/controllers", S_IFDIR, 0 }, +}; + +static void proc_capi_init(void) +{ + int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]); + int i; + + for (i=0; i < nelem; i++) { + struct procfsentries *p = procfsentries + i; + p->procent = create_proc_entry(p->name, p->mode, 0); + if (p->procent) p->procent->read_proc = p->read_proc; + } +} + +static void proc_capi_exit(void) +{ + int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]); + int i; + + for (i=nelem-1; i >= 0; i--) { + struct procfsentries *p = procfsentries + i; + if (p->procent) { + remove_proc_entry(p->name, 0); + p->procent = 0; + } + } +} + +/* -------- NCCI Handling ------------------------------------- */ + +static inline void mq_init(struct capi_ncci * np) +{ + int i; + np->msgidqueue = 0; + np->msgidlast = 0; + np->nmsg = 0; + memset(np->msgidpool, 0, sizeof(np->msgidpool)); + np->msgidfree = &np->msgidpool[0]; + for (i = 1; i < np->winsize; i++) { + np->msgidpool[i].next = np->msgidfree; + np->msgidfree = &np->msgidpool[i]; + } +} + +static inline int mq_enqueue(struct capi_ncci * np, __u16 msgid) +{ + struct msgidqueue *mq; + if ((mq = np->msgidfree) == 0) + return 0; + np->msgidfree = mq->next; + mq->msgid = msgid; + mq->next = 0; + if (np->msgidlast) + np->msgidlast->next = mq; + np->msgidlast = mq; + if (!np->msgidqueue) + np->msgidqueue = mq; + np->nmsg++; + return 1; +} + +static inline int mq_dequeue(struct capi_ncci * np, __u16 msgid) +{ + struct msgidqueue **pp; + for (pp = &np->msgidqueue; *pp; pp = &(*pp)->next) { + if ((*pp)->msgid == msgid) { + struct msgidqueue *mq = *pp; + *pp = mq->next; + if (mq == np->msgidlast) + np->msgidlast = 0; + mq->next = np->msgidfree; + np->msgidfree = mq; + np->nmsg--; + return 1; + } + } + return 0; +} + +static void controllercb_appl_registered(struct capi_ctr * card, __u16 appl) +{ +} + +static void controllercb_appl_released(struct capi_ctr * card, __u16 appl) +{ + struct capi_ncci **pp, **nextpp; + for (pp = &APPL(appl)->nccilist; *pp; pp = nextpp) { + if (NCCI2CTRL((*pp)->ncci) == card->cnr) { + struct capi_ncci *np = *pp; + *pp = np->next; + printk(KERN_INFO "kcapi: appl %d ncci 0x%x down!\n", appl, np->ncci); + kfree(np); + APPL(appl)->nncci--; + nextpp = pp; + } else { + nextpp = &(*pp)->next; + } + } + APPL(appl)->releasing--; + if (APPL(appl)->releasing <= 0) { + APPL(appl)->signal = 0; + APPL_MARK_FREE(appl); + printk(KERN_INFO "kcapi: appl %d down\n", appl); + } +} +/* + * ncci managment + */ + +static void controllercb_new_ncci(struct capi_ctr * card, + __u16 appl, __u32 ncci, __u32 winsize) +{ + struct capi_ncci *np; + if (!VALID_APPLID(appl)) { + printk(KERN_ERR "avmb1_handle_new_ncci: illegal appl %d\n", appl); + return; + } + if ((np = (struct capi_ncci *) kmalloc(sizeof(struct capi_ncci), GFP_ATOMIC)) == 0) { + printk(KERN_ERR "capi_new_ncci: alloc failed ncci 0x%x\n", ncci); + return; + } + if (winsize > CAPI_MAXDATAWINDOW) { + printk(KERN_ERR "capi_new_ncci: winsize %d too big, set to %d\n", + winsize, CAPI_MAXDATAWINDOW); + winsize = CAPI_MAXDATAWINDOW; + } + np->applid = appl; + np->ncci = ncci; + np->winsize = winsize; + mq_init(np); + np->next = APPL(appl)->nccilist; + APPL(appl)->nccilist = np; + APPL(appl)->nncci++; + printk(KERN_INFO "kcapi: appl %d ncci 0x%x up\n", appl, ncci); + +} + +static void controllercb_free_ncci(struct capi_ctr * card, + __u16 appl, __u32 ncci) +{ + struct capi_ncci **pp; + if (!VALID_APPLID(appl)) { + printk(KERN_ERR "free_ncci: illegal appl %d\n", appl); + return; + } + for (pp = &APPL(appl)->nccilist; *pp; pp = &(*pp)->next) { + if ((*pp)->ncci == ncci) { + struct capi_ncci *np = *pp; + *pp = np->next; + kfree(np); + APPL(appl)->nncci--; + printk(KERN_INFO "kcapi: appl %d ncci 0x%x down\n", appl, ncci); + return; + } + } + printk(KERN_ERR "free_ncci: ncci 0x%x not found\n", ncci); +} + + +static struct capi_ncci *find_ncci(struct capi_appl * app, __u32 ncci) +{ + struct capi_ncci *np; + for (np = app->nccilist; np; np = np->next) { + if (np->ncci == ncci) + return np; + } + return 0; +} + +/* -------- Receiver ------------------------------------------ */ + +static void recv_handler(void *dummy) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&recv_queue)) != 0) { + __u16 appl = CAPIMSG_APPID(skb->data); + struct capi_ncci *np; + if (!VALID_APPLID(appl)) { + printk(KERN_ERR "kcapi: recv_handler: applid %d ? (%s)\n", + appl, capi_message2str(skb->data)); + kfree_skb(skb); + continue; + } + if (APPL(appl)->signal == 0) { + printk(KERN_ERR "kcapi: recv_handler: applid %d has no signal function\n", + appl); + kfree_skb(skb); + continue; + } + if ( CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3 + && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF + && (np = find_ncci(APPL(appl), CAPIMSG_NCCI(skb->data))) != 0 + && mq_dequeue(np, CAPIMSG_MSGID(skb->data)) == 0) { + printk(KERN_ERR "kcapi: msgid %hu ncci 0x%x not on queue\n", + CAPIMSG_MSGID(skb->data), np->ncci); + } + if ( CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3 + && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) { + APPL(appl)->nrecvdatapkt++; + } else { + APPL(appl)->nrecvctlpkt++; + } + skb_queue_tail(&APPL(appl)->recv_queue, skb); + (APPL(appl)->signal) (APPL(appl)->applid, APPL(appl)->param); + } +} + +static void controllercb_handle_capimsg(struct capi_ctr * card, + __u16 appl, struct sk_buff *skb) +{ + int showctl = 0; + __u8 cmd, subcmd; + + if (card->cardstate != CARD_RUNNING) { + printk(KERN_INFO "kcapi: controller %d not active, got: %s", + card->cnr, capi_message2str(skb->data)); + goto error; + } + cmd = CAPIMSG_COMMAND(skb->data); + subcmd = CAPIMSG_SUBCOMMAND(skb->data); + if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) { + card->nrecvdatapkt++; + if (card->traceflag > 2) showctl |= 2; + if (card->traceflag) showctl |= 2; + } else { + card->nrecvctlpkt++; + } + showctl |= (card->traceflag & 1); + if (showctl & 2) { + if (showctl & 1) { + printk(KERN_DEBUG "kcapi: got [0x%lx] id#%d %s len=%u\n", + (unsigned long) card->cnr, + CAPIMSG_APPID(skb->data), + capi_cmd2str(cmd, subcmd), + CAPIMSG_LEN(skb->data)); + } else { + printk(KERN_DEBUG "kcapi: got [0x%lx] %s\n", + (unsigned long) card->cnr, + capi_message2str(skb->data)); + } + + } + skb_queue_tail(&recv_queue, skb); + queue_task(&tq_recv_notify, &tq_immediate); + mark_bh(IMMEDIATE_BH); + return; + +error: + kfree_skb(skb); +} + +/* -------- Notifier ------------------------------------------ */ + +static void notify_up(__u16 contr) +{ + struct capi_interface_user *p; + + printk(KERN_NOTICE "kcapi: notify up contr %d\n", contr); + for (p = capi_users; p; p = p->next) { + if (!p->callback) continue; + (*p->callback) (KCI_CONTRUP, contr, &CARD(contr)->profile); + } +} + +static void notify_down(__u16 contr) +{ + struct capi_interface_user *p; + printk(KERN_NOTICE "kcapi: notify down contr %d\n", contr); + for (p = capi_users; p; p = p->next) { + if (!p->callback) continue; + (*p->callback) (KCI_CONTRDOWN, contr, 0); + } +} + +static void notify_handler(void *dummy) +{ + __u16 contr; + + for (contr=1; VALID_CARD(contr); contr++) + if (test_and_clear_bit(contr, ¬ify_up_set)) + notify_up(contr); + for (contr=1; VALID_CARD(contr); contr++) + if (test_and_clear_bit(contr, ¬ify_down_set)) + notify_down(contr); +} + + +static void controllercb_ready(struct capi_ctr * card) +{ + __u16 appl; + + card->cardstate = CARD_RUNNING; + + for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { + if (!VALID_APPLID(appl)) continue; + if (APPL(appl)->releasing) continue; + card->driver->register_appl(card, appl, &APPL(appl)->rparam); + } + + set_bit(CARDNR(card), ¬ify_up_set); + queue_task(&tq_state_notify, &tq_scheduler); + + printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n", + CARDNR(card), card->name); +} + +static void controllercb_reseted(struct capi_ctr * card) +{ + __u16 appl; + + if (card->cardstate == CARD_FREE) + return; + if (card->cardstate == CARD_DETECTED) + return; + + card->cardstate = CARD_DETECTED; + + memset(card->manu, 0, sizeof(card->manu)); + memset(&card->version, 0, sizeof(card->version)); + memset(&card->profile, 0, sizeof(card->profile)); + memset(card->serial, 0, sizeof(card->serial)); + + for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { + struct capi_ncci **pp, **nextpp; + for (pp = &APPL(appl)->nccilist; *pp; pp = nextpp) { + if (NCCI2CTRL((*pp)->ncci) == card->cnr) { + struct capi_ncci *np = *pp; + *pp = np->next; + printk(KERN_INFO "kcapi: appl %d ncci 0x%x forced down!\n", appl, np->ncci); + kfree(np); + nextpp = pp; + } else { + nextpp = &(*pp)->next; + } + } + } + set_bit(CARDNR(card), ¬ify_down_set); + queue_task(&tq_state_notify, &tq_scheduler); + printk(KERN_NOTICE "kcapi: card %d down.\n", CARDNR(card)); +} + +static void controllercb_suspend_output(struct capi_ctr *card) +{ + if (!card->blocked) { + printk(KERN_DEBUG "kcapi: card %d suspend\n", CARDNR(card)); + card->blocked = 1; + } +} + +static void controllercb_resume_output(struct capi_ctr *card) +{ + if (card->blocked) { + printk(KERN_DEBUG "kcapi: card %d resume\n", CARDNR(card)); + card->blocked = 0; + } +} + +/* ------------------------------------------------------------- */ + + +struct capi_ctr * +drivercb_attach_ctr(struct capi_driver *driver, char *name, void *driverdata) +{ + struct capi_ctr *card, **pp; + int i; + + for (i=0; i < CAPI_MAXCONTR && cards[i].cardstate != CARD_FREE; i++) ; + + if (i == CAPI_MAXCONTR) { + printk(KERN_ERR "kcapi: out of controller slots\n"); + return 0; + } + card = &cards[i]; + memset(card, 0, sizeof(struct capi_ctr)); + card->driver = driver; + card->cnr = CARDNR(card); + strncpy(card->name, name, sizeof(card->name)); + card->cardstate = CARD_DETECTED; + card->blocked = 0; + card->driverdata = driverdata; + card->traceflag = showcapimsgs; + + card->ready = controllercb_ready; + card->reseted = controllercb_reseted; + card->suspend_output = controllercb_suspend_output; + card->resume_output = controllercb_resume_output; + card->handle_capimsg = controllercb_handle_capimsg; + card->appl_registered = controllercb_appl_registered; + card->appl_released = controllercb_appl_released; + card->new_ncci = controllercb_new_ncci; + card->free_ncci = controllercb_free_ncci; + + for (pp = &driver->controller; *pp; pp = &(*pp)->next) ; + card->next = 0; + *pp = card; + driver->ncontroller++; + sprintf(card->procfn, "capi/controllers/%d", card->cnr); + card->procent = create_proc_entry(card->procfn, 0, 0); + if (card->procent) { + card->procent->read_proc = + (int (*)(char *,char **,off_t,int,int *,void *)) + driver->ctr_read_proc; + card->procent->data = card; + } + + ncards++; + printk(KERN_NOTICE "kcapi: Controller %d: %s attached\n", + card->cnr, card->name); + return card; +} + +static int drivercb_detach_ctr(struct capi_ctr *card) +{ + struct capi_driver *driver = card->driver; + struct capi_ctr **pp; + + if (card->cardstate == CARD_FREE) + return 0; + if (card->cardstate != CARD_DETECTED) + controllercb_reseted(card); + for (pp = &driver->controller; *pp ; pp = &(*pp)->next) { + if (*pp == card) { + *pp = card->next; + driver->ncontroller--; + ncards--; + break; + } + } + if (card->procent) { + remove_proc_entry(card->procfn, 0); + card->procent = 0; + } + card->cardstate = CARD_FREE; + printk(KERN_NOTICE "kcapi: Controller %d: %s unregistered\n", + card->cnr, card->name); + return 0; +} + +/* ------------------------------------------------------------- */ + +/* fallback if no driver read_proc function defined by driver */ + +static int driver_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capi_driver *driver = (struct capi_driver *)data; + int len = 0; + + len += sprintf(page+len, "%-16s %s\n", "name", driver->name); + len += sprintf(page+len, "%-16s %s\n", "revision", driver->revision); + + if (len < off) + return 0; + *eof = 1; + *start = page - off; + return ((count < len-off) ? count : len-off); +} + +/* ------------------------------------------------------------- */ + +static struct capi_driver_interface di = { + drivercb_attach_ctr, + drivercb_detach_ctr, +}; + +struct capi_driver_interface *attach_capi_driver(struct capi_driver *driver) +{ + struct capi_driver **pp; + + for (pp = &drivers; *pp; pp = &(*pp)->next) ; + driver->next = 0; + *pp = driver; + printk(KERN_NOTICE "kcapi: driver %s attached\n", driver->name); +#ifdef CONFIG_AVMB1_COMPAT + if (strcmp(driver->name, "b1isa") == 0 && driver->add_card) + b1isa_driver = driver; + if (strcmp(driver->name, "t1isa") == 0 && driver->add_card) + t1isa_driver = driver; +#endif + sprintf(driver->procfn, "capi/drivers/%s", driver->name); + driver->procent = create_proc_entry(driver->procfn, 0, 0); + if (driver->procent) { + if (driver->driver_read_proc) { + driver->procent->read_proc = + (int (*)(char *,char **,off_t,int,int *,void *)) + driver->driver_read_proc; + } else { + driver->procent->read_proc = driver_read_proc; + } + driver->procent->data = driver; + } + return &di; +} + +void detach_capi_driver(struct capi_driver *driver) +{ + struct capi_driver **pp; + for (pp = &drivers; *pp && *pp != driver; pp = &(*pp)->next) ; + if (*pp) { + *pp = (*pp)->next; +#ifdef CONFIG_AVMB1_COMPAT + if (driver == b1isa_driver) b1isa_driver = 0; + if (driver == t1isa_driver) t1isa_driver = 0; +#endif + printk(KERN_NOTICE "kcapi: driver %s detached\n", driver->name); + } else { + printk(KERN_ERR "kcapi: driver %s double detach ?\n", driver->name); + } + if (driver->procent) { + remove_proc_entry(driver->procfn, 0); + driver->procent = 0; + } +} + +/* ------------------------------------------------------------- */ +/* -------- CAPI2.0 Interface ---------------------------------- */ +/* ------------------------------------------------------------- */ + +static int capi_installed(void) +{ + int i; + for (i = 0; i < CAPI_MAXCONTR; i++) { + if (cards[i].cardstate == CARD_RUNNING) + return 1; + } + return 0; +} + +static __u16 capi_register(capi_register_params * rparam, __u16 * applidp) +{ + int appl; + int i; + + if (rparam->datablklen < 128) + return CAPI_LOGBLKSIZETOSMALL; + + for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { + if (APPL_IS_FREE(appl)) + break; + } + if (appl > CAPI_MAXAPPL) + return CAPI_TOOMANYAPPLS; + + APPL_MARK_USED(appl); + skb_queue_head_init(&APPL(appl)->recv_queue); + APPL(appl)->nncci = 0; + + memcpy(&APPL(appl)->rparam, rparam, sizeof(capi_register_params)); + + for (i = 0; i < CAPI_MAXCONTR; i++) { + if (cards[i].cardstate != CARD_RUNNING) + continue; + cards[i].driver->register_appl(&cards[i], appl, + &APPL(appl)->rparam); + } + *applidp = appl; + printk(KERN_INFO "kcapi: appl %d up\n", appl); + + return CAPI_NOERROR; +} + +static __u16 capi_release(__u16 applid) +{ + struct sk_buff *skb; + int i; + + if (!VALID_APPLID(applid) || APPL(applid)->releasing) + return CAPI_ILLAPPNR; + while ((skb = skb_dequeue(&APPL(applid)->recv_queue)) != 0) + kfree_skb(skb); + for (i = 0; i < CAPI_MAXCONTR; i++) { + if (cards[i].cardstate != CARD_RUNNING) + continue; + APPL(applid)->releasing++; + cards[i].driver->release_appl(&cards[i], applid); + } + if (APPL(applid)->releasing <= 0) { + APPL(applid)->signal = 0; + APPL_MARK_FREE(applid); + printk(KERN_INFO "kcapi: appl %d down\n", applid); + } + return CAPI_NOERROR; +} + +static __u16 capi_put_message(__u16 applid, struct sk_buff *skb) +{ + struct capi_ncci *np; + int contr; + int showctl = 0; + __u8 cmd, subcmd; + + if (ncards == 0) + return CAPI_REGNOTINSTALLED; + if (!VALID_APPLID(applid)) + return CAPI_ILLAPPNR; + if (skb->len < 12 + || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data)) + || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data))) + return CAPI_ILLCMDORSUBCMDORMSGTOSMALL; + contr = CAPIMSG_CONTROLLER(skb->data); + if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) { + contr = 1; + if (CARD(contr)->cardstate != CARD_RUNNING) + return CAPI_REGNOTINSTALLED; + } + if (CARD(contr)->blocked) + return CAPI_SENDQUEUEFULL; + + cmd = CAPIMSG_COMMAND(skb->data); + subcmd = CAPIMSG_SUBCOMMAND(skb->data); + + if (cmd == CAPI_DATA_B3 && subcmd== CAPI_REQ) { + if ((np = find_ncci(APPL(applid), CAPIMSG_NCCI(skb->data))) != 0 + && mq_enqueue(np, CAPIMSG_MSGID(skb->data)) == 0) + return CAPI_SENDQUEUEFULL; + CARD(contr)->nsentdatapkt++; + APPL(applid)->nsentdatapkt++; + if (CARD(contr)->traceflag > 2) showctl |= 2; + } else { + CARD(contr)->nsentctlpkt++; + APPL(applid)->nsentctlpkt++; + if (CARD(contr)->traceflag) showctl |= 2; + } + showctl |= (CARD(contr)->traceflag & 1); + if (showctl & 2) { + if (showctl & 1) { + printk(KERN_DEBUG "kcapi: put [0x%lx] id#%d %s len=%u\n", + (unsigned long) contr, + CAPIMSG_APPID(skb->data), + capi_cmd2str(cmd, subcmd), + CAPIMSG_LEN(skb->data)); + } else { + printk(KERN_DEBUG "kcapi: put [0x%lx] %s\n", + (unsigned long) contr, + capi_message2str(skb->data)); + } + + } + CARD(contr)->driver->send_message(CARD(contr), skb); + return CAPI_NOERROR; +} + +static __u16 capi_get_message(__u16 applid, struct sk_buff **msgp) +{ + struct sk_buff *skb; + + if (!VALID_APPLID(applid)) + return CAPI_ILLAPPNR; + if ((skb = skb_dequeue(&APPL(applid)->recv_queue)) == 0) + return CAPI_RECEIVEQUEUEEMPTY; + *msgp = skb; + return CAPI_NOERROR; +} + +static __u16 capi_set_signal(__u16 applid, + void (*signal) (__u16 applid, __u32 param), + __u32 param) +{ + if (!VALID_APPLID(applid)) + return CAPI_ILLAPPNR; + APPL(applid)->signal = signal; + APPL(applid)->param = param; + return CAPI_NOERROR; +} + +static __u16 capi_get_manufacturer(__u16 contr, __u8 buf[CAPI_MANUFACTURER_LEN]) +{ + if (contr == 0) { + strncpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN); + return CAPI_NOERROR; + } + if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) + return 0x2002; + + strncpy(buf, CARD(contr)->manu, CAPI_MANUFACTURER_LEN); + return CAPI_NOERROR; +} + +static __u16 capi_get_version(__u16 contr, struct capi_version *verp) +{ + if (contr == 0) { + *verp = driver_version; + return CAPI_NOERROR; + } + if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) + return 0x2002; + + memcpy((void *) verp, &CARD(contr)->version, sizeof(capi_version)); + return CAPI_NOERROR; +} + +static __u16 capi_get_serial(__u16 contr, __u8 serial[CAPI_SERIAL_LEN]) +{ + if (contr == 0) { + strncpy(serial, driver_serial, CAPI_SERIAL_LEN); + return CAPI_NOERROR; + } + if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) + return 0x2002; + + strncpy((void *) serial, CARD(contr)->serial, CAPI_SERIAL_LEN); + return CAPI_NOERROR; +} + +static __u16 capi_get_profile(__u16 contr, struct capi_profile *profp) +{ + if (contr == 0) { + profp->ncontroller = ncards; + return CAPI_NOERROR; + } + if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) + return 0x2002; + + memcpy((void *) profp, &CARD(contr)->profile, + sizeof(struct capi_profile)); + return CAPI_NOERROR; +} + +#ifdef CONFIG_AVMB1_COMPAT +static int old_capi_manufacturer(unsigned int cmd, void *data) +{ + avmb1_loadandconfigdef ldef; + avmb1_extcarddef cdef; + avmb1_resetdef rdef; + avmb1_getdef gdef; + struct capi_driver *driver; + struct capi_ctr *card; + capicardparams cparams; + capiloaddata ldata; + int retval; + + switch (cmd) { + case AVMB1_ADDCARD: + case AVMB1_ADDCARD_WITH_TYPE: + if (cmd == AVMB1_ADDCARD) { + if ((retval = copy_from_user((void *) &cdef, data, + sizeof(avmb1_carddef)))) + return retval; + cdef.cardtype = AVM_CARDTYPE_B1; + } else { + if ((retval = copy_from_user((void *) &cdef, data, + sizeof(avmb1_extcarddef)))) + return retval; + } + cparams.port = cdef.port; + cparams.irq = cdef.irq; + cparams.cardnr = cdef.cardnr; + + switch (cdef.cardtype) { + case AVM_CARDTYPE_B1: driver = b1isa_driver; break; + case AVM_CARDTYPE_T1: driver = t1isa_driver; break; + default: driver = 0; + } + if (!driver || !driver->add_card) { + return -EIO; + } + + return driver->add_card(driver, &cparams); + + case AVMB1_LOAD: + case AVMB1_LOAD_AND_CONFIG: + + if (cmd == AVMB1_LOAD) { + if ((retval = copy_from_user((void *) &ldef, data, + sizeof(avmb1_loaddef)))) + return retval; + ldef.t4config.len = 0; + ldef.t4config.data = 0; + } else { + if ((retval = copy_from_user((void *) &ldef, data, + sizeof(avmb1_loadandconfigdef)))) + return retval; + } + if (!VALID_CARD(ldef.contr)) + return -ESRCH; + + card = CARD(ldef.contr); + if (card->cardstate == CARD_FREE) + return -ESRCH; + + if (ldef.t4file.len <= 0) { + printk(KERN_DEBUG "kcapi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len); + return -EINVAL; + } + if (ldef.t4file.data == 0) { + printk(KERN_DEBUG "kcapi: load: invalid parameter: dataptr is 0\n"); + return -EINVAL; + } + + ldata.firmware.user = 1; + ldata.firmware.data = ldef.t4file.data; + ldata.firmware.len = ldef.t4file.len; + ldata.configuration.user = 1; + ldata.configuration.data = ldef.t4config.data; + ldata.configuration.len = ldef.t4config.len; + + if (card->cardstate != CARD_DETECTED) { + printk(KERN_INFO "kcapi: load: contr=%d not in detect state\n", ldef.contr); + return -EBUSY; + } + card->cardstate = CARD_LOADING; + + retval = card->driver->load_firmware(card, &ldata); + + if (retval) { + card->cardstate = CARD_DETECTED; + return retval; + } + + while (card->cardstate != CARD_RUNNING) { + + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); /* 0.1 sec */ + + if (signal_pending(current)) + return -EINTR; + } + return 0; + + case AVMB1_RESETCARD: + if ((retval = copy_from_user((void *) &rdef, data, + sizeof(avmb1_resetdef)))) + return retval; + if (!VALID_CARD(rdef.contr)) + return -ESRCH; + card = CARD(rdef.contr); + + if (card->cardstate == CARD_FREE) + return -ESRCH; + if (card->cardstate == CARD_DETECTED) + return 0; + + card->driver->reset_ctr(card); + + while (card->cardstate > CARD_DETECTED) { + + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); /* 0.1 sec */ + + if (signal_pending(current)) + return -EINTR; + } + return 0; + + case AVMB1_GET_CARDINFO: + if ((retval = copy_from_user((void *) &gdef, data, + sizeof(avmb1_getdef)))) + return retval; + + if (!VALID_CARD(gdef.contr)) + return -ESRCH; + + card = CARD(gdef.contr); + + if (card->cardstate == CARD_FREE) + return -ESRCH; + + gdef.cardstate = card->cardstate; + if (card->driver == b1isa_driver) + gdef.cardtype = AVM_CARDTYPE_B1; + else if (card->driver == t1isa_driver) + gdef.cardtype = AVM_CARDTYPE_T1; + else gdef.cardtype = AVM_CARDTYPE_B1; + + if ((retval = copy_to_user(data, (void *) &gdef, + sizeof(avmb1_getdef)))) + return retval; + + return 0; + + case AVMB1_REMOVECARD: + if ((retval = copy_from_user((void *) &rdef, data, + sizeof(avmb1_resetdef)))) + return retval; + + if (!VALID_CARD(rdef.contr)) + return -ESRCH; + card = CARD(rdef.contr); + + if (card->cardstate == CARD_FREE) + return -ESRCH; + + if (card->cardstate != CARD_DETECTED) + return -EBUSY; + + card->driver->remove_ctr(card); + + while (card->cardstate != CARD_FREE) { + + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); /* 0.1 sec */ + + if (signal_pending(current)) + return -EINTR; + } + return 0; + } + return -EINVAL; +} +#endif + +static int capi_manufacturer(unsigned int cmd, void *data) +{ + struct capi_ctr *card; + kcapi_flagdef fdef; + int retval; + + switch (cmd) { +#ifdef CONFIG_AVMB1_COMPAT + case AVMB1_ADDCARD: + case AVMB1_ADDCARD_WITH_TYPE: + case AVMB1_LOAD: + case AVMB1_LOAD_AND_CONFIG: + case AVMB1_RESETCARD: + case AVMB1_GET_CARDINFO: + case AVMB1_REMOVECARD: + return old_capi_manufacturer(cmd, data); +#endif + case KCAPI_CMD_TRACE: + if ((retval = copy_from_user((void *) &fdef, data, + sizeof(kcapi_flagdef)))) + return retval; + + if (!VALID_CARD(fdef.contr)) + return -ESRCH; + card = CARD(fdef.contr); + if (card->cardstate == CARD_FREE) + return -ESRCH; + card->traceflag = fdef.flag; + printk(KERN_INFO "kcapi: contr %d set trace=%d\n", + card->cnr, card->traceflag); + return 0; + } + return -EINVAL; +} + +struct capi_interface avmb1_interface = +{ + capi_installed, + capi_register, + capi_release, + capi_put_message, + capi_get_message, + capi_set_signal, + capi_get_manufacturer, + capi_get_version, + capi_get_serial, + capi_get_profile, + capi_manufacturer +}; + +/* ------------------------------------------------------------- */ +/* -------- Exported Functions --------------------------------- */ +/* ------------------------------------------------------------- */ + +struct capi_interface *attach_capi_interface(struct capi_interface_user *userp) +{ + struct capi_interface_user *p; + + for (p = capi_users; p; p = p->next) { + if (p == userp) { + printk(KERN_ERR "kcapi: double attach from %s\n", + userp->name); + return 0; + } + } + userp->next = capi_users; + capi_users = userp; + MOD_INC_USE_COUNT; + printk(KERN_NOTICE "kcapi: %s attached\n", userp->name); + + return &avmb1_interface; +} + +int detach_capi_interface(struct capi_interface_user *userp) +{ + struct capi_interface_user **pp; + + for (pp = &capi_users; *pp; pp = &(*pp)->next) { + if (*pp == userp) { + *pp = userp->next; + userp->next = 0; + MOD_DEC_USE_COUNT; + printk(KERN_NOTICE "kcapi: %s detached\n", userp->name); + return 0; + } + } + printk(KERN_ERR "kcapi: double detach from %s\n", userp->name); + return -1; +} + +/* ------------------------------------------------------------- */ +/* -------- Init & Cleanup ------------------------------------- */ +/* ------------------------------------------------------------- */ + +EXPORT_SYMBOL(attach_capi_interface); +EXPORT_SYMBOL(detach_capi_interface); +EXPORT_SYMBOL(attach_capi_driver); +EXPORT_SYMBOL(detach_capi_driver); + +#ifndef MODULE +#ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA +extern int b1isa_init(void); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCI +extern int b1pci_init(void); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_T1ISA +extern int t1isa_init(void); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA +extern int b1pcmcia_init(void); +#endif +#endif + +/* + * init / exit functions + */ + +#ifdef MODULE +#define kcapi_init init_module +#endif + +int kcapi_init(void) +{ + char *p; + char rev[10]; + + skb_queue_head_init(&recv_queue); + /* init_bh(CAPI_BH, do_capi_bh); */ + + tq_state_notify.routine = notify_handler; + tq_state_notify.data = 0; + + tq_recv_notify.routine = recv_handler; + tq_recv_notify.data = 0; + + proc_capi_init(); + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else + strcpy(rev, "1.0"); + +#ifdef MODULE + printk(KERN_NOTICE "CAPI-driver Rev%s: loaded\n", rev); +#else + printk(KERN_NOTICE "CAPI-driver Rev%s: started\n", rev); +#ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA + (void)b1isa_init(); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCI + (void)b1pci_init(); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_T1ISA + (void)t1isa_init(); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA + (void)b1pcmcia_init(); +#endif +#endif + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + char rev[10]; + char *p; + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else { + strcpy(rev, "1.0"); + } + + schedule(); /* execute queued tasks .... */ + proc_capi_exit(); + printk(KERN_NOTICE "CAPI-driver Rev%s: unloaded\n", rev); +} +#endif diff --git a/drivers/isdn/avmb1/t1isa.c b/drivers/isdn/avmb1/t1isa.c new file mode 100644 index 000000000000..eb8c5714918e --- /dev/null +++ b/drivers/isdn/avmb1/t1isa.c @@ -0,0 +1,545 @@ +/* + * $Id: t1isa.c,v 1.4 1999/07/09 15:05:50 keil Exp $ + * + * Module for AVM T1 HEMA-card. + * + * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: t1isa.c,v $ + * Revision 1.4 1999/07/09 15:05:50 keil + * compat.h is now isdn_compat.h + * + * Revision 1.3 1999/07/06 07:42:04 calle + * - changes in /proc interface + * - check and changed calls to [dev_]kfree_skb and [dev_]alloc_skb. + * + * Revision 1.2 1999/07/05 15:09:54 calle + * - renamed "appl_release" to "appl_released". + * - version und profile data now cleared on controller reset + * - extended /proc interface, to allow driver and controller specific + * informations to include by driver hackers. + * + * Revision 1.1 1999/07/01 15:26:44 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "capicmd.h" +#include "capiutil.h" +#include "capilli.h" +#include "avmcard.h" + +static char *revision = "$Revision: 1.4 $"; + +/* ------------------------------------------------------------- */ + +MODULE_AUTHOR("Carsten Paeth "); + +/* ------------------------------------------------------------- */ + +static struct capi_driver_interface *di; + +/* ------------------------------------------------------------- */ + +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 */ +}; + +static int t1_detectandinit(unsigned int base, unsigned irq, int cardnr) +{ + unsigned char cregs[8]; + unsigned char reverse_cardnr; + unsigned long flags; + unsigned char dummy; + int 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; + + 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); + + 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; +} + +static void t1_handle_interrupt(avmcard * card) +{ + struct capi_ctr *ctrl = card->ctrl; + unsigned char b1cmd; + struct sk_buff *skb; + + unsigned ApplId; + unsigned MsgLen; + unsigned DataB3Len; + unsigned NCCI; + unsigned WindowSize; + + while (b1_rx_full(card->port)) { + + b1cmd = b1_get_byte(card->port); + + switch (b1cmd) { + + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = t1_get_slice(card->port, card->msgbuf); + DataB3Len = t1_get_slice(card->port, card->databuf); + + if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "t1isa: incoming packet dropped\n"); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len); + CAPIMSG_SETDATA(skb->data, skb->data + MsgLen); + ctrl->handle_capimsg(ctrl, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = t1_get_slice(card->port, card->msgbuf); + if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "t1isa: incoming packet dropped\n"); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + ctrl->handle_capimsg(ctrl, ApplId, skb); + } + break; + + case RECEIVE_NEW_NCCI: + + ApplId = b1_get_word(card->port); + NCCI = b1_get_word(card->port); + WindowSize = b1_get_word(card->port); + + ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize); + + break; + + case RECEIVE_FREE_NCCI: + + ApplId = b1_get_word(card->port); + NCCI = b1_get_word(card->port); + + if (NCCI != 0xffffffff) + ctrl->free_ncci(ctrl, ApplId, NCCI); + else ctrl->appl_released(ctrl, ApplId); + break; + + case RECEIVE_START: + b1_put_byte(card->port, SEND_POLLACK); + ctrl->resume_output(ctrl); + break; + + case RECEIVE_STOP: + ctrl->suspend_output(ctrl); + break; + + case RECEIVE_INIT: + + card->versionlen = t1_get_slice(card->port, card->versionbuf); + b1_parse_version(card); + printk(KERN_INFO "%s: %s-card (%s) now active\n", + card->name, + card->version[VER_CARDTYPE], + card->version[VER_DRIVER]); + ctrl->ready(ctrl); + break; + + case RECEIVE_TASK_READY: + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = t1_get_slice(card->port, card->msgbuf); + card->msgbuf[MsgLen--] = 0; + while ( MsgLen >= 0 + && ( card->msgbuf[MsgLen] == '\n' + || card->msgbuf[MsgLen] == '\r')) + card->msgbuf[MsgLen--] = 0; + printk(KERN_INFO "%s: task %d \"%s\" ready.\n", + card->name, ApplId, card->msgbuf); + break; + + case RECEIVE_DEBUGMSG: + MsgLen = t1_get_slice(card->port, card->msgbuf); + card->msgbuf[MsgLen--] = 0; + while ( MsgLen >= 0 + && ( card->msgbuf[MsgLen] == '\n' + || card->msgbuf[MsgLen] == '\r')) + card->msgbuf[MsgLen--] = 0; + printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); + break; + + case 0xff: + printk(KERN_ERR "%s: card reseted ?\n", card->name); + return; + default: + printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n", + card->name, b1cmd); + return; + } + } +} + +/* ------------------------------------------------------------- */ + +static void t1isa_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmcard *card; + + card = (avmcard *) devptr; + + if (!card) { + printk(KERN_WARNING "t1_interrupt: wrong device\n"); + return; + } + if (card->interrupt) { + printk(KERN_ERR "t1_interrupt: reentering interrupt hander (%s)\n", card->name); + return; + } + + card->interrupt = 1; + + t1_handle_interrupt(card); + + card->interrupt = 0; +} +/* ------------------------------------------------------------- */ + +static int t1isa_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + unsigned long flags; + int retval; + + t1_disable_irq(port); + b1_reset(port); + + if ((retval = b1_load_t4file(port, &data->firmware))) { + b1_reset(port); + printk(KERN_ERR "%s: failed to load t4file!!\n", + card->name); + return retval; + } + + if (data->configuration.len > 0 && data->configuration.data) { + if ((retval = b1_load_config(port, &data->configuration))) { + b1_reset(port); + printk(KERN_ERR "%s: failed to load config!!\n", + card->name); + return retval; + } + } + + if (!b1_loaded(port)) { + printk(KERN_ERR "%s: failed to load t4file.\n", card->name); + return -EIO; + } + + save_flags(flags); + cli(); + b1_setinterrupt(port, card->irq, card->cardtype); + b1_put_byte(port, SEND_INIT); + b1_put_word(port, AVM_NAPPS); + b1_put_word(port, AVM_NCCI_PER_CHANNEL*30); + b1_put_word(port, ctrl->cnr - 1); + restore_flags(flags); + + return 0; +} + +void t1isa_reset_ctr(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + + t1_disable_irq(port); + b1_reset(port); + b1_reset(port); + + memset(card->version, 0, sizeof(card->version)); + ctrl->reseted(ctrl); +} + +static void t1isa_remove_ctr(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + + t1_disable_irq(port); + b1_reset(port); + b1_reset(port); + t1_reset(port); + + di->detach_ctr(ctrl); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + kfree(card); + + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------- */ + +static int t1isa_add_card(struct capi_driver *driver, struct capicardparams *p) +{ + struct capi_ctr *ctrl; + avmcard *card; + int retval; + + card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); + + if (!card) { + printk(KERN_WARNING "t1isa: no memory.\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(avmcard)); + sprintf(card->name, "t1isa-%x", p->port); + card->port = p->port; + card->irq = p->irq; + card->cardtype = avm_t1isa; + card->cardnr = p->cardnr; + + if (!(((card->port & 0x7) == 0) && ((card->port & 0x30) != 0x30))) { + printk(KERN_WARNING "t1isa: illegal port 0x%x.\n", card->port); + kfree(card); + return -EINVAL; + } + + if (check_region(card->port, AVMB1_PORTLEN)) { + printk(KERN_WARNING + "t1isa: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + if (hema_irq_table[card->irq & 0xf] == 0) { + printk(KERN_WARNING "t1isa: irq %d not valid.\n", card->irq); + kfree(card); + return -EINVAL; + } + for (ctrl = driver->controller; ctrl; ctrl = ctrl->next) { + if (((avmcard *)(ctrl->driverdata))->cardnr == card->cardnr) { + printk(KERN_WARNING "t1isa: card with number %d already installed.\n", card->cardnr); + kfree(card); + return -EBUSY; + } + } + if ((retval = t1_detectandinit(card->port, card->irq, card->cardnr)) != 0) { + printk(KERN_NOTICE "t1isa: NO card at 0x%x (%d)\n", + card->port, retval); + kfree(card); + return -EIO; + } + t1_disable_irq(card->port); + b1_reset(card->port); + + request_region(p->port, AVMB1_PORTLEN, card->name); + + retval = request_irq(card->irq, t1isa_interrupt, 0, card->name, card); + if (retval) { + printk(KERN_ERR "t1isa: unable to get IRQ %d.\n", card->irq); + release_region(card->port, AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + + card->ctrl = di->attach_ctr(driver, card->name, card); + if (!card->ctrl) { + printk(KERN_ERR "t1isa: attach controller failed.\n"); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + + MOD_INC_USE_COUNT; + return 0; +} + +static void t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + unsigned long flags; + __u16 len = CAPIMSG_LEN(skb->data); + __u8 cmd = CAPIMSG_COMMAND(skb->data); + __u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data); + + save_flags(flags); + cli(); + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + __u16 dlen = CAPIMSG_DATALEN(skb->data); + b1_put_byte(port, SEND_DATA_B3_REQ); + t1_put_slice(port, skb->data, len); + t1_put_slice(port, skb->data + len, dlen); + } else { + b1_put_byte(port, SEND_MESSAGE); + t1_put_slice(port, skb->data, len); + } + restore_flags(flags); + dev_kfree_skb(skb); +} +/* ------------------------------------------------------------- */ + +static char *t1isa_procinfo(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + if (!card) + return ""; + sprintf(card->infobuf, "%s %s 0x%x %d %d", + card->cardname[0] ? card->cardname : "-", + card->version[VER_DRIVER] ? card->version[VER_DRIVER] : "-", + card->port, card->irq, card->cardnr + ); + return card->infobuf; +} + + +/* ------------------------------------------------------------- */ + +static struct capi_driver t1isa_driver = { + "t1isa", + "0.0", + t1isa_load_firmware, + t1isa_reset_ctr, + t1isa_remove_ctr, + b1_register_appl, + b1_release_appl, + t1isa_send_message, + + t1isa_procinfo, + b1ctl_read_proc, + 0, /* use standard driver_read_proc */ + + t1isa_add_card, +}; + +#ifdef MODULE +#define t1isa_init init_module +void cleanup_module(void); +#endif + +int t1isa_init(void) +{ + struct capi_driver *driver = &t1isa_driver; + char *p; + + if ((p = strchr(revision, ':'))) { + strncpy(driver->revision, p + 1, sizeof(driver->revision)); + p = strchr(driver->revision, '$'); + *p = 0; + } + + printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision); + + di = attach_capi_driver(driver); + + if (!di) { + printk(KERN_ERR "%s: failed to attach capi_driver\n", + driver->name); + return -EIO; + } + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + detach_capi_driver(&t1isa_driver); +} +#endif diff --git a/drivers/isdn/divert/Makefile b/drivers/isdn/divert/Makefile new file mode 100644 index 000000000000..109c900d8ac5 --- /dev/null +++ b/drivers/isdn/divert/Makefile @@ -0,0 +1,21 @@ +L_OBJS := +LX_OBJS := +M_OBJS := +MX_OBJS := +O_OBJS := +OX_OBJS := +L_TARGET := +O_TARGET := + +O_OBJS += isdn_divert.o divert_procfs.o +O_TARGET := dss1_divert.o +M_OBJS += dss1_divert.o +OX_OBJS += divert_init.o + +include $(TOPDIR)/Rules.make + + + + + + diff --git a/drivers/isdn/divert/divert_init.c b/drivers/isdn/divert/divert_init.c new file mode 100644 index 000000000000..7ee924993b01 --- /dev/null +++ b/drivers/isdn/divert/divert_init.c @@ -0,0 +1,105 @@ +/* + * $Id: divert_init.c,v 1.3 1999/07/05 20:21:39 werner Exp $ + * + * Module init for DSS1 diversion services for i4l. + * + * Copyright 1999 by Werner Cornelius (werner@isdn4linux.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: divert_init.c,v $ + * Revision 1.3 1999/07/05 20:21:39 werner + * changes to use diversion sources for all kernel versions. + * removed static device, only proc filesystem used + * + * Revision 1.2 1999/07/04 21:37:30 werner + * Ported from kernel version 2.0 + * + * + * + */ + +#include +#include +#include +#include "isdn_divert.h" + +/********************/ +/* needed externals */ +/********************/ +extern int printk(const char *fmt,...); + +/****************************************/ +/* structure containing interface to hl */ +/****************************************/ +isdn_divert_if divert_if = + { DIVERT_IF_MAGIC, /* magic value */ + DIVERT_CMD_REG, /* register cmd */ + ll_callback, /* callback routine from ll */ + NULL, /* command still not specified */ + NULL, /* drv_to_name */ + NULL, /* name_to_drv */ + }; + +/*************************/ +/* Module interface code */ +/* no cmd line parms */ +/*************************/ +int init_module(void) +{ int i; + + if (divert_dev_init()) + { printk(KERN_WARNING "dss1_divert: cannot install device, not loaded\n"); + return(-EIO); + } + if ((i = DIVERT_REG_NAME(&divert_if)) != DIVERT_NO_ERR) + { divert_dev_deinit(); + printk(KERN_WARNING "dss1_divert: error %d registering module, not loaded\n",i); + return(-EIO); + } +#if (LINUX_VERSION_CODE < 0x020111) + register_symtab(0); +#endif + printk(KERN_INFO "dss1_divert module successfully installed\n"); + return(0); +} /* init_module */ + +/**********************/ +/* Module deinit code */ +/**********************/ +void cleanup_module(void) +{ int flags; + int i; + + save_flags(flags); + cli(); + divert_if.cmd = DIVERT_CMD_REL; /* release */ + if ((i = DIVERT_REG_NAME(&divert_if)) != DIVERT_NO_ERR) + { printk(KERN_WARNING "dss1_divert: error %d releasing module\n",i); + restore_flags(flags); + return; + } + if (divert_dev_deinit()) + { printk(KERN_WARNING "dss1_divert: device busy, remove cancelled\n"); + restore_flags(flags); + return; + } + restore_flags(flags); + deleterule(-1); /* delete all rules and free mem */ + deleteprocs(); + printk(KERN_INFO "dss1_divert module successfully removed \n"); +} /* cleanup_module */ + + diff --git a/drivers/isdn/divert/divert_procfs.c b/drivers/isdn/divert/divert_procfs.c new file mode 100644 index 000000000000..035aa363613a --- /dev/null +++ b/drivers/isdn/divert/divert_procfs.c @@ -0,0 +1,456 @@ +/* + * $Id: divert_procfs.c,v 1.4 1999/08/06 07:42:48 calle Exp $ + * + * Filesystem handling for the diversion supplementary services. + * + * Copyright 1998 by Werner Cornelius (werner@isdn4linux.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: divert_procfs.c,v $ + * Revision 1.4 1999/08/06 07:42:48 calle + * Added COMPAT_HAS_NEW_WAITQ for rd_queue for newer kernels. + * + * Revision 1.3 1999/07/05 20:21:41 werner + * changes to use diversion sources for all kernel versions. + * removed static device, only proc filesystem used + * + * Revision 1.2 1999/07/04 21:37:31 werner + * Ported from kernel version 2.0 + * + * + * + */ + +#include +#define __NO_VERSION__ +#include +#include +#if (LINUX_VERSION_CODE >= 0x020117) +#include +#endif +#ifdef CONFIG_PROC_FS + #include +#else + #include +#endif +#include +#include +#include "isdn_divert.h" + +/*********************************/ +/* Variables for interface queue */ +/*********************************/ +ulong if_used = 0; /* number of interface users */ +static struct divert_info *divert_info_head = NULL; /* head of queue */ +static struct divert_info *divert_info_tail = NULL; /* pointer to last entry */ +#ifdef COMPAT_HAS_NEW_WAITQ +static wait_queue_head_t rd_queue; +#else +static struct wait_queue *rd_queue = 0; /* Queue IO */ +#endif + +/*********************************/ +/* put an info buffer into queue */ +/*********************************/ +void put_info_buffer(char *cp) +{ struct divert_info *ib; + int flags; + + if (if_used <= 0) return; + if (!cp) return; + if (!*cp) return; + if (!(ib = (struct divert_info *) kmalloc(sizeof(struct divert_info)+strlen(cp), GFP_ATOMIC))) return; /* no memory */ + strcpy(ib->info_start,cp); /* set output string */ + ib->next = NULL; + save_flags(flags); + cli(); + ib->usage_cnt = if_used; + if (!divert_info_head) + divert_info_head = ib; /* new head */ + else + divert_info_tail->next = ib; /* follows existing messages */ + divert_info_tail = ib; /* new tail */ + restore_flags(flags); + + /* delete old entrys */ + while (divert_info_head->next) + { if ((divert_info_head->usage_cnt <= 0) && + (divert_info_head->next->usage_cnt <= 0)) + { ib = divert_info_head; + divert_info_head = divert_info_head->next; + kfree(ib); + } + else break; + } /* divert_info_head->next */ + wake_up_interruptible(&(rd_queue)); +} /* put_info_buffer */ + +/**********************************/ +/* deflection device read routine */ +/**********************************/ +#if (LINUX_VERSION_CODE < 0x020117) +static int isdn_divert_read(struct inode *inode, struct file *file, char *buf, RWARG count) +#else +static ssize_t isdn_divert_read(struct file *file, char *buf, size_t count, loff_t *off) +#endif +{ struct divert_info *inf; + int len; + + if (!*((struct divert_info **)file->private_data)) + { if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + interruptible_sleep_on(&(rd_queue)); + } + if (!(inf = *((struct divert_info **)file->private_data))) return(0); + + inf->usage_cnt--; /* new usage count */ + (struct divert_info **)file->private_data = &inf->next; /* next structure */ + if ((len = strlen(inf->info_start)) <= count) + { if (copy_to_user(buf, inf->info_start, len)) + return -EFAULT; + file->f_pos += len; + return(len); + } + return(0); +} /* isdn_divert_read */ + +/**********************************/ +/* deflection device write routine */ +/**********************************/ +#if (LINUX_VERSION_CODE < 0x020117) +static int isdn_divert_write(struct inode *inode, struct file *file, const char *buf, RWARG count) +#else +static ssize_t isdn_divert_write(struct file *file, const char *buf, size_t count, loff_t *off) +#endif +{ + return(-ENODEV); +} /* isdn_divert_write */ + + +/***************************************/ +/* select routines for various kernels */ +/***************************************/ +#if (LINUX_VERSION_CODE < 0x020117) +static int isdn_divert_select(struct inode *inode, struct file *file, int type, select_table * st) +{ + if (*((struct divert_info **)file->private_data)) + return 1; + else + { if (st) select_wait(&(rd_queue), st); + return 0; + } +} /* isdn_divert_select */ +#else +static unsigned int isdn_divert_poll(struct file *file, poll_table * wait) +{ unsigned int mask = 0; + + poll_wait(file, &(rd_queue), wait); + /* mask = POLLOUT | POLLWRNORM; */ + if (*((struct divert_info **)file->private_data)) + { mask |= POLLIN | POLLRDNORM; + } + return mask; +} /* isdn_divert_poll */ +#endif + +/****************/ +/* Open routine */ +/****************/ +static int isdn_divert_open(struct inode *ino, struct file *filep) +{ int flags; + + MOD_INC_USE_COUNT; + save_flags(flags); + cli(); + if_used++; + if (divert_info_head) + (struct divert_info **)filep->private_data = &(divert_info_tail->next); + else + (struct divert_info **)filep->private_data = &divert_info_head; + restore_flags(flags); + /* start_divert(); */ + return(0); +} /* isdn_divert_open */ + +/*******************/ +/* close routine */ +/*******************/ +#if (LINUX_VERSION_CODE < 0x020117) +static void isdn_divert_close(struct inode *ino, struct file *filep) +#else +static int isdn_divert_close(struct inode *ino, struct file *filep) +#endif +{ struct divert_info *inf; + int flags; + + save_flags(flags); + cli(); + if_used--; + inf = *((struct divert_info **)filep->private_data); + while (inf) + { inf->usage_cnt--; + inf = inf->next; + } + restore_flags(flags); + if (if_used <= 0) + while (divert_info_head) + { inf = divert_info_head; + divert_info_head = divert_info_head->next; + kfree(inf); + } + MOD_DEC_USE_COUNT; +#if (LINUX_VERSION_CODE < 0x020117) +#else + return(0); +#endif +} /* isdn_divert_close */ + +/*********/ +/* IOCTL */ +/*********/ +static int isdn_divert_ioctl(struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ divert_ioctl dioctl; + int i, flags; + divert_rule *rulep; + char *cp; + + if ((i = copy_from_user(&dioctl,(char *) arg, sizeof(dioctl)))) + return(i); + + switch (cmd) + { + case IIOCGETVER: + dioctl.drv_version = DIVERT_IIOC_VERSION ; /* set version */ + break; + + case IIOCGETDRV: + if ((dioctl.getid.drvid = divert_if.name_to_drv(dioctl.getid.drvnam)) < 0) + return(-EINVAL); + break; + + case IIOCGETNAM: + cp = divert_if.drv_to_name(dioctl.getid.drvid); + if (!cp) return(-EINVAL); + if (!*cp) return(-EINVAL); + strcpy(dioctl.getid.drvnam,cp); + break; + + case IIOCGETRULE: + if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx))) + return(-EINVAL); + dioctl.getsetrule.rule = *rulep; /* copy data */ + break; + + case IIOCMODRULE: + if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx))) + return(-EINVAL); + save_flags(flags); + cli(); + *rulep = dioctl.getsetrule.rule; /* copy data */ + restore_flags(flags); + return(0); /* no copy required */ + break; + + case IIOCINSRULE: + return(insertrule(dioctl.getsetrule.ruleidx,&dioctl.getsetrule.rule)); + break; + + case IIOCDELRULE: + return(deleterule(dioctl.getsetrule.ruleidx)); + break; + + case IIOCDODFACT: + return(deflect_extern_action(dioctl.fwd_ctrl.subcmd, + dioctl.fwd_ctrl.callid, + dioctl.fwd_ctrl.to_nr)); + + case IIOCDOCFACT: + case IIOCDOCFDIS: + case IIOCDOCFINT: + if (!divert_if.drv_to_name(dioctl.cf_ctrl.drvid)) + return(-EINVAL); /* invalid driver */ + if ((i = cf_command(dioctl.cf_ctrl.drvid, + (cmd == IIOCDOCFACT) ? 1: (cmd == IIOCDOCFDIS) ? 0:2, + dioctl.cf_ctrl.cfproc, + dioctl.cf_ctrl.msn, + dioctl.cf_ctrl.service, + dioctl.cf_ctrl.fwd_nr, + &dioctl.cf_ctrl.procid))) + return(i); + break; + + default: + return(-EINVAL); + } /* switch cmd */ + return(copy_to_user((char *) arg, &dioctl, sizeof(dioctl))); /* success */ +} /* isdn_divert_ioctl */ + + +#ifdef CONFIG_PROC_FS +#if (LINUX_VERSION_CODE < 0x020117) +static LSTYPE +isdn_divert_lseek(struct inode *inode, struct file *file, LSARG offset, int orig) +#else +static loff_t +isdn_divert_lseek(struct file *file, loff_t offset, int orig) +#endif +{ + return -ESPIPE; +} + +#if (LINUX_VERSION_CODE < 0x020117) +static struct file_operations isdn_fops = +{ + isdn_divert_lseek, + isdn_divert_read, + isdn_divert_write, + NULL, /* isdn_readdir */ + isdn_divert_select, /* isdn_select */ + isdn_divert_ioctl, /* isdn_ioctl */ + NULL, /* isdn_mmap */ + isdn_divert_open, + isdn_divert_close, + NULL /* fsync */ +}; + +#else + +static struct file_operations isdn_fops = +{ + isdn_divert_lseek, + isdn_divert_read, + isdn_divert_write, + NULL, /* isdn_readdir */ + isdn_divert_poll, /* isdn_poll */ + isdn_divert_ioctl, /* isdn_ioctl */ + NULL, /* isdn_mmap */ + isdn_divert_open, + NULL, /* flush */ + isdn_divert_close, + NULL /* fsync */ +}; +#endif /* kernel >= 2.1 */ + + +/* + * proc directories can do almost nothing.. + */ +struct inode_operations proc_isdn_inode_ops = { + &isdn_fops, /* isdn divert special file-ops */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + +/****************************/ +/* isdn subdir in /proc/net */ +/****************************/ +static struct proc_dir_entry isdn_proc_entry = + { 0, 4, "isdn", S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, 0, + &proc_dir_inode_operations,NULL,NULL,NULL,NULL,NULL + }; + +static struct proc_dir_entry isdn_divert_entry = +{ 0, 6, "divert",S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_isdn_inode_ops, + NULL + }; + +/*****************************************************************/ +/* variables used for automatic determining existence of proc fs */ +/*****************************************************************/ +static int (*proc_reg_dynamic)(struct proc_dir_entry *, struct proc_dir_entry *) = NULL; +static int (*proc_unreg)(struct proc_dir_entry *, int) = NULL; + +#endif CONFIG_PROC_FS + +/***************************************************************************/ +/* divert_dev_init must be called before the proc filesystem may be used */ +/***************************************************************************/ +int divert_dev_init(void) +{ int i; + +#ifdef COMPAT_HAS_NEW_WAITQ + init_waitqueue_head(&rd_queue); +#endif + +#ifdef CONFIG_PROC_FS +#if (LINUX_VERSION_CODE < 0x020117) + (void *) proc_reg_dynamic = get_module_symbol("","proc_register_dynamic"); + (void *) proc_unreg = get_module_symbol("","proc_unregister"); + if (proc_unreg) + { i = proc_reg_dynamic(&proc_net,&isdn_proc_entry); + if (i) return(i); + i = proc_reg_dynamic(&isdn_proc_entry,&isdn_divert_entry); + if (i) + { proc_unreg(&proc_net,isdn_proc_entry.low_ino); + return(i); + } + } /* proc exists */ +#else + (void *) proc_reg_dynamic = get_module_symbol("","proc_register"); + (void *) proc_unreg = get_module_symbol("","proc_unregister"); + if (proc_unreg) + { i = proc_reg_dynamic(proc_net,&isdn_proc_entry); + if (i) return(i); + i = proc_reg_dynamic(&isdn_proc_entry,&isdn_divert_entry); + if (i) + { proc_unreg(proc_net,isdn_proc_entry.low_ino); + return(i); + } + } /* proc exists */ +#endif +#endif CONFIG_PROC_FS + + return(0); +} /* divert_dev_init */ + +/***************************************************************************/ +/* divert_dev_deinit must be called before leaving isdn when included as */ +/* a module. */ +/***************************************************************************/ +int divert_dev_deinit(void) +{ int i; + +#ifdef CONFIG_PROC_FS + if (proc_unreg) + { i = proc_unreg(&isdn_proc_entry,isdn_divert_entry.low_ino); + if (i) return(i); +#if (LINUX_VERSION_CODE < 0x020117) + i = proc_unreg(&proc_net,isdn_proc_entry.low_ino); +#else + i = proc_unreg(proc_net,isdn_proc_entry.low_ino); +#endif + if (i) return(i); + } /* proc exists */ +#endif CONFIG_PROC_FS + + return(0); +} /* divert_dev_deinit */ + diff --git a/drivers/isdn/divert/isdn_divert.c b/drivers/isdn/divert/isdn_divert.c new file mode 100644 index 000000000000..7454aa0958be --- /dev/null +++ b/drivers/isdn/divert/isdn_divert.c @@ -0,0 +1,902 @@ +/* + * $Id: isdn_divert.c,v 1.2 1999/07/04 21:37:32 werner Exp $ + * + * DSS1 main diversion supplementary handling for i4l. + * + * Copyright 1999 by Werner Cornelius (werner@isdn4linux.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: isdn_divert.c,v $ + * Revision 1.2 1999/07/04 21:37:32 werner + * Ported from kernel version 2.0 + * + * + * + */ + + + +#include +#define __NO_VERSION__ +#include +#include +#include +#include "isdn_divert.h" + +/**********************************/ +/* structure keeping calling info */ +/**********************************/ +struct call_struc + { isdn_ctrl ics; /* delivered setup + driver parameters */ + ulong divert_id; /* Id delivered to user */ + unsigned char akt_state; /* actual state */ + char deflect_dest[35]; /* deflection destination */ + struct timer_list timer; /* timer control structure */ + char info[90]; /* device info output */ + struct call_struc *next; /* pointer to next entry */ + struct call_struc *prev; + }; + + +/********************************************/ +/* structure keeping deflection table entry */ +/********************************************/ +struct deflect_struc + { struct deflect_struc *next,*prev; + divert_rule rule; /* used rule */ + }; + + +/*****************************************/ +/* variables for main diversion services */ +/*****************************************/ +/* diversion/deflection processes */ +static struct call_struc *divert_head = NULL; /* head of remembered entrys */ +static ulong next_id = 1; /* next info id */ +static struct deflect_struc *table_head = NULL; +static struct deflect_struc *table_tail = NULL; +static unsigned char extern_wait_max = 4; /* maximum wait in s for external process */ + +/***************************/ +/* timer callback function */ +/***************************/ +static void deflect_timer_expire(ulong arg) +{ int flags; + struct call_struc *cs = (struct call_struc *) arg; + + save_flags(flags); + cli(); + del_timer(&cs->timer); /* delete active timer */ + restore_flags(flags); + + switch(cs->akt_state) + { case DEFLECT_PROCEED: + cs->ics.command = ISDN_CMD_HANGUP; /* cancel action */ + divert_if.ll_cmd(&cs->ics); + save_flags(flags); + cli(); + cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ + cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); + add_timer(&cs->timer); + restore_flags(flags); + break; + + case DEFLECT_ALERT: + cs->ics.command = ISDN_CMD_REDIR; /* protocol */ + strcpy(cs->ics.parm.setup.phone,cs->deflect_dest); + strcpy(cs->ics.parm.setup.eazmsn,"Testtext delayed"); + divert_if.ll_cmd(&cs->ics); + save_flags(flags); + cli(); + cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ + cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); + add_timer(&cs->timer); + restore_flags(flags); + break; + + case DEFLECT_AUTODEL: + default: + save_flags(flags); + cli(); + if (cs->prev) + cs->prev->next = cs->next; /* forward link */ + else + divert_head = cs->next; + if (cs->next) + cs->next->prev = cs->prev; /* back link */ + restore_flags(flags); + kfree(cs); + return; + + } /* switch */ +} /* deflect_timer_func */ + + +/*****************************************/ +/* handle call forwarding de/activations */ +/* 0 = deact, 1 = act, 2 = interrogate */ +/*****************************************/ +int cf_command(int drvid, int mode, + u_char proc, char *msn, + u_char service, char *fwd_nr, ulong *procid) +{ int retval,msnlen,flags; + int fwd_len; + char *p,*ielenp,tmp[60]; + struct call_struc *cs; + + if (strchr(msn,'.')) return(-EINVAL); /* subaddress not allowed in msn */ + if ((proc & 0x7F) > 2) return(-EINVAL); + proc &= 3; + p = tmp; + *p++ = 0x30; /* enumeration */ + ielenp = p++; /* remember total length position */ + *p++ = 0xa; /* proc tag */ + *p++ = 1; /* length */ + *p++ = proc & 0x7F; /* procedure to de/activate/interrogate */ + *p++ = 0xa; /* service tag */ + *p++ = 1; /* length */ + *p++ = service; /* service to handle */ + + if (mode == 1) + { if (!*fwd_nr) return(-EINVAL); /* destination missing */ + if (strchr(fwd_nr,'.')) return(-EINVAL); /* subaddress not allowed */ + fwd_len = strlen(fwd_nr); + *p++ = 0x30; /* number enumeration */ + *p++ = fwd_len + 2; /* complete forward to len */ + *p++ = 0x80; /* fwd to nr */ + *p++ = fwd_len; /* length of number */ + strcpy(p,fwd_nr); /* copy number */ + p += fwd_len; /* pointer beyond fwd */ + } /* activate */ + + msnlen = strlen(msn); + *p++ = 0x80; /* msn number */ + if (msnlen > 1) + { *p++ = msnlen; /* length */ + strcpy(p,msn); + p += msnlen; + } + else *p++ = 0; + + *ielenp = p - ielenp - 1; /* set total IE length */ + + /* allocate mem for information struct */ + if (!(cs = (struct call_struc *) kmalloc(sizeof(struct call_struc), GFP_ATOMIC))) + return(-ENOMEM); /* no memory */ + init_timer(&cs->timer); + cs->info[0] = '\0'; + cs->timer.function = deflect_timer_expire; + cs->timer.data = (ulong) cs; /* pointer to own structure */ + cs->ics.driver = drvid; + cs->ics.command = ISDN_CMD_PROT_IO; /* protocol specific io */ + cs->ics.arg = DSS1_CMD_INVOKE; /* invoke supplementary service */ + cs->ics.parm.dss1_io.proc = (mode == 1) ? 7: (mode == 2) ? 11:8; /* operation */ + cs->ics.parm.dss1_io.timeout = 4000; /* from ETS 300 207-1 */ + cs->ics.parm.dss1_io.datalen = p - tmp; /* total len */ + cs->ics.parm.dss1_io.data = tmp; /* start of buffer */ + + save_flags(flags); + cli(); + cs->ics.parm.dss1_io.ll_id = next_id++; /* id for callback */ + restore_flags(flags); + *procid = cs->ics.parm.dss1_io.ll_id; + + sprintf(cs->info,"%d 0x%lx %s%s 0 %s %0x %d%s%s\n", + (!mode ) ? DIVERT_DEACTIVATE : (mode == 1) ? DIVERT_ACTIVATE : DIVERT_REPORT, + cs->ics.parm.dss1_io.ll_id, + (mode != 2) ? "" : "0 ", + divert_if.drv_to_name(cs->ics.driver), + msn, + service & 0xFF, + proc, + (mode != 1) ? "" : " 0 ", + (mode != 1) ? "" : fwd_nr); + + retval = divert_if.ll_cmd(&cs->ics); /* excute command */ + + if (!retval) + { cs->prev = NULL; + save_flags(flags); + cli(); + cs->next = divert_head; + divert_head = cs; + restore_flags(flags); + } + else + kfree(cs); + return(retval); +} /* cf_command */ + + +/****************************************/ +/* handle a external deflection command */ +/****************************************/ +int deflect_extern_action(u_char cmd, ulong callid, char *to_nr) +{ struct call_struc *cs; + isdn_ctrl ic; + int flags; + int i; + + if ((cmd & 0x7F) > 2) return(-EINVAL); /* invalid command */ + cs = divert_head; /* start of parameter list */ + while (cs) + { if (cs->divert_id == callid) break; /* found */ + cs = cs->next; + } /* search entry */ + if (!cs) return(-EINVAL); /* invalid callid */ + + ic.driver = cs->ics.driver; + ic.arg = cs->ics.arg; + i = -EINVAL; + if (cs->akt_state == DEFLECT_AUTODEL) return(i); /* no valid call */ + switch (cmd & 0x7F) + { case 0: /* hangup */ + del_timer(&cs->timer); + ic.command = ISDN_CMD_HANGUP; + i = divert_if.ll_cmd(&ic); + save_flags(flags); + cli(); + cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ + cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); + add_timer(&cs->timer); + restore_flags(flags); + break; + + case 1: /* alert */ + if (cs->akt_state == DEFLECT_ALERT) return(0); + cmd &= 0x7F; /* never wait */ + del_timer(&cs->timer); + ic.command = ISDN_CMD_ALERT; + if ((i = divert_if.ll_cmd(&ic))) + { save_flags(flags); + cli(); + cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ + cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); + add_timer(&cs->timer); + restore_flags(flags); + } + else + cs->akt_state = DEFLECT_ALERT; + break; + + case 2: /* redir */ + del_timer(&cs->timer); + strcpy(cs->ics.parm.setup.phone, to_nr); + strcpy(cs->ics.parm.setup.eazmsn, "Testtext manual"); + ic.command = ISDN_CMD_REDIR; + if ((i = divert_if.ll_cmd(&ic))) + { save_flags(flags); + cli(); + cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ + cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); + add_timer(&cs->timer); + restore_flags(flags); + } + else + cs->akt_state = DEFLECT_ALERT; + break; + + } /* switch */ + return(i); +} /* deflect_extern_action */ + +/********************************/ +/* insert a new rule before idx */ +/********************************/ +int insertrule(int idx, divert_rule *newrule) +{ struct deflect_struc *ds,*ds1; + int flags; + + if (!(ds = (struct deflect_struc *) kmalloc(sizeof(struct deflect_struc), + GFP_KERNEL))) + return(-ENOMEM); /* no memory */ + + ds->rule = *newrule; /* set rule */ + + save_flags(flags); + cli(); + + if (idx >= 0) + { ds1 = table_head; + while ((ds1) && (idx > 0)) + { idx--; + ds1 = ds1->next; + } + if (!ds1) idx = -1; + } + + if (idx < 0) + { ds->prev = table_tail; /* previous entry */ + ds->next = NULL; /* end of chain */ + if (ds->prev) + ds->prev->next = ds; /* last forward */ + else + table_head = ds; /* is first entry */ + table_tail = ds; /* end of queue */ + } + else + { ds->next = ds1; /* next entry */ + ds->prev = ds1->prev; /* prev entry */ + ds1->prev = ds; /* backward chain old element */ + if (!ds->prev) + table_head = ds; /* first element */ + } + + restore_flags(flags); + return(0); +} /* insertrule */ + +/***********************************/ +/* delete the rule at position idx */ +/***********************************/ +int deleterule(int idx) +{ struct deflect_struc *ds,*ds1; + int flags; + + if (idx < 0) + { save_flags(flags); + cli(); + ds = table_head; + table_head = NULL; + table_tail = NULL; + restore_flags(flags); + while (ds) + { ds1 = ds; + ds = ds->next; + kfree(ds1); + } + return(0); + } + + save_flags(flags); + cli(); + ds = table_head; + + while ((ds) && (idx > 0)) + { idx--; + ds = ds->next; + } + + if (!ds) + { restore_flags(flags); + return(-EINVAL); + } + + if (ds->next) + ds->next->prev = ds->prev; /* backward chain */ + else + table_tail = ds->prev; /* end of chain */ + + if (ds->prev) + ds->prev->next = ds->next; /* forward chain */ + else + table_head = ds->next; /* start of chain */ + + restore_flags(flags); + kfree(ds); + return(0); +} /* deleterule */ + +/*******************************************/ +/* get a pointer to a specific rule number */ +/*******************************************/ +divert_rule *getruleptr(int idx) +{ struct deflect_struc *ds = table_head; + + if (idx < 0) return(NULL); + while ((ds) && (idx >= 0)) + { if (!(idx--)) + { return(&ds->rule); + break; + } + ds = ds->next; + } + return(NULL); +} /* getruleptr */ + +/*************************************************/ +/* called from common module on an incoming call */ +/*************************************************/ +int isdn_divert_icall(isdn_ctrl *ic) +{ int retval = 0; + int flags; + struct call_struc *cs = NULL; + struct deflect_struc *dv; + char *p,*p1; + u_char accept; + + /* first check the internal deflection table */ + for (dv = table_head; dv ; dv = dv->next ) + { /* scan table */ + if (((dv->rule.callopt == 1) && (ic->command == ISDN_STAT_ICALLW)) || + ((dv->rule.callopt == 2) && (ic->command == ISDN_STAT_ICALL))) + continue; /* call option check */ + if (!(dv->rule.drvid & (1L << ic->driver))) + continue; /* driver not matching */ + if ((dv->rule.si1) && (dv->rule.si1 != ic->parm.setup.si1)) + continue; /* si1 not matching */ + if ((dv->rule.si2) && (dv->rule.si2 != ic->parm.setup.si2)) + continue; /* si2 not matching */ + + p = dv->rule.my_msn; + p1 = ic->parm.setup.eazmsn; + accept = 0; + while (*p) + { /* complete compare */ + if (*p == '-') + { accept = 1; /* call accepted */ + break; + } + if (*p++ != *p1++) + break; /* not accepted */ + if ((!*p) && (!*p1)) + accept = 1; + } /* complete compare */ + if (!accept) continue; /* not accepted */ + + if ((strcmp(dv->rule.caller,"0")) || (ic->parm.setup.phone[0])) + { p = dv->rule.caller; + p1 = ic->parm.setup.phone; + accept = 0; + while (*p) + { /* complete compare */ + if (*p == '-') + { accept = 1; /* call accepted */ + break; + } + if (*p++ != *p1++) + break; /* not accepted */ + if ((!*p) && (!*p1)) + accept = 1; + } /* complete compare */ + if (!accept) continue; /* not accepted */ + } + + switch (dv->rule.action) + { case DEFLECT_IGNORE: + return(0); + break; + + case DEFLECT_ALERT: + case DEFLECT_PROCEED: + case DEFLECT_REPORT: + case DEFLECT_REJECT: + if (dv->rule.action == DEFLECT_PROCEED) + if ((!if_used) || ((!extern_wait_max) && (!dv->rule.waittime))) + return(0); /* no external deflection needed */ + if (!(cs = (struct call_struc *) kmalloc(sizeof(struct call_struc), GFP_ATOMIC))) + return(0); /* no memory */ + init_timer(&cs->timer); + cs->info[0] = '\0'; + cs->timer.function = deflect_timer_expire; + cs->timer.data = (ulong) cs; /* pointer to own structure */ + + cs->ics = *ic; /* copy incoming data */ + if (!cs->ics.parm.setup.phone[0]) strcpy(cs->ics.parm.setup.phone,"0"); + if (!cs->ics.parm.setup.eazmsn[0]) strcpy(cs->ics.parm.setup.eazmsn,"0"); + cs->ics.parm.setup.screen = dv->rule.screen; + if (dv->rule.waittime) + cs->timer.expires = jiffies + (HZ * dv->rule.waittime); + else + if (dv->rule.action == DEFLECT_PROCEED) + cs->timer.expires = jiffies + (HZ * extern_wait_max); + else + cs->timer.expires = 0; + cs->akt_state = dv->rule.action; + save_flags(flags); + cli(); + cs->divert_id = next_id++; /* new sequence number */ + restore_flags(flags); + cs->prev = NULL; + if (cs->akt_state == DEFLECT_ALERT) + { strcpy(cs->deflect_dest,dv->rule.to_nr); + if (!cs->timer.expires) + { strcpy(ic->parm.setup.eazmsn,"Testtext direkt"); + ic->parm.setup.screen = dv->rule.screen; + strcpy(ic->parm.setup.phone,dv->rule.to_nr); + cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ + cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); + retval = 4; + } + else + retval = 1; /* alerting */ + } + else + { cs->deflect_dest[0] = '\0'; + retval = 3; /* only proceed */ + } + sprintf(cs->info,"%d 0x%lx %s %s %s %s 0x%x 0x%x %d %d %s\n", + cs->akt_state, + cs->divert_id, + divert_if.drv_to_name(cs->ics.driver), + (ic->command == ISDN_STAT_ICALLW) ? "1":"0", + cs->ics.parm.setup.phone, + cs->ics.parm.setup.eazmsn, + cs->ics.parm.setup.si1, + cs->ics.parm.setup.si2, + cs->ics.parm.setup.screen, + dv->rule.waittime, + cs->deflect_dest); + if ((dv->rule.action == DEFLECT_REPORT) || + (dv->rule.action == DEFLECT_REJECT)) + { put_info_buffer(cs->info); + kfree(cs); /* remove */ + return((dv->rule.action == DEFLECT_REPORT) ? 0:2); /* nothing to do */ + } + break; + + default: + return(0); /* ignore call */ + break; + } /* switch action */ + break; + } /* scan_table */ + + if (cs) + { cs->prev = NULL; + save_flags(flags); + cli(); + cs->next = divert_head; + divert_head = cs; + if (cs->timer.expires) add_timer(&cs->timer); + restore_flags(flags); + + put_info_buffer(cs->info); + return(retval); + } + else + return(0); +} /* isdn_divert_icall */ + + +void deleteprocs(void) +{ struct call_struc *cs, *cs1; + int flags; + + save_flags(flags); + cli(); + cs = divert_head; + divert_head = NULL; + while (cs) + { del_timer(&cs->timer); + cs1 = cs; + cs = cs->next; + kfree(cs1); + } + restore_flags(flags); +} /* deleteprocs */ + +/****************************************************/ +/* put a address including address type into buffer */ +/****************************************************/ +int put_address(char *st, u_char *p, int len) +{ u_char retval = 0; + u_char adr_typ = 0; /* network standard */ + + if (len < 2) return(retval); + if (*p == 0xA1) + { retval = *(++p) + 2; /* total length */ + if (retval > len) return(0); /* too short */ + len = retval - 2; /* remaining length */ + if (len < 3) return(0); + if ((*(++p) != 0x0A) || (*(++p) != 1)) return(0); + adr_typ = *(++p); + len -= 3; + p++; + if (len < 2) return(0); + if (*p++ != 0x12) return(0); + if (*p > len) return(0); /* check number length */ + len = *p++; + } + else + if (*p == 0x80) + { retval = *(++p) + 2; /* total length */ + if (retval > len) return(0); + len = retval - 2; + p++; + } + else + return(0); /* invalid address information */ + + sprintf(st,"%d ",adr_typ); + st += strlen(st); + if (!len) + *st++ = '-'; + else + while (len--) + *st++ = *p++; + *st = '\0'; + return(retval); +} /* put_address */ + +/*************************************/ +/* report a succesfull interrogation */ +/*************************************/ +int interrogate_success(isdn_ctrl *ic, struct call_struc *cs) +{ char *src = ic->parm.dss1_io.data; + int restlen = ic->parm.dss1_io.datalen; + int cnt = 1; + u_char n,n1; + char st[90], *p, *stp; + + if (restlen < 2) return(-100); /* frame too short */ + if (*src++ != 0x30) return(-101); + if ((n = *src++) > 0x81) return(-102); /* invalid length field */ + restlen -= 2; /* remaining bytes */ + if (n == 0x80) + { if (restlen < 2) return(-103); + if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-104); + restlen -= 2; + } + else + if ( n == 0x81) + { n = *src++; + restlen--; + if (n > restlen) return(-105); + restlen = n; + } + else + if (n > restlen) return(-106); + else + restlen = n; /* standard format */ + if (restlen < 3) return(-107); /* no procedure */ + if ((*src++ != 2) || (*src++ != 1) || (*src++ != 0x0B)) return(-108); + restlen -= 3; + if (restlen < 2) return(-109); /* list missing */ + if (*src == 0x31) + { src++; + if ((n = *src++) > 0x81) return(-110); /* invalid length field */ + restlen -= 2; /* remaining bytes */ + if (n == 0x80) + { if (restlen < 2) return(-111); + if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-112); + restlen -= 2; + } + else + if ( n == 0x81) + { n = *src++; + restlen--; + if (n > restlen) return(-113); + restlen = n; + } + else + if (n > restlen) return(-114); + else + restlen = n; /* standard format */ + } /* result list header */ + + while (restlen >= 2) + { stp = st; + sprintf(stp,"%d 0x%lx %d %s ",DIVERT_REPORT, ic->parm.dss1_io.ll_id, + cnt++,divert_if.drv_to_name(ic->driver)); + stp += strlen(stp); + if (*src++ != 0x30) return(-115); /* invalid enum */ + n = *src++; + restlen -= 2; + if (n > restlen) return(-116); /* enum length wrong */ + restlen -= n; + p = src; /* one entry */ + src += n; + if (!(n1 = put_address(stp,p,n & 0xFF))) continue; + stp += strlen(stp); + p += n1; + n -= n1; + if (n < 6) continue; /* no service and proc */ + if ((*p++ != 0x0A) || (*p++ != 1)) continue; + sprintf(stp," 0x%02x ",(*p++) & 0xFF); + stp += strlen(stp); + if ((*p++ != 0x0A) || (*p++ != 1)) continue; + sprintf(stp,"%d ",(*p++) & 0xFF); + stp += strlen(stp); + n -= 6; + if (n > 2) + { if (*p++ != 0x30) continue; + if (*p > (n-2)) continue; + n = *p++; + if (!(n1 = put_address(stp,p,n & 0xFF))) continue; + stp += strlen(stp); + } + sprintf(stp,"\n"); + put_info_buffer(st); + } /* while restlen */ + if (restlen) return(-117); + return(0); +} /* interrogate_success */ + +/*********************************************/ +/* callback for protocol specific extensions */ +/*********************************************/ +int prot_stat_callback(isdn_ctrl *ic) +{ struct call_struc *cs, *cs1; + int i,flags; + + cs = divert_head; /* start of list */ + cs1 = NULL; + while (cs) + { if (ic->driver == cs->ics.driver) + { switch (cs->ics.arg) + { case DSS1_CMD_INVOKE: + if ((cs->ics.parm.dss1_io.ll_id == ic->parm.dss1_io.ll_id) && + (cs->ics.parm.dss1_io.hl_id == ic->parm.dss1_io.hl_id)) + { switch (ic->arg) + { case DSS1_STAT_INVOKE_ERR: + sprintf(cs->info,"128 0x%lx 0x%x\n", + ic->parm.dss1_io.ll_id, + ic->parm.dss1_io.timeout); + put_info_buffer(cs->info); + break; + + case DSS1_STAT_INVOKE_RES: + switch (cs->ics.parm.dss1_io.proc) + { case 7: + case 8: + put_info_buffer(cs->info); + break; + + case 11: + i = interrogate_success(ic,cs); + if (i) + sprintf(cs->info,"%d 0x%lx %d\n",DIVERT_REPORT, + ic->parm.dss1_io.ll_id,i); + put_info_buffer(cs->info); + break; + + default: + printk(KERN_WARNING "dss1_divert: unknown proc %d\n",cs->ics.parm.dss1_io.proc); + break; + } + + +#if 0 + sprintf(st, "0x%lx 0x%lx",ic->arg, ic->parm.dss1_io.ll_id); + p = st + strlen(st); + p1 = ic->parm.dss1_io.data; + i = ic->parm.dss1_io.datalen; + while ((i > 0) && (p - st < 530)) + { p += sprintf(p," %02x",(*p1++) & 0xFF); + i--; + } + sprintf(p, "\n"); + put_info_buffer(st); +#endif + break; + + default: + printk(KERN_WARNING "dss1_divert unknown invoke answer %lx\n",ic->arg); + break; + } + cs1 = cs; /* remember structure */ + cs = NULL; + continue; /* abort search */ + } /* id found */ + break; + + case DSS1_CMD_INVOKE_ABORT: + printk(KERN_WARNING "dss1_divert unhandled invoke abort\n"); + break; + + default: + printk(KERN_WARNING "dss1_divert unknown cmd 0x%lx\n",cs->ics.arg); + break; + } /* switch ics.arg */ + cs = cs->next; + } /* driver ok */ + } + + if (!cs1) + { printk(KERN_WARNING "dss1_divert unhandled process\n"); + return(0); + } + + if (cs1->ics.driver == -1) + { save_flags(flags); + cli(); + del_timer(&cs1->timer); + if (cs1->prev) + cs1->prev->next = cs1->next; /* forward link */ + else + divert_head = cs1->next; + if (cs1->next) + cs1->next->prev = cs1->prev; /* back link */ + restore_flags(flags); + kfree(cs1); + } + + return(0); +} /* prot_stat_callback */ + + +/***************************/ +/* status callback from HL */ +/***************************/ +int isdn_divert_stat_callback(isdn_ctrl *ic) +{ struct call_struc *cs, *cs1; + int flags, retval; + + retval = -1; + cs = divert_head; /* start of list */ + while (cs) + { if ((ic->driver == cs->ics.driver) && (ic->arg == cs->ics.arg)) + { switch (ic->command) + { case ISDN_STAT_DHUP: + sprintf(cs->info,"129 0x%lx\n",cs->divert_id); + del_timer(&cs->timer); + cs->ics.driver = -1; + break; + + case ISDN_STAT_CAUSE: + sprintf(cs->info,"130 0x%lx %s\n",cs->divert_id,ic->parm.num); + break; + + case ISDN_STAT_REDIR: + sprintf(cs->info,"131 0x%lx\n",cs->divert_id); + del_timer(&cs->timer); + cs->ics.driver = -1; + break; + + default: + sprintf(cs->info,"999 0x%lx 0x%x\n",cs->divert_id,(int)(ic->command)); + break; + } + put_info_buffer(cs->info); + retval = 0; + } + cs1 = cs; + cs = cs->next; + if (cs1->ics.driver == -1) + { + save_flags(flags); + cli(); + if (cs1->prev) + cs1->prev->next = cs1->next; /* forward link */ + else + divert_head = cs1->next; + if (cs1->next) + cs1->next->prev = cs1->prev; /* back link */ + restore_flags(flags); + kfree(cs1); + } + } + return(retval); /* not found */ +} /* isdn_divert_stat_callback */ + + +/********************/ +/* callback from ll */ +/********************/ +int ll_callback(isdn_ctrl *ic) +{ + switch (ic->command) + { case ISDN_STAT_ICALL: + case ISDN_STAT_ICALLW: + return(isdn_divert_icall(ic)); + break; + + case ISDN_STAT_PROT: + if ((ic->arg & 0xFF) == ISDN_PTYPE_EURO) + { if (ic->arg != DSS1_STAT_INVOKE_BRD) + return(prot_stat_callback(ic)); + else + return(0); /* DSS1 invoke broadcast */ + } + else + return(-1); /* protocol not euro */ + + default: + return(isdn_divert_stat_callback(ic)); + } +} /* ll_callback */ + diff --git a/drivers/isdn/divert/isdn_divert.h b/drivers/isdn/divert/isdn_divert.h new file mode 100644 index 000000000000..7a2b5cf08a8a --- /dev/null +++ b/drivers/isdn/divert/isdn_divert.h @@ -0,0 +1,159 @@ +/* + * $Id: isdn_divert.h,v 1.2 1999/07/04 21:37:33 werner Exp $ + * + * Header for the diversion supplementary ioctl interface. + * + * Copyright 1998 by Werner Cornelius (werner@ikt.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: isdn_divert.h,v $ + * Revision 1.2 1999/07/04 21:37:33 werner + * Ported from kernel version 2.0 + * + * + * + */ + + + +#include +#include +#include + +/******************************************/ +/* IOCTL codes for interface to user prog */ +/******************************************/ +#define DIVERT_IIOC_VERSION 0x01 /* actual version */ +#define IIOCGETVER _IO('I', 1) /* get version of interface */ +#define IIOCGETDRV _IO('I', 2) /* get driver number */ +#define IIOCGETNAM _IO('I', 3) /* get driver name */ +#define IIOCGETRULE _IO('I', 4) /* read one rule */ +#define IIOCMODRULE _IO('I', 5) /* modify/replace a rule */ +#define IIOCINSRULE _IO('I', 6) /* insert/append one rule */ +#define IIOCDELRULE _IO('I', 7) /* delete a rule */ +#define IIOCDODFACT _IO('I', 8) /* hangup/reject/alert/immediately deflect a call */ +#define IIOCDOCFACT _IO('I', 9) /* activate control forwarding in PBX */ +#define IIOCDOCFDIS _IO('I',10) /* deactivate control forwarding in PBX */ +#define IIOCDOCFINT _IO('I',11) /* interrogate control forwarding in PBX */ + +/*************************************/ +/* states reported through interface */ +/*************************************/ +#define DEFLECT_IGNORE 0 /* ignore incoming call */ +#define DEFLECT_REPORT 1 /* only report */ +#define DEFLECT_PROCEED 2 /* deflect when externally triggered */ +#define DEFLECT_ALERT 3 /* alert and deflect after delay */ +#define DEFLECT_REJECT 4 /* reject immediately */ +#define DIVERT_ACTIVATE 5 /* diversion activate */ +#define DIVERT_DEACTIVATE 6 /* diversion deactivate */ +#define DIVERT_REPORT 7 /* interrogation result */ +#define DEFLECT_AUTODEL 255 /* only for internal use */ + +#define DEFLECT_ALL_IDS 0xFFFFFFFF /* all drivers selected */ + +typedef struct + { ulong drvid; /* driver ids, bit mapped */ + char my_msn[35]; /* desired msn, subaddr allowed */ + char caller[35]; /* caller id, partial string with * + subaddr allowed */ + char to_nr[35]; /* deflected to number incl. subaddress */ + u_char si1,si2; /* service indicators, si1=bitmask, si1+2 0 = all */ + u_char screen; /* screening: 0 = no info, 1 = info, 2 = nfo with nr */ + u_char callopt; /* option for call handling: + 0 = all calls + 1 = only non waiting calls + 2 = only waiting calls */ + u_char action; /* desired action: + 0 = don't report call -> ignore + 1 = report call, do not allow/proceed for deflection + 2 = report call, send proceed, wait max waittime secs + 3 = report call, alert and deflect after waittime + 4 = report call, reject immediately + actions 1-2 only take place if interface is opened + */ + u_char waittime; /* maximum wait time for proceeding */ + } divert_rule; + +typedef union + { int drv_version; /* return of driver version */ + struct + { int drvid; /* id of driver */ + char drvnam[30]; /* name of driver */ + } getid; + struct + { int ruleidx; /* index of rule */ + divert_rule rule; /* rule parms */ + } getsetrule; + struct + { u_char subcmd; /* 0 = hangup/reject, + 1 = alert, + 2 = deflect */ + ulong callid; /* id of call delivered by ascii output */ + char to_nr[35]; /* destination when deflect, + else uus1 string (maxlen 31), + data from rule used if empty */ + } fwd_ctrl; + struct + { int drvid; /* id of driver */ + u_char cfproc; /* cfu = 0, cfb = 1, cfnr = 2 */ + ulong procid; /* process id returned when no error */ + u_char service; /* basically coded service, 0 = all */ + char msn[25]; /* desired msn, empty = all */ + char fwd_nr[35];/* forwarded to number + subaddress */ + } cf_ctrl; + } divert_ioctl; + +#ifdef __KERNEL__ + +#include +#include + +#define AUTODEL_TIME 30 /* timeout in s to delete internal entrys */ + +/**************************************************/ +/* structure keeping ascii info for device output */ +/**************************************************/ +struct divert_info + { struct divert_info *next; + ulong usage_cnt; /* number of files still to work */ + char info_start[2]; /* info string start */ + }; + + +/**************/ +/* Prototypes */ +/**************/ +extern ulong if_used; /* number of interface users */ +extern int divert_dev_deinit(void); +extern int divert_dev_init(void); +extern void put_info_buffer(char *); +extern int ll_callback(isdn_ctrl *); +extern isdn_divert_if divert_if; +extern divert_rule *getruleptr(int); +extern int insertrule(int, divert_rule *); +extern int deleterule(int); +extern void deleteprocs(void); +extern int deflect_extern_action(u_char, ulong, char *); +extern int cf_command(int, int, u_char, char *, u_char, char *, ulong *); + +#endif __KERNEL__ + + + + + + + + diff --git a/drivers/isdn/eicon/eicon.h b/drivers/isdn/eicon/eicon.h index 9552d2b54a43..88f408cd148f 100644 --- a/drivers/isdn/eicon/eicon.h +++ b/drivers/isdn/eicon/eicon.h @@ -1,4 +1,4 @@ -/* $Id: eicon.h,v 1.5 1999/03/29 11:19:41 armin Exp $ +/* $Id: eicon.h,v 1.8 1999/07/25 15:12:01 armin Exp $ * * ISDN low-level module for Eicon.Diehl active ISDN-Cards. * @@ -21,6 +21,19 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon.h,v $ + * Revision 1.8 1999/07/25 15:12:01 armin + * fix of some debug logs. + * enabled ISA-cards option. + * + * Revision 1.7 1999/07/11 17:16:23 armin + * Bugfixes in queue handling. + * Added DSP-DTMF decoder functions. + * Reorganized ack_handler. + * + * Revision 1.6 1999/06/09 19:31:24 armin + * Wrong PLX size for request_region() corrected. + * Added first MCA code from Erik Weber. + * * Revision 1.5 1999/03/29 11:19:41 armin * I/O stuff now in seperate file (eicon_io.c) * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented. @@ -100,6 +113,7 @@ typedef struct eicon_cdef { #define EICON_ISA_BOOT_NORMAL 2 /* Struct for downloading protocol via ioctl for ISA cards */ +/* same struct for downloading protocol via ioctl for MCA cards */ typedef struct { /* start-up parameters */ unsigned char tei; @@ -156,6 +170,7 @@ typedef struct { /* Data for downloading protocol via ioctl */ typedef union { eicon_isa_codebuf isa; + eicon_isa_codebuf mca; eicon_pci_codebuf pci; } eicon_codebuf; @@ -333,19 +348,39 @@ typedef struct { __u16 ref; /* saved reference */ } entity; +#define FAX_MAX_SCANLINE 256 + +typedef struct { + __u8 PrevObject; + __u8 NextObject; + __u8 abLine[FAX_MAX_SCANLINE]; + __u8 abFrame[FAX_MAX_SCANLINE]; + unsigned int LineLen; + unsigned int LineDataLen; + __u32 LineData; + unsigned int NullBytesPos; + __u8 NullByteExist; + int PageCount; + __u8 Dle; + __u8 Eop; +} eicon_ch_fax_buf; typedef struct { int No; /* Channel Number */ unsigned short callref; /* Call Reference */ unsigned short fsm_state; /* Current D-Channel state */ unsigned short eazmask; /* EAZ-Mask for this Channel */ - unsigned int queued; /* User-Data Bytes in TX queue */ - unsigned int waitq; /* User-Data Bytes in wait queue */ - unsigned int waitpq; /* User-Data Bytes in packet queue */ + int queued; /* User-Data Bytes in TX queue */ + int waitq; /* User-Data Bytes in wait queue */ + int waitpq; /* User-Data Bytes in packet queue */ unsigned short plci; unsigned short ncci; unsigned char l2prot; /* Layer 2 protocol */ unsigned char l3prot; /* Layer 3 protocol */ +#ifdef CONFIG_ISDN_TTY_FAX + T30_s *fax; /* pointer to fax data in LL */ + eicon_ch_fax_buf fax2; /* fax related struct */ +#endif entity e; /* Entity */ char cpn[32]; /* remember cpn */ char oad[32]; /* remember oad */ @@ -392,14 +427,10 @@ typedef struct { #define EICON_LOCK_TX 0 #define EICON_LOCK_RX 1 -typedef struct { - int dummy; -} eicon_mca_card; - typedef union { eicon_isa_card isa; eicon_pci_card pci; - eicon_mca_card mca; + eicon_isa_card mca; } eicon_hwif; typedef struct { @@ -465,6 +496,9 @@ typedef struct eicon_card { char *status_buf_end; isdn_if interface; /* Interface to upper layer */ char regname[35]; /* Name used for request_region */ +#ifdef CONFIG_MCA + int mca_slot; /* # of cards MCA slot */ +#endif } eicon_card; /* -----------------------------------------------------------** @@ -521,6 +555,12 @@ extern void eicon_io_transmit(eicon_card *card); extern void eicon_irq(int irq, void *dev_id, struct pt_regs *regs); extern void eicon_io_rcv_dispatch(eicon_card *ccard); extern void eicon_io_ack_dispatch(eicon_card *ccard); +#ifdef CONFIG_MCA +extern int eicon_mca_find_card(int, int, int, char *); +extern int eicon_mca_probe(int, int, int, int, char *); +extern int eicon_info(char *, int , void *); +#endif /* CONFIG_MCA */ + extern ulong DebugVar; #endif /* __KERNEL__ */ diff --git a/drivers/isdn/eicon/eicon_dsp.h b/drivers/isdn/eicon/eicon_dsp.h index 94a4595c8c10..9ffbd9bdbed3 100644 --- a/drivers/isdn/eicon/eicon_dsp.h +++ b/drivers/isdn/eicon/eicon_dsp.h @@ -1,4 +1,4 @@ -/* $Id: eicon_dsp.h,v 1.2 1999/03/29 11:19:42 armin Exp $ +/* $Id: eicon_dsp.h,v 1.4 1999/07/25 15:12:02 armin Exp $ * * ISDN lowlevel-module for Eicon.Diehl active cards. * DSP definitions @@ -21,6 +21,15 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_dsp.h,v $ + * Revision 1.4 1999/07/25 15:12:02 armin + * fix of some debug logs. + * enabled ISA-cards option. + * + * Revision 1.3 1999/07/11 17:16:24 armin + * Bugfixes in queue handling. + * Added DSP-DTMF decoder functions. + * Reorganized ack_handler. + * * Revision 1.2 1999/03/29 11:19:42 armin * I/O stuff now in seperate file (eicon_io.c) * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented. @@ -31,7 +40,7 @@ * */ -#ifndef DSP_H +#ifndef DSP_H #define DSP_H #define DSP_UDATA_REQUEST_RECONFIGURE 0 @@ -264,6 +273,10 @@ parameters: tone duration (ms) gap duration (ms) */ +typedef struct enable_dtmf_s { + __u16 tone; + __u16 gap; +} enable_dtmf_s; #define DSP_UDATA_REQUEST_DISABLE_DTMF_RECEIVER 18 /* @@ -300,5 +313,107 @@ returns: - none - */ +/* ============= FAX ================ */ + +#define EICON_FAXID_LEN 20 + +typedef struct eicon_t30_s { + __u8 code; + __u8 rate; + __u8 resolution; + __u8 format; + __u8 pages_low; + __u8 pages_high; + __u8 atf; + __u8 control_bits_low; + __u8 control_bits_high; + __u8 feature_bits_low; + __u8 feature_bits_high; + __u8 universal_5; + __u8 universal_6; + __u8 universal_7; + __u8 station_id_len; + __u8 head_line_len; + __u8 station_id[EICON_FAXID_LEN]; +/* __u8 head_line[]; */ +} eicon_t30_s; + + /* EDATA transmit messages */ +#define EDATA_T30_DIS 0x01 +#define EDATA_T30_FTT 0x02 +#define EDATA_T30_MCF 0x03 + + /* EDATA receive messages */ +#define EDATA_T30_DCS 0x81 +#define EDATA_T30_TRAIN_OK 0x82 +#define EDATA_T30_EOP 0x83 +#define EDATA_T30_MPS 0x84 +#define EDATA_T30_EOM 0x85 +#define EDATA_T30_DTC 0x86 + +#define T30_FORMAT_SFF 0 +#define T30_FORMAT_ASCII 1 +#define T30_FORMAT_COUNT 2 + +#define T30_CONTROL_BIT_DISABLE_FINE 0x0001 +#define T30_CONTROL_BIT_ENABLE_ECM 0x0002 +#define T30_CONTROL_BIT_ECM_64_BYTES 0x0004 +#define T30_CONTROL_BIT_ENABLE_2D_CODING 0x0008 +#define T30_CONTROL_BIT_ENABLE_T6_CODING 0x0010 +#define T30_CONTROL_BIT_ENABLE_UNCOMPR 0x0020 +#define T30_CONTROL_BIT_ACCEPT_POLLING 0x0040 +#define T30_CONTROL_BIT_REQUEST_POLLING 0x0080 +#define T30_CONTROL_BIT_MORE_DOCUMENTS 0x0100 + +#define T30_CONTROL_BIT_ALL_FEATURES\ + (T30_CONTROL_BIT_ENABLE_ECM | T30_CONTROL_BIT_ENABLE_2D_CODING |\ + T30_CONTROL_BIT_ENABLE_T6_CODING | T30_CONTROL_BIT_ENABLE_UNCOMPR) + +#define T30_FEATURE_BIT_FINE 0x0001 +#define T30_FEATURE_BIT_ECM 0x0002 +#define T30_FEATURE_BIT_ECM_64_BYTES 0x0004 +#define T30_FEATURE_BIT_2D_CODING 0x0008 +#define T30_FEATURE_BIT_T6_CODING 0x0010 +#define T30_FEATURE_BIT_UNCOMPR_ENABLED 0x0020 +#define T30_FEATURE_BIT_POLLING 0x0040 + +#define FAX_OBJECT_DOCU 1 +#define FAX_OBJECT_PAGE 2 +#define FAX_OBJECT_LINE 3 + +#define T4_EOL 0x800 +#define T4_EOL_BITSIZE 12 +#define T4_EOL_DWORD (T4_EOL << (32 - T4_EOL_BITSIZE)) +#define T4_EOL_MASK_DWORD ((__u32) -1 << (32 - T4_EOL_BITSIZE)) + +#define SFF_LEN_FLD_SIZE 3 + +#define _DLE_ 0x10 +#define _ETX_ 0x03 + +typedef struct eicon_sff_dochead { + __u32 id __attribute__ ((packed)); + __u8 version __attribute__ ((packed)); + __u8 reserved1 __attribute__ ((packed)); + __u16 userinfo __attribute__ ((packed)); + __u16 pagecount __attribute__ ((packed)); + __u16 off1pagehead __attribute__ ((packed)); + __u32 offnpagehead __attribute__ ((packed)); + __u32 offdocend __attribute__ ((packed)); +} eicon_sff_dochead; + +typedef struct eicon_sff_pagehead { + __u8 pageheadid __attribute__ ((packed)); + __u8 pageheadlen __attribute__ ((packed)); + __u8 resvert __attribute__ ((packed)); + __u8 reshoriz __attribute__ ((packed)); + __u8 coding __attribute__ ((packed)); + __u8 reserved2 __attribute__ ((packed)); + __u16 linelength __attribute__ ((packed)); + __u16 pagelength __attribute__ ((packed)); + __u32 offprevpage __attribute__ ((packed)); + __u32 offnextpage __attribute__ ((packed)); +} eicon_sff_pagehead; + #endif /* DSP_H */ diff --git a/drivers/isdn/eicon/eicon_idi.c b/drivers/isdn/eicon/eicon_idi.c index a28f316c113e..412eb46ba9d1 100644 --- a/drivers/isdn/eicon/eicon_idi.c +++ b/drivers/isdn/eicon/eicon_idi.c @@ -1,4 +1,4 @@ -/* $Id: eicon_idi.c,v 1.9 1999/03/29 11:19:42 armin Exp $ +/* $Id: eicon_idi.c,v 1.11 1999/07/25 15:12:03 armin Exp $ * * ISDN lowlevel-module for Eicon.Diehl active cards. * IDI interface @@ -21,6 +21,15 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_idi.c,v $ + * Revision 1.11 1999/07/25 15:12:03 armin + * fix of some debug logs. + * enabled ISA-cards option. + * + * Revision 1.10 1999/07/11 17:16:24 armin + * Bugfixes in queue handling. + * Added DSP-DTMF decoder functions. + * Reorganized ack_handler. + * * Revision 1.9 1999/03/29 11:19:42 armin * I/O stuff now in seperate file (eicon_io.c) * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented. @@ -68,7 +77,7 @@ #undef EICON_FULL_SERVICE_OKTETT -char *eicon_idi_revision = "$Revision: 1.9 $"; +char *eicon_idi_revision = "$Revision: 1.11 $"; eicon_manifbuf *manbuf; @@ -86,11 +95,15 @@ static char HLC_faxg3[2] = { 0x91, 0x84 }; int eicon_idi_manage_assign(eicon_card *card); int eicon_idi_manage_remove(eicon_card *card); +int idi_fill_in_T30(eicon_chan *chan, unsigned char *buffer); int idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan) { int l = 0; + int tmp; + + tmp = 0; if (!signet) { /* Signal Layer */ reqbuf->XBuffer.P[l++] = CAI; @@ -133,10 +146,25 @@ idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan) case ISDN_PROTO_L2_MODEM: reqbuf->XBuffer.P[l++] = 2; break; + case ISDN_PROTO_L2_FAX: + if (chan->fsm_state == EICON_STATE_IWAIT) + reqbuf->XBuffer.P[l++] = 3; /* autoconnect on incoming */ + else + reqbuf->XBuffer.P[l++] = 2; + break; default: reqbuf->XBuffer.P[l++] = 1; } switch(chan->l3prot) { + case ISDN_PROTO_L3_FAX: +#ifdef CONFIG_ISDN_TTY_FAX + reqbuf->XBuffer.P[l++] = 6; + reqbuf->XBuffer.P[l++] = NLC; + tmp = idi_fill_in_T30(chan, &reqbuf->XBuffer.P[l+1]); + reqbuf->XBuffer.P[l++] = tmp; + l += tmp; + break; +#endif case ISDN_PROTO_L3_TRANS: default: reqbuf->XBuffer.P[l++] = 4; @@ -210,6 +238,20 @@ idi_call_res_req(eicon_REQ *reqbuf, eicon_chan *chan) reqbuf->XBuffer.P[6] = 128; reqbuf->XBuffer.P[7] = 0; break; + case ISDN_PROTO_L2_FAX: + reqbuf->XBuffer.P[2] = 0x10; + reqbuf->XBuffer.P[3] = 0; + reqbuf->XBuffer.P[4] = 0; + reqbuf->XBuffer.P[5] = 0; + reqbuf->XBuffer.P[6] = 128; + reqbuf->XBuffer.P[7] = 0; + break; + case ISDN_PROTO_L2_TRANS: + switch(chan->l3prot) { + case ISDN_PROTO_L3_TRANSDSP: + reqbuf->XBuffer.P[2] = 22; /* DTMF, audio events on */ + } + break; } reqbuf->XBuffer.P[8] = 0; reqbuf->XBuffer.length = l; @@ -222,8 +264,8 @@ idi_call_res_req(eicon_REQ *reqbuf, eicon_chan *chan) int idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer) { - struct sk_buff *skb; - struct sk_buff *skb2; + struct sk_buff *skb = 0; + struct sk_buff *skb2 = 0; eicon_REQ *reqbuf; eicon_chan_ptr *chan2; @@ -232,7 +274,11 @@ idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer) if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No); + printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in do_req()\n", chan->No); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -241,7 +287,7 @@ idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer) reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ)); if (DebugVar & 8) - printk(KERN_DEBUG "idi_req: Ch%d: 0x%02x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig"); + printk(KERN_DEBUG "idi_req: Ch%d: req %x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig"); if (layer) cmd |= 0x700; switch(cmd) { case ASSIGN: @@ -282,6 +328,8 @@ idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer) default: if (DebugVar & 1) printk(KERN_ERR "idi_req: Ch%d: Unknown request\n", chan->No); + dev_kfree_skb(skb); + dev_kfree_skb(skb2); return(-1); } @@ -358,6 +406,9 @@ idi_hangup(eicon_card *card, eicon_chan *chan) chan->fsm_state = EICON_STATE_NULL; if (DebugVar & 8) printk(KERN_DEBUG"idi_req: Ch%d: Hangup\n", chan->No); +#ifdef CONFIG_ISDN_TTY_FAX + chan->fax = 0; +#endif return(0); } @@ -389,7 +440,11 @@ idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone, if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No); + printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in connect_req()\n", chan->No); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -472,6 +527,20 @@ idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone, reqbuf->XBuffer.P[l-2] = 128; reqbuf->XBuffer.P[l-1] = 0; break; + case ISDN_PROTO_L2_FAX: + reqbuf->XBuffer.P[l-6] = 0x10; + reqbuf->XBuffer.P[l-5] = 0; + reqbuf->XBuffer.P[l-4] = 0; + reqbuf->XBuffer.P[l-3] = 0; + reqbuf->XBuffer.P[l-2] = 128; + reqbuf->XBuffer.P[l-1] = 0; + break; + case ISDN_PROTO_L2_TRANS: + switch(chan->l3prot) { + case ISDN_PROTO_L3_TRANSDSP: + reqbuf->XBuffer.P[l-6] = 22; /* DTMF, audio events on */ + } + break; } reqbuf->XBuffer.P[l++] = 0; /* end */ @@ -813,6 +882,81 @@ idi_bc2si(unsigned char *bc, unsigned char *hlc, unsigned char *si1, unsigned ch } } + +int +idi_send_udata(eicon_card *card, eicon_chan *chan, int UReq, u_char *buffer, int len) +{ + struct sk_buff *skb; + struct sk_buff *skb2; + eicon_REQ *reqbuf; + eicon_chan_ptr *chan2; + + if ((chan->fsm_state == EICON_STATE_NULL) || (chan->fsm_state == EICON_STATE_LISTEN)) { + if (DebugVar & 1) + printk(KERN_DEBUG"idi_snd: Ch%d: send udata on state %d !\n", chan->No, chan->fsm_state); + return -ENODEV; + } + if (DebugVar & 8) + printk(KERN_DEBUG"idi_snd: Ch%d: udata 0x%x: %d %d %d %d\n", chan->No, + UReq, buffer[0], buffer[1], buffer[2], buffer[3]); + + skb = alloc_skb(sizeof(eicon_REQ) + len + 1, GFP_ATOMIC); + skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); + + if ((!skb) || (!skb2)) { + if (DebugVar & 1) + printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in send_udata()\n", chan->No); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); + return -ENOMEM; + } + + chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr)); + chan2->ptr = chan; + + reqbuf = (eicon_REQ *)skb_put(skb, 1 + len + sizeof(eicon_REQ)); + + reqbuf->Req = IDI_N_UDATA; + reqbuf->ReqCh = 0; + reqbuf->ReqId = 1; + + reqbuf->XBuffer.length = len + 1; + reqbuf->XBuffer.P[0] = UReq; + memcpy(&reqbuf->XBuffer.P[1], buffer, len); + reqbuf->Reference = 1; /* Net Entity */ + + skb_queue_tail(&chan->e.X, skb); + skb_queue_tail(&card->sndq, skb2); + eicon_schedule_tx(card); + return (0); +} + +void +idi_audio_cmd(eicon_card *ccard, eicon_chan *chan, int cmd, u_char *value) +{ + u_char buf[6]; + struct enable_dtmf_s *dtmf_buf = (struct enable_dtmf_s *)buf; + + memset(buf, 0, 6); + switch(cmd) { + case ISDN_AUDIO_SETDD: + if (value[0]) { + dtmf_buf->tone = (__u16) (value[1] * 5); + dtmf_buf->gap = (__u16) (value[1] * 5); + idi_send_udata(ccard, chan, + DSP_UDATA_REQUEST_ENABLE_DTMF_RECEIVER, + buf, 4); + } else { + idi_send_udata(ccard, chan, + DSP_UDATA_REQUEST_DISABLE_DTMF_RECEIVER, + buf, 0); + } + break; + } +} + void idi_parse_udata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int len) { @@ -822,6 +966,9 @@ idi_parse_udata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int {"", "V.21", "V.23", "V.22", "V.22bis", "V.32bis", "V.34", "V.8", "Bell 212A", "Bell 103", "V.29 Leased", "V.33 Leased", "V.90", "V.21 CH2", "V.27ter", "V.29", "V.33", "V.17"}; + static u_char dtmf_code[] = { + '1','4','7','*','2','5','8','0','3','6','9','#','A','B','C','D' + }; switch (buffer[0]) { case DSP_UDATA_INDICATION_SYNC: @@ -863,6 +1010,17 @@ idi_parse_udata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int if (DebugVar & 8) printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DISCONNECT cause %d\n", chan->No, buffer[1]); break; + case DSP_UDATA_INDICATION_DTMF_DIGITS_RECEIVED: + if (DebugVar & 8) + printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DTMF_REC '%c'\n", chan->No, + dtmf_code[buffer[1]]); + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_AUDIO; + cmd.parm.num[0] = ISDN_AUDIO_DTMF; + cmd.parm.num[1] = dtmf_code[buffer[1]]; + cmd.arg = chan->No; + ccard->interface.statcallb(&cmd); + break; default: if (DebugVar & 8) printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED UDATA Indication 0x%02x\n", chan->No, buffer[0]); @@ -887,7 +1045,7 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) if ((DebugVar & 128) || ((DebugVar & 16) && (ind->Ind != 8))) { - printk(KERN_DEBUG "idi_hdl: Ch%d: Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No, + printk(KERN_DEBUG "idi_hdl: Ch%d: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No, ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length); } @@ -921,6 +1079,9 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) cmd.command = ISDN_STAT_DHUP; ccard->interface.statcallb(&cmd); eicon_idi_listen_req(ccard, chan); +#ifdef CONFIG_ISDN_TTY_FAX + chan->fax = 0; +#endif break; case INDICATE_IND: if (DebugVar & 8) @@ -994,9 +1155,17 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) cmd.command = ISDN_STAT_DCONN; cmd.arg = chan->No; ccard->interface.statcallb(&cmd); - idi_do_req(ccard, chan, IDI_N_CONNECT, 1); + if (chan->l2prot != ISDN_PROTO_L2_FAX) { + idi_do_req(ccard, chan, IDI_N_CONNECT, 1); + } +#ifdef CONFIG_ISDN_TTY_FAX + else { + if (chan->fax) + chan->fax->phase = ISDN_FAX_PHASE_A; + } +#endif } else - idi_hangup(ccard, chan); + idi_hangup(ccard, chan); break; case CALL_CON: if (DebugVar & 8) @@ -1009,6 +1178,12 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) ccard->interface.statcallb(&cmd); idi_do_req(ccard, chan, ASSIGN, 1); idi_do_req(ccard, chan, IDI_N_CONNECT, 1); +#ifdef CONFIG_ISDN_TTY_FAX + if (chan->l2prot == ISDN_PROTO_L2_FAX) { + if (chan->fax) + chan->fax->phase = ISDN_FAX_PHASE_A; + } +#endif } else idi_hangup(ccard, chan); break; @@ -1038,6 +1213,27 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) chan->fsm_state = EICON_STATE_WMCONN; break; } + if (chan->l2prot == ISDN_PROTO_L2_FAX) { +#ifdef CONFIG_ISDN_TTY_FAX + chan->fsm_state = EICON_STATE_ACTIVE; + idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); + if (chan->fax) { + if (chan->fax->phase == ISDN_FAX_PHASE_B) { + idi_fax_send_header(ccard, chan, 2); + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_DCS; + ccard->interface.statcallb(&cmd); + } + } + else { + if (DebugVar & 1) + printk(KERN_DEBUG "idi_ind: N_CONNECT_ACK with NULL fax struct, ERROR\n"); + } +#endif + break; + } chan->fsm_state = EICON_STATE_ACTIVE; cmd.driver = ccard->myid; cmd.command = ISDN_STAT_BCONN; @@ -1048,6 +1244,9 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) if (DebugVar & 16) printk(KERN_DEBUG"idi_ind: Ch%d: N_Connect\n", chan->No); if (chan->e.B2Id) idi_do_req(ccard, chan, IDI_N_CONNECT_ACK, 1); + if (chan->l2prot == ISDN_PROTO_L2_FAX) { + break; + } if (chan->l2prot == ISDN_PROTO_L2_MODEM) { chan->fsm_state = EICON_STATE_WMCONN; break; @@ -1065,6 +1264,12 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) idi_do_req(ccard, chan, IDI_N_DISC_ACK, 1); idi_do_req(ccard, chan, REMOVE, 1); } +#ifdef CONFIG_ISDN_TTY_FAX + if (chan->l2prot == ISDN_PROTO_L2_FAX) { + idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); + idi_fax_hangup(ccard, chan); + } +#endif chan->queued = 0; chan->waitq = 0; chan->waitpq = 0; @@ -1078,6 +1283,12 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) case IDI_N_DISC_ACK: if (DebugVar & 16) printk(KERN_DEBUG"idi_ind: Ch%d: N_DISC_ACK\n", chan->No); +#ifdef CONFIG_ISDN_TTY_FAX + if (chan->l2prot == ISDN_PROTO_L2_FAX) { + idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); + idi_fax_hangup(ccard, chan); + } +#endif break; case IDI_N_DATA_ACK: if (DebugVar & 16) @@ -1087,12 +1298,23 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) skb_pull(skb, sizeof(eicon_IND) - 1); if (DebugVar & 128) printk(KERN_DEBUG"idi_rcv: Ch%d: %d bytes\n", chan->No, skb->len); - ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb); - free_buff = 0; + if (chan->l2prot == ISDN_PROTO_L2_FAX) { +#ifdef CONFIG_ISDN_TTY_FAX + idi_faxdata_rcv(ccard, chan, skb); +#endif + } else { + ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb); + free_buff = 0; + } break; case IDI_N_UDATA: idi_parse_udata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); break; +#ifdef CONFIG_ISDN_TTY_FAX + case IDI_N_EDATA: + idi_edata_action(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); + break; +#endif default: if (DebugVar & 8) printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED NetIndication 0x%02x\n", chan->No, ind->Ind); @@ -1106,89 +1328,134 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) } void -idi_handle_ack(eicon_card *ccard, struct sk_buff *skb) +idi_handle_ack_ok(eicon_card *ccard, eicon_chan *chan, eicon_RC *ack) { - int j; - eicon_RC *ack = (eicon_RC *)skb->data; - eicon_chan *chan; isdn_ctrl cmd; - if ((ack->Rc != ASSIGN_OK) && (ack->Rc != OK)) { - if ((chan = ccard->IdTable[ack->RcId]) != NULL) { - chan->e.busy = 0; - if (DebugVar & 24) - printk(KERN_ERR "eicon_ack: Ch%d: Not OK: Rc=%d Id=%d Ch=%d\n", chan->No, - ack->Rc, ack->RcId, ack->RcCh); - if (chan->No == ccard->nchannels) { /* Management */ - chan->fsm_state = 2; - } else { /* any other channel */ - /* card reports error: we hangup */ - idi_hangup(ccard, chan); - cmd.driver = ccard->myid; - cmd.command = ISDN_STAT_DHUP; - cmd.arg = chan->No; - ccard->interface.statcallb(&cmd); - } + if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) { + /* I dont know why this happens, just ignoring this RC */ + if (DebugVar & 16) + printk(KERN_DEBUG "idi_ack: Ch%d: RcId %d not equal to last %d\n", chan->No, + ack->RcId, (chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id); + return; + } + + /* Management Interface */ + if (chan->No == ccard->nchannels) { + /* Managementinterface: changing state */ + if (chan->e.Req == 0x04) + chan->fsm_state = 1; + } + + /* Remove an Id */ + if (chan->e.Req == REMOVE) { + if (ack->Reference != chan->e.ref) { + if (DebugVar & 1) + printk(KERN_DEBUG "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No, + ack->Reference, chan->e.ref); } - } - else { - if ((chan = ccard->IdTable[ack->RcId]) != NULL) { - if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) { - if (DebugVar & 16) - printk(KERN_DEBUG "idi_ack: Ch%d: RcId %d not equal to last %d\n", chan->No, - ack->RcId, (chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id); - } else { - if (chan->No == ccard->nchannels) { /* Management */ - if (chan->e.Req == 0x04) chan->fsm_state = 1; - } - if (chan->e.ReqCh) { - switch(chan->e.Req & 0x0f) { - case IDI_N_MDATA: - case IDI_N_DATA: - chan->queued -= chan->waitq; - if (chan->queued < 0) chan->queued = 0; - if ((chan->e.Req & 0x0f) == IDI_N_DATA) { + ccard->IdTable[ack->RcId] = NULL; + if (DebugVar & 16) + printk(KERN_DEBUG "idi_ack: Ch%d: Removed : Id=%d Ch=%d (%s)\n", chan->No, + ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig"); + if (!chan->e.ReqCh) + chan->e.D3Id = 0; + else + chan->e.B2Id = 0; + return; + } + + /* Signal layer */ + if (!chan->e.ReqCh) { + if (DebugVar & 16) + printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%d Ch=%d (ref:%d)\n", chan->No, + ack->RcId, ack->RcCh, ack->Reference); + } else { + /* Network layer */ + switch(chan->e.Req & 0x0f) { + case IDI_N_MDATA: + case IDI_N_DATA: + if ((chan->e.Req & 0x0f) == IDI_N_DATA) { + if (chan->queued) { + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_BSENT; + cmd.arg = chan->No; + cmd.parm.length = chan->waitpq; + ccard->interface.statcallb(&cmd); + } + chan->waitpq = 0; +#ifdef CONFIG_ISDN_TTY_FAX + if (chan->l2prot == ISDN_PROTO_L2_FAX) { + if (((chan->queued - chan->waitq) < 1) && + (chan->fax2.Eop)) { + chan->fax2.Eop = 0; + if (chan->fax) { cmd.driver = ccard->myid; - cmd.command = ISDN_STAT_BSENT; + cmd.command = ISDN_STAT_FAXIND; cmd.arg = chan->No; - cmd.parm.length = chan->waitpq; - chan->waitpq = 0; + chan->fax->r_code = ISDN_TTY_FAX_SENT; ccard->interface.statcallb(&cmd); } - break; - default: - if (DebugVar & 16) - printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%d Ch=%d (ref:%d)\n", chan->No, - ack->RcId, ack->RcCh, ack->Reference); + else { + if (DebugVar & 1) + printk(KERN_DEBUG "idi_ack: Sent with NULL fax struct, ERROR\n"); + } + } } - } - else { - if (DebugVar & 16) - printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%d Ch=%d (ref:%d)\n", chan->No, - ack->RcId, ack->RcCh, ack->Reference); +#endif } + chan->queued -= chan->waitq; + if (chan->queued < 0) chan->queued = 0; + break; + default: + if (DebugVar & 16) + printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%d Ch=%d (ref:%d)\n", chan->No, + ack->RcId, ack->RcCh, ack->Reference); + } + } +} - if (chan->e.Req == REMOVE) { - if (ack->Reference == chan->e.ref) { - ccard->IdTable[ack->RcId] = NULL; - if (DebugVar & 16) - printk(KERN_DEBUG "idi_ack: Ch%d: Removed : Id=%d Ch=%d (%s)\n", chan->No, - ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig"); - if (!chan->e.ReqCh) - chan->e.D3Id = 0; - else - chan->e.B2Id = 0; - } - else { - if (DebugVar & 16) - printk(KERN_DEBUG "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No, - ack->Reference, chan->e.ref); - } - } - chan->e.busy = 0; +void +idi_handle_ack(eicon_card *ccard, struct sk_buff *skb) +{ + int j; + eicon_RC *ack = (eicon_RC *)skb->data; + eicon_chan *chan; + isdn_ctrl cmd; + int dCh = -1; + + if ((chan = ccard->IdTable[ack->RcId]) != NULL) + dCh = chan->No; + + + switch (ack->Rc) { + case OK_FC: + case N_FLOW_CONTROL: + case ASSIGN_RC: + if (DebugVar & 1) + printk(KERN_ERR "idi_ack: Ch%d: unhandled RC 0x%x\n", + dCh, ack->Rc); + break; + case READY_INT: + case TIMER_INT: + /* we do nothing here */ + break; + + case OK: + if (!chan) { + if (DebugVar & 1) + printk(KERN_ERR "idi_ack: Ch%d: OK on chan without Id\n", dCh); + break; + } + idi_handle_ack_ok(ccard, chan, ack); + break; + + case ASSIGN_OK: + if (chan) { + if (DebugVar & 1) + printk(KERN_ERR "idi_ack: Ch%d: ASSIGN-OK on chan already assigned (%x,%x)\n", + chan->No, chan->e.D3Id, chan->e.B2Id); } - } - else { for(j = 0; j < ccard->nchannels + 1; j++) { if (ccard->bch[j].e.ref == ack->Reference) { if (!ccard->bch[j].e.ReqCh) @@ -1199,7 +1466,7 @@ idi_handle_ack(eicon_card *ccard, struct sk_buff *skb) ccard->bch[j].e.busy = 0; ccard->bch[j].e.ref = 0; if (DebugVar & 16) - printk(KERN_DEBUG"idi_ack: Ch%d: Id %d assigned (%s)\n", j, + printk(KERN_DEBUG"idi_ack: Ch%d: Id %x assigned (%s)\n", j, ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig"); break; } @@ -1209,14 +1476,40 @@ idi_handle_ack(eicon_card *ccard, struct sk_buff *skb) printk(KERN_DEBUG"idi_ack: Ch??: ref %d not found for Id %d\n", ack->Reference, ack->RcId); } - } + break; + + case OUT_OF_RESOURCES: + case UNKNOWN_COMMAND: + case WRONG_COMMAND: + case WRONG_ID: + case WRONG_CH: + case UNKNOWN_IE: + case WRONG_IE: + default: + chan->e.busy = 0; + if (DebugVar & 24) + printk(KERN_ERR "eicon_ack: Ch%d: Not OK: Rc=%d Id=%d Ch=%d\n", dCh, + ack->Rc, ack->RcId, ack->RcCh); + if (dCh == ccard->nchannels) { /* Management */ + chan->fsm_state = 2; + } else if (dCh >= 0) { + /* any other channel */ + /* card reports error: we hangup */ + idi_hangup(ccard, chan); + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan->No; + ccard->interface.statcallb(&cmd); + } } - dev_kfree_skb(skb); - eicon_schedule_tx(ccard); + if (chan) + chan->e.busy = 0; + dev_kfree_skb(skb); + eicon_schedule_tx(ccard); } int -idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb) +idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb, int que) { struct sk_buff *xmit_skb; struct sk_buff *skb2; @@ -1240,6 +1533,7 @@ idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb) return 0; if (DebugVar & 128) printk(KERN_DEBUG"idi_snd: Ch%d: %d bytes\n", chan->No, len); + save_flags(flags); cli(); while(offset < len) { @@ -1249,10 +1543,14 @@ idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb) xmit_skb = alloc_skb(plen + sizeof(eicon_REQ), GFP_ATOMIC); skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); - if ((!skb) || (!skb2)) { + if ((!xmit_skb) || (!skb2)) { restore_flags(flags); if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No); + printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in send_data()\n", chan->No); + if (xmit_skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -1278,7 +1576,8 @@ idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb) offset += plen; } - chan->queued += len; + if (que) + chan->queued += len; restore_flags(flags); eicon_schedule_tx(card); dev_kfree_skb(skb); @@ -1303,7 +1602,11 @@ eicon_idi_manage_assign(eicon_card *card) if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: alloc_skb failed\n"); + printk(KERN_WARNING "idi_err: alloc_skb failed in manage_assign()\n"); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -1342,7 +1645,11 @@ eicon_idi_manage_remove(eicon_card *card) if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: alloc_skb failed\n"); + printk(KERN_WARNING "idi_err: alloc_skb failed in manage_remove()\n"); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -1379,7 +1686,8 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb) chan = &(card->bch[card->nchannels]); - if (chan->e.D3Id) return -EBUSY; + if (chan->e.D3Id) + return -EBUSY; chan->e.D3Id = 1; while((skb2 = skb_dequeue(&chan->e.X))) dev_kfree_skb(skb2); @@ -1409,6 +1717,7 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb) return -ENOMEM; } if (copy_from_user(manbuf, mb, sizeof(eicon_manifbuf))) { + kfree(manbuf); chan->e.D3Id = 0; return -EFAULT; } @@ -1418,7 +1727,11 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb) if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err_manif: alloc_skb failed\n"); + printk(KERN_WARNING "idi_err_manif: alloc_skb failed in manage()\n"); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); kfree(manbuf); chan->e.D3Id = 0; return -ENOMEM; @@ -1464,11 +1777,13 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb) } if ((ret = eicon_idi_manage_remove(card))) { + kfree(manbuf); chan->e.D3Id = 0; return(ret); } if (copy_to_user(mb, manbuf, sizeof(eicon_manifbuf))) { + kfree(manbuf); chan->e.D3Id = 0; return -EFAULT; } diff --git a/drivers/isdn/eicon/eicon_idi.h b/drivers/isdn/eicon/eicon_idi.h index a0605cdef864..9a1da1751303 100644 --- a/drivers/isdn/eicon/eicon_idi.h +++ b/drivers/isdn/eicon/eicon_idi.h @@ -1,4 +1,4 @@ -/* $Id: eicon_idi.h,v 1.4 1999/03/29 11:19:44 armin Exp $ +/* $Id: eicon_idi.h,v 1.6 1999/07/25 15:12:04 armin Exp $ * * ISDN lowlevel-module for the Eicon.Diehl active cards. * IDI-Interface @@ -21,6 +21,15 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_idi.h,v $ + * Revision 1.6 1999/07/25 15:12:04 armin + * fix of some debug logs. + * enabled ISA-cards option. + * + * Revision 1.5 1999/07/11 17:16:26 armin + * Bugfixes in queue handling. + * Added DSP-DTMF decoder functions. + * Reorganized ack_handler. + * * Revision 1.4 1999/03/29 11:19:44 armin * I/O stuff now in seperate file (eicon_io.c) * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented. @@ -232,6 +241,12 @@ typedef struct { __u8 B[1]; /* buffer space for Req,Ind and Rc */ } eicon_pr_ram; +typedef struct { + __u8 *Data; + unsigned int Size; + unsigned int Len; + __u8 *Next; +} eicon_OBJBUFFER; extern int idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer); extern int idi_hangup(eicon_card *card, eicon_chan *chan); @@ -243,6 +258,11 @@ extern int idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone, extern void idi_handle_ack(eicon_card *card, struct sk_buff *skb); extern void idi_handle_ind(eicon_card *card, struct sk_buff *skb); extern int eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb); -extern int idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb); +extern int idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb, int que); +extern void idi_audio_cmd(eicon_card *ccard, eicon_chan *chan, int cmd, u_char *value); +#ifdef CONFIG_ISDN_TTY_FAX +extern void idi_fax_cmd(eicon_card *card, eicon_chan *chan); +extern int idi_faxdata_send(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb); +#endif #endif diff --git a/drivers/isdn/eicon/eicon_io.c b/drivers/isdn/eicon/eicon_io.c index 1c69d37cd1a2..79ce41eed87c 100644 --- a/drivers/isdn/eicon/eicon_io.c +++ b/drivers/isdn/eicon/eicon_io.c @@ -1,4 +1,4 @@ -/* $Id: eicon_io.c,v 1.1 1999/03/29 11:19:45 armin Exp $ +/* $Id: eicon_io.c,v 1.2 1999/07/25 15:12:05 armin Exp $ * * ISDN low-level module for Eicon.Diehl active ISDN-Cards. * Code for communicating with hardware. @@ -24,6 +24,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_io.c,v $ + * Revision 1.2 1999/07/25 15:12:05 armin + * fix of some debug logs. + * enabled ISA-cards option. + * * Revision 1.1 1999/03/29 11:19:45 armin * I/O stuff now in seperate file (eicon_io.c) * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented. @@ -57,7 +61,7 @@ eicon_io_rcv_dispatch(eicon_card *ccard) { break; default: printk(KERN_ERR "idi: Indication for unknown channel Ind=%d Id=%d\n", ind->Ind, ind->IndId); - printk(KERN_DEBUG "idi_hdl: Ch??: Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", + printk(KERN_DEBUG "idi_hdl: Ch??: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length); } } @@ -303,6 +307,7 @@ eicon_io_transmit(eicon_card *ccard) { } switch(ccard->type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: @@ -314,6 +319,7 @@ eicon_io_transmit(eicon_card *ccard) { scom = 0; prram = (eicon_pr_ram *)isa_card->shmem; break; +#endif case EICON_CTYPE_MAESTRAP: scom = 0; ram = (char *)pci_card->PCIram; @@ -434,7 +440,7 @@ eicon_io_transmit(eicon_card *ccard) { chan->e.busy = 1; restore_flags(flags); if (DebugVar & 32) - printk(KERN_DEBUG "eicon: Req=%x Id=%x Ch=%x Len=%x Ref=%d\n", + printk(KERN_DEBUG "eicon: Req=%d Id=%x Ch=%d Len=%d Ref=%d\n", reqbuf->Req, ram_inb(ccard, &ReqOut->ReqId), reqbuf->ReqCh, reqbuf->XBuffer.length, @@ -510,6 +516,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { isa_card = &ccard->hwif.isa; switch(ccard->type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: @@ -523,6 +530,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { prram = (eicon_pr_ram *)isa_card->shmem; irqprobe = &isa_card->irqprobe; break; +#endif case EICON_CTYPE_MAESTRAP: scom = 0; ram = (char *)pci_card->PCIram; @@ -546,6 +554,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { if (*irqprobe) { switch(ccard->type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: @@ -563,6 +572,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { } (*irqprobe)++; break; +#endif case EICON_CTYPE_MAESTRAP: if (readb(&ram[0x3fe])) { writeb(0, &prram->RcOutput); @@ -581,6 +591,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { } switch(ccard->type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: @@ -592,6 +603,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { return; } break; +#endif case EICON_CTYPE_MAESTRAP: if (!(readb(&ram[0x3fe]))) { /* card did not interrupt */ if (DebugVar & 1) @@ -629,7 +641,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { ack->RcCh = ram_inb(ccard, &com->RcCh); ack->Reference = ccard->ref_in++; if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Rc=%d Id=%d Ch=%d Ref=%d\n", + printk(KERN_INFO "eicon: IRQ Rc=%d Id=%x Ch=%d Ref=%d\n", tmp,ack->RcId,ack->RcCh,ack->Reference); skb_queue_tail(&ccard->rackq, skb); eicon_schedule_ack(ccard); @@ -652,7 +664,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { ind->MLength = ram_inw(ccard, &com->MLength); ind->RBuffer.length = len; if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", + printk(KERN_INFO "eicon: IRQ Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", tmp,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len); ram_copyfromcard(ccard, &ind->RBuffer.P, &com->RBuffer.P, len); skb_queue_tail(&ccard->rcvq, skb); @@ -679,7 +691,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { ack->RcCh = ram_inb(ccard, &RcIn->RcCh); ack->Reference = ram_inw(ccard, &RcIn->Reference); if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Rc=%d Id=%d Ch=%d Ref=%d\n", + printk(KERN_INFO "eicon: IRQ Rc=%d Id=%x Ch=%d Ref=%d\n", Rc,ack->RcId,ack->RcCh,ack->Reference); ram_outb(ccard, &RcIn->Rc, 0); skb_queue_tail(&ccard->rackq, skb); @@ -711,7 +723,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { ind->MLength = ram_inw(ccard, &IndIn->MLength); ind->RBuffer.length = len; if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", + printk(KERN_INFO "eicon: IRQ Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len); ram_copyfromcard(ccard, &ind->RBuffer.P, &IndIn->RBuffer.P, len); skb_queue_tail(&ccard->rcvq, skb); @@ -728,6 +740,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { /* clear interrupt */ switch(ccard->type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_QUADRO: writeb(0, isa_card->intack); writeb(0, &com[0x401]); @@ -738,6 +751,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { case EICON_CTYPE_S2M: writeb(0, isa_card->intack); break; +#endif case EICON_CTYPE_MAESTRAP: writew(MP_IRQ_RESET_VAL, &cfg[MP_IRQ_RESET]); writew(0, &cfg[MP_IRQ_RESET + 2]); diff --git a/drivers/isdn/eicon/eicon_isa.c b/drivers/isdn/eicon/eicon_isa.c index 184f1c394f51..924d5620f686 100644 --- a/drivers/isdn/eicon/eicon_isa.c +++ b/drivers/isdn/eicon/eicon_isa.c @@ -1,4 +1,4 @@ -/* $Id: eicon_isa.c,v 1.5 1999/04/01 12:48:33 armin Exp $ +/* $Id: eicon_isa.c,v 1.6 1999/07/25 15:12:06 armin Exp $ * * ISDN low-level module for Eicon.Diehl active ISDN-Cards. * Hardware-specific code for old ISA cards. @@ -22,6 +22,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_isa.c,v $ + * Revision 1.6 1999/07/25 15:12:06 armin + * fix of some debug logs. + * enabled ISA-cards option. + * * Revision 1.5 1999/04/01 12:48:33 armin * Changed some log outputs. * @@ -53,7 +57,9 @@ #define release_shmem release_region #define request_shmem request_region -char *eicon_isa_revision = "$Revision: 1.5 $"; +char *eicon_isa_revision = "$Revision: 1.6 $"; + +#ifdef CONFIG_ISDN_DRV_EICON_ISA /* Mask for detecting invalid IRQ parameter */ static int eicon_isa_valid_irq[] = { @@ -430,3 +436,5 @@ eicon_isa_load(eicon_isa_card *card, eicon_isa_codebuf *cb) { card->irqprobe = 0; return 0; } + +#endif /* CONFIG_ISDN_DRV_EICON_ISA */ diff --git a/drivers/isdn/eicon/eicon_mod.c b/drivers/isdn/eicon/eicon_mod.c index c14d91d7e845..9efb770ed1f3 100644 --- a/drivers/isdn/eicon/eicon_mod.c +++ b/drivers/isdn/eicon/eicon_mod.c @@ -1,4 +1,4 @@ -/* $Id: eicon_mod.c,v 1.5 1999/04/01 12:48:35 armin Exp $ +/* $Id: eicon_mod.c,v 1.8 1999/07/25 15:12:08 armin Exp $ * * ISDN lowlevel-module for Eicon.Diehl active cards. * @@ -26,6 +26,19 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_mod.c,v $ + * Revision 1.8 1999/07/25 15:12:08 armin + * fix of some debug logs. + * enabled ISA-cards option. + * + * Revision 1.7 1999/07/11 17:16:27 armin + * Bugfixes in queue handling. + * Added DSP-DTMF decoder functions. + * Reorganized ack_handler. + * + * Revision 1.6 1999/06/09 19:31:26 armin + * Wrong PLX size for request_region() corrected. + * Added first MCA code from Erik Weber. + * * Revision 1.5 1999/04/01 12:48:35 armin * Changed some log outputs. * @@ -53,14 +66,18 @@ #include #include #include +#ifdef CONFIG_MCA +#include +#endif #include "eicon.h" #define INCLUDE_INLINE_FUNCS -static eicon_card *cards = (eicon_card *) NULL; +static eicon_card *cards = (eicon_card *) NULL; /* glob. var , contains + start of card-list */ -static char *eicon_revision = "$Revision: 1.5 $"; +static char *eicon_revision = "$Revision: 1.8 $"; extern char *eicon_pci_revision; extern char *eicon_isa_revision; @@ -75,19 +92,23 @@ extern char *eicon_idi_revision; ulong DebugVar; /* Parameters to be set by insmod */ +#ifdef CONFIG_ISDN_DRV_EICON_ISA static int membase = -1; static int irq = -1; +#endif static char *id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; MODULE_DESCRIPTION( "Driver for Eicon.Diehl active ISDN cards"); MODULE_AUTHOR( "Armin Schindler"); MODULE_SUPPORTED_DEVICE( "ISDN subsystem"); +MODULE_PARM_DESC(id, "ID-String of first card"); +MODULE_PARM(id, "s"); +#ifdef CONFIG_ISDN_DRV_EICON_ISA MODULE_PARM_DESC(membase, "Base address of first ISA card"); MODULE_PARM_DESC(irq, "IRQ of first card"); -MODULE_PARM_DESC(id, "ID-String of first card"); MODULE_PARM(membase, "i"); MODULE_PARM(irq, "i"); -MODULE_PARM(id, "s"); +#endif char *eicon_ctype_name[] = { "ISDN-S", @@ -291,10 +312,10 @@ eicon_rcv_dispatch(struct eicon_card *card) { switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: case EICON_BUS_PCI: eicon_io_rcv_dispatch(card); break; - case EICON_BUS_MCA: default: if (DebugVar & 1) printk(KERN_WARNING @@ -307,10 +328,10 @@ eicon_ack_dispatch(struct eicon_card *card) { switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: case EICON_BUS_PCI: eicon_io_ack_dispatch(card); break; - case EICON_BUS_MCA: default: if (DebugVar & 1) printk(KERN_WARNING @@ -323,10 +344,10 @@ eicon_transmit(struct eicon_card *card) { switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: case EICON_BUS_PCI: eicon_io_transmit(card); break; - case EICON_BUS_MCA: default: if (DebugVar & 1) printk(KERN_WARNING @@ -356,6 +377,7 @@ eicon_command(eicon_card * card, isdn_ctrl * c) case EICON_IOCTL_GETMMIO: switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: return (int)card->hwif.isa.shmem; #if CONFIG_PCI case EICON_BUS_PCI: @@ -368,11 +390,13 @@ eicon_command(eicon_card * card, isdn_ctrl * c) card->bus); ret = -ENODEV; } +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_IOCTL_SETMMIO: if (card->flags & EICON_FLAGS_LOADED) return -EBUSY; switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: if (eicon_isa_find_card(a, card->hwif.isa.irq, card->regname) < 0) @@ -386,9 +410,11 @@ eicon_command(eicon_card * card, isdn_ctrl * c) card->bus); ret = -ENODEV; } +#endif case EICON_IOCTL_GETIRQ: switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: return card->hwif.isa.irq; #if CONFIG_PCI case EICON_BUS_PCI: @@ -408,6 +434,7 @@ eicon_command(eicon_card * card, isdn_ctrl * c) return -EFAULT; switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: card->hwif.isa.irq = a; return 0; default: @@ -417,11 +444,13 @@ eicon_command(eicon_card * card, isdn_ctrl * c) card->bus); ret = -ENODEV; } +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_IOCTL_LOADBOOT: if (card->flags & EICON_FLAGS_RUNNING) return -EBUSY; switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: ret = eicon_isa_bootload( &(card->hwif.isa), &(((eicon_codebuf *)a)->isa)); @@ -434,11 +463,14 @@ eicon_command(eicon_card * card, isdn_ctrl * c) ret = -ENODEV; } return ret; +#endif +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_IOCTL_LOADISA: if (card->flags & EICON_FLAGS_RUNNING) return -EBUSY; switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: ret = eicon_isa_load( &(card->hwif.isa), &(((eicon_codebuf *)a)->isa)); @@ -465,7 +497,7 @@ eicon_command(eicon_card * card, isdn_ctrl * c) ret = -ENODEV; } return ret; - +#endif case EICON_IOCTL_MANIF: if (!card->flags & EICON_FLAGS_RUNNING) return -ENODEV; @@ -639,11 +671,6 @@ eicon_command(eicon_card * card, isdn_ctrl * c) case ISDN_CMD_SETL3: if (!card->flags & EICON_FLAGS_RUNNING) return -ENODEV; - if ((c->arg >> 8) != ISDN_PROTO_L3_TRANS) { - if (DebugVar & 1) - printk(KERN_WARNING "L3 protocol unknown\n"); - return -1; - } if (!(chan = find_channel(card, c->arg & 0x1f))) break; chan->l3prot = (c->arg >> 8); @@ -678,6 +705,13 @@ eicon_command(eicon_card * card, isdn_ctrl * c) case ISDN_CMD_UNLOCK: MOD_DEC_USE_COUNT; return 0; + case ISDN_CMD_AUDIO: + if (!card->flags & EICON_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x1f))) + break; + idi_audio_cmd(card, chan, c->arg >> 8, c->parm.num); + return 0; } return -EINVAL; @@ -753,6 +787,10 @@ if_sendbuf(int id, int channel, int ack, struct sk_buff *skb) { eicon_card *card = eicon_findcard(id); eicon_chan *chan; + int ret = 0; + int len; + + len = skb->len; if (card) { if (!card->flags & EICON_FLAGS_RUNNING) { @@ -763,9 +801,10 @@ if_sendbuf(int id, int channel, int ack, struct sk_buff *skb) dev_kfree_skb(skb); return -ENODEV; } - if (chan->fsm_state == EICON_STATE_ACTIVE) - return (idi_send_data(card, chan, ack, skb)); - else { + if (chan->fsm_state == EICON_STATE_ACTIVE) { + ret = idi_send_data(card, chan, ack, skb, 1); + return (ret); + } else { dev_kfree_skb(skb); return -ENODEV; } @@ -787,7 +826,9 @@ eicon_alloccard(int Type, int membase, int irq, char *id) int i; int j; int qloop; +#ifdef CONFIG_ISDN_DRV_EICON_ISA char qid[5]; +#endif eicon_card *card; #if CONFIG_PCI eicon_pci_card *pcic; @@ -828,12 +869,32 @@ eicon_alloccard(int Type, int membase, int irq, char *id) card->myid = -1; card->type = Type; switch (Type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA +#if CONFIG_MCA /* only needed for MCA */ + case EICON_CTYPE_S: + case EICON_CTYPE_SX: + case EICON_CTYPE_SCOM: + if (membase == -1) + membase = EICON_ISA_MEMBASE; + if (irq == -1) + irq = EICON_ISA_IRQ; + card->bus = EICON_BUS_MCA; + card->hwif.isa.card = (void *)card; + card->hwif.isa.shmem = (eicon_isa_shmem *)membase; + card->hwif.isa.master = 1; + + card->hwif.isa.irq = irq; + card->hwif.isa.type = Type; + card->nchannels = 2; + card->interface.channels = 1; + break; +#endif /* CONFIG_MCA */ case EICON_CTYPE_QUADRO: if (membase == -1) membase = EICON_ISA_MEMBASE; if (irq == -1) irq = EICON_ISA_IRQ; - card->bus = EICON_BUS_ISA; + card->bus = EICON_BUS_ISA; card->hwif.isa.card = (void *)card; card->hwif.isa.shmem = (eicon_isa_shmem *)(membase + (i+1) * EICON_ISA_QOFFSET); card->hwif.isa.master = 0; @@ -868,6 +929,7 @@ eicon_alloccard(int Type, int membase, int irq, char *id) card->nchannels = 2; card->interface.channels = 1; break; +#endif #if CONFIG_PCI case EICON_CTYPE_MAESTRA: (eicon_pci_card *)pcic = (eicon_pci_card *)membase; @@ -876,7 +938,10 @@ eicon_alloccard(int Type, int membase, int irq, char *id) ISDN_FEATURE_L2_V11096 | ISDN_FEATURE_L2_V11019 | ISDN_FEATURE_L2_V11038 | - ISDN_FEATURE_L2_MODEM; + ISDN_FEATURE_L2_MODEM | + ISDN_FEATURE_L2_FAX | + ISDN_FEATURE_L3_TRANSDSP | + ISDN_FEATURE_L3_FAX; card->hwif.pci.card = (void *)card; card->hwif.pci.PCIreg = pcic->PCIreg; card->hwif.pci.PCIcfg = pcic->PCIcfg; @@ -897,7 +962,10 @@ eicon_alloccard(int Type, int membase, int irq, char *id) ISDN_FEATURE_L2_V11096 | ISDN_FEATURE_L2_V11019 | ISDN_FEATURE_L2_V11038 | - ISDN_FEATURE_L2_MODEM; + ISDN_FEATURE_L2_MODEM | + ISDN_FEATURE_L2_FAX | + ISDN_FEATURE_L3_TRANSDSP | + ISDN_FEATURE_L3_FAX; card->hwif.pci.card = (void *)card; card->hwif.pci.shmem = (eicon_pci_shmem *)pcic->shmem; card->hwif.pci.PCIreg = pcic->PCIreg; @@ -913,6 +981,7 @@ eicon_alloccard(int Type, int membase, int irq, char *id) card->interface.channels = 1; break; #endif +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_ISABRI: if (membase == -1) membase = EICON_ISA_MEMBASE; @@ -932,7 +1001,7 @@ eicon_alloccard(int Type, int membase, int irq, char *id) membase = EICON_ISA_MEMBASE; if (irq == -1) irq = EICON_ISA_IRQ; - card->bus = EICON_BUS_ISA; + card->bus = EICON_BUS_ISA; card->hwif.isa.card = (void *)card; card->hwif.isa.shmem = (eicon_isa_shmem *)membase; card->hwif.isa.master = 1; @@ -941,6 +1010,7 @@ eicon_alloccard(int Type, int membase, int irq, char *id) card->nchannels = 30; card->interface.channels = 1; break; +#endif default: printk(KERN_WARNING "eicon_alloccard: Invalid type %d\n", Type); kfree(card); @@ -978,15 +1048,21 @@ static int eicon_registercard(eicon_card * card) { switch (card->bus) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_BUS_ISA: /* TODO something to print */ break; +#ifdef CONFIG_MCA + case EICON_BUS_MCA: + eicon_isa_printpar(&card->hwif.isa); + break; +#endif +#endif case EICON_BUS_PCI: #if CONFIG_PCI eicon_pci_printpar(&card->hwif.pci); break; #endif - case EICON_BUS_MCA: default: if (DebugVar & 1) printk(KERN_WARNING @@ -1015,15 +1091,19 @@ unregister_card(eicon_card * card) cmd.driver = card->myid; card->interface.statcallb(&cmd); switch (card->bus) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_BUS_ISA: +#ifdef CONFIG_MCA + case EICON_BUS_MCA: +#endif eicon_isa_release(&card->hwif.isa); break; +#endif case EICON_BUS_PCI: #if CONFIG_PCI eicon_pci_release(&card->hwif.pci); break; #endif - case EICON_BUS_MCA: default: if (DebugVar & 1) printk(KERN_WARNING @@ -1050,9 +1130,11 @@ eicon_addcard(int Type, int membase, int irq, char *id) int added = 0; int failed = 0; +#ifdef CONFIG_ISDN_DRV_EICON_ISA if (!Type) /* ISA */ if ((Type = eicon_isa_find_card(membase, irq, id)) < 0) return 0; +#endif eicon_alloccard(Type, membase, irq, id); p = cards; while (p) { @@ -1063,11 +1145,14 @@ eicon_addcard(int Type, int membase, int irq, char *id) */ added++; switch (p->bus) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_BUS_ISA: + case EICON_BUS_MCA: if (eicon_registercard(p)) break; registered = 1; break; +#endif case EICON_BUS_PCI: #if CONFIG_PCI if (eicon_registercard(p)) @@ -1075,7 +1160,6 @@ eicon_addcard(int Type, int membase, int irq, char *id) registered = 1; break; #endif - case EICON_BUS_MCA: default: if (DebugVar & 1) printk(KERN_WARNING @@ -1119,7 +1203,7 @@ eicon_addcard(int Type, int membase, int irq, char *id) __initfunc(int eicon_init(void)) { - int tmp = 0; + int card_count = 0; int release = 0; char tmprev[50]; @@ -1130,10 +1214,18 @@ eicon_init(void)) printk("%s/", eicon_getrev(tmprev)); release += getrel(tmprev); strcpy(tmprev, eicon_pci_revision); +#if CONFIG_PCI printk("%s/", eicon_getrev(tmprev)); +#else + printk("---/"); +#endif release += getrel(tmprev); strcpy(tmprev, eicon_isa_revision); +#ifdef CONFIG_ISDN_DRV_EICON_ISA printk("%s/", eicon_getrev(tmprev)); +#else + printk("---/"); +#endif release += getrel(tmprev); strcpy(tmprev, eicon_idi_revision); printk("%s\n", eicon_getrev(tmprev)); @@ -1142,18 +1234,46 @@ eicon_init(void)) printk(KERN_INFO "%s Release: %s.%s\n", DRIVERNAME, DRIVERRELEASE, tmprev); - tmp = eicon_addcard(0, membase, irq, id); +#ifdef CONFIG_ISDN_DRV_EICON_ISA +#ifdef CONFIG_MCA + /* Check if we have MCA-bus */ + if (!MCA_bus) + { + printk(KERN_INFO + "eicon: No MCA bus, ISDN-interfaces not probed.\n"); + } else { + if (DebugVar & 8) + printk(KERN_DEBUG + "eicon_mca_find_card, irq=%d.\n", + irq); + if (!eicon_mca_find_card(0, membase, irq, id)) + card_count++; + }; +#else + card_count = eicon_addcard(0, membase, irq, id); +#endif /* CONFIG_MCA */ +#endif /* CONFIG_ISDN_DRV_EICON_ISA */ + #if CONFIG_PCI - tmp += eicon_pci_find_card(id); + card_count += eicon_pci_find_card(id); #endif if (!cards) { #ifdef MODULE +#ifndef CONFIG_PCI +#ifndef CONFIG_ISDN_DRV_EICON_ISA + printk(KERN_INFO "Eicon: Driver is neither ISA nor PCI compiled !\n"); +#else printk(KERN_INFO "Eicon: No cards defined, driver not loaded !\n"); +#endif +#else + printk(KERN_INFO "Eicon: No PCI-cards found, driver not loaded !\n"); +#endif #endif return -ENODEV; } else - printk(KERN_INFO "Eicon: %d card%s added\n", tmp, (tmp>1)?"s":""); + printk(KERN_INFO "Eicon: %d card%s added\n", card_count, + (card_count>1)?"s":""); /* No symbols to export, hide all symbols */ EXPORT_NO_SYMBOLS; return 0; @@ -1166,6 +1286,13 @@ cleanup_module(void) eicon_card *card = cards; eicon_card *last; while (card) { +#ifdef CONFIG_MCA + if (MCA_bus) + { + mca_mark_as_unused (card->mca_slot); + mca_set_adapter_procfn(card->mca_slot, NULL, NULL); + }; +#endif unregister_card(card); card = card->next; } @@ -1178,7 +1305,7 @@ cleanup_module(void) printk(KERN_INFO "%s unloaded\n", DRIVERNAME); } -#else +#else /* no module */ __initfunc(void eicon_setup(char *str, int *ints)) { @@ -1207,4 +1334,173 @@ eicon_setup(char *str, int *ints)) printk(KERN_INFO "eicon: membase=0x%x irq=%d id=%s\n", membase, irq, id); } } -#endif +#endif /* MODULE */ + +#ifdef CONFIG_ISDN_DRV_EICON_ISA +#ifdef CONFIG_MCA + +struct eicon_mca_adapters_struct { + char * name; + int adf_id; +}; +/* possible MCA-brands of eicon cards */ +struct eicon_mca_adapters_struct eicon_mca_adapters[] = { + { "ISDN-P/2 Adapter", 0x6abb }, + { "ISDN-[S|SX|SCOM]/2 Adapter", 0x6a93 }, + { "DIVA /MCA", 0x6336 }, + { NULL, 0 }, +}; + +int eicon_mca_find_card(int type, /* type-idx of eicon-card */ + int membase, + int irq, + char * id) /* name of eicon-isdn-dev */ +{ + int j, curr_slot = 0; + + if (DebugVar & 8) + printk(KERN_DEBUG + "eicon_mca_find_card type: %d, membase: %#x, irq %d \n", + type, membase, irq); + /* find a no-driver-assigned eicon card */ + for (j=0; eicon_mca_adapters[j].adf_id != 0; j++) + { + for ( curr_slot=0; curr_slot<=MCA_MAX_SLOT_NR; curr_slot++) + { + curr_slot = mca_find_unused_adapter( + eicon_mca_adapters[j].adf_id, curr_slot); + if (curr_slot != MCA_NOTFOUND) + { + /* check if pre-set parameters match + these of the card, check cards memory */ + if (!(int) eicon_mca_probe(curr_slot, + j, + membase, + irq, + id)) + { + return 0; + /* means: adapter parms did match */ + }; + }; + break; + /* MCA_NOTFOUND-branch: no matching adapter of + THIS flavor found, next flavor */ + + }; + }; + /* all adapter flavors checked without match, finito with: */ + return ENODEV; +}; + + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * stolen from 3c523.c/elmc_getinfo, ewe, 10.5.1999 + */ +int eicon_info(char * buf, int slot, void *d) +{ + int len = 0; + struct eicon_card *dev; + + dev = (struct eicon_card *) d; + + if (dev == NULL) + return len; + len += sprintf(buf+len, "eicon ISDN adapter, type %d.\n",dev->type); + len += sprintf(buf+len, "IRQ: %d\n", dev->hwif.isa.irq); + len += sprintf(buf+len, "MEMBASE: %#lx\n", (unsigned long)dev->hwif.isa.shmem); + + return len; +}; + +int eicon_mca_probe(int slot, /* slot-nr where the card was detected */ + int a_idx, /* idx-nr of probed card in eicon_mca_adapters */ + int membase, + int irq, + char * id) /* name of eicon-isdn-dev */ +{ + unsigned char adf_pos0; + int cards_irq, cards_membase, cards_io; + int type = EICON_CTYPE_S; + int irq_array[]={0,3,4,2}; + int irq_array1[]={3,4,0,0,2,10,11,12}; + + adf_pos0 = mca_read_stored_pos(slot,2); + if (DebugVar & 8) + printk(KERN_DEBUG + "eicon_mca_probe irq=%d, membase=%d\n", + irq, + membase); + switch (a_idx) { + case 0: /* P/2-Adapter (== PRI/S2M ? ) */ + cards_membase= 0xC0000+((adf_pos0>>4)*0x4000); + if (membase == -1) { + membase = cards_membase; + } else { + if (membase != cards_membase) + return ENODEV; + }; + cards_irq=irq_array[((adf_pos0 & 0xC)>>2)]; + if (irq == -1) { + irq = cards_irq; + } else { + if (irq != irq) + return ENODEV; + }; + cards_io= 0xC00 + ((adf_pos0>>4)*0x10); + type = EICON_CTYPE_ISAPRI; + break; + + case 1: /* [S|SX|SCOM]/2 */ + cards_membase= 0xC0000+((adf_pos0>>4)*0x2000); + if (membase == -1) { + membase = cards_membase; + } else { + if (membase != cards_membase) + return ENODEV; + }; + cards_irq=irq_array[((adf_pos0 & 0xC)>>2)]; + if (irq == -1) { + irq = cards_irq; + } else { + if (irq != cards_irq) + return ENODEV; + }; + + cards_io= 0xC00 + ((adf_pos0>>4)*0x10); + type = EICON_CTYPE_SCOM; + break; + + case 2: /* DIVA/MCA */ + cards_io = 0x200+ ((adf_pos0>>4)* 0x20); + cards_irq = irq_array1[(adf_pos0 & 0x7)]; + if (irq == -1) { + irq = cards_irq; + } else { + if (irq != irq) + return ENODEV; + }; + type = 0; + break; + default: + return ENODEV; + }; + /* Uebereinstimmung vorgegebener membase & irq */ + if ( 1 == eicon_addcard(type, membase, irq, id)) { + mca_set_adapter_name(slot, eicon_mca_adapters[a_idx].name); + mca_set_adapter_procfn(slot, (MCA_ProcFn) eicon_info, cards); + + mca_mark_as_used(slot); + cards->mca_slot = slot; + /* card->io noch setzen oder ?? */ + if (DebugVar & 8) + printk("eicon_addcard: erfolgreich fuer slot: %d.\n", + cards->mca_slot+1); + return 0 ; /* eicon_addcard hat eine Karte zugefuegt */ + } else { + return ENODEV; + }; +}; +#endif /* CONFIG_MCA */ +#endif /* CONFIG_ISDN_DRV_EICON_ISA */ + diff --git a/drivers/isdn/eicon/eicon_pci.c b/drivers/isdn/eicon/eicon_pci.c index 3d07167ce177..a5611394c3aa 100644 --- a/drivers/isdn/eicon/eicon_pci.c +++ b/drivers/isdn/eicon/eicon_pci.c @@ -1,4 +1,4 @@ -/* $Id: eicon_pci.c,v 1.6 1999/04/01 12:48:37 armin Exp $ +/* $Id: eicon_pci.c,v 1.9 1999/08/11 21:01:11 keil Exp $ * * ISDN low-level module for Eicon.Diehl active ISDN-Cards. * Hardware-specific code for PCI cards. @@ -26,6 +26,16 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_pci.c,v $ + * Revision 1.9 1999/08/11 21:01:11 keil + * new PCI codefix + * + * Revision 1.8 1999/08/10 16:02:20 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.7 1999/06/09 19:31:29 armin + * Wrong PLX size for request_region() corrected. + * Added first MCA code from Erik Weber. + * * Revision 1.6 1999/04/01 12:48:37 armin * Changed some log outputs. * @@ -54,14 +64,13 @@ * */ -#include #include #include "eicon.h" #include "eicon_pci.h" -char *eicon_pci_revision = "$Revision: 1.6 $"; +char *eicon_pci_revision = "$Revision: 1.9 $"; #if CONFIG_PCI /* intire stuff is only for PCI */ @@ -136,8 +145,8 @@ int eicon_pci_find_card(char *ID) aparms->type = EICON_CTYPE_MAESTRA; aparms->irq = pdev->irq; - preg = pdev->base_address[2] & 0xfffffffc; - pcfg = pdev->base_address[1] & 0xffffff80; + preg = get_pcibase(pdev, 2) & 0xfffffffc; + pcfg = get_pcibase(pdev, 1) & 0xffffff80; #ifdef EICON_PCI_DEBUG printk(KERN_DEBUG "eicon_pci: irq=%d\n", aparms->irq); @@ -158,9 +167,9 @@ int eicon_pci_find_card(char *ID) printk(KERN_INFO "Eicon: DIVA Server PRI/PCI detected !\n"); aparms->type = EICON_CTYPE_MAESTRAP; /*includes 9M,30M*/ aparms->irq = pdev->irq; - pram = pdev->base_address[0] & 0xfffff000; - preg = pdev->base_address[2] & 0xfffff000; - pcfg = pdev->base_address[4] & 0xfffff000; + pram = get_pcibase(pdev, 0) & 0xfffff000; + preg = get_pcibase(pdev, 2) & 0xfffff000; + pcfg = get_pcibase(pdev, 4) & 0xfffff000; #ifdef EICON_PCI_DEBUG printk(KERN_DEBUG "eicon_pci: irq=%d\n", aparms->irq); @@ -194,12 +203,13 @@ int eicon_pci_find_card(char *ID) } else { request_region(aparms->PCIreg, 0x20, "eicon reg"); } - if (check_region((aparms->PCIcfg), 0x100)) { + if (check_region((aparms->PCIcfg), 0x80)) { printk(KERN_WARNING "eicon_pci: cfg port already in use !\n"); aparms->PCIcfg = 0; + release_region(aparms->PCIreg, 0x20); break; } else { - request_region(aparms->PCIcfg, 0x100, "eicon cfg"); + request_region(aparms->PCIcfg, 0x80, "eicon cfg"); } break; case PCI_MAESTRAQ: @@ -327,7 +337,7 @@ eicon_pci_release_shmem(eicon_pci_card *card) { outw(0, card->PCIreg + M_DATA); release_region(card->PCIreg, 0x20); - release_region(card->PCIcfg, 0x100); + release_region(card->PCIcfg, 0x80); break; case EICON_CTYPE_MAESTRAQ: case EICON_CTYPE_MAESTRAQ_U: diff --git a/drivers/isdn/hisax/Makefile b/drivers/isdn/hisax/Makefile index 9e7cb777373f..051df6182716 100644 --- a/drivers/isdn/hisax/Makefile +++ b/drivers/isdn/hisax/Makefile @@ -25,13 +25,11 @@ ifeq ($(CONFIG_HISAX_1TR6),y) endif ISAC_OBJ := -ARCOFI_OBJ := HSCX_OBJ := ISAR_OBJ := HFC_OBJ := HFC_2BDS0 := -RAWHDLC_OBJ := - +JADE_OBJ := ifeq ($(CONFIG_HISAX_16_0),y) O_OBJS += teles0.o ISAC_OBJ := isac.o @@ -78,7 +76,6 @@ 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) @@ -127,16 +124,15 @@ endif ifeq ($(CONFIG_HISAX_NETJET),y) O_OBJS += netjet.o ISAC_OBJ := isac.o -# RAWHDLC_OBJ := rawhdlc.o endif -ifeq ($(CONFIG_HISAX_TELES3C),y) - O_OBJS += teles3c.o +ifeq ($(CONFIG_HISAX_HFCS),y) + O_OBJS += hfcscard.o HFC_2BDS0 := hfc_2bds0.o endif -ifeq ($(CONFIG_HISAX_AMD7930),y) - O_OBJS += amd7930.o - RAWHDLC_OBJ := rawhdlc.o + +ifeq ($(CONFIG_HISAX_HFC_PCI),y) + HFC_2BDS0 += hfc_pci.o endif ifeq ($(CONFIG_HISAX_NICCY),y) @@ -145,8 +141,45 @@ ifeq ($(CONFIG_HISAX_NICCY),y) HSCX_OBJ := hscx.o endif -O_OBJS += $(ISAC_OBJ) $(HSCX_OBJ) $(ISAR_OBJ) $(ARCOFI_OBJ) -O_OBJS += $(HFC_OBJ) $(HFC_2BDS0) $(RAWHDLC_OBJ) +ifeq ($(CONFIG_HISAX_ISURF),y) + O_OBJS += isurf.o + ISAC_OBJ := isac.o + ISAR_OBJ := isar.o +endif + +ifeq ($(CONFIG_HISAX_HSTSAPHIR),y) + O_OBJS += saphir.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_BKM_A4T),y) + O_OBJS += bkm_a4t.o + ISAC_OBJ := isac.o + JADE_OBJ := jade.o +endif +ifeq ($(CONFIG_HISAX_SCT_QUADRO),y) + O_OBJS += bkm_a8.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_GAZEL),y) + O_OBJS += gazel.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +# ifeq ($(CONFIG_HISAX_TESTEMU),y) +# O_OBJS += testemu.o +# endif + +ifeq ($(ISAC_OBJ), isac.o) + ISAC_OBJ += arcofi.o +endif + +O_OBJS += $(ISAC_OBJ) $(HSCX_OBJ) $(ISAR_OBJ) $(JADE_OBJ) +O_OBJS += $(HFC_OBJ) $(HFC_2BDS0) OX_OBJS += config.o O_TARGET := @@ -164,7 +197,8 @@ endif include $(TOPDIR)/Rules.make MD5FILES += isac.c isdnl1.c isdnl2.c isdnl3.c \ - tei.c callc.c cert.c l3dss1.c l3_1tr6.c elsa.c + tei.c callc.c cert.c l3dss1.c l3_1tr6.c \ + elsa.c diva.c CERT = $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?) diff --git a/drivers/isdn/hisax/amd7930.c b/drivers/isdn/hisax/amd7930.c index 1cf2307b3f6b..2770aa65e1f0 100644 --- a/drivers/isdn/hisax/amd7930.c +++ b/drivers/isdn/hisax/amd7930.c @@ -1,4 +1,4 @@ -/* $Id: amd7930.c,v 1.2 1998/02/12 23:07:10 keil Exp $ +/* $Id: amd7930.c,v 1.3 1999/07/12 21:04:52 keil Exp $ * * HiSax ISDN driver - chip specific routines for AMD 7930 * @@ -7,6 +7,10 @@ * * * $Log: amd7930.c,v $ + * Revision 1.3 1999/07/12 21:04:52 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * * Revision 1.2 1998/02/12 23:07:10 keil * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() * @@ -105,7 +109,7 @@ #include "rawhdlc.h" #include -static const char *amd7930_revision = "$Revision: 1.2 $"; +static const char *amd7930_revision = "$Revision: 1.3 $"; #define RCV_BUFSIZE 1024 /* Size of raw receive buffer in bytes */ #define RCV_BUFBLKS 4 /* Number of blocks to divide buffer into @@ -734,8 +738,6 @@ amd7930_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_amd7930(cs); return(0); - case CARD_SETIRQ: - return(0); case CARD_INIT: cs->l1cmd = amd7930_l1cmd; amd7930_liu_init(0, &amd7930_liu_callback, (void *)cs); @@ -747,8 +749,8 @@ amd7930_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init -setup_amd7930(struct IsdnCard *card) +__initfunc(int +setup_amd7930(struct IsdnCard *card)) { struct IsdnCardState *cs = card->cs; char tmp[64]; diff --git a/drivers/isdn/hisax/arcofi.c b/drivers/isdn/hisax/arcofi.c index a7c09132138f..641106a19197 100644 --- a/drivers/isdn/hisax/arcofi.c +++ b/drivers/isdn/hisax/arcofi.c @@ -1,4 +1,4 @@ -/* $Id: arcofi.c,v 1.6 1998/09/30 22:21:56 keil Exp $ +/* $Id: arcofi.c,v 1.7 1999/07/01 08:11:17 keil Exp $ * arcofi.c Ansteuerung ARCOFI 2165 * @@ -7,6 +7,9 @@ * * * $Log: arcofi.c,v $ + * Revision 1.7 1999/07/01 08:11:17 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.6 1998/09/30 22:21:56 keil * cosmetics * @@ -32,48 +35,120 @@ #include "hisax.h" #include "isdnl1.h" #include "isac.h" +#include "arcofi.h" -int -send_arcofi(struct IsdnCardState *cs, const u_char *msg, int bc, int receive) { +#define ARCOFI_TIMER_VALUE 20 + +static void +add_arcofi_timer(struct IsdnCardState *cs) { + if (test_and_set_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) { + del_timer(&cs->dc.isac.arcofitimer); + } + init_timer(&cs->dc.isac.arcofitimer); + cs->dc.isac.arcofitimer.expires = jiffies + ((ARCOFI_TIMER_VALUE * HZ)/1000); + add_timer(&cs->dc.isac.arcofitimer); +} + +static void +send_arcofi(struct IsdnCardState *cs) { u_char val; - long flags; - int cnt=30; - cs->mon_txp = 0; - cs->mon_txc = msg[0]; - memcpy(cs->mon_tx, &msg[1], cs->mon_txc); - switch(bc) { + add_arcofi_timer(cs); + cs->dc.isac.mon_txp = 0; + cs->dc.isac.mon_txc = cs->dc.isac.arcofi_list->len; + memcpy(cs->dc.isac.mon_tx, cs->dc.isac.arcofi_list->msg, cs->dc.isac.mon_txc); + switch(cs->dc.isac.arcofi_bc) { case 0: break; - case 1: cs->mon_tx[1] |= 0x40; + case 1: cs->dc.isac.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); + cs->dc.isac.mocr &= 0x0f; + cs->dc.isac.mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.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); + cs->writeisac(cs, ISAC_MOX1, cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]); + cs->dc.isac.mocr |= 0x10; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); +} + +int +arcofi_fsm(struct IsdnCardState *cs, int event, void *data) { + if (cs->debug & L1_DEB_MONITOR) { + debugl1(cs, "arcofi state %d event %d", cs->dc.isac.arcofi_state, event); + } + if (event == ARCOFI_TIMEOUT) { + cs->dc.isac.arcofi_state = ARCOFI_NOP; + test_and_set_bit(FLG_ARCOFI_ERROR, &cs->HW_Flags); + wake_up_interruptible(&cs->dc.isac.arcofi_wait); + return(1); } - if (receive) { - while (cnt && !test_bit(HW_MON1_RX_END, &cs->HW_Flags)) { - cnt--; - udelay(500); - } + switch (cs->dc.isac.arcofi_state) { + case ARCOFI_NOP: + if (event == ARCOFI_START) { + cs->dc.isac.arcofi_list = data; + cs->dc.isac.arcofi_state = ARCOFI_TRANSMIT; + send_arcofi(cs); + } + break; + case ARCOFI_TRANSMIT: + if (event == ARCOFI_TX_END) { + if (cs->dc.isac.arcofi_list->receive) { + add_arcofi_timer(cs); + cs->dc.isac.arcofi_state = ARCOFI_RECEIVE; + } else { + if (cs->dc.isac.arcofi_list->next) { + cs->dc.isac.arcofi_list = + cs->dc.isac.arcofi_list->next; + send_arcofi(cs); + } else { + if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) { + del_timer(&cs->dc.isac.arcofitimer); + } + cs->dc.isac.arcofi_state = ARCOFI_NOP; + wake_up_interruptible(&cs->dc.isac.arcofi_wait); + } + } + } + break; + case ARCOFI_RECEIVE: + if (event == ARCOFI_RX_END) { + if (cs->dc.isac.arcofi_list->next) { + cs->dc.isac.arcofi_list = + cs->dc.isac.arcofi_list->next; + cs->dc.isac.arcofi_state = ARCOFI_TRANSMIT; + send_arcofi(cs); + } else { + if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) { + del_timer(&cs->dc.isac.arcofitimer); + } + cs->dc.isac.arcofi_state = ARCOFI_NOP; + wake_up_interruptible(&cs->dc.isac.arcofi_wait); + } + } + break; + default: + debugl1(cs, "Arcofi unknown state %x", cs->dc.isac.arcofi_state); + return(2); } - restore_flags(flags); - if (cnt <= 0) { - printk(KERN_WARNING"HiSax arcofi monitor timed out\n"); - debugl1(cs, "HiSax arcofi monitor timed out"); + return(0); +} + +static void +arcofi_timer(struct IsdnCardState *cs) { + arcofi_fsm(cs, ARCOFI_TIMEOUT, NULL); +} + +void +clear_arcofi(struct IsdnCardState *cs) { + if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) { + del_timer(&cs->dc.isac.arcofitimer); } - return(cnt); +} + +void +init_arcofi(struct IsdnCardState *cs) { + cs->dc.isac.arcofitimer.function = (void *) arcofi_timer; + cs->dc.isac.arcofitimer.data = (long) cs; + init_timer(&cs->dc.isac.arcofitimer); } diff --git a/drivers/isdn/hisax/arcofi.h b/drivers/isdn/hisax/arcofi.h index be1097d15bfd..86617d6a1b63 100644 --- a/drivers/isdn/hisax/arcofi.h +++ b/drivers/isdn/hisax/arcofi.h @@ -1,4 +1,4 @@ -/* $Id: arcofi.h,v 1.3 1998/05/25 12:57:39 keil Exp $ +/* $Id: arcofi.h,v 1.4 1999/07/01 08:11:18 keil Exp $ * arcofi.h Ansteuerung ARCOFI 2165 * @@ -7,6 +7,9 @@ * * * $Log: arcofi.h,v $ + * Revision 1.4 1999/07/01 08:11:18 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.3 1998/05/25 12:57:39 keil * HiSax golden code from certification, Don't use !!! * No leased lines, no X75, but many changes. @@ -21,4 +24,16 @@ #define ARCOFI_USE 1 -extern int send_arcofi(struct IsdnCardState *cs, const u_char *msg, int bc, int receive); +/* states */ +#define ARCOFI_NOP 0 +#define ARCOFI_TRANSMIT 1 +#define ARCOFI_RECEIVE 2 +/* events */ +#define ARCOFI_START 1 +#define ARCOFI_TX_END 2 +#define ARCOFI_RX_END 3 +#define ARCOFI_TIMEOUT 4 + +extern int arcofi_fsm(struct IsdnCardState *cs, int event, void *data); +extern void init_arcofi(struct IsdnCardState *cs); +extern void clear_arcofi(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/asuscom.c b/drivers/isdn/hisax/asuscom.c index eeaad83f5ddc..679cf9d1cd3a 100644 --- a/drivers/isdn/hisax/asuscom.c +++ b/drivers/isdn/hisax/asuscom.c @@ -1,4 +1,4 @@ -/* $Id: asuscom.c,v 1.5 1998/11/15 23:54:19 keil Exp $ +/* $Id: asuscom.c,v 1.7 1999/07/12 21:04:53 keil Exp $ * asuscom.c low level stuff for ASUSCOM NETWORK INC. ISDNLink cards * @@ -8,6 +8,13 @@ * * * $Log: asuscom.c,v $ + * Revision 1.7 1999/07/12 21:04:53 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.6 1999/07/01 08:11:18 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.5 1998/11/15 23:54:19 keil * changes from 2.0 * @@ -32,7 +39,7 @@ extern const char *CardType[]; -const char *Asuscom_revision = "$Revision: 1.5 $"; +const char *Asuscom_revision = "$Revision: 1.7 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -181,7 +188,7 @@ static void asuscom_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; if (!cs) { printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n"); @@ -189,16 +196,12 @@ asuscom_interrupt(int intno, void *dev_id, struct pt_regs *regs) } val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40); Start_HSCX: - if (val) { + 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) { + 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) @@ -211,23 +214,19 @@ asuscom_interrupt(int intno, void *dev_id, struct pt_regs *regs) 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); - } + 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.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0x0); + 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); } 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; + u_char ista, val, icnt = 5; if (!cs) { printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n"); @@ -290,13 +289,13 @@ reset_asuscom(struct IsdnCardState *cs) save_flags(flags); sti(); current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); 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; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); 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); @@ -317,13 +316,6 @@ Asus_card_msg(struct IsdnCardState *cs, int mt, void *arg) 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); @@ -334,8 +326,8 @@ Asus_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init -setup_asuscom(struct IsdnCard *card) +__initfunc(int +setup_asuscom(struct IsdnCard *card)) { int bytecnt; struct IsdnCardState *cs = card->cs; @@ -378,6 +370,7 @@ setup_asuscom(struct IsdnCard *card) cs->writeisac = &WriteISAC_IPAC; cs->readisacfifo = &ReadISACfifo_IPAC; cs->writeisacfifo = &WriteISACfifo_IPAC; + cs->irq_func = &asuscom_interrupt_ipac; printk(KERN_INFO "Asus: IPAC version %x\n", val); } else { cs->subtyp = ASUS_ISACHSCX; @@ -390,6 +383,7 @@ setup_asuscom(struct IsdnCard *card) cs->writeisac = &WriteISAC; cs->readisacfifo = &ReadISACfifo; cs->writeisacfifo = &WriteISACfifo; + cs->irq_func = &asuscom_interrupt; ISACVersion(cs, "ISDNLink:"); if (HscxVersion(cs, "ISDNLink:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/avm_a1.c b/drivers/isdn/hisax/avm_a1.c index e782708fb744..55df36f0f738 100644 --- a/drivers/isdn/hisax/avm_a1.c +++ b/drivers/isdn/hisax/avm_a1.c @@ -1,4 +1,4 @@ -/* $Id: avm_a1.c,v 2.10 1998/11/15 23:54:21 keil Exp $ +/* $Id: avm_a1.c,v 2.11 1999/07/12 21:04:54 keil Exp $ * avm_a1.c low level stuff for AVM A1 (Fritz) isdn cards * @@ -6,6 +6,10 @@ * * * $Log: avm_a1.c,v $ + * Revision 2.11 1999/07/12 21:04:54 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * * Revision 2.10 1998/11/15 23:54:21 keil * changes from 2.0 * @@ -66,7 +70,7 @@ #include "isdnl1.h" extern const char *CardType[]; -static const char *avm_revision = "$Revision: 2.10 $"; +static const char *avm_revision = "$Revision: 2.11 $"; #define AVM_A1_STAT_ISAC 0x01 #define AVM_A1_STAT_HSCX 0x02 @@ -153,7 +157,7 @@ static void avm_a1_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, sval, stat = 0; + u_char val, sval; if (!cs) { printk(KERN_WARNING "AVM A1: Spurious interrupt!\n"); @@ -167,29 +171,21 @@ avm_a1_interrupt(int intno, void *dev_id, struct pt_regs *regs) debugl1(cs, "avm IntStatus %x", sval); if (!(sval & AVM_A1_STAT_HSCX)) { val = readreg(cs->hw.avm.hscx[1], HSCX_ISTA); - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } } if (!(sval & AVM_A1_STAT_ISAC)) { val = readreg(cs->hw.avm.isac, ISAC_ISTA); - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } } } - if (stat & 1) { - 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(cs->hw.avm.isac, ISAC_MASK, 0xFF); - writereg(cs->hw.avm.isac, ISAC_MASK, 0x0); - } + writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0xFF); + writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0xFF); + writereg(cs->hw.avm.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.avm.isac, ISAC_MASK, 0x0); + writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0x0); + writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0x0); } inline static void @@ -219,9 +215,6 @@ AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) 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); @@ -234,8 +227,8 @@ AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init -setup_avm_a1(struct IsdnCard *card) +__initfunc(int +setup_avm_a1(struct IsdnCard *card)) { u_char val; struct IsdnCardState *cs = card->cs; @@ -378,6 +371,7 @@ setup_avm_a1(struct IsdnCard *card) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &AVM_card_msg; + cs->irq_func = &avm_a1_interrupt; ISACVersion(cs, "AVM A1:"); if (HscxVersion(cs, "AVM A1:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/avm_a1p.c b/drivers/isdn/hisax/avm_a1p.c index c11ac41e4448..29b630c9b269 100644 --- a/drivers/isdn/hisax/avm_a1p.c +++ b/drivers/isdn/hisax/avm_a1p.c @@ -1,4 +1,4 @@ -/* $Id: avm_a1p.c,v 2.3 1998/11/15 23:54:22 keil Exp $ +/* $Id: avm_a1p.c,v 2.4 1999/07/12 21:04:55 keil Exp $ * * avm_a1p.c low level stuff for the following AVM cards: * A1 PCMCIA @@ -8,6 +8,10 @@ * Author Carsten Paeth (calle@calle.in-berlin.de) * * $Log: avm_a1p.c,v $ + * Revision 2.4 1999/07/12 21:04:55 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * * Revision 2.3 1998/11/15 23:54:22 keil * changes from 2.0 * @@ -67,7 +71,7 @@ #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) -static const char *avm_revision = "$Revision: 2.3 $"; +static const char *avm_revision = "$Revision: 2.4 $"; static inline u_char ReadISAC(struct IsdnCardState *cs, u_char offset) @@ -195,7 +199,7 @@ static void avm_a1p_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, sval, stat = 0; + u_char val, sval; if (!cs) { printk(KERN_WARNING "AVM A1 PCMCIA: Spurious interrupt!\n"); @@ -206,35 +210,26 @@ avm_a1p_interrupt(int intno, void *dev_id, struct pt_regs *regs) debugl1(cs, "avm IntStatus %x", sval); if (sval & ASL0_R_HSCX) { val = ReadHSCX(cs, 1, HSCX_ISTA); - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } } if (sval & ASL0_R_ISAC) { val = ReadISAC(cs, ISAC_ISTA); - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } } } - if (stat & 1) { - WriteHSCX(cs, 0, HSCX_MASK, 0xff); - WriteHSCX(cs, 1, HSCX_MASK, 0xff); - WriteHSCX(cs, 0, HSCX_MASK, 0x00); - WriteHSCX(cs, 1, HSCX_MASK, 0x00); - } - if (stat & 2) { - WriteISAC(cs, ISAC_MASK, 0xff); - WriteISAC(cs, ISAC_MASK, 0x00); - } + WriteHSCX(cs, 0, HSCX_MASK, 0xff); + WriteHSCX(cs, 1, HSCX_MASK, 0xff); + WriteISAC(cs, ISAC_MASK, 0xff); + WriteISAC(cs, ISAC_MASK, 0x00); + WriteHSCX(cs, 0, HSCX_MASK, 0x00); + WriteHSCX(cs, 1, HSCX_MASK, 0x00); } static int AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - int ret; switch (mt) { case CARD_RESET: byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00); @@ -249,15 +244,6 @@ AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) /* free_irq(cs->irq, cs); */ return 0; - case CARD_SETIRQ: - ret = request_irq(cs->irq, &avm_a1p_interrupt, - I4L_IRQ_FLAG, "HiSax", cs); - if (ret) - return ret; - byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET, - ASL0_W_TDISABLE|ASL0_W_TRESET|ASL0_W_IRQENABLE); - return 0; - case CARD_INIT: clear_pending_isac_ints(cs); clear_pending_hscx_ints(cs); @@ -323,6 +309,7 @@ setup_avm_a1_pcmcia(struct IsdnCard *card)) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &AVM_card_msg; + cs->irq_func = &avm_a1p_interrupt; ISACVersion(cs, "AVM A1 PCMCIA:"); if (HscxVersion(cs, "AVM A1 PCMCIA:")) { diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c index c0f04f91c9d3..ef5ddd45fc4b 100644 --- a/drivers/isdn/hisax/avm_pci.c +++ b/drivers/isdn/hisax/avm_pci.c @@ -1,4 +1,4 @@ -/* $Id: avm_pci.c,v 1.7 1999/02/22 18:26:30 keil Exp $ +/* $Id: avm_pci.c,v 1.11 1999/08/11 21:01:18 keil Exp $ * avm_pci.c low level stuff for AVM Fritz!PCI and ISA PnP isdn cards * Thanks to AVM, Berlin for informations @@ -7,6 +7,19 @@ * * * $Log: avm_pci.c,v $ + * Revision 1.11 1999/08/11 21:01:18 keil + * new PCI codefix + * + * Revision 1.10 1999/08/10 16:01:44 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.9 1999/07/12 21:04:57 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.8 1999/07/01 08:11:19 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.7 1999/02/22 18:26:30 keil * Argh ! ISAC address was only set with PCI * @@ -37,10 +50,13 @@ #include "isac.h" #include "isdnl1.h" #include +#ifndef COMPAT_HAS_NEW_PCI +#include +#endif #include extern const char *CardType[]; -static const char *avm_pci_rev = "$Revision: 1.7 $"; +static const char *avm_pci_rev = "$Revision: 1.11 $"; #define AVM_FRITZ_PCI 1 #define AVM_FRITZ_PNP 2 @@ -466,7 +482,7 @@ HDLC_irq(struct BCState *bcs, u_int stat) { if (bcs->st->lli.l1writewakeup && (PACKET_NOACK != bcs->tx_skb->pkt_type)) bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hdlc.count); - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->hw.hdlc.count = 0; bcs->tx_skb = NULL; } @@ -593,7 +609,7 @@ close_hdlcstate(struct BCState *bcs) discard_queue(&bcs->rqueue); discard_queue(&bcs->squeue); if (bcs->tx_skb) { - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); } @@ -687,7 +703,7 @@ static void avm_pcipnp_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; u_char sval; if (!cs) { @@ -701,15 +717,12 @@ avm_pcipnp_interrupt(int intno, void *dev_id, struct pt_regs *regs) if (!(sval & AVM_STATUS0_IRQ_ISAC)) { val = ReadISAC(cs, ISAC_ISTA); isac_interrupt(cs, val); - stat |= 2; } if (!(sval & AVM_STATUS0_IRQ_HDLC)) { HDLC_irq_main(cs); } - if (stat & 2) { - WriteISAC(cs, ISAC_MASK, 0xFF); - WriteISAC(cs, ISAC_MASK, 0x0); - } + WriteISAC(cs, ISAC_MASK, 0xFF); + WriteISAC(cs, ISAC_MASK, 0x0); } static void @@ -733,8 +746,6 @@ reset_avmpcipnp(struct IsdnCardState *cs) static int AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - u_int irq_flag; - switch (mt) { case CARD_RESET: reset_avmpcipnp(cs); @@ -743,13 +754,6 @@ AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) outb(0, cs->hw.avm.cfg_reg + 2); release_region(cs->hw.avm.cfg_reg, 32); return(0); - case CARD_SETIRQ: - if (cs->subtyp == AVM_FRITZ_PCI) - irq_flag = I4L_IRQ_FLAG | SA_SHIRQ; - else - irq_flag = I4L_IRQ_FLAG; - return(request_irq(cs->irq, &avm_pcipnp_interrupt, - irq_flag, "HiSax", cs)); case CARD_INIT: clear_pending_isac_ints(cs); initisac(cs); @@ -769,7 +773,11 @@ AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } +#ifdef COMPAT_HAS_NEW_PCI static struct pci_dev *dev_avm __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif __initfunc(int setup_avm_pcipnp(struct IsdnCard *card)) @@ -788,6 +796,7 @@ setup_avm_pcipnp(struct IsdnCard *card)) cs->subtyp = AVM_FRITZ_PNP; } else { #if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI if (!pci_present()) { printk(KERN_ERR "FritzPCI: no PCI bus present\n"); return(0); @@ -799,7 +808,7 @@ setup_avm_pcipnp(struct IsdnCard *card)) printk(KERN_WARNING "FritzPCI: No IRQ for PCI card found\n"); return(0); } - cs->hw.avm.cfg_reg = dev_avm->base_address[1] & + cs->hw.avm.cfg_reg = get_pcibase(dev_avm, 1) & PCI_BASE_ADDRESS_IO_MASK; if (!cs->hw.avm.cfg_reg) { printk(KERN_WARNING "FritzPCI: No IO-Adr for PCI card found\n"); @@ -810,6 +819,37 @@ setup_avm_pcipnp(struct IsdnCard *card)) printk(KERN_WARNING "FritzPCI: No PCI card found\n"); return(0); } +#else + for (; pci_index < 255; pci_index++) { + unsigned char pci_bus, pci_device_fn; + unsigned int ioaddr; + unsigned char irq; + + if (pcibios_find_device (PCI_VENDOR_AVM, + PCI_FRITZPCI_ID, pci_index, + &pci_bus, &pci_device_fn) != 0) { + continue; + } + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &irq); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &ioaddr); + cs->irq = irq; + cs->hw.avm.cfg_reg = ioaddr & PCI_BASE_ADDRESS_IO_MASK; + if (!cs->hw.avm.cfg_reg) { + printk(KERN_WARNING "FritzPCI: No IO-Adr for PCI card found\n"); + return(0); + } + cs->subtyp = AVM_FRITZ_PCI; + break; + } + if (pci_index == 255) { + printk(KERN_WARNING "FritzPCI: No PCI card found\n"); + return(0); + } + pci_index++; +#endif /* COMPAT_HAS_NEW_PCI */ + cs->irq_flags |= SA_SHIRQ; #else printk(KERN_WARNING "FritzPCI: NO_PCI_BIOS\n"); return (0); @@ -846,8 +886,6 @@ setup_avm_pcipnp(struct IsdnCard *card)) break; default: printk(KERN_WARNING "AVM unknown subtype %d\n", cs->subtyp); - outb(0, cs->hw.avm.cfg_reg + 2); - release_region(cs->hw.avm.cfg_reg, 32); return(0); } printk(KERN_INFO "HiSax: %s config irq:%d base:0x%X\n", @@ -860,6 +898,7 @@ setup_avm_pcipnp(struct IsdnCard *card)) cs->writeisacfifo = &WriteISACfifo; cs->BC_Send_Data = &fill_hdlc; cs->cardmsg = &AVM_card_msg; + cs->irq_func = &avm_pcipnp_interrupt; ISACVersion(cs, (cs->subtyp == AVM_FRITZ_PCI) ? "AVM PCI:" : "AVM PnP:"); return (1); } diff --git a/drivers/isdn/hisax/bkm_a4t.c b/drivers/isdn/hisax/bkm_a4t.c new file mode 100644 index 000000000000..e97c8e9bf01e --- /dev/null +++ b/drivers/isdn/hisax/bkm_a4t.c @@ -0,0 +1,403 @@ +/* $Id: bkm_a4t.c,v 1.6 1999/08/11 21:01:22 keil Exp $ + * bkm_a4t.c low level stuff for T-Berkom A4T + * derived from the original file sedlbauer.c + * derived from the original file niccy.c + * derived from the original file netjet.c + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * $Log: bkm_a4t.c,v $ + * Revision 1.6 1999/08/11 21:01:22 keil + * new PCI codefix + * + * Revision 1.5 1999/08/10 16:01:46 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.4 1999/07/14 11:43:14 keil + * correct PCI_SUBSYSTEM_VENDOR_ID + * + * Revision 1.3 1999/07/12 21:04:58 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.2 1999/07/01 08:07:53 keil + * Initial version + * + * + */ + +#define __NO_VERSION__ + +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "jade.h" +#include "isdnl1.h" +#include "bkm_ax.h" +#include +#ifndef COMPAT_HAS_NEW_PCI +#include +#endif + +extern const char *CardType[]; + +const char *bkm_a4t_revision = "$Revision: 1.6 $"; + + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_int ret; + long flags; + unsigned int *po = (unsigned int *) adr; /* Postoffice */ + save_flags(flags); + cli(); + *po = (GCS_2 | PO_WRITE | off); + __WAITI20__(po); + *po = (ale | PO_READ); + __WAITI20__(po); + ret = *po; + restore_flags(flags); + return ((unsigned char) 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 */ + int i; + for (i = 0; i < size; i++) + *data++ = readreg(ale, adr, off); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + unsigned int *po = (unsigned int *) adr; /* Postoffice */ + save_flags(flags); + cli(); + *po = (GCS_2 | PO_WRITE | off); + __WAITI20__(po); + *po = (ale | PO_WRITE | data); + __WAITI20__(po); + 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 */ + int i; + + for (i = 0; i < size; i++) + writereg(ale, adr, off, *data++); +} + + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, 0, data, size); +} + +static u_char +ReadJADE(struct IsdnCardState *cs, int jade, u_char offset) +{ + return (readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, offset + (jade == -1 ? 0 : (jade ? 0xC0 : 0x80)))); +} + +static void +WriteJADE(struct IsdnCardState *cs, int jade, u_char offset, u_char value) +{ + writereg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, offset + (jade == -1 ? 0 : (jade ? 0xC0 : 0x80)), value); +} + +/* + * fast interrupt JADE stuff goes here + */ + +#define READJADE(cs, nr, reg) readreg(cs->hw.ax.jade_ale,\ + cs->hw.ax.jade_adr, reg + (nr == -1 ? 0 : (nr ? 0xC0 : 0x80))) +#define WRITEJADE(cs, nr, reg, data) writereg(cs->hw.ax.jade_ale,\ + cs->hw.ax.jade_adr, reg + (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), data) + +#define READJADEFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ax.jade_ale,\ + cs->hw.ax.jade_adr, (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), ptr, cnt) +#define WRITEJADEFIFO(cs, nr, ptr, cnt) writefifo( cs->hw.ax.jade_ale,\ + cs->hw.ax.jade_adr, (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), ptr, cnt) + +#include "jade_irq.c" + +static void +bkm_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val = 0; + I20_REGISTER_FILE *pI20_Regs; + + if (!cs) { + printk(KERN_WARNING "HiSax: Telekom A4T: Spurious interrupt!\n"); + return; + } + pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base); + + /* ISDN interrupt pending? */ + if (pI20_Regs->i20IntStatus & intISDN) { + /* Reset the ISDN interrupt */ + pI20_Regs->i20IntStatus = intISDN; + /* Disable ISDN interrupt */ + pI20_Regs->i20IntCtrl &= ~intISDN; + /* Channel A first */ + val = readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, jade_HDLC_ISR + 0x80); + if (val) { + jade_int_main(cs, val, 0); + } + /* Channel B */ + val = readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, jade_HDLC_ISR + 0xC0); + if (val) { + jade_int_main(cs, val, 1); + } + /* D-Channel */ + val = readreg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, ISAC_ISTA); + if (val) { + isac_interrupt(cs, val); + } + /* Reenable ISDN interrupt */ + pI20_Regs->i20IntCtrl |= intISDN; + } +} + +void +release_io_bkm(struct IsdnCardState *cs) +{ + if (cs->hw.ax.base) { + iounmap((void *) cs->hw.ax.base); + cs->hw.ax.base = 0; + } +} + +static void +enable_bkm_int(struct IsdnCardState *cs, unsigned bEnable) +{ + if (cs->typ == ISDN_CTYPE_BKM_A4T) { + I20_REGISTER_FILE *pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base); + if (bEnable) + pI20_Regs->i20IntCtrl |= (intISDN | intPCI); + else + /* CAUTION: This disables the video capture driver too */ + pI20_Regs->i20IntCtrl &= ~(intISDN | intPCI); + } +} + +static void +reset_bkm(struct IsdnCardState *cs) +{ + long flags; + + if (cs->typ == ISDN_CTYPE_BKM_A4T) { + I20_REGISTER_FILE *pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base); + save_flags(flags); + sti(); + /* Issue the I20 soft reset */ + pI20_Regs->i20SysControl = 0xFF; /* all in */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + /* Remove the soft reset */ + pI20_Regs->i20SysControl = sysRESET | 0xFF; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + /* Set our configuration */ + pI20_Regs->i20SysControl = sysRESET | sysCFG; + /* Issue ISDN reset */ + pI20_Regs->i20GuestControl = guestWAIT_CFG | + g_A4T_JADE_RES | + g_A4T_ISAR_RES | + g_A4T_ISAC_RES | + g_A4T_JADE_BOOTR | + g_A4T_ISAR_BOOTR; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + + /* Remove RESET state from ISDN */ + pI20_Regs->i20GuestControl &= ~(g_A4T_ISAC_RES | + g_A4T_JADE_RES | + g_A4T_ISAR_RES); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + restore_flags(flags); + } +} + +static int +BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + /* Disable ints */ + enable_bkm_int(cs, 0); + reset_bkm(cs); + return (0); + case CARD_RELEASE: + /* Sanity */ + enable_bkm_int(cs, 0); + reset_bkm(cs); + release_io_bkm(cs); + return (0); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_jade_ints(cs); + initisac(cs); + initjade(cs); + /* Enable ints */ + enable_bkm_int(cs, 1); + return (0); + case CARD_TEST: + return (0); + } + return (0); +} + +#ifdef COMPAT_HAS_NEW_PCI +static struct pci_dev *dev_a4t __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif + +__initfunc(int + setup_bkm_a4t(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + u_int pci_memaddr = 0, found = 0; + I20_REGISTER_FILE *pI20_Regs; +#if CONFIG_PCI +#ifndef COMPAT_HAS_NEW_PCI + u_char pci_bus, pci_device_fn, pci_irq = 0; +#endif +#endif + + strcpy(tmp, bkm_a4t_revision); + printk(KERN_INFO "HiSax: T-Berkom driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ == ISDN_CTYPE_BKM_A4T) { + cs->subtyp = BKM_A4T; + } else + return (0); + +#if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI + if (!pci_present()) { + printk(KERN_ERR "bkm_a4t: no PCI bus present\n"); + return (0); + } + if ((dev_a4t = pci_find_device(I20_VENDOR_ID, I20_DEVICE_ID, dev_a4t))) { + u_int sub_sys_id = 0; + + pci_read_config_dword(dev_a4t, PCI_SUBSYSTEM_VENDOR_ID, + &sub_sys_id); + if (sub_sys_id == ((A4T_SUBSYS_ID << 16) | A4T_SUBVEN_ID)) { + found = 1; + pci_memaddr = get_pcibase(dev_a4t, 0); + cs->irq = dev_a4t->irq; + } + } +#else + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(I20_VENDOR_ID, + I20_DEVICE_ID, + pci_index, + &pci_bus, + &pci_device_fn) == PCIBIOS_SUCCESSFUL) { + u_int sub_sys_id = 0; + + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_SUBSYSTEM_VENDOR_ID, &sub_sys_id); + if (sub_sys_id == ((A4T_SUBSYS_ID << 16) | A4T_SUBVEN_ID)) { + found = 1; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + cs->irq = pci_irq; + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_memaddr); + break; + } + } + } +#endif /* COMPAT_HAS_NEW_PCI */ + if (!found) { + printk(KERN_WARNING "HiSax: %s: Card not found\n", CardType[card->typ]); + return (0); + } +#ifndef COMPAT_HAS_NEW_PCI + pci_index++; +#endif + if (!cs->irq) { /* IRQ range check ?? */ + printk(KERN_WARNING "HiSax: %s: No IRQ\n", CardType[card->typ]); + return (0); + } + if (!pci_memaddr) { + printk(KERN_WARNING "HiSax: %s: No Memory base address\n", CardType[card->typ]); + return (0); + } + pci_memaddr &= PCI_BASE_ADDRESS_MEM_MASK; + cs->hw.ax.base = (u_int) ioremap(pci_memaddr, 4096); + /* Check suspecious address */ + pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base); + if ((pI20_Regs->i20IntStatus & 0x8EFFFFFF) != 0) { + printk(KERN_WARNING "HiSax: %s address %x-%x suspecious\n", + CardType[card->typ], cs->hw.ax.base, cs->hw.ax.base + 4096); + iounmap((void *) cs->hw.ax.base); + cs->hw.ax.base = 0; + return (0); + } + cs->hw.ax.isac_adr = cs->hw.ax.base + PO_OFFSET; + cs->hw.ax.jade_adr = cs->hw.ax.base + PO_OFFSET; + cs->hw.ax.isac_ale = GCS_1; + cs->hw.ax.jade_ale = GCS_3; +#else + printk(KERN_WARNING "HiSax: %s: NO_PCI_BIOS\n", CardType[card->typ]); + printk(KERN_WARNING "HiSax: %s: unable to configure\n", CardType[card->typ]); + return (0); +#endif /* CONFIG_PCI */ + printk(KERN_INFO "HiSax: %s: Card configured at 0x%X IRQ %d\n", + CardType[card->typ], cs->hw.ax.base, cs->irq); + + reset_bkm(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadJADE; + cs->BC_Write_Reg = &WriteJADE; + cs->BC_Send_Data = &jade_fill_fifo; + cs->cardmsg = &BKM_card_msg; + cs->irq_func = &bkm_interrupt; + cs->irq_flags |= SA_SHIRQ; + ISACVersion(cs, "Telekom A4T:"); + /* Jade version */ + JadeVersion(cs, "Telekom A4T:"); + return (1); +} diff --git a/drivers/isdn/hisax/bkm_a8.c b/drivers/isdn/hisax/bkm_a8.c new file mode 100644 index 000000000000..0e014ebc06e1 --- /dev/null +++ b/drivers/isdn/hisax/bkm_a8.c @@ -0,0 +1,529 @@ +/* $Id: bkm_a8.c,v 1.6 1999/08/11 21:01:24 keil Exp $ + * bkm_a8.c low level stuff for Scitel Quadro (4*S0, passive) + * derived from the original file sedlbauer.c + * derived from the original file niccy.c + * derived from the original file netjet.c + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * $Log: bkm_a8.c,v $ + * Revision 1.6 1999/08/11 21:01:24 keil + * new PCI codefix + * + * Revision 1.5 1999/08/10 16:01:48 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.4 1999/07/14 11:43:15 keil + * correct PCI_SUBSYSTEM_VENDOR_ID + * + * Revision 1.3 1999/07/12 21:04:59 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.2 1999/07/01 08:07:54 keil + * Initial version + * + * + */ +#define __NO_VERSION__ + +#include "hisax.h" +#include "isac.h" +#include "ipac.h" +#include "hscx.h" +#include "isdnl1.h" +#include "bkm_ax.h" +#include +#ifndef COMPAT_HAS_NEW_PCI +#include +#endif + +#define ATTEMPT_PCI_REMAPPING /* Required for PLX rev 1 */ + +extern const char *CardType[]; + +const char sct_quadro_revision[] = "$Revision: 1.6 $"; + +/* To survive the startup phase */ +typedef struct { + u_int active; /* true/false */ + u_int base; /* ipac base address */ +} IPAC_STATE; + +static IPAC_STATE ipac_state[4 + 1] __initdata = +{ + {0, 0}, /* dummy */ + {0, 0}, /* SCT_1 */ + {0, 0}, /* SCT_2 */ + {0, 0}, /* SCT_3 */ + {0, 0} /* SCT_4 */ +}; + +static const char *sct_quadro_subtypes[] = +{ + "", + "#1", + "#2", + "#3", + "#4" +}; + + +#define wordout(addr,val) outw(val,addr) +#define wordin(addr) inw(addr) + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + save_flags(flags); + cli(); + wordout(ale, off); + ret = wordin(adr) & 0xFF; + 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 */ + int i; + wordout(ale, off); + for (i = 0; i < size; i++) + data[i] = wordin(adr) & 0xFF; +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + save_flags(flags); + cli(); + wordout(ale, off); + wordout(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 */ + int i; + wordout(ale, off); + for (i = 0; i < size; i++) + wordout(adr, data[i]); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.ax.base, cs->hw.ax.data_adr, offset | 0x80)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset | 0x80, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.ax.base, cs->hw.ax.data_adr, 0x80, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.ax.base, cs->hw.ax.data_adr, 0x80, data, size); +} + + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0), value); +} + +/* Check whether the specified ipac is already active or not */ +static int +is_ipac_active(u_int ipac_nr) +{ + return (ipac_state[ipac_nr].active); +} + +/* Set the specific ipac to active */ +static void +set_ipac_active(u_int ipac_nr, u_int active) +{ + /* set activation state */ + ipac_state[ipac_nr].active = active; +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.ax.base, \ + cs->hw.ax.data_adr, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ax.base, \ + cs->hw.ax.data_adr, reg + (nr ? 0x40 : 0), data) +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ax.base, \ + cs->hw.ax.data_adr, (nr ? 0x40 : 0), ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ax.base, \ + cs->hw.ax.data_adr, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +bkm_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char ista, val, icnt = 5; + int i; + if (!cs) { + printk(KERN_WARNING "HiSax: Scitel Quadro: Spurious interrupt!\n"); + return; + } + ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA); + + Start_IPAC: + if (cs->debug & L1_DEB_IPAC) + debugl1(cs, "IPAC ISTA %02X", ista); + if (ista & 0x0f) { + val = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, 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.ax.base, cs->hw.ax.data_adr, ISAC_ISTA | 0x80); + if (val) { + isac_interrupt(cs, val); + } + } + if (ista & 0x10) { + val = 0x01; + isac_interrupt(cs, val); + } + ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA); + if ((ista & 0x3f) && icnt) { + icnt--; + goto Start_IPAC; + } + if (!icnt) + printk(KERN_WARNING "HiSax: %s (%s) IRQ LOOP\n", + CardType[cs->typ], + sct_quadro_subtypes[cs->subtyp]); + writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xFF); + writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xC0); + + /* Read out all interrupt sources from currently not active ipacs */ + /* "Handle" all interrupts from currently not active ipac by reading the regs */ + for (i = SCT_1; i <= SCT_4; i++) + if (!is_ipac_active(i)) { + u_int base = ipac_state[i].base; + if (readreg(base, base + 4, 0xC1)) { + readreg(base, base + 4, 0xA0); + readreg(base, base + 4, 0xA4); + readreg(base, base + 4, 0x20); + readreg(base, base + 4, 0x24); + readreg(base, base + 4, 0x60); + readreg(base, base + 4, 0x64); + readreg(base, base + 4, 0xC1); + readreg(base, base + 4, ISAC_CIR0 + 0x80); + } + } +} + + +void +release_io_sct_quadro(struct IsdnCardState *cs) +{ + /* ?? */ +} + +static void +enable_bkm_int(struct IsdnCardState *cs, unsigned bEnable) +{ + if (cs->typ == ISDN_CTYPE_SCT_QUADRO) { + if (bEnable) + wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) | 0x41)); + else + /* Issue general di only if no ipac is active */ + if (!is_ipac_active(SCT_1) && + !is_ipac_active(SCT_2) && + !is_ipac_active(SCT_3) && + !is_ipac_active(SCT_4)) + wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) & ~0x41)); + } +} + +static void +reset_bkm(struct IsdnCardState *cs) +{ + long flags; + + if (cs->typ == ISDN_CTYPE_SCT_QUADRO) { + if (!is_ipac_active(SCT_1) && + !is_ipac_active(SCT_2) && + !is_ipac_active(SCT_3) && + !is_ipac_active(SCT_4)) { + /* Issue total reset only if no ipac is active */ + wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) & ~4)); + + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + + /* Remove the soft reset */ + wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) | 4)); + + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + restore_flags(flags); + } + } +} + +static int +BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + /* Disable ints */ + set_ipac_active(cs->subtyp, 0); + enable_bkm_int(cs, 0); + reset_bkm(cs); + return (0); + case CARD_RELEASE: + /* Sanity */ + set_ipac_active(cs->subtyp, 0); + enable_bkm_int(cs, 0); + reset_bkm(cs); + release_io_sct_quadro(cs); + return (0); + case CARD_INIT: + cs->debug |= L1_DEB_IPAC; + set_ipac_active(cs->subtyp, 1); + inithscxisac(cs, 3); + /* Enable ints */ + enable_bkm_int(cs, 1); + return (0); + case CARD_TEST: + return (0); + } + return (0); +} + +#ifdef COMPAT_HAS_NEW_PCI +static struct pci_dev *dev_a8 __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif + +__initfunc(int + setup_sct_quadro(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; +#if CONFIG_PCI + u_char pci_bus = 0, pci_device_fn = 0, pci_irq = 0, pci_rev_id; + u_int found = 0; + u_int pci_ioaddr1, pci_ioaddr2, pci_ioaddr3, pci_ioaddr4, pci_ioaddr5; +#endif + + strcpy(tmp, sct_quadro_revision); + printk(KERN_INFO "HiSax: T-Berkom driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ == ISDN_CTYPE_SCT_QUADRO) { + cs->subtyp = SCT_1; /* Preset */ + } else + return (0); + + /* Identify subtype by para[0] */ + if (card->para[0] >= SCT_1 && card->para[0] <= SCT_4) + cs->subtyp = card->para[0]; + else + printk(KERN_WARNING "HiSax: %s: Invalid subcontroller in configuration, default to 1\n", + CardType[card->typ]); +#if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI + if (!pci_present()) { + printk(KERN_ERR "bkm_a4t: no PCI bus present\n"); + return (0); + } + if ((dev_a8 = pci_find_device(PLX_VENDOR_ID, PLX_DEVICE_ID, dev_a8))) { + u_int sub_sys_id = 0; + + pci_read_config_dword(dev_a8, PCI_SUBSYSTEM_VENDOR_ID, + &sub_sys_id); + if (sub_sys_id == ((SCT_SUBSYS_ID << 16) | SCT_SUBVEN_ID)) { + found = 1; + pci_ioaddr1 = get_pcibase(dev_a8, 1); + pci_irq = dev_a8->irq; + pci_bus = dev_a8->bus->number; + pci_device_fn = dev_a8->devfn; + } + } +#else + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device( + PLX_VENDOR_ID, + PLX_DEVICE_ID, + pci_index, + &pci_bus, + &pci_device_fn) == PCIBIOS_SUCCESSFUL) { + + u_int sub_sys_id = 0; + + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_SUBSYSTEM_VENDOR_ID, &sub_sys_id); + if (sub_sys_id == ((SCT_SUBSYS_ID << 16) | SCT_SUBVEN_ID)) { + found = 1; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr1); + cs->irq = pci_irq; + break; + } + } + } +#endif /* COMPAT_HAS_NEW_PCI */ + if (!found) { + printk(KERN_WARNING "HiSax: %s (%s): Card not found\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + return (0); + } +#ifndef COMPAT_HAS_NEW_PCI + pci_index++; /* need for more as one card */ +#endif + if (!pci_irq) { /* IRQ range check ?? */ + printk(KERN_WARNING "HiSax: %s (%s): No IRQ\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + return (0); + } +#ifdef ATTEMPT_PCI_REMAPPING +/* HACK: PLX revision 1 bug: PLX address bit 7 must not be set */ + pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_REVISION_ID, &pci_rev_id); + if ((pci_ioaddr1 & 0x80) && (pci_rev_id == 1)) { + printk(KERN_WARNING "HiSax: %s (%s): PLX rev 1, remapping required!\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + /* Restart PCI negotiation */ + pcibios_write_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, (u_int) - 1); + /* Move up by 0x80 byte */ + pci_ioaddr1 += 0x80; + pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK; + pcibios_write_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, pci_ioaddr1); +#ifdef COMPAT_HAS_NEW_PCI + get_pcibase(dev_a8, 1) = pci_ioaddr1; +#endif /* COMPAT_HAS_NEW_PCI */ + } +/* End HACK */ +#endif + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &pci_ioaddr1); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &pci_ioaddr2); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_3, &pci_ioaddr3); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_4, &pci_ioaddr4); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_5, &pci_ioaddr5); + if (!pci_ioaddr1 || !pci_ioaddr2 || !pci_ioaddr3 || !pci_ioaddr4 || !pci_ioaddr5) { + printk(KERN_WARNING "HiSax: %s (%s): No IO base address(es)\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + return (0); + } + pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK; + pci_ioaddr2 &= PCI_BASE_ADDRESS_IO_MASK; + pci_ioaddr3 &= PCI_BASE_ADDRESS_IO_MASK; + pci_ioaddr4 &= PCI_BASE_ADDRESS_IO_MASK; + pci_ioaddr5 &= PCI_BASE_ADDRESS_IO_MASK; + /* Take over */ + cs->irq = pci_irq; + cs->irq_flags |= SA_SHIRQ; + /* pci_ioaddr1 is unique to all subdevices */ + /* pci_ioaddr2 is for the fourth subdevice only */ + /* pci_ioaddr3 is for the third subdevice only */ + /* pci_ioaddr4 is for the second subdevice only */ + /* pci_ioaddr5 is for the first subdevice only */ + cs->hw.ax.plx_adr = pci_ioaddr1; + /* Enter all ipac_base addresses */ + ipac_state[SCT_1].base = pci_ioaddr5 + 0x00; + ipac_state[SCT_2].base = pci_ioaddr4 + 0x08; + ipac_state[SCT_3].base = pci_ioaddr3 + 0x10; + ipac_state[SCT_4].base = pci_ioaddr2 + 0x20; + /* For isac and hscx control path */ + cs->hw.ax.base = ipac_state[cs->subtyp].base; + /* For isac and hscx data path */ + cs->hw.ax.data_adr = cs->hw.ax.base + 4; +#else + printk(KERN_WARNING "HiSax: %s (%s): NO_PCI_BIOS\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + printk(KERN_WARNING "HiSax: %s (%s): Unable to configure\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + return (0); +#endif /* CONFIG_PCI */ + printk(KERN_INFO "HiSax: %s (%s) configured at 0x%.4X, 0x%.4X, 0x%.4X and IRQ %d\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp], + cs->hw.ax.plx_adr, + cs->hw.ax.base, + cs->hw.ax.data_adr, + cs->irq); + + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + + /* Disable all currently not active ipacs */ + if (!is_ipac_active(SCT_1)) + set_ipac_active(SCT_1, 0); + if (!is_ipac_active(SCT_2)) + set_ipac_active(SCT_2, 0); + if (!is_ipac_active(SCT_3)) + set_ipac_active(SCT_3, 0); + if (!is_ipac_active(SCT_4)) + set_ipac_active(SCT_4, 0); + + /* Perfom general reset (if possible) */ + reset_bkm(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 = &BKM_card_msg; + cs->irq_func = &bkm_interrupt_ipac; + + printk(KERN_INFO "HiSax: %s (%s): IPAC Version %d\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp], + readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ID)); + return (1); +} diff --git a/drivers/isdn/hisax/bkm_ax.h b/drivers/isdn/hisax/bkm_ax.h new file mode 100644 index 000000000000..e2d5fa70e4bd --- /dev/null +++ b/drivers/isdn/hisax/bkm_ax.h @@ -0,0 +1,133 @@ +/* $Id: bkm_ax.h,v 1.2 1999/07/01 08:07:55 keil Exp $ + * bkm_ax.h low level decls for T-Berkom cards A4T and Scitel Quadro (4*S0, passive) + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * $Log: bkm_ax.h,v $ + * Revision 1.2 1999/07/01 08:07:55 keil + * Initial version + * + * + * + */ + +#ifndef __BKM_AX_H__ +#define __BKM_AX_H__ + +/* Supported boards (subtypes) */ +#define SCT_1 1 +#define SCT_2 2 +#define SCT_3 3 +#define SCT_4 4 +#define BKM_A4T 5 + + +/* A4T */ +#define I20_DEVICE_ID 0x6120 /* I20 PCI device ID */ +#define I20_VENDOR_ID 0x11DE /* I20 PCI vendor ID */ +#define A4T_SUBVEN_ID 0x0871 +#define A4T_SUBSYS_ID 0xFFA4 +/* Scitel Quadro */ +#define PLX_DEVICE_ID 0x9050 /* Scitel Quadro PLX */ +#define PLX_VENDOR_ID 0x10B5 +#define SCT_SUBVEN_ID 0x0871 +#define SCT_SUBSYS_ID 0xFFA8 + + +#define PLX_ADDR_PLX 0x14 /* Addr PLX configuration */ +#define PLX_ADDR_ISAC 0x18 /* Addr ISAC */ +#define PLX_ADDR_HSCX 0x1C /* Addr HSCX */ +#define PLX_ADDR_ALE 0x20 /* Addr ALE */ +#define PLX_ADDR_ALEPLUS 0x24 /* Next Addr behind ALE */ + +#define PLX_SUBVEN 0x2C /* Offset SubVendor */ +#define PLX_SUBSYS 0x2E /* Offset SubSystem */ + + +/* Application specific registers I20 (Siemens SZB6120H) */ +typedef struct { + /* Video front end horizontal configuration register */ + volatile u_int i20VFEHorzCfg; /* Offset 00 */ + /* Video front end vertical configuration register */ + volatile u_int i20VFEVertCfg; /* Offset 04 */ + /* Video front end scaler and pixel format register */ + volatile u_int i20VFEScaler; /* Offset 08 */ + /* Video display top register */ + volatile u_int i20VDispTop; /* Offset 0C */ + /* Video display bottom register */ + volatile u_int i20VDispBottom; /* Offset 10 */ + /* Video stride, status and frame grab register */ + volatile u_int i20VidFrameGrab;/* Offset 14 */ + /* Video display configuration register */ + volatile u_int i20VDispCfg; /* Offset 18 */ + /* Video masking map top */ + volatile u_int i20VMaskTop; /* Offset 1C */ + /* Video masking map bottom */ + volatile u_int i20VMaskBottom; /* Offset 20 */ + /* Overlay control register */ + volatile u_int i20OvlyControl; /* Offset 24 */ + /* System, PCI and general purpose pins control register */ + volatile u_int i20SysControl; /* Offset 28 */ +#define sysRESET 0x01000000 /* bit 24:Softreset (Low) */ + /* GPIO 4...0: Output fixed for our cfg! */ +#define sysCFG 0x000000E0 /* GPIO 7,6,5: Input */ + /* General purpose pins and guest bus control register */ + volatile u_int i20GuestControl;/* Offset 2C */ +#define guestWAIT_CFG 0x00005555 /* 4 PCI waits for all */ +#define guestISDN_INT_E 0x01000000 /* ISDN Int en (low) */ +#define guestVID_INT_E 0x02000000 /* Video interrupt en (low) */ +#define guestADI1_INT_R 0x04000000 /* ADI #1 int req (low) */ +#define guestADI2_INT_R 0x08000000 /* ADI #2 int req (low) */ +#define guestISDN_RES 0x10000000 /* ISDN reset bit (high) */ +#define guestADI1_INT_S 0x20000000 /* ADI #1 int pending (low) */ +#define guestADI2_INT_S 0x40000000 /* ADI #2 int pending (low) */ +#define guestISDN_INT_S 0x80000000 /* ISAC int pending (low) */ + +#define g_A4T_JADE_RES 0x01000000 /* JADE Reset (High) */ +#define g_A4T_ISAR_RES 0x02000000 /* ISAR Reset (High) */ +#define g_A4T_ISAC_RES 0x04000000 /* ISAC Reset (High) */ +#define g_A4T_JADE_BOOTR 0x08000000 /* JADE enable boot SRAM (Low) NOT USED */ +#define g_A4T_ISAR_BOOTR 0x10000000 /* ISAR enable boot SRAM (Low) NOT USED */ +#define g_A4T_JADE_INT_S 0x20000000 /* JADE interrupt pnd (Low) */ +#define g_A4T_ISAR_INT_S 0x40000000 /* ISAR interrupt pnd (Low) */ +#define g_A4T_ISAC_INT_S 0x80000000 /* ISAC interrupt pnd (Low) */ + + volatile u_int i20CodeSource; /* Offset 30 */ + volatile u_int i20CodeXferCtrl;/* Offset 34 */ + volatile u_int i20CodeMemPtr; /* Offset 38 */ + + volatile u_int i20IntStatus; /* Offset 3C */ + volatile u_int i20IntCtrl; /* Offset 40 */ +#define intISDN 0x40000000 /* GIRQ1En (ISAC/ADI) (High) */ +#define intVID 0x20000000 /* GIRQ0En (VSYNC) (High) */ +#define intCOD 0x10000000 /* CodRepIrqEn (High) */ +#define intPCI 0x01000000 /* PCI IntA enable (High) */ + + volatile u_int i20I2CCtrl; /* Offset 44 */ +} I20_REGISTER_FILE, *PI20_REGISTER_FILE; + +/* + * Postoffice structure for A4T + * + */ +#define PO_OFFSET 0x00000200 /* Postoffice offset from base */ + +#define GCS_0 0x00000000 /* Guest bus chip selects */ +#define GCS_1 0x00100000 +#define GCS_2 0x00200000 +#define GCS_3 0x00300000 + +#define PO_READ 0x00000000 /* R/W from/to guest bus */ +#define PO_WRITE 0x00800000 + +#define PO_PEND 0x02000000 + +#define POSTOFFICE(postoffice) *(volatile unsigned int*)(postoffice) + +/* Wait unlimited (don't worry) */ +#define __WAITI20__(postoffice) \ +do { \ + while ((POSTOFFICE(postoffice) & PO_PEND)) ; \ +} while (0) + +#endif /* __BKM_AX_H__ */ diff --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c index bf09acd20b50..6284a4cf38ca 100644 --- a/drivers/isdn/hisax/callc.c +++ b/drivers/isdn/hisax/callc.c @@ -1,4 +1,4 @@ -/* $Id: callc.c,v 2.25 1999/01/02 11:17:20 keil Exp $ +/* $Id: callc.c,v 2.31 1999/08/05 20:43:10 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -11,6 +11,25 @@ * Fritz Elfert * * $Log: callc.c,v $ + * Revision 2.31 1999/08/05 20:43:10 keil + * ISAR analog modem support + * + * Revision 2.30 1999/07/25 16:24:04 keil + * Fixed TEI now working again + * + * Revision 2.29 1999/07/13 21:05:41 werner + * Modified set_channel_limit to use new callback ISDN_STAT_DISCH. + * + * Revision 2.28 1999/07/09 08:30:02 keil + * cosmetics + * + * Revision 2.27 1999/07/05 23:51:38 werner + * Allow limiting of available HiSax B-chans per card. Controlled by hisaxctrl + * hisaxctrl id 10 + * + * Revision 2.26 1999/07/01 08:11:21 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.25 1999/01/02 11:17:20 keil * Changes for 2.2 * @@ -112,10 +131,15 @@ #include "../avmb1/capicmd.h" /* this should be moved in a common place */ #ifdef MODULE +#ifdef COMPAT_HAS_NEW_SYMTAB #define MOD_USE_COUNT ( GET_USE_COUNT (&__this_module)) -#endif /* MODULE */ +#else +extern long mod_use_count_; +#define MOD_USE_COUNT mod_use_count_ +#endif /* COMPAT_HAS_NEW_SYMTAB */ +#endif /* MODULE */ -const char *lli_revision = "$Revision: 2.25 $"; +const char *lli_revision = "$Revision: 2.31 $"; extern struct IsdnCard cards[]; extern int nrcards; @@ -146,28 +170,7 @@ static int chancount = 0; /* 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 40000 +#define FLG_START_B 0 /* * Find card with given driverId @@ -192,7 +195,7 @@ discard_queue(struct sk_buff_head *q) int ret=0; while ((skb = skb_dequeue(q))) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); ret++; } return(ret); @@ -211,44 +214,40 @@ link_debug(struct Channel *chanp, int direction, char *fmt, ...) va_end(args); } - enum { - ST_NULL, /* 0 inactive */ - ST_OUT_WAIT_D, /* 1 outgoing, awaiting d-channel establishment */ - ST_IN_WAIT_D, /* 2 incoming, awaiting d-channel establishment */ - ST_OUT_DIAL, /* 3 outgoing, SETUP send; awaiting confirm */ - ST_IN_WAIT_LL, /* 4 incoming call received; wait for LL confirm */ - ST_IN_ALERT_SEND, /* 5 incoming call received; ALERT send */ - ST_IN_WAIT_CONN_ACK, /* 6 incoming CONNECT send; awaiting CONN_ACK */ - ST_WAIT_BCONN, /* 7 CONNECT/CONN_ACK received, awaiting b-channel prot. estbl. */ - ST_ACTIVE, /* 8 active, b channel prot. established */ - ST_WAIT_BRELEASE, /* 9 call clear. (initiator), awaiting b channel prot. rel. */ - ST_WAIT_BREL_DISC, /* 10 call clear. (receiver), DISCONNECT req. received */ - ST_WAIT_DCOMMAND, /* 11 call clear. (receiver), awaiting DCHANNEL message */ - ST_WAIT_DRELEASE, /* 12 DISCONNECT sent, awaiting RELEASE */ - ST_WAIT_D_REL_CNF, /* 13 RELEASE sent, awaiting RELEASE confirm */ - ST_WAIT_DSHUTDOWN, /* 14 awaiting d-channel shutdown */ + ST_NULL, /* 0 inactive */ + ST_OUT_DIAL, /* 1 outgoing, SETUP send; awaiting confirm */ + ST_IN_WAIT_LL, /* 2 incoming call received; wait for LL confirm */ + ST_IN_ALERT_SENT, /* 3 incoming call received; ALERT send */ + ST_IN_WAIT_CONN_ACK, /* 4 incoming CONNECT send; awaiting CONN_ACK */ + ST_WAIT_BCONN, /* 5 CONNECT/CONN_ACK received, awaiting b-channel prot. estbl. */ + ST_ACTIVE, /* 6 active, b channel prot. established */ + ST_WAIT_BRELEASE, /* 7 call clear. (initiator), awaiting b channel prot. rel. */ + ST_WAIT_BREL_DISC, /* 8 call clear. (receiver), DISCONNECT req. received */ + ST_WAIT_DCOMMAND, /* 9 call clear. (receiver), awaiting DCHANNEL message */ + ST_WAIT_DRELEASE, /* 10 DISCONNECT sent, awaiting RELEASE */ + ST_WAIT_D_REL_CNF, /* 11 RELEASE sent, awaiting RELEASE confirm */ + ST_IN_PROCEED_SEND, /* 12 incoming call, proceeding send */ }; - -#define STATE_COUNT (ST_WAIT_DSHUTDOWN +1) - -static char *strState[] = -{ - "ST_NULL", - "ST_OUT_WAIT_D", - "ST_IN_WAIT_D", - "ST_OUT_DIAL", - "ST_IN_WAIT_LL", - "ST_IN_ALERT_SEND", - "ST_IN_WAIT_CONN_ACK", - "ST_WAIT_BCONN", - "ST_ACTIVE", + + +#define STATE_COUNT (ST_IN_PROCEED_SEND + 1) + + static char *strState[] = + { + "ST_NULL", + "ST_OUT_DIAL", + "ST_IN_WAIT_LL", + "ST_IN_ALERT_SENT", + "ST_IN_WAIT_CONN_ACK", + "ST_WAIT_BCONN", + "ST_ACTIVE", "ST_WAIT_BRELEASE", "ST_WAIT_BREL_DISC", "ST_WAIT_DCOMMAND", "ST_WAIT_DRELEASE", "ST_WAIT_D_REL_CNF", - "ST_WAIT_DSHUTDOWN", + "ST_IN_PROCEED_SEND", }; enum { @@ -256,29 +255,28 @@ enum { EV_SETUP_CNF, /* 1 */ EV_ACCEPTB, /* 2 */ EV_DISCONNECT_IND, /* 3 */ - EV_RELEASE_CNF, /* 4 */ - EV_DLEST, /* 5 */ - EV_DLRL, /* 6 */ + EV_RELEASE, /* 4 */ + EV_LEASED, /* 5 */ + EV_LEASED_REL, /* 6 */ EV_SETUP_IND, /* 7 */ - EV_RELEASE_IND, /* 8 */ - EV_ACCEPTD, /* 9 */ - EV_SETUP_CMPL_IND, /* 10 */ - EV_BC_EST, /* 11 */ - EV_WRITEBUF, /* 12 */ - EV_ESTABLISH, /* 13 */ - EV_HANGUP, /* 14 */ - EV_BC_REL, /* 15 */ - EV_CINF, /* 16 */ - EV_SUSPEND, /* 17 */ - EV_RESUME, /* 18 */ - EV_SHUTDOWN_D, /* 19 */ - EV_NOSETUP_RSP, /* 20 */ - EV_SETUP_ERR, /* 21 */ - EV_CONNECT_ERR, /* 22 */ - EV_RELEASE_ERR, /* 23 */ + EV_ACCEPTD, /* 8 */ + EV_SETUP_CMPL_IND, /* 9 */ + EV_BC_EST, /* 10 */ + EV_WRITEBUF, /* 11 */ + EV_HANGUP, /* 12 */ + EV_BC_REL, /* 13 */ + EV_CINF, /* 14 */ + EV_SUSPEND, /* 15 */ + EV_RESUME, /* 16 */ + EV_NOSETUP_RSP, /* 17 */ + EV_SETUP_ERR, /* 18 */ + EV_CONNECT_ERR, /* 19 */ + EV_PROCEED, /* 20 */ + EV_ALERT, /* 21 */ + EV_REDIR, /* 22 */ }; -#define EVENT_COUNT (EV_RELEASE_ERR +1) +#define EVENT_COUNT (EV_REDIR + 1) static char *strEvent[] = { @@ -286,150 +284,152 @@ static char *strEvent[] = "EV_SETUP_CNF", "EV_ACCEPTB", "EV_DISCONNECT_IND", - "EV_RELEASE_CNF", - "EV_DLEST", - "EV_DLRL", + "EV_RELEASE", + "EV_LEASED", + "EV_LEASED_REL", "EV_SETUP_IND", - "EV_RELEASE_IND", "EV_ACCEPTD", "EV_SETUP_CMPL_IND", "EV_BC_EST", "EV_WRITEBUF", - "EV_ESTABLISH", "EV_HANGUP", "EV_BC_REL", "EV_CINF", "EV_SUSPEND", "EV_RESUME", - "EV_SHUTDOWN_D", "EV_NOSETUP_RSP", "EV_SETUP_ERR", "EV_CONNECT_ERR", - "EV_RELEASE_ERR", + "EV_PROCEED", + "EV_ALERT", + "EV_REDIR", }; + static inline void -lli_deliver_cause(struct Channel *chanp, isdn_ctrl *ic) +HL_LL(struct Channel *chanp, int command) { - if (chanp->proc->para.cause < 0) + isdn_ctrl ic; + + ic.driver = chanp->cs->myid; + ic.command = command; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); +} + +static inline void +lli_deliver_cause(struct Channel *chanp) +{ + isdn_ctrl ic; + + if (chanp->proc->para.cause == NO_CAUSE) return; - ic->driver = chanp->cs->myid; - ic->command = ISDN_STAT_CAUSE; - ic->arg = chanp->chan; + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_CAUSE; + ic.arg = chanp->chan; if (chanp->cs->protocol == ISDN_PTYPE_EURO) - sprintf(ic->parm.num, "E%02X%02X", chanp->proc->para.loc & 0x7f, + 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->proc->para.loc & 0x7f, + sprintf(ic.parm.num, "%02X%02X", chanp->proc->para.loc & 0x7f, chanp->proc->para.cause & 0x7f); - chanp->cs->iif.statcallb(ic); + chanp->cs->iif.statcallb(&ic); } -static void -lli_d_established(struct FsmInst *fi, int event, void *arg) +static inline void +lli_close(struct FsmInst *fi) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); - if (chanp->leased) { - isdn_ctrl ic; - int ret; - - 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, 0, "STAT_ICALL_LEASED"); - 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) - link_debug(chanp, 1, "statcallb ret=%d", ret); - 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); + FsmChangeState(fi, ST_NULL); + chanp->Flags = 0; + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); } -static void -lli_d_released(struct FsmInst *fi, int event, void *arg) + static void +lli_leased_in(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - - test_and_clear_bit(FLG_START_D, &chanp->Flags); + struct Channel *chanp = fi->userdata; + + isdn_ctrl ic; + int ret; + + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); + FsmChangeState(fi, ST_IN_WAIT_LL); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_ICALL_LEASED"); + ic.driver = chanp->cs->myid; + ic.command = ((chanp->chan < 2) ? ISDN_STAT_ICALL : ISDN_STAT_ICALLW); + 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) + link_debug(chanp, 1, "statcallb ret=%d", ret); + + if (!ret) { + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); + FsmChangeState(fi, ST_NULL); + } } + /* * Dial out */ +static void +lli_init_bchan_out(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + FsmChangeState(fi, ST_WAIT_BCONN); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_DCONN"); + HL_LL(chanp, ISDN_STAT_DCONN); + init_b_st(chanp, 0); + chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL); +} + static void 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; - if (test_bit(FLG_ESTAB_D, &chanp->Flags)) { - FsmEvent(fi, EV_DLEST, NULL); - } else { - 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 -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) { - FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL); - } else { - 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); - } + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); + if (chanp->leased) { + lli_init_bchan_out(fi, event, arg); + } else { + FsmChangeState(fi, ST_OUT_DIAL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | REQUEST, chanp); + } } static void -lli_init_bchan_out(struct FsmInst *fi, int event, void *arg) +lli_resume(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - isdn_ctrl ic; - FsmChangeState(fi, ST_WAIT_BCONN); - test_and_set_bit(FLG_LL_DCONN, &chanp->Flags); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DCONN"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DCONN; - ic.arg = chanp->chan; - 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); + FsmDelTimer(&chanp->drel_timer, 60); + FsmDelTimer(&chanp->dial_timer, 73); + chanp->l2_active_protocol = chanp->l2_protocol; + chanp->incoming = 0; + + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); + if (chanp->leased) { + lli_init_bchan_out(fi, event, arg); + } else { + FsmChangeState(fi, ST_OUT_DIAL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_RESUME | REQUEST, chanp); + } } static void @@ -438,12 +438,15 @@ 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; - test_and_set_bit(FLG_CONNECT_B, &chanp->Flags); + if (chanp->bcs->conmsg) + strcpy(ic.parm.num, chanp->bcs->conmsg); + else + ic.parm.num[0] = 0; if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BCONN"); - test_and_set_bit(FLG_LL_BCONN, &chanp->Flags); + link_debug(chanp, 0, "STAT_BCONN %s", ic.parm.num); ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BCONN; ic.arg = chanp->chan; @@ -451,33 +454,13 @@ lli_go_active(struct FsmInst *fi, int event, void *arg) chanp->cs->cardmsg(chanp->cs, MDL_INFO_CONN, (void *) (long)chanp->chan); } + /* * RESUME */ /* incomming call */ -static void -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 (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 if (!test_and_set_bit(FLG_START_D, &chanp->Flags)) - chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL); -} - static void lli_deliver_call(struct FsmInst *fi, int event, void *arg) { @@ -493,11 +476,11 @@ lli_deliver_call(struct FsmInst *fi, int event, void *arg) */ if (1) { /* for only one TEI */ FsmChangeState(fi, ST_IN_WAIT_LL); - test_and_set_bit(FLG_CALL_REC, &chanp->Flags); if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_ICALL"); + link_debug(chanp, 0, (chanp->chan < 2) ? "STAT_ICALL" : "STAT_ICALLW"); ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_ICALL; + ic.command = ((chanp->chan < 2) ? ISDN_STAT_ICALL : ISDN_STAT_ICALLW); + ic.arg = chanp->chan; /* * No need to return "unknown" for calls without OAD, @@ -507,118 +490,95 @@ lli_deliver_call(struct FsmInst *fi, int event, void *arg) ret = chanp->cs->iif.statcallb(&ic); if (chanp->debug & 1) link_debug(chanp, 1, "statcallb ret=%d", ret); + switch (ret) { - case 1: /* OK, anybody likes this call */ + case 1: /* OK, someone likes this call */ 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); - } + FsmChangeState(fi, ST_IN_ALERT_SENT); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); + break; + case 4: /* direct redirect */ + case 3: /* Proceeding desired */ + FsmDelTimer(&chanp->drel_timer, 61); + FsmChangeState(fi, ST_IN_PROCEED_SEND); + chanp->d_st->lli.l4l3(chanp->d_st, CC_PROCEED_SEND | REQUEST, chanp->proc); + if (ret == 4) + { chanp->setup = ic.parm.setup; + chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc); + } break; case 2: /* Rejecting Call */ - test_and_clear_bit(FLG_CALL_REC, &chanp->Flags); break; case 0: /* OK, nobody likes this call */ default: /* statcallb problems */ 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); - 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->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); - 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) +lli_send_dconnect(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); + FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); } static void -lli_do_action(struct FsmInst *fi, int event, void *arg) +lli_send_alert(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); - } + FsmChangeState(fi, ST_IN_ALERT_SENT); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); } static void -lli_send_dconnect(struct FsmInst *fi, int event, void *arg) +lli_send_redir(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); - chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); + chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc); } static void lli_init_bchan_in(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_BCONN); - test_and_set_bit(FLG_LL_DCONN, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, 0, "STAT_DCONN"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DCONN; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); + HL_LL(chanp, ISDN_STAT_DCONN); chanp->l2_active_protocol = chanp->l2_protocol; chanp->incoming = !0; 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 +lli_setup_rsp(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + if (chanp->leased) { + lli_init_bchan_in(fi, event, arg); + } else { + FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); +#ifdef WANT_ALERT + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); +#endif + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); + } +} + /* Call suspend */ static void @@ -632,82 +592,52 @@ lli_suspend(struct FsmInst *fi, int event, void *arg) /* Call clearing */ static void -lli_cancel_call(struct FsmInst *fi, int event, void *arg) +lli_disconnect_req(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_DRELEASE); - if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_BHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - } - if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) - release_b_st(chanp); - chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ + 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 -lli_shutdown_d(struct FsmInst *fi, int event, void *arg) +lli_disconnect_reject(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - FsmDelTimer(&chanp->drel_timer, 62); - 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); - } + FsmChangeState(fi, ST_WAIT_DRELEASE); + chanp->proc->para.cause = 0x15; /* Call Rejected */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc); } static void -lli_timeout_d(struct FsmInst *fi, int event, void *arg) +lli_dhup_close(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; - - test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - lli_deliver_cause(chanp, &ic); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - FsmChangeState(fi, ST_NULL); - 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); + struct Channel *chanp = fi->userdata; + + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_DHUP"); + lli_deliver_cause(chanp); + HL_LL(chanp, ISDN_STAT_DHUP); + + lli_close(fi); } static void -lli_go_null(struct FsmInst *fi, int event, void *arg) +lli_reject_req(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); + struct Channel *chanp = fi->userdata; + +#ifndef ALERT_REJECT + chanp->proc->para.cause = 0x15; /* Call Rejected */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc); + lli_dhup_close(fi, event, arg); +#else + FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63); + FsmChangeState(fi, ST_IN_ALERT_SENT); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); +#endif } static void @@ -717,30 +647,15 @@ lli_disconn_bchan(struct FsmInst *fi, int event, void *arg) chanp->data_open = 0; FsmChangeState(fi, ST_WAIT_BRELEASE); - test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags); chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); } static void -lli_send_d_disc(struct FsmInst *fi, int event, void *arg) +lli_start_disc(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - if (test_bit(FLG_DISC_REC, &chanp->Flags) || - test_bit(FLG_REL_REC, &chanp->Flags)) - return; - FsmChangeState(fi, ST_WAIT_DRELEASE); - if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_BHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - } - 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; @@ -748,43 +663,46 @@ lli_send_d_disc(struct FsmInst *fi, int event, void *arg) chanp->cs->iif.statcallb(&ic); if (chanp->debug & 1) link_debug(chanp, 0, "STAT_DHUP"); - 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); + HL_LL(chanp, ISDN_STAT_DHUP); + lli_close(fi); } 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); + lli_disconnect_req(fi, event, arg); } } static void -lli_released_bchan(struct FsmInst *fi, int event, void *arg) +lli_rel_b_disc(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; + struct Channel *chanp = fi->userdata; + + release_b_st(chanp); + lli_start_disc(fi, event, arg); +} - FsmChangeState(fi, ST_WAIT_DCOMMAND); - chanp->data_open = 0; - if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_BHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - } - release_b_st(chanp); - test_and_clear_bit(FLG_START_B, &chanp->Flags); +static void +lli_bhup_disc(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_BHUP"); + HL_LL(chanp, ISDN_STAT_BHUP); + + lli_rel_b_disc(fi, event, arg); } +static void +lli_bhup_rel_b(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + FsmChangeState(fi, ST_WAIT_DCOMMAND); + chanp->data_open = 0; + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_BHUP"); + HL_LL(chanp, ISDN_STAT_BHUP); + release_b_st(chanp); +} static void lli_release_bchan(struct FsmInst *fi, int event, void *arg) @@ -792,117 +710,74 @@ lli_release_bchan(struct FsmInst *fi, int event, void *arg) struct Channel *chanp = fi->userdata; chanp->data_open = 0; - test_and_set_bit(FLG_DISC_REC, &chanp->Flags); FsmChangeState(fi, ST_WAIT_BREL_DISC); - test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags); chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); } + static void -lli_received_d_rel(struct FsmInst *fi, int event, void *arg) +lli_rel_b_dhup(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; + struct Channel *chanp = fi->userdata; - chanp->data_open = 0; - 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 (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_BHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - } - if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) - release_b_st(chanp); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - lli_deliver_cause(chanp, &ic); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - 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); + release_b_st(chanp); + lli_dhup_close(fi, event, arg); } static void -lli_received_d_relcnf(struct FsmInst *fi, int event, void *arg) +lli_bhup_dhup(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; + struct Channel *chanp = fi->userdata; - chanp->data_open = 0; - 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 (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_BHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - } - if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) - release_b_st(chanp); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - lli_deliver_cause(chanp, &ic); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - 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); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_BHUP"); + HL_LL(chanp, ISDN_STAT_BHUP); + + lli_rel_b_dhup(fi, event, arg); } static void -lli_received_d_disc(struct FsmInst *fi, int event, void *arg) +lli_abort(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; + struct Channel *chanp = fi->userdata; - chanp->data_open = 0; - FsmChangeState(fi, ST_WAIT_D_REL_CNF); - 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, 0, "STAT_BHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_BHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - } - if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) - release_b_st(chanp); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - lli_deliver_cause(chanp, &ic); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - 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); + chanp->data_open = 0; + chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); + + lli_bhup_dhup(fi, event, arg); +} + +static void +lli_release_req(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + FsmChangeState(fi, ST_WAIT_D_REL_CNF); + chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE | REQUEST, chanp->proc); +} + +static void +lli_rel_b_release_req(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + release_b_st(chanp); + lli_release_req(fi, event, arg); } +static void +lli_bhup_release_req(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_BHUP"); + HL_LL(chanp, ISDN_STAT_BHUP); + + lli_rel_b_release_req(fi, event, arg); +} + + /* processing charge info */ static void lli_charge_info(struct FsmInst *fi, int event, void *arg) @@ -920,259 +795,150 @@ lli_charge_info(struct FsmInst *fi, int event, void *arg) /* error procedures */ static void -lli_no_dchan(struct FsmInst *fi, int event, void *arg) +lli_dchan_not_ready(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - isdn_ctrl ic; if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_NODCH"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_NODCH; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - chanp->Flags = 0; - FsmChangeState(fi, ST_NULL); - chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); + link_debug(chanp, 0, "STAT_DHUP"); + HL_LL(chanp, ISDN_STAT_DHUP); } static void -lli_no_dchan_ready(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; if (chanp->debug & 1) link_debug(chanp, 0, "STAT_DHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); + HL_LL(chanp, ISDN_STAT_DHUP); + lli_close(fi); } static void -lli_no_dchan_in(struct FsmInst *fi, int event, void *arg) +lli_error(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; - - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - 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); - chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); + FsmChangeState(fi, ST_WAIT_DRELEASE); } static void -lli_no_setup_rsp(struct FsmInst *fi, int event, void *arg) +lli_failure_l(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; - - FsmChangeState(fi, ST_NULL); - test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - lli_shutdown_d(fi, event, arg); + struct Channel *chanp = fi->userdata; + isdn_ctrl ic; + + FsmChangeState(fi, ST_NULL); + 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); + HL_LL(chanp, ISDN_STAT_DHUP); + chanp->Flags = 0; + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); } static void -lli_setup_err(struct FsmInst *fi, int event, void *arg) +lli_rel_b_fail(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; + struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_WAIT_DRELEASE); - test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - lli_deliver_cause(chanp, &ic); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); /* DISCONN was sent from L3 */ + release_b_st(chanp); + lli_failure_l(fi, event, arg); } static void -lli_connect_err(struct FsmInst *fi, int event, void *arg) +lli_bhup_fail(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; + struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_WAIT_DRELEASE); - test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - lli_deliver_cause(chanp, &ic); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); /* DISCONN was sent from L3 */ + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_BHUP"); + HL_LL(chanp, ISDN_STAT_BHUP); + + lli_rel_b_fail(fi, event, arg); } static void -lli_got_dlrl(struct FsmInst *fi, int event, void *arg) +lli_failure_a(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; + struct Channel *chanp = fi->userdata; - chanp->data_open = 0; - 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 (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_BHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - } - 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->cs->iif.statcallb(&ic); - chanp->Flags = 0; - } else { - test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - 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); - 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); -} - -/* *INDENT-OFF* */ -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}, + chanp->data_open = 0; + chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); + + lli_bhup_fail(fi, event, arg); +} + + /* *INDENT-OFF* */ + static struct FsmNode fnlist[] HISAX_INITDATA = + { + {ST_NULL, EV_DIAL, lli_prep_dialout}, + {ST_NULL, EV_RESUME, lli_resume}, + {ST_NULL, EV_SETUP_IND, lli_deliver_call}, + {ST_NULL, EV_LEASED, lli_leased_in}, + {ST_OUT_DIAL, EV_SETUP_CNF, lli_init_bchan_out}, + {ST_OUT_DIAL, EV_HANGUP, lli_disconnect_req}, + {ST_OUT_DIAL, EV_DISCONNECT_IND, lli_release_req}, + {ST_OUT_DIAL, EV_RELEASE, lli_dhup_close}, + {ST_OUT_DIAL, EV_NOSETUP_RSP, lli_no_setup_rsp}, + {ST_OUT_DIAL, EV_SETUP_ERR, lli_error}, + {ST_IN_WAIT_LL, EV_LEASED_REL, lli_failure_l}, + {ST_IN_WAIT_LL, EV_ACCEPTD, lli_setup_rsp}, + {ST_IN_WAIT_LL, EV_HANGUP, lli_reject_req}, + {ST_IN_WAIT_LL, EV_DISCONNECT_IND, lli_release_req}, + {ST_IN_WAIT_LL, EV_RELEASE, lli_dhup_close}, + {ST_IN_ALERT_SENT, EV_SETUP_CMPL_IND, lli_init_bchan_in}, + {ST_IN_ALERT_SENT, EV_ACCEPTD, lli_send_dconnect}, + {ST_IN_ALERT_SENT, EV_HANGUP, lli_disconnect_reject}, + {ST_IN_ALERT_SENT, EV_DISCONNECT_IND, lli_release_req}, + {ST_IN_ALERT_SENT, EV_RELEASE, lli_dhup_close}, + {ST_IN_ALERT_SENT, EV_REDIR, lli_send_redir}, + {ST_IN_PROCEED_SEND, EV_REDIR, lli_send_redir}, + {ST_IN_PROCEED_SEND, EV_ALERT, lli_send_alert}, + {ST_IN_PROCEED_SEND, EV_ACCEPTD, lli_send_dconnect}, + {ST_IN_PROCEED_SEND, EV_HANGUP, lli_disconnect_reject}, + {ST_IN_PROCEED_SEND, EV_DISCONNECT_IND, lli_dhup_close}, + {ST_IN_ALERT_SENT, EV_RELEASE, lli_dhup_close}, + {ST_IN_WAIT_CONN_ACK, EV_SETUP_CMPL_IND, lli_init_bchan_in}, + {ST_IN_WAIT_CONN_ACK, EV_HANGUP, lli_disconnect_req}, + {ST_IN_WAIT_CONN_ACK, EV_DISCONNECT_IND, lli_release_req}, + {ST_IN_WAIT_CONN_ACK, EV_RELEASE, lli_dhup_close}, + {ST_IN_WAIT_CONN_ACK, EV_CONNECT_ERR, lli_error}, + {ST_WAIT_BCONN, EV_BC_EST, lli_go_active}, + {ST_WAIT_BCONN, EV_BC_REL, lli_rel_b_disc}, + {ST_WAIT_BCONN, EV_HANGUP, lli_rel_b_disc}, + {ST_WAIT_BCONN, EV_DISCONNECT_IND, lli_rel_b_release_req}, + {ST_WAIT_BCONN, EV_RELEASE, lli_rel_b_dhup}, + {ST_WAIT_BCONN, EV_LEASED_REL, lli_rel_b_fail}, + {ST_WAIT_BCONN, EV_CINF, lli_charge_info}, + {ST_ACTIVE, EV_CINF, lli_charge_info}, + {ST_ACTIVE, EV_BC_REL, lli_bhup_rel_b}, + {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, lli_abort}, + {ST_ACTIVE, EV_LEASED_REL, lli_failure_a}, + {ST_WAIT_BRELEASE, EV_BC_REL, lli_bhup_disc}, + {ST_WAIT_BRELEASE, EV_DISCONNECT_IND, lli_bhup_release_req}, + {ST_WAIT_BRELEASE, EV_RELEASE, lli_bhup_dhup}, + {ST_WAIT_BRELEASE, EV_LEASED_REL, lli_bhup_fail}, + {ST_WAIT_BREL_DISC, EV_BC_REL, lli_bhup_release_req}, + {ST_WAIT_BREL_DISC, EV_RELEASE, lli_bhup_dhup}, + {ST_WAIT_DCOMMAND, EV_HANGUP, lli_start_disc}, + {ST_WAIT_DCOMMAND, EV_DISCONNECT_IND, lli_release_req}, + {ST_WAIT_DCOMMAND, EV_RELEASE, lli_dhup_close}, + {ST_WAIT_DCOMMAND, EV_LEASED_REL, lli_failure_l}, + {ST_WAIT_DRELEASE, EV_RELEASE, lli_dhup_close}, + {ST_WAIT_DRELEASE, EV_DIAL, lli_dchan_not_ready}, + /* ETS 300-104 16.1 */ + {ST_WAIT_D_REL_CNF, EV_RELEASE, lli_dhup_close}, + {ST_WAIT_D_REL_CNF, EV_DIAL, lli_dchan_not_ready}, }; -/* *INDENT-ON* */ + /* *INDENT-ON* */ -#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode)) + #define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode)) HISAX_INITFUNC(void CallcNew(void)) @@ -1195,21 +961,23 @@ release_b_st(struct Channel *chanp) { 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; - } + if(test_and_clear_bit(FLG_START_B, &chanp->Flags)) { + 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; + } + } } struct Channel -*selectfreechannel(struct PStack *st) +*selectfreechannel(struct PStack *st, int bch) { struct IsdnCardState *cs = st->l1.hardware; struct Channel *chanp = st->lli.userdata; @@ -1219,74 +987,60 @@ struct Channel i=1; else i=0; - while (i<2) { + + if (!bch) + { i = 2; /* virtual channel */ + chanp += 2; + } + + while (i < ((bch) ? cs->chanlimit : (2 + MAX_WAITING_CALLS))) { if (chanp->fi.state == ST_NULL) return (chanp); chanp++; i++; } - return (NULL); -} - -int -is_activ(struct PStack *st) -{ - struct IsdnCardState *cs = st->l1.hardware; - struct Channel *chanp = st->lli.userdata; - int i; - if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) - i=1; - else - i=0; - while (i<2) { - if (test_bit(FLG_ESTAB_D, &chanp->Flags)) - return (1); + if (bch) /* number of channels is limited */ + { i = 2; /* virtual channel */ + chanp = st->lli.userdata; + chanp += i; + while (i < (2 + MAX_WAITING_CALLS)) { + if (chanp->fi.state == ST_NULL) + return (chanp); chanp++; i++; - } - return (0); + } + } + return (NULL); } +static void stat_redir_result(struct IsdnCardState *cs, int chan, ulong result) +{ isdn_ctrl ic; + + ic.driver = cs->myid; + ic.command = ISDN_STAT_REDIR; + ic.arg = chan; + (ulong)(ic.parm.num[0]) = result; + cs->iif.statcallb(&ic); +} /* stat_redir_result */ + static void dchan_l3l4(struct PStack *st, int pr, void *arg) { struct l3_process *pc = arg; - struct IsdnCardState *cs = st->l1.hardware; + struct IsdnCardState *cs = st->l1.hardware; struct Channel *chanp; - int event; - switch (pr) { - case (DL_ESTABLISH | INDICATION): - event = EV_DLEST; - break; - case (DL_RELEASE | INDICATION): - event = EV_DLRL; - break; - default: - event = -1; - break; - } - 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; + if(!pc) + return; + + if (pr == (CC_SETUP | INDICATION)) { + if (!(chanp = selectfreechannel(pc->st, pc->para.bchannel))) { + pc->para.cause = 0x11; /* User busy */ + pc->st->lli.l4l3(pc->st, CC_REJECT | REQUEST, pc); + } else { + chanp->proc = pc; + pc->chan = chanp; FsmEvent(&chanp->fi, EV_SETUP_IND, NULL); } return; @@ -1299,19 +1053,19 @@ dchan_l3l4(struct PStack *st, int pr, void *arg) FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL); break; case (CC_RELEASE | CONFIRM): - FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL); + FsmEvent(&chanp->fi, EV_RELEASE, NULL); break; case (CC_SUSPEND | CONFIRM): - FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL); + FsmEvent(&chanp->fi, EV_RELEASE, 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); + FsmEvent(&chanp->fi, EV_RELEASE, NULL); break; case (CC_RELEASE | INDICATION): - FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL); + FsmEvent(&chanp->fi, EV_RELEASE, NULL); break; case (CC_SETUP_COMPL | INDICATION): FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL); @@ -1332,15 +1086,21 @@ dchan_l3l4(struct PStack *st, int pr, void *arg) FsmEvent(&chanp->fi, EV_CONNECT_ERR, NULL); break; case (CC_RELEASE_ERR): - FsmEvent(&chanp->fi, EV_RELEASE_ERR, NULL); + FsmEvent(&chanp->fi, EV_RELEASE, NULL); break; + case (CC_PROCEED_SEND | INDICATION): case (CC_PROCEEDING | INDICATION): case (CC_ALERTING | INDICATION): + case (CC_PROGRESS | INDICATION): + case (CC_NOTIFY | INDICATION): break; + case (CC_REDIR | INDICATION): + stat_redir_result(cs, chanp->chan, pc->redir_result); + break; default: if (chanp->debug & 0x800) { HiSax_putstatus(chanp->cs, "Ch", - "%d L3->L4 unknown primitiv %x", + "%d L3->L4 unknown primitiv %#x", chanp->chan, pr); } } @@ -1406,6 +1166,7 @@ init_PStack(struct PStack **stp) { (*stp)->l2.l2l1 = dummy_pstack; (*stp)->l2.l2l3 = dummy_pstack; (*stp)->l3.l3l2 = dummy_pstack; + (*stp)->l3.l3ml3 = dummy_pstack; (*stp)->l3.l3l4 = dummy_pstack; (*stp)->lli.l4l3 = dummy_pstack; (*stp)->ma.layer = dummy_pstack; @@ -1446,14 +1207,19 @@ init_chan(int chan, struct IsdnCardState *csta) int CallcNewChan(struct IsdnCardState *csta) -{ +{ int i; + chancount += 2; init_chan(0, csta); init_chan(1, csta); printk(KERN_INFO "HiSax: 2 channels added\n"); + + for (i = 0; i < MAX_WAITING_CALLS; i++) + init_chan(i+2,csta); + printk(KERN_INFO "HiSax: MAX_WAITING_CALLS 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); } @@ -1482,14 +1248,13 @@ CallcFreeChan(struct IsdnCardState *csta) for (i = 0; i < 2; i++) { FsmDelTimer(&csta->channel[i].drel_timer, 74); FsmDelTimer(&csta->channel[i].dial_timer, 75); - 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 + if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) + release_d_st(csta->channel + i); + if (csta->channel[i].b_st) { + 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); @@ -1509,7 +1274,7 @@ lldata_handler(struct PStack *st, int pr, void *arg) if (chanp->data_open) chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb); else { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } break; case (DL_ESTABLISH | INDICATION): @@ -1521,7 +1286,7 @@ lldata_handler(struct PStack *st, int pr, void *arg) FsmEvent(&chanp->fi, EV_BC_REL, NULL); break; default: - printk(KERN_WARNING "lldata_handler unknown primitive %x\n", + printk(KERN_WARNING "lldata_handler unknown primitive %#x\n", pr); break; } @@ -1539,7 +1304,7 @@ lltrans_handler(struct PStack *st, int pr, void *arg) chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb); else { link_debug(chanp, 0, "channel not open"); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } break; case (PH_ACTIVATE | INDICATION): @@ -1551,7 +1316,7 @@ lltrans_handler(struct PStack *st, int pr, void *arg) FsmEvent(&chanp->fi, EV_BC_REL, NULL); break; default: - printk(KERN_WARNING "lltrans_handler unknown primitive %x\n", + printk(KERN_WARNING "lltrans_handler unknown primitive %#x\n", pr); break; } @@ -1566,7 +1331,7 @@ ll_writewakeup(struct PStack *st, int len) ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BSENT; ic.arg = chanp->chan; -// ic.parm.length = len; + ic.parm.length = len; chanp->cs->iif.statcallb(&ic); } @@ -1590,12 +1355,11 @@ init_b_st(struct Channel *chanp, int incoming) 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; + st->l1.mode = L1_MODE_V32; break; -#endif } + chanp->bcs->conmsg = NULL; if (chanp->bcs->BC_SetStack(st, chanp->bcs)) return (-1); st->l2.flag = 0; @@ -1622,7 +1386,7 @@ init_b_st(struct Channel *chanp, int incoming) break; case (ISDN_PROTO_L2_HDLC): case (ISDN_PROTO_L2_TRANS): -// case (ISDN_PROTO_L2_MODEM): + case (ISDN_PROTO_L2_MODEM): st->l1.l1l2 = lltrans_handler; st->lli.userdata = chanp; st->lli.l1writewakeup = ll_writewakeup; @@ -1630,6 +1394,8 @@ init_b_st(struct Channel *chanp, int incoming) setstack_l3bc(st, chanp); break; } + test_and_set_bit(FLG_START_B, &chanp->Flags); + return (0); } @@ -1642,7 +1408,7 @@ leased_l4l3(struct PStack *st, int pr, void *arg) switch (pr) { case (DL_DATA | REQUEST): link_debug(chanp, 0, "leased line d-channel DATA"); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); break; case (DL_ESTABLISH | REQUEST): st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL); @@ -1650,7 +1416,7 @@ leased_l4l3(struct PStack *st, int pr, void *arg) case (DL_RELEASE | REQUEST): break; default: - printk(KERN_WARNING "transd_l4l3 unknown primitive %x\n", + printk(KERN_WARNING "transd_l4l3 unknown primitive %#x\n", pr); break; } @@ -1661,16 +1427,16 @@ 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; + int i,event = EV_LEASED_REL; switch (pr) { case (PH_DATA | INDICATION): link_debug(chanp, 0, "leased line d-channel DATA"); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); break; case (PH_ACTIVATE | INDICATION): case (PH_ACTIVATE | CONFIRM): - event = EV_DLEST; + event = EV_LEASED; case (PH_DEACTIVATE | INDICATION): case (PH_DEACTIVATE | CONFIRM): if (test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags)) @@ -1685,7 +1451,7 @@ leased_l1l2(struct PStack *st, int pr, void *arg) break; default: printk(KERN_WARNING - "transd_l1l2 unknown primitive %x\n", pr); + "transd_l1l2 unknown primitive %#x\n", pr); break; } } @@ -1701,7 +1467,7 @@ distr_debug(struct IsdnCardState *csta, int debugflags) int i; struct Channel *chanp = csta->channel; - for (i = 0; i < 2; i++) { + for (i = 0; i < (2 + MAX_WAITING_CALLS) ; i++) { chanp[i].debug = debugflags; chanp[i].fi.debug = debugflags & 2; chanp[i].d_st->l2.l2m.debug = debugflags & 8; @@ -1774,10 +1540,40 @@ lli_got_manufacturer(struct Channel *chanp, struct IsdnCardState *cs, capi_msg * } } + +/***************************************************************/ +/* Limit the available number of channels for the current card */ +/***************************************************************/ +static int +set_channel_limit(struct IsdnCardState *cs, int chanmax) +{ isdn_ctrl ic; + int i, ii; + + if ((chanmax < 0) || (chanmax > 2)) + return(-EINVAL); + cs->chanlimit = 0; + for (ii = 0; ii < 2; ii++) { + ic.driver = cs->myid; + ic.command = ISDN_STAT_DISCH; + ic.arg = ii; + if (ii >= chanmax) + ic.parm.num[0] = 0; /* disabled */ + else + ic.parm.num[0] = 1; /* enabled */ + i = cs->iif.statcallb(&ic); + if (i) return(-EINVAL); + if (ii < chanmax) + cs->chanlimit++; + } + return(0); +} /* set_channel_limit */ + + int HiSax_command(isdn_ctrl * ic) { struct IsdnCardState *csta = hisax_findcard(ic->driver); + struct PStack *st; struct Channel *chanp; int i; u_int num; @@ -1868,7 +1664,7 @@ HiSax_command(isdn_ctrl * ic) HiSax_mod_inc_use_count(); #ifdef MODULE if (csta->channel[0].debug & 0x400) - HiSax_putstatus(csta, " LOCK ", "modcnt %x", + HiSax_putstatus(csta, " LOCK ", "modcnt %lx", MOD_USE_COUNT); #endif /* MODULE */ break; @@ -1876,7 +1672,7 @@ HiSax_command(isdn_ctrl * ic) HiSax_mod_dec_use_count(); #ifdef MODULE if (csta->channel[0].debug & 0x400) - HiSax_putstatus(csta, " UNLOCK ", "modcnt %x", + HiSax_putstatus(csta, " UNLOCK ", "modcnt %lx", MOD_USE_COUNT); #endif /* MODULE */ break; @@ -1949,8 +1745,6 @@ HiSax_command(isdn_ctrl * ic) HiSax_putstatus(csta, "set card ", "in PTP mode"); printk(KERN_DEBUG "HiSax: set card in PTP mode\n"); printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n"); - test_and_set_bit(FLG_START_D, &csta->channel[0].Flags); - test_and_set_bit(FLG_START_D, &csta->channel[1].Flags); csta->channel[0].d_st->lli.l4l3(csta->channel[0].d_st, DL_ESTABLISH | REQUEST, NULL); } else { @@ -1969,6 +1763,8 @@ HiSax_command(isdn_ctrl * ic) HiSax_putstatus(csta, "set card ", "in FIXED TEI (%d) mode", num); printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n", num); + chanp->d_st->lli.l4l3(chanp->d_st, + DL_ESTABLISH | REQUEST, NULL); break; case (9): /* load firmware */ memcpy(&adr, ic->parm.num, sizeof(ulong)); @@ -1977,8 +1773,7 @@ HiSax_command(isdn_ctrl * ic) break; #ifdef MODULE case (55): - while ( MOD_USE_COUNT > 0) - MOD_DEC_USE_COUNT; + MOD_USE_COUNT = 0; HiSax_mod_inc_use_count(); break; #endif /* MODULE */ @@ -2001,12 +1796,58 @@ HiSax_command(isdn_ctrl * ic) printk(KERN_DEBUG "HiSax: l3 debugging flags card %d set to %x\n", csta->cardnr + 1, *(unsigned int *) ic->parm.num); break; + case (10): + i = *(unsigned int *) ic->parm.num; + if ((i < 0) || (i > 2)) + return(-EINVAL); /* invalid number */ + return(set_channel_limit(csta, i)); + break; + case (12): + i = *(unsigned int *) ic->parm.num; +#ifdef CONFIG_HISAX_HFC_PCI + if (csta->typ != ISDN_CTYPE_HFC_PCI) + return(-EINVAL); + return(hfcpci_set_echo(csta,i)); +#else + return(-EINVAL); +#endif + break; default: printk(KERN_DEBUG "HiSax: invalid ioclt %d\n", (int) ic->arg); return (-EINVAL); } break; + + case (ISDN_CMD_PROCEED): + chanp = csta->channel + ic->arg; + if (chanp->debug & 1) + link_debug(chanp, 1, "PROCEED"); + FsmEvent(&chanp->fi, EV_PROCEED, NULL); + break; + + case (ISDN_CMD_ALERT): + chanp = csta->channel + ic->arg; + if (chanp->debug & 1) + link_debug(chanp, 1, "ALERT"); + FsmEvent(&chanp->fi, EV_ALERT, NULL); + break; + + case (ISDN_CMD_REDIR): + chanp = csta->channel + ic->arg; + if (chanp->debug & 1) + link_debug(chanp, 1, "REDIR"); + chanp->setup = ic->parm.setup; + FsmEvent(&chanp->fi, EV_REDIR, NULL); + break; + + /* protocol specific io commands */ + case (ISDN_CMD_PROT_IO): + for (st = csta->stlist; st; st = st->next) + if (st->protocol == (ic->arg & 0xFF)) + return(st->lli.l4l3_proto(st, ic)); + return(-EINVAL); + break; default: break; } @@ -2049,7 +1890,8 @@ HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb) if (chanp->debug & 0x800) link_debug(chanp, 1, "writebuf: no buffers for %d bytes", len); return 0; - } + } else if (chanp->debug & 0x800) + link_debug(chanp, 1, "writebuf %d/%d/%d", len, chanp->bcs->tx_cnt,MAX_DATA_MEM); save_flags(flags); cli(); nskb = skb_clone(skb, GFP_ATOMIC); @@ -2062,7 +1904,7 @@ HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb) chanp->bcs->tx_cnt += len; st->l2.l2l1(st, PH_DATA | REQUEST, nskb); } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_WRITE); } else len = 0; restore_flags(flags); diff --git a/drivers/isdn/hisax/cert.c b/drivers/isdn/hisax/cert.c index a76736b60034..03f416d5bff3 100644 --- a/drivers/isdn/hisax/cert.c +++ b/drivers/isdn/hisax/cert.c @@ -1,4 +1,4 @@ -/* $Id: cert.c,v 2.1 1998/11/15 23:51:15 keil Exp $ +/* $Id: cert.c,v 2.2 1999/08/07 17:35:05 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * @@ -7,6 +7,9 @@ * ../../../Documentation/isdn/HiSax.cert * * $Log: cert.c,v $ + * Revision 2.2 1999/08/07 17:35:05 keil + * approval for Eicon Technology Diva 2.01 PCI + * * Revision 2.1 1998/11/15 23:51:15 keil * certification stuff * @@ -29,6 +32,7 @@ certification_check(int output) { printk(KERN_INFO "HiSax: Approval registration numbers:\n"); printk(KERN_INFO "HiSax: German D133361J CETECOM ICT Services GmbH\n"); printk(KERN_INFO "HiSax: EU (D133362J) CETECOM ICT Services GmbH\n"); + printk(KERN_INFO "HiSax: Approved with Eicon Technology Diva 2.01 PCI cards\n"); } return(0); #endif diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c index d3bf31d90aea..6e54fd1dcfeb 100644 --- a/drivers/isdn/hisax/config.c +++ b/drivers/isdn/hisax/config.c @@ -1,10 +1,33 @@ -/* $Id: config.c,v 2.23 1999/02/17 10:53:02 cpetig Exp $ +/* $Id: config.c,v 2.30 1999/08/05 20:43:14 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden * * * $Log: config.c,v $ + * Revision 2.30 1999/08/05 20:43:14 keil + * ISAR analog modem support + * + * Revision 2.29 1999/07/21 14:46:00 keil + * changes from EICON certification + * + * Revision 2.28 1999/07/14 12:38:36 werner + * Added changes for echo channel handling + * + * Revision 2.27 1999/07/12 21:05:00 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 2.26 1999/07/08 21:27:17 keil + * version 3.2 + * + * Revision 2.25 1999/07/05 23:51:44 werner + * Allow limiting of available HiSax B-chans per card. Controlled by hisaxctrl + * hisaxctrl id 10 + * + * Revision 2.24 1999/07/01 08:11:26 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.23 1999/02/17 10:53:02 cpetig * Added Hisax_closecard to exported symbols. * As indicated by Oliver Schoett . @@ -139,7 +162,13 @@ * 26 AVM A1 PCMCIA (Fritz) p0=irq p1=iobase * 27 AVM PnP/PCI p0=irq p1=iobase (PCI no parameter) * 28 Sedlbauer Speed Fax+ p0=irq p1=iobase (from isapnp setup) - * + * 29 Siemens I-Surf p0=irq p1=iobase p2=memory (from isapnp setup) + * 30 ACER P10 p0=irq p1=iobase (from isapnp setup) + * 31 HST Saphir p0=irq p1=iobase + * 32 Telekom A4T none + * 33 Scitel Quadro p0=subcontroller (4*S0, subctrl 1...4) + * 34 Gazel ISDN cards + * 35 HFC 2BDS0 PCI none * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1 * * @@ -152,15 +181,27 @@ const char *CardType[] = "Sedlbauer Speed Card", "USR Sportster", "ith mic Linux", "Elsa PCI", "Compaq ISA", "NETjet", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)", "AMD 7930", "NICCY", "S0Box", "AVM A1 (PCMCIA)", "AVM Fritz PnP/PCI", - "Sedlbauer Speed Fax +" + "Sedlbauer Speed Fax +", "Siemens I-Surf", "Acer P10", "HST Saphir", + "Telekom A4T", "Scitel Quadro", "Gazel", "HFC 2BDS0 PCI", }; +void HiSax_closecard(int cardnr); + #ifdef CONFIG_HISAX_ELSA #define DEFAULT_CARD ISDN_CTYPE_ELSA #define DEFAULT_CFG {0,0,0,0} int elsa_init_pcmcia(void*, int, int*, int); +#ifdef COMPAT_HAS_NEW_SYMTAB EXPORT_SYMBOL(elsa_init_pcmcia); -#endif +#else +static struct symbol_table hisax_syms_elsa = { +#include + X(elsa_init_pcmcia), +#include +}; +#endif /* COMPAT_HAS_NEW_SYMTAB */ +#endif /* CONFIG_HISAX_ELSA */ + #ifdef CONFIG_HISAX_AVM_A1 #undef DEFAULT_CARD #undef DEFAULT_CFG @@ -174,8 +215,17 @@ EXPORT_SYMBOL(elsa_init_pcmcia); #define DEFAULT_CARD ISDN_CTYPE_A1_PCMCIA #define DEFAULT_CFG {11,0x170,0,0} int avm_a1_init_pcmcia(void*, int, int*, int); +#ifdef COMPAT_HAS_NEW_SYMTAB EXPORT_SYMBOL(avm_a1_init_pcmcia); -#endif +#else +static struct symbol_table hisax_syms_avm_a1= { +#include + X(avm_a1_init_pcmcia), + X(HiSax_closecard), +#include +}; +#endif /* COMPAT_HAS_NEW_SYMTAB */ +#endif /* CONFIG_HISAX_AVM_A1_PCMCIA */ #ifdef CONFIG_HISAX_FRITZPCI #undef DEFAULT_CARD @@ -190,12 +240,14 @@ EXPORT_SYMBOL(avm_a1_init_pcmcia); #define DEFAULT_CARD ISDN_CTYPE_16_3 #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 @@ -244,8 +296,16 @@ EXPORT_SYMBOL(avm_a1_init_pcmcia); #define DEFAULT_CARD ISDN_CTYPE_SEDLBAUER #define DEFAULT_CFG {11,0x270,0,0} int sedl_init_pcmcia(void*, int, int*, int); +#ifdef COMPAT_HAS_NEW_SYMTAB EXPORT_SYMBOL(sedl_init_pcmcia); -#endif +#else +static struct symbol_table hisax_syms_sedl= { +#include + X(sedl_init_pcmcia), +#include +}; +#endif /* COMPAT_HAS_NEW_SYMTAB */ +#endif /* CONFIG_HISAX_SEDLBAUER */ #ifdef CONFIG_HISAX_SPORTSTER #undef DEFAULT_CARD @@ -268,13 +328,21 @@ EXPORT_SYMBOL(sedl_init_pcmcia); #define DEFAULT_CFG {0,0,0,0} #endif -#ifdef CONFIG_HISAX_TELES3C +#ifdef CONFIG_HISAX_HFCS #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_TELES3C #define DEFAULT_CFG {5,0x500,0,0} #endif +#ifdef CONFIG_HISAX_HFC_PCI +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_HFC_PCI +#define DEFAULT_CFG {0,0,0,0} +#endif + + #ifdef CONFIG_HISAX_AMD7930 #undef DEFAULT_CARD #undef DEFAULT_CFG @@ -289,6 +357,41 @@ EXPORT_SYMBOL(sedl_init_pcmcia); #define DEFAULT_CFG {0,0x0,0,0} #endif +#ifdef CONFIG_HISAX_ISURF +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_ISURF +#define DEFAULT_CFG {5,0x100,0xc8000,0} +#endif + +#ifdef CONFIG_HISAX_HSTSAPHIR +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_HSTSAPHIR +#define DEFAULT_CFG {5,0x250,0,0} +#endif + +#ifdef CONFIG_HISAX_BKM_A4T +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_BKM_A4T +#define DEFAULT_CFG {0,0x0,0,0} +#endif + +#ifdef CONFIG_HISAX_SCT_QUADRO +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_SCT_QUADRO +#define DEFAULT_CFG {1,0x0,0,0} +#endif + +#ifdef CONFIG_HISAX_GAZEL +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_GAZEL +#define DEFAULT_CFG {15,0x180,0,0} +#endif + #ifdef CONFIG_HISAX_1TR6 #define DEFAULT_PROTO ISDN_PTYPE_1TR6 #define DEFAULT_PROTO_NAME "1TR6" @@ -366,6 +469,7 @@ static int mem[] HISAX_INITDATA = {0, 0, 0, 0, 0, 0, 0, 0}; static char *id HISAX_INITDATA = HiSaxID; +#ifdef COMPAT_HAS_NEW_SYMTAB MODULE_AUTHOR("Karsten Keil"); MODULE_PARM(type, "1-8i"); MODULE_PARM(protocol, "1-8i"); @@ -377,7 +481,7 @@ MODULE_PARM(id, "s"); MODULE_PARM(io0, "1-8i"); MODULE_PARM(io1, "1-8i"); #endif /* CONFIG_HISAX_16_3 */ - +#endif /* COMPAT_HAS_NEW_SYMTAB */ #endif /* MODULE */ int nrcards; @@ -410,9 +514,9 @@ HiSaxVersion(void)) printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n"); #ifdef MODULE - printk(KERN_INFO "HiSax: Version 3.1a (module)\n"); + printk(KERN_INFO "HiSax: Version 3.3 (module)\n"); #else - printk(KERN_INFO "HiSax: Version 3.1a (kernel)\n"); + printk(KERN_INFO "HiSax: Version 3.3 (kernel)\n"); #endif strcpy(tmp, l1_revision); printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp)); @@ -442,8 +546,8 @@ HiSax_mod_inc_use_count(void) #ifdef MODULE #define HiSax_init init_module #else -void __init -HiSax_setup(char *str, int *ints) +__initfunc(void +HiSax_setup(char *str, int *ints)) { int i, j, argc; @@ -552,8 +656,12 @@ extern int setup_mic(struct IsdnCard *card); extern int setup_netjet(struct IsdnCard *card); #endif -#if CARD_TELES3C -extern int setup_t163c(struct IsdnCard *card); +#if CARD_HFCS +extern int setup_hfcs(struct IsdnCard *card); +#endif + +#if CARD_HFC_PCI +extern int setup_hfcpci(struct IsdnCard *card); #endif #if CARD_AMD7930 @@ -564,6 +672,30 @@ extern int setup_amd7930(struct IsdnCard *card); extern int setup_niccy(struct IsdnCard *card); #endif +#if CARD_ISURF +extern int setup_isurf(struct IsdnCard *card); +#endif + +#if CARD_HSTSAPHIR +extern int setup_saphir(struct IsdnCard *card); +#endif + +#if CARD_TESTEMU +extern int setup_testemu(struct IsdnCard *card); +#endif + +#if CARD_BKM_A4T +extern int setup_bkm_a4t(struct IsdnCard *card); +#endif + +#if CARD_SCT_QUADRO +extern int setup_sct_quadro(struct IsdnCard *card); +#endif + +#if CARD_GAZEL +extern int setup_gazel(struct IsdnCard *card); +#endif + /* * Find card with given driverId */ @@ -579,6 +711,18 @@ static inline struct IsdnCardState return (NULL); } +/* + * Find card with given card number + */ +struct IsdnCardState +*hisax_get_card(int cardnr) +{ + if ((cardnr <= nrcards) && (cardnr>0)) + if (cards[cardnr-1].cs) + return (cards[cardnr-1].cs); + return (NULL); +} + int HiSax_readstatus(u_char * buf, int len, int user, int id, int channel) { @@ -782,23 +926,18 @@ closecard(int cardnr) csta->bcs->BC_Close(csta->bcs); } + discard_queue(&csta->rq); + discard_queue(&csta->sq); if (csta->rcvbuf) { kfree(csta->rcvbuf); csta->rcvbuf = NULL; } - discard_queue(&csta->rq); - discard_queue(&csta->sq); if (csta->tx_skb) { - dev_kfree_skb(csta->tx_skb); + idev_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; + if (csta->DC_Close != NULL) { + csta->DC_Close(csta); } csta->cardmsg(csta, CARD_RELEASE, NULL); if (csta->dbusytimer.function != NULL) @@ -811,12 +950,14 @@ HISAX_INITFUNC(static int init_card(struct IsdnCardState *cs)) int irq_cnt, cnt = 3; long flags; + if (!cs->irq) + return(cs->cardmsg(cs, CARD_INIT, NULL)); save_flags(flags); cli(); irq_cnt = kstat_irqs(cs->irq); printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], cs->irq, irq_cnt); - if (cs->cardmsg(cs, CARD_SETIRQ, NULL)) { + if (request_irq(cs->irq, cs->irq_func, cs->irq_flags, "HiSax", cs)) { printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n", cs->irq); restore_flags(flags); @@ -827,7 +968,7 @@ HISAX_INITFUNC(static int init_card(struct IsdnCardState *cs)) sti(); current->state = TASK_INTERRUPTIBLE; /* Timeout 10ms */ - schedule_timeout((10 * HZ) / 1000); + schedule_timeout((10*HZ)/1000); restore_flags(flags); printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], cs->irq, kstat_irqs(cs->irq)); @@ -871,198 +1012,229 @@ checkcard(int cardnr, char *id, int *busy_flag)) } memset(cs, 0, sizeof(struct IsdnCardState)); card->cs = cs; + cs->chanlimit = 2; /* maximum B-channel number */ + cs->logecho = 0; /* No echo logging */ cs->cardnr = cardnr; cs->debug = L1_DEB_WARN; cs->HW_Flags = 0; cs->busy_flag = busy_flag; + cs->irq_flags = I4L_IRQ_FLAG; #if TEI_PER_CARD #else test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags); #endif cs->protocol = card->protocol; - if ((card->typ > 0) && (card->typ < 31)) { - if (!((1 << card->typ) & SUPORTED_CARDS)) { + if ((card->typ > 0) && (card->typ <= ISDN_CTYPE_COUNT)) { + if (!(cs->dlog = kmalloc(MAX_DLOG_SPACE, GFP_ATOMIC))) { printk(KERN_WARNING - "HiSax: Support for %s Card not selected\n", - CardType[card->typ]); + "HiSax: No memory for dlog(card %d)\n", + cardnr + 1); restore_flags(flags); return (0); } - } else { - printk(KERN_WARNING - "HiSax: Card Type %d out of range\n", - card->typ); - restore_flags(flags); - return (0); - } - if (!(cs->dlog = kmalloc(MAX_DLOG_SPACE, GFP_ATOMIC))) { - printk(KERN_WARNING - "HiSax: No memory for dlog(card %d)\n", - cardnr + 1); - restore_flags(flags); - return (0); - } - 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(cs->dlog); - restore_flags(flags); - return (0); - } - cs->stlist = NULL; - 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 | + 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(cs->dlog); + restore_flags(flags); + return (0); + } + cs->stlist = 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 - ISDN_FEATURE_P_1TR6 | + ISDN_FEATURE_P_1TR6 | #endif #ifdef CONFIG_HISAX_EURO - ISDN_FEATURE_P_EURO | -#endif -#ifdef CONFIG_HISAX_NI1 - ISDN_FEATURE_P_NI1 | -#endif - 0; - - 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", cs->iif.id, cs->myid); - switch (card->typ) { + ISDN_FEATURE_P_EURO | +#endif +#ifdef CONFIG_HISAX_NI1 + ISDN_FEATURE_P_NI1 | +#endif + 0; + + 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", cs->iif.id, cs->myid); + switch (card->typ) { #if CARD_TELES0 - case ISDN_CTYPE_16_0: - case ISDN_CTYPE_8_0: - ret = setup_teles0(card); - break; + case ISDN_CTYPE_16_0: + case ISDN_CTYPE_8_0: + ret = setup_teles0(card); + break; #endif #if CARD_TELES3 - case ISDN_CTYPE_16_3: - case ISDN_CTYPE_PNP: - case ISDN_CTYPE_TELESPCMCIA: - case ISDN_CTYPE_COMPAQ_ISA: - ret = setup_teles3(card); - break; + 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; + case ISDN_CTYPE_S0BOX: + ret = setup_s0box(card); + break; #endif #if CARD_TELESPCI - case ISDN_CTYPE_TELESPCI: - ret = setup_telespci(card); - break; + case ISDN_CTYPE_TELESPCI: + ret = setup_telespci(card); + break; #endif #if CARD_AVM_A1 - case ISDN_CTYPE_A1: - ret = setup_avm_a1(card); - break; + case ISDN_CTYPE_A1: + ret = setup_avm_a1(card); + break; #endif #if CARD_AVM_A1_PCMCIA - case ISDN_CTYPE_A1_PCMCIA: - ret = setup_avm_a1_pcmcia(card); - break; + case ISDN_CTYPE_A1_PCMCIA: + ret = setup_avm_a1_pcmcia(card); + break; #endif #if CARD_FRITZPCI - case ISDN_CTYPE_FRITZPCI: - ret = setup_avm_pcipnp(card); - break; + case ISDN_CTYPE_FRITZPCI: + ret = setup_avm_pcipnp(card); + break; #endif #if CARD_ELSA - case ISDN_CTYPE_ELSA: - case ISDN_CTYPE_ELSA_PNP: - case ISDN_CTYPE_ELSA_PCMCIA: - case ISDN_CTYPE_ELSA_PCI: - ret = setup_elsa(card); - break; + case ISDN_CTYPE_ELSA: + case ISDN_CTYPE_ELSA_PNP: + case ISDN_CTYPE_ELSA_PCMCIA: + case ISDN_CTYPE_ELSA_PCI: + ret = setup_elsa(card); + break; #endif #if CARD_IX1MICROR2 - case ISDN_CTYPE_IX1MICROR2: - ret = setup_ix1micro(card); - break; + case ISDN_CTYPE_IX1MICROR2: + ret = setup_ix1micro(card); + break; #endif #if CARD_DIEHLDIVA - case ISDN_CTYPE_DIEHLDIVA: - ret = setup_diva(card); - break; + case ISDN_CTYPE_DIEHLDIVA: + ret = setup_diva(card); + break; #endif #if CARD_ASUSCOM - case ISDN_CTYPE_ASUSCOM: - ret = setup_asuscom(card); - break; + case ISDN_CTYPE_ASUSCOM: + ret = setup_asuscom(card); + break; #endif #if CARD_TELEINT - case ISDN_CTYPE_TELEINT: - ret = setup_TeleInt(card); - break; + case ISDN_CTYPE_TELEINT: + ret = setup_TeleInt(card); + break; #endif #if CARD_SEDLBAUER - case ISDN_CTYPE_SEDLBAUER: - case ISDN_CTYPE_SEDLBAUER_PCMCIA: - case ISDN_CTYPE_SEDLBAUER_FAX: - ret = setup_sedlbauer(card); - break; + case ISDN_CTYPE_SEDLBAUER: + case ISDN_CTYPE_SEDLBAUER_PCMCIA: + case ISDN_CTYPE_SEDLBAUER_FAX: + ret = setup_sedlbauer(card); + break; #endif #if CARD_SPORTSTER - case ISDN_CTYPE_SPORTSTER: - ret = setup_sportster(card); - break; + case ISDN_CTYPE_SPORTSTER: + ret = setup_sportster(card); + break; #endif #if CARD_MIC - case ISDN_CTYPE_MIC: - ret = setup_mic(card); - break; + case ISDN_CTYPE_MIC: + ret = setup_mic(card); + break; #endif #if CARD_NETJET - case ISDN_CTYPE_NETJET: - ret = setup_netjet(card); - break; + case ISDN_CTYPE_NETJET: + ret = setup_netjet(card); + break; #endif -#if CARD_TELES3C - case ISDN_CTYPE_TELES3C: - ret = setup_t163c(card); - break; +#if CARD_HFCS + case ISDN_CTYPE_TELES3C: + case ISDN_CTYPE_ACERP10: + ret = setup_hfcs(card); + break; +#endif +#if CARD_HFC_PCI + case ISDN_CTYPE_HFC_PCI: + ret = setup_hfcpci(card); + break; #endif #if CARD_NICCY - case ISDN_CTYPE_NICCY: - ret = setup_niccy(card); - break; + case ISDN_CTYPE_NICCY: + ret = setup_niccy(card); + break; #endif #if CARD_AMD7930 - case ISDN_CTYPE_AMD7930: - ret = setup_amd7930(card); + case ISDN_CTYPE_AMD7930: + ret = setup_amd7930(card); + break; +#endif +#if CARD_ISURF + case ISDN_CTYPE_ISURF: + ret = setup_isurf(card); + break; +#endif +#if CARD_HSTSAPHIR + case ISDN_CTYPE_HSTSAPHIR: + ret = setup_saphir(card); + break; +#endif +#if CARD_TESTEMU + case ISDN_CTYPE_TESTEMU: + ret = setup_testemu(card); + break; +#endif +#if CARD_BKM_A4T + case ISDN_CTYPE_BKM_A4T: + ret = setup_bkm_a4t(card); break; +#endif +#if CARD_SCT_QUADRO + case ISDN_CTYPE_SCT_QUADRO: + ret = setup_sct_quadro(card); + break; +#endif +#if CARD_GAZEL + case ISDN_CTYPE_GAZEL: + ret = setup_gazel(card); + break; #endif default: - printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n", - card->typ); + printk(KERN_WARNING + "HiSax: Support for %s Card not selected\n", + CardType[card->typ]); ll_unload(cs); restore_flags(flags); return (0); + } + } else { + printk(KERN_WARNING + "HiSax: Card Type %d out of range\n", + card->typ); + restore_flags(flags); + return (0); } if (!ret) { ll_unload(cs); @@ -1168,7 +1340,8 @@ HiSax_closecard(int cardnr) ll_stop(cards[cardnr].cs); release_tei(cards[cardnr].cs); closecard(cardnr); - free_irq(cards[cardnr].cs->irq, cards[cardnr].cs); + if (cards[cardnr].cs->irq) + free_irq(cards[cardnr].cs->irq, cards[cardnr].cs); kfree((void *) cards[cardnr].cs); cards[cardnr].cs = NULL; } @@ -1180,8 +1353,6 @@ HiSax_closecard(int cardnr) nrcards--; } -EXPORT_SYMBOL(HiSax_closecard); - void HiSax_reportcard(int cardnr) { @@ -1202,11 +1373,13 @@ HiSax_reportcard(int cardnr) cs->bcs[0].mode, cs->bcs[0].channel); printk(KERN_DEBUG "HiSax: bcs 1 mode %d ch%d\n", cs->bcs[1].mode, cs->bcs[1].channel); + printk(KERN_DEBUG "HiSax: cs setstack_d 0x%lX\n", (ulong) cs->setstack_d); 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: dst%d l1.l1hw 0x%lX\n", i, (ulong) stptr->l1.l1hw); 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); @@ -1236,8 +1409,8 @@ HiSax_reportcard(int cardnr) } -int __init -HiSax_init(void) +__initfunc(int +HiSax_init(void)) { int i; @@ -1246,18 +1419,27 @@ HiSax_init(void) #ifdef CONFIG_HISAX_ELSA if (type[0] == ISDN_CTYPE_ELSA_PCMCIA) { /* we have exported and return in this case */ +#ifndef COMPAT_HAS_NEW_SYMTAB + register_symtab(&hisax_syms_elsa); +#endif return 0; } #endif #ifdef CONFIG_HISAX_SEDLBAUER if (type[0] == ISDN_CTYPE_SEDLBAUER_PCMCIA) { /* we have to export and return in this case */ +#ifndef COMPAT_HAS_NEW_SYMTAB + register_symtab(&hisax_syms_sedl); +#endif return 0; } #endif #ifdef CONFIG_HISAX_AVM_A1_PCMCIA if (type[0] == ISDN_CTYPE_A1_PCMCIA) { /* we have to export and return in this case */ +#ifndef COMPAT_HAS_NEW_SYMTAB + register_symtab(&hisax_syms_avm_a1); +#endif return 0; } #endif @@ -1318,16 +1500,29 @@ HiSax_init(void) case ISDN_CTYPE_SPORTSTER: case ISDN_CTYPE_MIC: case ISDN_CTYPE_TELES3C: + case ISDN_CTYPE_ACERP10: case ISDN_CTYPE_S0BOX: case ISDN_CTYPE_FRITZPCI: + case ISDN_CTYPE_HSTSAPHIR: + case ISDN_CTYPE_GAZEL: + cards[i].para[0] = irq[i]; + cards[i].para[1] = io[i]; + break; + case ISDN_CTYPE_ISURF: cards[i].para[0] = irq[i]; cards[i].para[1] = io[i]; + cards[i].para[2] = mem[i]; break; case ISDN_CTYPE_ELSA_PCI: case ISDN_CTYPE_NETJET: case ISDN_CTYPE_AMD7930: case ISDN_CTYPE_TELESPCI: break; + case ISDN_CTYPE_BKM_A4T: + break; + case ISDN_CTYPE_SCT_QUADRO: + cards[i].para[0] = irq[i]; + break; } } if (!nzproto) { @@ -1353,11 +1548,13 @@ HiSax_init(void) Isdnl1New(); if (HiSax_inithardware(NULL)) { /* Install only, if at least one card found */ - /* No symbols to export, hide all symbols */ - #ifdef MODULE +#ifndef COMPAT_HAS_NEW_SYMTAB + /* No symbols to export, hide all symbols */ + register_symtab(NULL); printk(KERN_INFO "HiSax: module installed\n"); -#endif +#endif /* COMPAT_HAS_NEW_SYMTAB */ +#endif /* MODULE */ return (0); } else { Isdnl1Free(); diff --git a/drivers/isdn/hisax/diva.c b/drivers/isdn/hisax/diva.c index 4a829c9bebe4..482a90695f87 100644 --- a/drivers/isdn/hisax/diva.c +++ b/drivers/isdn/hisax/diva.c @@ -1,13 +1,36 @@ -/* $Id: diva.c,v 1.10 1998/11/15 23:54:31 keil Exp $ +/* $Id: diva.c,v 1.16 1999/08/11 21:01:25 keil Exp $ * diva.c low level stuff for Eicon.Diehl Diva Family ISDN cards * * Author Karsten Keil (keil@isdn4linux.de) * - * Thanks to Eicon Technology Diehl GmbH & Co. oHG for documents and informations + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/HiSax.cert + * + * Thanks to Eicon Technology for documents and informations * * * $Log: diva.c,v $ + * Revision 1.16 1999/08/11 21:01:25 keil + * new PCI codefix + * + * Revision 1.15 1999/08/10 16:01:49 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.14 1999/08/07 17:35:08 keil + * approval for Eicon Technology Diva 2.01 PCI + * + * Revision 1.13 1999/07/21 14:46:07 keil + * changes from EICON certification + * + * Revision 1.12 1999/07/12 21:05:04 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.11 1999/07/01 08:11:29 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.10 1998/11/15 23:54:31 keil * changes from 2.0 * @@ -51,10 +74,13 @@ #include "ipac.h" #include "isdnl1.h" #include +#ifndef COMPAT_HAS_NEW_PCI +#include +#endif extern const char *CardType[]; -const char *Diva_revision = "$Revision: 1.10 $"; +const char *Diva_revision = "$Revision: 1.16 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -75,6 +101,7 @@ const char *Diva_revision = "$Revision: 1.10 $"; #define DIVA_ISA 1 #define DIVA_PCI 2 #define DIVA_IPAC_ISA 3 +#define DIVA_IPAC_PCI 4 /* PCI stuff */ #define PCI_VENDOR_EICON_DIEHL 0x1133 @@ -82,10 +109,12 @@ const char *Diva_revision = "$Revision: 1.10 $"; #define PCI_DIVA20_ID 0xe002 #define PCI_DIVA20PRO_U_ID 0xe003 #define PCI_DIVA20_U_ID 0xe004 +#define PCI_DIVA_201 0xe005 /* CTRL (Read) */ #define DIVA_IRQ_STAT 0x01 #define DIVA_EEPROM_SDA 0x02 + /* CTRL (Write) */ #define DIVA_IRQ_REQ 0x01 #define DIVA_RESET 0x08 @@ -96,6 +125,13 @@ const char *Diva_revision = "$Revision: 1.10 $"; #define DIVA_ISA_LED_B 0x40 #define DIVA_IRQ_CLR 0x80 +/* Siemens PITA */ +#define PITA_MISC_REG 0x1c +#define PITA_PARA_SOFTRESET 0x01000000 +#define PITA_PARA_MPX_MODE 0x04000000 +#define PITA_INT0_ENABLE 0x00020000 +#define PITA_INT0_STATUS 0x00000002 + static inline u_char readreg(unsigned int ale, unsigned int adr, u_char off) { @@ -140,6 +176,22 @@ writefifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size outsb(adr, data, size); } +static inline u_char +memreadreg(unsigned long adr, u_char off) +{ + return(0xff & *((unsigned int *) + (((unsigned int *)adr) + off))); +} + +static inline void +memwritereg(unsigned long adr, u_char off, u_char data) +{ + register u_char *p; + + p = (unsigned char *)(((unsigned int *)adr) + off); + *p = data; +} + /* Interface functions */ static u_char @@ -204,6 +256,44 @@ WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0), value); } +static u_char +MemReadISAC_IPAC(struct IsdnCardState *cs, u_char offset) +{ + return (memreadreg(cs->hw.diva.cfg_reg, offset+0x80)); +} + +static void +MemWriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + memwritereg(cs->hw.diva.cfg_reg, offset|0x80, value); +} + +static void +MemReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) +{ + while(size--) + *data++ = memreadreg(cs->hw.diva.cfg_reg, 0x80); +} + +static void +MemWriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) +{ + while(size--) + memwritereg(cs->hw.diva.cfg_reg, 0x80, *data++); +} + +static u_char +MemReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return(memreadreg(cs->hw.diva.cfg_reg, offset + (hscx ? 0x40 : 0))); +} + +static void +MemWriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + memwritereg(cs->hw.diva.cfg_reg, offset + (hscx ? 0x40 : 0), value); +} + /* * fast interrupt HSCX stuff goes here */ @@ -225,8 +315,8 @@ 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; + u_char val, sval; + int cnt=5; if (!cs) { printk(KERN_WARNING "Diva: Spurious interrupt!\n"); @@ -234,44 +324,36 @@ diva_interrupt(int intno, void *dev_id, struct pt_regs *regs) } 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) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA); - if (val) { + 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); - } + 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.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0x0); + 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); } static void -diva_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) +diva_irq_ipac_isa(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; u_char ista,val; - int icnt=20; + int icnt=5; 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: +Start_IPACISA: if (cs->debug & L1_DEB_IPAC) debugl1(cs, "IPAC ISTA %02X", ista); if (ista & 0x0f) { @@ -298,7 +380,7 @@ Start_IPAC: ista = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA); if ((ista & 0x3f) && icnt) { icnt--; - goto Start_IPAC; + goto Start_IPACISA; } if (!icnt) printk(KERN_WARNING "DIVA IPAC IRQ LOOP\n"); @@ -306,13 +388,348 @@ Start_IPAC: writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xC0); } +static inline void +MemwaitforCEC(struct IsdnCardState *cs, int hscx) +{ + int to = 50; + + while ((MemReadHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: waitforCEC timeout\n"); +} + + +static inline void +MemwaitforXFW(struct IsdnCardState *cs, int hscx) +{ + int to = 50; + + while ((!(MemReadHSCX(cs, hscx, HSCX_STAR) & 0x44) == 0x40) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: waitforXFW timeout\n"); +} + +static inline void +MemWriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + MemwaitforCEC(cs, hscx); + MemWriteHSCX(cs, hscx, HSCX_CMDR, data); + restore_flags(flags); +} + +static void +Memhscx_empty_fifo(struct BCState *bcs, int count) +{ + u_char *ptr; + struct IsdnCardState *cs = bcs->cs; + long flags; + int cnt; + + 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"); + MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80); + bcs->hw.hscx.rcvidx = 0; + return; + } + save_flags(flags); + cli(); + ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx; + cnt = count; + while (cnt--) + *ptr++ = memreadreg(cs->hw.diva.cfg_reg, bcs->hw.hscx.hscx ? 0x40 : 0); + MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80); + ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx; + bcs->hw.hscx.rcvidx += count; + restore_flags(flags); + if (cs->debug & L1_DEB_HSCX_FIFO) { + char *t = bcs->blog; + + t += sprintf(t, "hscx_empty_fifo %c cnt %d", + bcs->hw.hscx.hscx ? 'B' : 'A', count); + QuickHex(t, ptr, count); + debugl1(cs, bcs->blog); + } +} + +static void +Memhscx_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + int more, count, cnt; + int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32; + u_char *ptr,*p; + long flags; + + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hscx_fill_fifo"); + + if (!bcs->tx_skb) + return; + if (bcs->tx_skb->len <= 0) + return; + + more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0; + if (bcs->tx_skb->len > fifo_size) { + more = !0; + count = fifo_size; + } else + count = bcs->tx_skb->len; + cnt = count; + MemwaitforXFW(cs, bcs->hw.hscx.hscx); + save_flags(flags); + cli(); + p = ptr = bcs->tx_skb->data; + skb_pull(bcs->tx_skb, count); + bcs->tx_cnt -= count; + bcs->hw.hscx.count += count; + while(cnt--) + memwritereg(cs->hw.diva.cfg_reg, bcs->hw.hscx.hscx ? 0x40 : 0, + *p++); + MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, more ? 0x8 : 0xa); + restore_flags(flags); + if (cs->debug & L1_DEB_HSCX_FIFO) { + char *t = bcs->blog; + + t += sprintf(t, "hscx_fill_fifo %c cnt %d", + bcs->hw.hscx.hscx ? 'B' : 'A', count); + QuickHex(t, ptr, count); + debugl1(cs, bcs->blog); + } +} + +static inline void +Memhscx_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; + + if (!test_bit(BC_FLG_INIT, &bcs->Flag)) + return; + + if (val & 0x80) { /* RME */ + r = MemReadHSCX(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) + debugl1(cs, "HSCX RDO mode=%d", + bcs->mode); + if (!(r & 0x20)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX CRC error"); + MemWriteHSCXCMDR(cs, hscx, 0x80); + } else { + count = MemReadHSCX(cs, hscx, HSCX_RBCL) & ( + test_bit(HW_IPAC, &cs->HW_Flags)? 0x3f: 0x1f); + if (count == 0) + count = fifo_size; + Memhscx_empty_fifo(bcs, count); + if ((count = bcs->hw.hscx.rcvidx - 1) > 0) { + if (cs->debug & L1_DEB_HSCX_FIFO) + debugl1(cs, "HX Frame %d", count); + 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 */ + Memhscx_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->tx_skb) { + if (bcs->tx_skb->len) { + Memhscx_fill_fifo(bcs); + return; + } else { + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->hw.hscx.count = 0; + bcs->tx_skb = NULL; + } + } + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + bcs->hw.hscx.count = 0; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + Memhscx_fill_fifo(bcs); + } else { + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + hscx_sched_event(bcs, B_XMTBUFREADY); + } + } +} + +static inline void +Memhscx_int_main(struct IsdnCardState *cs, u_char val) +{ + + u_char exval; + struct BCState *bcs; + + if (val & 0x01) { + bcs = cs->bcs + 1; + exval = MemReadHSCX(cs, 1, HSCX_EXIR); + if (exval & 0x40) { + if (bcs->mode == 1) + Memhscx_fill_fifo(bcs); + else { + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame. + */ + if (bcs->tx_skb) { + skb_push(bcs->tx_skb, bcs->hw.hscx.count); + bcs->tx_cnt += bcs->hw.hscx.count; + bcs->hw.hscx.count = 0; + } + MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX B EXIR %x Lost TX", exval); + } + } else if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX B EXIR %x", exval); + } + if (val & 0xf8) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX B interrupt %x", val); + Memhscx_interrupt(cs, val, 1); + } + if (val & 0x02) { + bcs = cs->bcs; + exval = MemReadHSCX(cs, 0, HSCX_EXIR); + if (exval & 0x40) { + if (bcs->mode == L1_MODE_TRANS) + Memhscx_fill_fifo(bcs); + else { + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame. + */ + if (bcs->tx_skb) { + skb_push(bcs->tx_skb, bcs->hw.hscx.count); + bcs->tx_cnt += bcs->hw.hscx.count; + bcs->hw.hscx.count = 0; + } + MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX A EXIR %x Lost TX", exval); + } + } else if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX A EXIR %x", exval); + } + if (val & 0x04) { + exval = MemReadHSCX(cs, 0, HSCX_ISTA); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX A interrupt %x", exval); + Memhscx_interrupt(cs, exval, 0); + } +} + +static void +diva_irq_ipac_pci(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char ista,val; + int icnt=5; + u_char *cfg; + + if (!cs) { + printk(KERN_WARNING "Diva: Spurious interrupt!\n"); + return; + } + cfg = (u_char *) cs->hw.diva.pci_cfg; + val = *cfg; + if (!(val & PITA_INT0_STATUS)) + return; /* other shared IRQ */ + *cfg = PITA_INT0_STATUS; /* Reset pending INT0 */ + ista = memreadreg(cs->hw.diva.cfg_reg, IPAC_ISTA); +Start_IPACPCI: + if (cs->debug & L1_DEB_IPAC) + debugl1(cs, "IPAC ISTA %02X", ista); + if (ista & 0x0f) { + val = memreadreg(cs->hw.diva.cfg_reg, HSCX_ISTA + 0x40); + if (ista & 0x01) + val |= 0x01; + if (ista & 0x04) + val |= 0x02; + if (ista & 0x08) + val |= 0x04; + if (val) + Memhscx_int_main(cs, val); + } + if (ista & 0x20) { + val = 0xfe & memreadreg(cs->hw.diva.cfg_reg, ISAC_ISTA + 0x80); + if (val) { + isac_interrupt(cs, val); + } + } + if (ista & 0x10) { + val = 0x01; + isac_interrupt(cs, val); + } + ista = memreadreg(cs->hw.diva.cfg_reg, IPAC_ISTA); + if ((ista & 0x3f) && icnt) { + icnt--; + goto Start_IPACPCI; + } + if (!icnt) + printk(KERN_WARNING "DIVA IPAC PCI IRQ LOOP\n"); + memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xFF); + memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xC0); +} void release_io_diva(struct IsdnCardState *cs) { int bytecnt; - if (cs->subtyp != DIVA_IPAC_ISA) { + if (cs->subtyp == DIVA_IPAC_PCI) { + u_int *cfg = (unsigned int *)cs->hw.diva.pci_cfg; + + *cfg = 0; /* disable INT0/1 */ + *cfg = 2; /* reset pending INT0 */ + iounmap((void *)cs->hw.diva.cfg_reg); + iounmap((void *)cs->hw.diva.pci_cfg); + return; + } else 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 */ @@ -336,24 +753,37 @@ reset_diva(struct IsdnCardState *cs) if (cs->subtyp == DIVA_IPAC_ISA) { writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x20); current->state = TASK_INTERRUPTIBLE; - schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + schedule_timeout((10*HZ)/1000); writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x00); current->state = TASK_INTERRUPTIBLE; - schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + schedule_timeout((10*HZ)/1000); writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xc0); - } else { + } else if (cs->subtyp == DIVA_IPAC_PCI) { + unsigned int *ireg = (unsigned int *)(cs->hw.diva.pci_cfg + + PITA_MISC_REG); + *ireg = PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10*HZ)/1000); + *ireg = PITA_PARA_MPX_MODE; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10*HZ)/1000); + memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xc0); + } else { /* DIVA 2.0 */ cs->hw.diva.ctrl_reg = 0; /* Reset On */ byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); current->state = TASK_INTERRUPTIBLE; - schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + schedule_timeout((10*HZ)/1000); cs->hw.diva.ctrl_reg |= DIVA_RESET; /* Reset Off */ byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); current->state = TASK_INTERRUPTIBLE; - schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + schedule_timeout((10*HZ)/1000); if (cs->subtyp == DIVA_ISA) cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A; - else + else { + /* Workaround PCI9060 */ + byteout(cs->hw.diva.pci_cfg + 0x69, 9); cs->hw.diva.ctrl_reg |= DIVA_PCI_LED_A; + } byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); } restore_flags(flags); @@ -366,7 +796,7 @@ diva_led_handler(struct IsdnCardState *cs) { int blink = 0; - if (cs->subtyp == DIVA_IPAC_ISA) + if ((cs->subtyp == DIVA_IPAC_ISA) || (cs->subtyp == DIVA_IPAC_PCI)) return; del_timer(&cs->hw.diva.tl); if (cs->hw.diva.status & DIVA_ASSIGN) @@ -399,7 +829,7 @@ diva_led_handler(struct IsdnCardState *cs) static int Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - u_int irq_flag = I4L_IRQ_FLAG; + u_int *ireg; switch (mt) { case CARD_RESET: @@ -408,17 +838,11 @@ Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_io_diva(cs); return(0); - case CARD_SETIRQ: - if (cs->subtyp == DIVA_PCI) - irq_flag |= SA_SHIRQ; - if (cs->subtyp == DIVA_IPAC_ISA) { - return(request_irq(cs->irq, &diva_interrupt_ipac, - irq_flag, "HiSax", cs)); - } else { - return(request_irq(cs->irq, &diva_interrupt, - irq_flag, "HiSax", cs)); - } case CARD_INIT: + if (cs->subtyp == DIVA_IPAC_PCI) { + ireg = (unsigned int *)cs->hw.diva.pci_cfg; + *ireg = PITA_INT0_ENABLE; + } inithscxisac(cs, 3); return(0); case CARD_TEST: @@ -451,16 +875,21 @@ Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg) } break; } - if (cs->subtyp != DIVA_IPAC_ISA) + if ((cs->subtyp != DIVA_IPAC_ISA) && (cs->subtyp != DIVA_IPAC_PCI)) diva_led_handler(cs); return(0); } +#ifdef COMPAT_HAS_NEW_PCI static struct pci_dev *dev_diva __initdata = NULL; static struct pci_dev *dev_diva_u __initdata = NULL; +static struct pci_dev *dev_diva201 __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif -int __init -setup_diva(struct IsdnCard *card) +__initfunc(int +setup_diva(struct IsdnCard *card)) { int bytecnt; u_char val; @@ -478,7 +907,7 @@ setup_diva(struct IsdnCard *card) 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) { + if ((val == 1) || (val==2)) { cs->subtyp = DIVA_IPAC_ISA; cs->hw.diva.ctrl = 0; cs->hw.diva.isac = card->para[1] + DIVA_IPAC_DATA; @@ -498,6 +927,7 @@ setup_diva(struct IsdnCard *card) bytecnt = 8; } else { #if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI if (!pci_present()) { printk(KERN_ERR "Diva: no PCI bus present\n"); return(0); @@ -506,20 +936,26 @@ setup_diva(struct IsdnCard *card) cs->subtyp = 0; if ((dev_diva = pci_find_device(PCI_VENDOR_EICON_DIEHL, PCI_DIVA20_ID, dev_diva))) { - cs->subtyp = DIVA_PCI; - /* get IRQ */ + cs->subtyp = DIVA_PCI; cs->irq = dev_diva->irq; - /* get IO address */ - cs->hw.diva.cfg_reg = dev_diva->resource[2].start + cs->hw.diva.cfg_reg = get_pcibase(dev_diva, 2) & PCI_BASE_ADDRESS_IO_MASK; } else if ((dev_diva_u = pci_find_device(PCI_VENDOR_EICON_DIEHL, PCI_DIVA20_U_ID, dev_diva_u))) { - cs->subtyp = DIVA_PCI; - /* get IRQ */ + cs->subtyp = DIVA_PCI; cs->irq = dev_diva_u->irq; - /* get IO address */ - cs->hw.diva.cfg_reg = dev_diva_u->resource[2].start + cs->hw.diva.cfg_reg = get_pcibase(dev_diva_u, 2) & PCI_BASE_ADDRESS_IO_MASK; + } else if ((dev_diva201 = pci_find_device(PCI_VENDOR_EICON_DIEHL, + PCI_DIVA_201, dev_diva201))) { + cs->subtyp = DIVA_IPAC_PCI; + cs->irq = dev_diva201->irq; + cs->hw.diva.pci_cfg = + (ulong) ioremap((get_pcibase(dev_diva201, 0) + & PCI_BASE_ADDRESS_IO_MASK), 4096); + cs->hw.diva.cfg_reg = + (ulong) ioremap((get_pcibase(dev_diva201, 1) + & PCI_BASE_ADDRESS_IO_MASK), 4096); } else { printk(KERN_WARNING "Diva: No PCI card found\n"); return(0); @@ -534,35 +970,112 @@ setup_diva(struct IsdnCard *card) printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n"); return(0); } - cs->hw.diva.ctrl = cs->hw.diva.cfg_reg + DIVA_PCI_CTRL; - cs->hw.diva.isac = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_DATA; - cs->hw.diva.hscx = cs->hw.diva.cfg_reg + DIVA_HSCX_DATA; - cs->hw.diva.isac_adr = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_ADR; - cs->hw.diva.hscx_adr = cs->hw.diva.cfg_reg + DIVA_HSCX_ADR; - bytecnt = 32; +#else + 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_U_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = DIVA_PCI; + else if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL, + PCI_DIVA_201, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = DIVA_IPAC_PCI; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + if (cs->subtyp == DIVA_IPAC_PCI) { + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + cs->hw.diva.pci_cfg = (ulong) ioremap(pci_ioaddr, + 4096); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr); + cs->hw.diva.cfg_reg = (ulong) ioremap(pci_ioaddr, + 4096); + } else { + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr); + cs->hw.diva.pci_cfg = pci_ioaddr & ~3; + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_2, &pci_ioaddr); + cs->hw.diva.cfg_reg = pci_ioaddr & ~3; + } + 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); + } + cs->irq = pci_irq; +#endif /* COMPAT_HAS_NEW_PCI */ + cs->irq_flags |= SA_SHIRQ; #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 */ + if (cs->subtyp == DIVA_IPAC_PCI) { + cs->hw.diva.ctrl = 0; + cs->hw.diva.isac = 0; + cs->hw.diva.hscx = 0; + cs->hw.diva.isac_adr = 0; + cs->hw.diva.hscx_adr = 0; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + bytecnt = 0; + } else { + cs->hw.diva.ctrl = cs->hw.diva.cfg_reg + DIVA_PCI_CTRL; + cs->hw.diva.isac = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_DATA; + cs->hw.diva.hscx = cs->hw.diva.cfg_reg + DIVA_HSCX_DATA; + cs->hw.diva.isac_adr = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_ADR; + cs->hw.diva.hscx_adr = cs->hw.diva.cfg_reg + DIVA_HSCX_ADR; + bytecnt = 32; + } } printk(KERN_INFO - "Diva: %s card configured at 0x%x IRQ %d\n", + "Diva: %s card configured at %#lx IRQ %d\n", (cs->subtyp == DIVA_PCI) ? "PCI" : - (cs->subtyp == DIVA_ISA) ? "ISA" : "IPAC", + (cs->subtyp == DIVA_ISA) ? "ISA" : + (cs->subtyp == DIVA_IPAC_ISA) ? "IPAC ISA" : "IPAC PCI", cs->hw.diva.cfg_reg, cs->irq); - if (check_region(cs->hw.diva.cfg_reg, bytecnt)) { - printk(KERN_WARNING - "HiSax: %s config port %x-%x already in use\n", - CardType[card->typ], - cs->hw.diva.cfg_reg, - cs->hw.diva.cfg_reg + bytecnt); - return (0); - } else { - request_region(cs->hw.diva.cfg_reg, bytecnt, "diva isdn"); + if ((cs->subtyp == DIVA_IPAC_PCI) || (cs->subtyp == DIVA_PCI)) + printk(KERN_INFO "Diva: %s PCI space at %#lx\n", + (cs->subtyp == DIVA_PCI) ? "PCI" : "IPAC PCI", + cs->hw.diva.pci_cfg); + if (cs->subtyp != DIVA_IPAC_PCI) { + if (check_region(cs->hw.diva.cfg_reg, bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %lx-%lx 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; @@ -573,9 +1086,21 @@ setup_diva(struct IsdnCard *card) cs->writeisac = &WriteISAC_IPAC; cs->readisacfifo = &ReadISACfifo_IPAC; cs->writeisacfifo = &WriteISACfifo_IPAC; + cs->irq_func = &diva_irq_ipac_isa; val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ID); printk(KERN_INFO "Diva: IPAC version %x\n", val); - } else { + } else if (cs->subtyp == DIVA_IPAC_PCI) { + cs->readisac = &MemReadISAC_IPAC; + cs->writeisac = &MemWriteISAC_IPAC; + cs->readisacfifo = &MemReadISACfifo_IPAC; + cs->writeisacfifo = &MemWriteISACfifo_IPAC; + cs->BC_Read_Reg = &MemReadHSCX; + cs->BC_Write_Reg = &MemWriteHSCX; + cs->BC_Send_Data = &Memhscx_fill_fifo; + cs->irq_func = &diva_irq_ipac_pci; + val = memreadreg(cs->hw.diva.cfg_reg, IPAC_ID); + printk(KERN_INFO "Diva: IPAC version %x\n", val); + } else { /* DIVA 2.0 */ cs->hw.diva.tl.function = (void *) diva_led_handler; cs->hw.diva.tl.data = (long) cs; init_timer(&cs->hw.diva.tl); @@ -583,6 +1108,7 @@ setup_diva(struct IsdnCard *card) cs->writeisac = &WriteISAC; cs->readisacfifo = &ReadISACfifo; cs->writeisacfifo = &WriteISACfifo; + cs->irq_func = &diva_interrupt; ISACVersion(cs, "Diva:"); if (HscxVersion(cs, "Diva:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c index 4a4b7d26492d..8f47869ba8f7 100644 --- a/drivers/isdn/hisax/elsa.c +++ b/drivers/isdn/hisax/elsa.c @@ -1,4 +1,4 @@ -/* $Id: elsa.c,v 2.12 1998/11/15 23:54:35 keil Exp $ +/* $Id: elsa.c,v 2.17 1999/08/11 20:57:40 keil Exp $ * elsa.c low level stuff for Elsa isdn cards * @@ -13,8 +13,26 @@ * Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE) * for ELSA PCMCIA support * - * * $Log: elsa.c,v $ + * Revision 2.17 1999/08/11 20:57:40 keil + * bugfix IPAC version 1.1 + * new PCI codefix + * + * Revision 2.16 1999/08/10 16:01:51 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 2.15 1999/08/09 19:25:21 keil + * Support (alpha version) for the '98 model of ELSA Microlink ISDN/MC + * by Christer Weinigel, Cendio Systems AB + * Add support for IPAC 1.2 + * + * Revision 2.14 1999/07/12 21:05:07 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 2.13 1999/07/01 08:11:31 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.12 1998/11/15 23:54:35 keil * changes from 2.0 * @@ -70,16 +88,19 @@ #include "hscx.h" #include "isdnl1.h" #include - -//#define KDEBUG_DEF -//#include "../kdebug.h" +#ifndef COMPAT_HAS_NEW_PCI +#include +#endif +#include +#include extern const char *CardType[]; -static const char *Elsa_revision = "$Revision: 2.12 $"; +const char *Elsa_revision = "$Revision: 2.17 $"; const char *Elsa_Types[] = {"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro", - "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI"}; + "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI", + "PCMCIA-IPAC" }; const char *ITACVer[] = {"?0?", "?1?", "?2?", "?3?", "?4?", "V2.2", @@ -109,6 +130,7 @@ const char *ITACVer[] = #define ELSA_QS3000 8 #define ELSA_QS1000PCI 9 #define ELSA_QS3000PCI 10 +#define ELSA_PCMCIA_IPAC 11 /* PCI stuff */ #define PCI_VENDOR_ELSA 0x1048 @@ -158,16 +180,26 @@ const char *ITACVer[] = #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 struct arcofi_msg ARCOFI_XOP_F = + {NULL,0,2,{0xa1,0x3f,0,0,0,0,0,0,0,0}}; /* Normal OP */ +static struct arcofi_msg ARCOFI_XOP_1 = + {&ARCOFI_XOP_F,0,2,{0xa1,0x31,0,0,0,0,0,0,0,0}}; /* PWR UP */ +static struct arcofi_msg ARCOFI_SOP_F = + {&ARCOFI_XOP_1,0,10,{0xa1,0x1f,0x00,0x50,0x10,0x00,0x00,0x80,0x02,0x12}}; +static struct arcofi_msg ARCOFI_COP_9 = + {&ARCOFI_SOP_F,0,10,{0xa1,0x29,0x80,0xcb,0xe9,0x88,0x00,0xc8,0xd8,0x80}}; /* RX */ +static struct arcofi_msg ARCOFI_COP_8 = + {&ARCOFI_COP_9,0,10,{0xa1,0x28,0x49,0x31,0x8,0x13,0x6e,0x88,0x2a,0x61}}; /* TX */ +static struct arcofi_msg ARCOFI_COP_7 = + {&ARCOFI_COP_8,0,4,{0xa1,0x27,0x80,0x80,0,0,0,0,0,0}}; /* GZ */ +static struct arcofi_msg ARCOFI_COP_6 = + {&ARCOFI_COP_7,0,6,{0xa1,0x26,0,0,0x82,0x7c,0,0,0,0}}; /* GRL GRH */ +static struct arcofi_msg ARCOFI_COP_5 = + {&ARCOFI_COP_6,0,4,{0xa1,0x25,0xbb,0x4a,0,0,0,0,0,0}}; /* GTX */ +static struct arcofi_msg ARCOFI_VERSION = + {NULL,1,2,{0xa0,0,0,0,0,0,0,0,0,0}}; +static struct arcofi_msg ARCOFI_XOP_0 = + {NULL,0,2,{0xa1,0x30,0,0,0,0,0,0,0,0}}; /* PWR Down */ static void set_arcofi(struct IsdnCardState *cs, int bc); @@ -343,7 +375,7 @@ elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; u_char val; - int icnt=20; + int icnt=5; if (!cs) { printk(KERN_WARNING "Elsa: Spurious interrupt!\n"); @@ -420,15 +452,17 @@ elsa_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; u_char ista,val; - int icnt=20; + int icnt=5; if (!cs) { printk(KERN_WARNING "Elsa: Spurious interrupt!\n"); return; } - val = bytein(cs->hw.elsa.cfg + 0x4c); /* PCI IRQ */ - if (!(val & ELSA_PCI_IRQ_MASK)) - return; + if (cs->subtyp == ELSA_QS1000PCI || cs->subtyp == ELSA_QS3000PCI) { + val = bytein(cs->hw.elsa.cfg + 0x4c); /* PCI IRQ */ + if (!(val & ELSA_PCI_IRQ_MASK)) + return; + } #if ARCOFI_USE if (cs->hw.elsa.MFlag) { val = serial_inp(cs, UART_IIR); @@ -480,6 +514,7 @@ release_io_elsa(struct IsdnCardState *cs) int bytecnt = 8; del_timer(&cs->hw.elsa.tl); + clear_arcofi(cs); if (cs->hw.elsa.ctrl) byteout(cs->hw.elsa.ctrl, 0); /* LEDs Out */ if (cs->subtyp == ELSA_QS1000PCI) { @@ -493,6 +528,9 @@ release_io_elsa(struct IsdnCardState *cs) writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff); release_region(cs->hw.elsa.cfg, 0x80); } + if (cs->subtyp == ELSA_PCMCIA_IPAC) { + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff); + } if ((cs->subtyp == ELSA_PCFPRO) || (cs->subtyp == ELSA_QS3000) || (cs->subtyp == ELSA_PCF) || @@ -527,19 +565,25 @@ reset_elsa(struct IsdnCardState *cs) if (cs->hw.elsa.trig) byteout(cs->hw.elsa.trig, 0xff); } - if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) { + if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI) || (cs->subtyp == ELSA_PCMCIA_IPAC)) { save_flags(flags); sti(); writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20); current->state = TASK_INTERRUPTIBLE; - schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x00); current->state = TASK_INTERRUPTIBLE; - schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0); + schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ 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); + if (cs->subtyp != ELSA_PCMCIA_IPAC) { + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x0); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0x3c); + } else { + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_PCFG, 0x10); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x4); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0xf8); + } 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 */ @@ -548,41 +592,11 @@ reset_elsa(struct IsdnCardState *cs) } } -static void -init_arcofi(struct IsdnCardState *cs) { - send_arcofi(cs, ARCOFI_XOP_0, 1, 0); -/* send_arcofi(cs, ARCOFI_XOP_F, 1); -*/ -} - -#define ARCDEL 500 - static void set_arcofi(struct IsdnCardState *cs, int bc) { - long flags; - - debugl1(cs,"set_arcofi bc=%d", bc); - 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); - debugl1(cs,"end set_arcofi bc=%d", bc); + cs->dc.isac.arcofi_bc = bc; + arcofi_fsm(cs, ARCOFI_START, &ARCOFI_COP_5); + interruptible_sleep_on(&cs->dc.isac.arcofi_wait); } static int @@ -594,23 +608,24 @@ check_arcofi(struct IsdnCardState *cs) char *t; u_char *p; - if (!cs->mon_tx) - if (!(cs->mon_tx=kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (!cs->dc.isac.mon_tx) + if (!(cs->dc.isac.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)) { - debugl1(cs, "Arcofi response received %d bytes", cs->mon_rxp); - p = cs->mon_rx; + cs->dc.isac.arcofi_bc = 0; + arcofi_fsm(cs, ARCOFI_START, &ARCOFI_VERSION); + interruptible_sleep_on(&cs->dc.isac.arcofi_wait); + if (!test_and_clear_bit(FLG_ARCOFI_ERROR, &cs->HW_Flags)) { + debugl1(cs, "Arcofi response received %d bytes", cs->dc.isac.mon_rxp); + p = cs->dc.isac.mon_rx; t = tmp; t += sprintf(tmp, "Arcofi data"); - QuickHex(t, p, cs->mon_rxp); + QuickHex(t, p, cs->dc.isac.mon_rxp); debugl1(cs, tmp); - if ((cs->mon_rxp == 2) && (cs->mon_rx[0] == 0xa0)) { - switch(cs->mon_rx[1]) { + if ((cs->dc.isac.mon_rxp == 2) && (cs->dc.isac.mon_rx[0] == 0xa0)) { + switch(cs->dc.isac.mon_rx[1]) { case 0x80: debugl1(cs, "Arcofi 2160 detected"); arcofi_present = 1; @@ -629,9 +644,8 @@ check_arcofi(struct IsdnCardState *cs) } } else debugl1(cs, "undefined Monitor response"); - cs->mon_rxp = 0; - } - } else if (cs->mon_tx) { + cs->dc.isac.mon_rxp = 0; + } else if (cs->dc.isac.mon_tx) { debugl1(cs, "Arcofi not detected"); } if (arcofi_present) { @@ -672,7 +686,8 @@ check_arcofi(struct IsdnCardState *cs) "Elsa: %s detected modem at 0x%x\n", Elsa_Types[cs->subtyp], cs->hw.elsa.base+8); - init_arcofi(cs); + arcofi_fsm(cs, ARCOFI_START, &ARCOFI_XOP_0); + interruptible_sleep_on(&cs->dc.isac.arcofi_wait); return(1); } #endif @@ -684,7 +699,7 @@ elsa_led_handler(struct IsdnCardState *cs) { int blink = 0; - if (cs->subtyp == ELSA_PCMCIA) + if (cs->subtyp == ELSA_PCMCIA || cs->subtyp == ELSA_PCMCIA_IPAC) return; del_timer(&cs->hw.elsa.tl); if (cs->hw.elsa.status & ELSA_ASSIGN) @@ -734,15 +749,6 @@ Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) 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 | SA_SHIRQ, "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); @@ -757,6 +763,7 @@ Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); case CARD_TEST: if ((cs->subtyp == ELSA_PCMCIA) || + (cs->subtyp == ELSA_PCMCIA_IPAC) || (cs->subtyp == ELSA_QS1000PCI)) { return(0); } else if (cs->subtyp == ELSA_QS3000PCI) { @@ -770,7 +777,6 @@ Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); byteout(cs->hw.elsa.timer, 0); current->state = TASK_INTERRUPTIBLE; - /* Timeout 110ms */ schedule_timeout((110*HZ)/1000); restore_flags(flags); cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT; @@ -908,8 +914,12 @@ probe_elsa(struct IsdnCardState *cs) return (CARD_portlist[i]); } +#ifdef COMPAT_HAS_NEW_PCI static struct pci_dev *dev_qs1000 __initdata = NULL; static struct pci_dev *dev_qs3000 __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif int setup_elsa(struct IsdnCard *card) @@ -1004,10 +1014,19 @@ setup_elsa(struct IsdnCard *card) } 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; + val = readreg(cs->hw.elsa.base + 0, cs->hw.elsa.base + 2, IPAC_ID); + if ((val == 1) || (val == 2)) { /* IPAC version 1.1/1.2 */ + cs->subtyp = ELSA_PCMCIA_IPAC; + cs->hw.elsa.ale = cs->hw.elsa.base + 0; + cs->hw.elsa.isac = cs->hw.elsa.base + 2; + cs->hw.elsa.hscx = cs->hw.elsa.base + 2; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + } else { + 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; @@ -1018,6 +1037,7 @@ setup_elsa(struct IsdnCard *card) cs->irq); } else if (cs->typ == ISDN_CTYPE_ELSA_PCI) { #if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI if (!pci_present()) { printk(KERN_ERR "Elsa: no PCI bus present\n"); return(0); @@ -1027,17 +1047,17 @@ setup_elsa(struct IsdnCard *card) dev_qs1000))) { cs->subtyp = ELSA_QS1000PCI; cs->irq = dev_qs1000->irq; - cs->hw.elsa.cfg = dev_qs1000->resource[1].start & + cs->hw.elsa.cfg = get_pcibase(dev_qs1000, 1) & PCI_BASE_ADDRESS_IO_MASK; - cs->hw.elsa.base = dev_qs1000->resource[3].start & + cs->hw.elsa.base = get_pcibase(dev_qs1000, 3) & PCI_BASE_ADDRESS_IO_MASK; } else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ELSA, PCI_QS3000_ID, dev_qs3000))) { cs->subtyp = ELSA_QS3000PCI; cs->irq = dev_qs3000->irq; - cs->hw.elsa.cfg = dev_qs3000->resource[1].start & + cs->hw.elsa.cfg = get_pcibase(dev_qs3000, 1) & PCI_BASE_ADDRESS_IO_MASK; - cs->hw.elsa.base = dev_qs3000->resource[3].start & + cs->hw.elsa.base = get_pcibase(dev_qs3000, 3) & PCI_BASE_ADDRESS_IO_MASK; } else { printk(KERN_WARNING "Elsa: No PCI card found\n"); @@ -1052,18 +1072,6 @@ setup_elsa(struct IsdnCard *card) printk(KERN_WARNING "Elsa: No IO-Adr for PCI card found\n"); return(0); } - cs->hw.elsa.ale = cs->hw.elsa.base; - cs->hw.elsa.isac = cs->hw.elsa.base +1; - cs->hw.elsa.hscx = cs->hw.elsa.base +1; - 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); if ((cs->hw.elsa.cfg & 0xff) || (cs->hw.elsa.base & 0xf)) { printk(KERN_WARNING "Elsa: You may have a wrong PCI bios\n"); printk(KERN_WARNING "Elsa: If your system hangs now, read\n"); @@ -1074,6 +1082,67 @@ setup_elsa(struct IsdnCard *card) HZDELAY(500); /* wait 500*10 ms */ restore_flags(flags); } +#else + 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->irq = pci_irq; +#endif /* COMPAT_HAS_NEW_PCI */ + cs->hw.elsa.ale = cs->hw.elsa.base; + cs->hw.elsa.isac = cs->hw.elsa.base +1; + cs->hw.elsa.hscx = cs->hw.elsa.base +1; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + cs->hw.elsa.timer = 0; + cs->hw.elsa.trig = 0; + cs->irq_flags |= SA_SHIRQ; + 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"); @@ -1088,6 +1157,7 @@ setup_elsa(struct IsdnCard *card) case ELSA_PCC16: case ELSA_QS1000: case ELSA_PCMCIA: + case ELSA_PCMCIA_IPAC: bytecnt = 8; break; case ELSA_PCFPRO: @@ -1129,6 +1199,7 @@ setup_elsa(struct IsdnCard *card) request_region(cs->hw.elsa.cfg, 0x80, "elsa isdn pci"); } } + init_arcofi(cs); cs->hw.elsa.tl.function = (void *) elsa_led_handler; cs->hw.elsa.tl.data = (long) cs; init_timer(&cs->hw.elsa.tl); @@ -1161,11 +1232,12 @@ setup_elsa(struct IsdnCard *card) cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &Elsa_card_msg; reset_elsa(cs); - if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) { + if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI) || (cs->subtyp == ELSA_PCMCIA_IPAC)) { cs->readisac = &ReadISAC_IPAC; cs->writeisac = &WriteISAC_IPAC; cs->readisacfifo = &ReadISACfifo_IPAC; cs->writeisacfifo = &WriteISACfifo_IPAC; + cs->irq_func = &elsa_interrupt_ipac; val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ID); printk(KERN_INFO "Elsa: IPAC version %x\n", val); } else { @@ -1173,6 +1245,7 @@ setup_elsa(struct IsdnCard *card) cs->writeisac = &WriteISAC; cs->readisacfifo = &ReadISACfifo; cs->writeisacfifo = &WriteISACfifo; + cs->irq_func = &elsa_interrupt; ISACVersion(cs, "Elsa:"); if (HscxVersion(cs, "Elsa:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c index f5ec29839b9c..a1016fd4b40f 100644 --- a/drivers/isdn/hisax/elsa_ser.c +++ b/drivers/isdn/hisax/elsa_ser.c @@ -1,4 +1,3 @@ -#include #include #include @@ -298,7 +297,7 @@ modem_fill(struct BCState *bcs) { (PACKET_NOACK != bcs->tx_skb->pkt_type)) bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count); - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; } } @@ -510,13 +509,13 @@ close_elsastate(struct BCState *bcs) bcs->hw.hscx.rcvbuf = NULL; } while ((skb = skb_dequeue(&bcs->rqueue))) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } while ((skb = skb_dequeue(&bcs->squeue))) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_WRITE); } if (bcs->tx_skb) { - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); } @@ -664,7 +663,9 @@ modem_l2l1(struct PStack *st, int pr, void *arg) 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->dc.isac.arcofi_bc = st->l1.bc; + arcofi_fsm(st->l1.bcs->cs, ARCOFI_START, &ARCOFI_XOP_0); + interruptible_sleep_on(&st->l1.bcs->cs->dc.isac.arcofi_wait); st->l1.bcs->cs->hw.elsa.MFlag=1; } else { printk(KERN_WARNING"ElsaSer: unknown pr %x\n", pr); diff --git a/drivers/isdn/hisax/gazel.c b/drivers/isdn/hisax/gazel.c new file mode 100644 index 000000000000..ea27f28b230f --- /dev/null +++ b/drivers/isdn/hisax/gazel.c @@ -0,0 +1,751 @@ +/* $Id: gazel.c,v 2.5 1999/08/11 21:01:26 keil Exp $ + + * gazel.c low level stuff for Gazel isdn cards + * + * Author BeWan Systems + * based on source code from Karsten Keil + * + * $Log: gazel.c,v $ + * Revision 2.5 1999/08/11 21:01:26 keil + * new PCI codefix + * + * Revision 2.4 1999/08/10 16:01:54 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 2.3 1999/07/12 21:05:09 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 2.1 1999/07/08 21:26:17 keil + * new card + * + * Revision 1.0 1999/28/06 + * Initial revision + * + */ +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" +#include "ipac.h" +#include +#ifndef COMPAT_HAS_NEW_PCI +#include +#endif + +extern const char *CardType[]; +const char *gazel_revision = "$Revision: 2.5 $"; + +#define R647 1 +#define R685 2 +#define R753 3 +#define R742 4 + +/* Gazel R685 stuff */ +#define GAZEL_MANUFACTURER 0x10b5 +#define GAZEL_R685 0x1030 +#define GAZEL_R753 0x1152 +#define GAZEL_DJINN_ITOO 0x1151 + +#define PLX_CNTRL 0x50 /* registre de controle PLX */ +#define RESET_GAZEL 0x4 +#define RESET_9050 0x40000000 +#define PLX_INCSR 0x4C /* registre d'IT du 9050 */ +#define INT_ISAC_EN 0x8 /* 1 = enable IT isac */ +#define INT_ISAC 0x20 /* 1 = IT isac en cours */ +#define INT_HSCX_EN 0x1 /* 1 = enable IT hscx */ +#define INT_HSCX 0x4 /* 1 = IT hscx en cours */ +#define INT_PCI_EN 0x40 /* 1 = enable IT PCI */ +#define INT_IPAC_EN 0x3 /* enable IT ipac */ + + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +static inline u_char +readreg(unsigned int adr, u_short off) +{ + return bytein(adr + off); +} + +static inline void +writereg(unsigned int adr, u_short off, u_char data) +{ + byteout(adr + off, data); +} + + +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); +} + +static inline u_char +readreg_ipac(unsigned int adr, u_short off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(adr, off); + ret = bytein(adr + 4); + restore_flags(flags); + return ret; +} + +static inline void +writereg_ipac(unsigned int adr, u_short off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(adr, off); + byteout(adr + 4, data); + restore_flags(flags); +} + + +static inline void +read_fifo_ipac(unsigned int adr, u_short off, u_char * data, int size) +{ + byteout(adr, off); + insb(adr + 4, data, size); +} + +static void +write_fifo_ipac(unsigned int adr, u_short off, u_char * data, int size) +{ + byteout(adr, off); + outsb(adr + 4, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + u_short off2 = offset; + + switch (cs->subtyp) { + case R647: + off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf)); + case R685: + return (readreg(cs->hw.gazel.isac, off2)); + case R753: + case R742: + return (readreg_ipac(cs->hw.gazel.ipac, 0x80 + off2)); + } + return 0; +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + u_short off2 = offset; + + switch (cs->subtyp) { + case R647: + off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf)); + case R685: + writereg(cs->hw.gazel.isac, off2, value); + break; + case R753: + case R742: + writereg_ipac(cs->hw.gazel.ipac, 0x80 + off2, value); + break; + } +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + switch (cs->subtyp) { + case R647: + case R685: + read_fifo(cs->hw.gazel.isacfifo, data, size); + break; + case R753: + case R742: + read_fifo_ipac(cs->hw.gazel.ipac, 0x80, data, size); + break; + } +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + switch (cs->subtyp) { + case R647: + case R685: + write_fifo(cs->hw.gazel.isacfifo, data, size); + break; + case R753: + case R742: + write_fifo_ipac(cs->hw.gazel.ipac, 0x80, data, size); + break; + } +} + +static void +ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size) +{ + switch (cs->subtyp) { + case R647: + case R685: + read_fifo(cs->hw.gazel.hscxfifo[hscx], data, size); + break; + case R753: + case R742: + read_fifo_ipac(cs->hw.gazel.ipac, hscx * 0x40, data, size); + break; + } +} + +static void +WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size) +{ + switch (cs->subtyp) { + case R647: + case R685: + write_fifo(cs->hw.gazel.hscxfifo[hscx], data, size); + break; + case R753: + case R742: + write_fifo_ipac(cs->hw.gazel.ipac, hscx * 0x40, data, size); + break; + } +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + u_short off2 = offset; + + switch (cs->subtyp) { + case R647: + off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf)); + case R685: + return (readreg(cs->hw.gazel.hscx[hscx], off2)); + case R753: + case R742: + return (readreg_ipac(cs->hw.gazel.ipac, hscx * 0x40 + off2)); + } + return 0; +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + u_short off2 = offset; + + switch (cs->subtyp) { + case R647: + off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf)); + case R685: + writereg(cs->hw.gazel.hscx[hscx], off2, value); + break; + case R753: + case R742: + writereg_ipac(cs->hw.gazel.ipac, hscx * 0x40 + off2, value); + break; + } +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) ReadHSCX(cs, nr, reg) +#define WRITEHSCX(cs, nr, reg, data) WriteHSCX(cs, nr, reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) ReadHSCXfifo(cs, nr, ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) WriteHSCXfifo(cs, nr, ptr, cnt) + +#include "hscx_irq.c" + +static void +gazel_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ +#define MAXCOUNT 5 + struct IsdnCardState *cs = dev_id; + u_char valisac, valhscx; + int count = 0; + + if (!cs) { + printk(KERN_WARNING "Gazel: Spurious interrupt!\n"); + return; + } + do { + valhscx = ReadHSCX(cs, 1, HSCX_ISTA); + if (valhscx) + hscx_int_main(cs, valhscx); + valisac = ReadISAC(cs, ISAC_ISTA); + if (valisac) + isac_interrupt(cs, valisac); + count++; + } while ((valhscx || valisac) && (count < MAXCOUNT)); + + WriteHSCX(cs, 0, HSCX_MASK, 0xFF); + WriteHSCX(cs, 1, HSCX_MASK, 0xFF); + WriteISAC(cs, ISAC_MASK, 0xFF); + WriteISAC(cs, ISAC_MASK, 0x0); + WriteHSCX(cs, 0, HSCX_MASK, 0x0); + WriteHSCX(cs, 1, HSCX_MASK, 0x0); +} + + +static void +gazel_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char ista, val; + int count = 0; + + if (!cs) { + printk(KERN_WARNING "Gazel: Spurious interrupt!\n"); + return; + } + ista = ReadISAC(cs, IPAC_ISTA - 0x80); + do { + if (ista & 0x0f) { + val = ReadHSCX(cs, 1, HSCX_ISTA); + 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 & ReadISAC(cs, ISAC_ISTA); + if (val) { + isac_interrupt(cs, val); + } + } + if (ista & 0x10) { + val = 0x01; + isac_interrupt(cs, val); + } + ista = ReadISAC(cs, IPAC_ISTA - 0x80); + count++; + } + while ((ista & 0x3f) && (count < MAXCOUNT)); + + WriteISAC(cs, IPAC_MASK - 0x80, 0xFF); + WriteISAC(cs, IPAC_MASK - 0x80, 0xC0); +} +void +release_io_gazel(struct IsdnCardState *cs) +{ + unsigned int i; + + switch (cs->subtyp) { + case R647: + for (i = 0x0000; i < 0xC000; i += 0x1000) + release_region(i + cs->hw.gazel.hscx[0], 16); + release_region(0xC000 + cs->hw.gazel.hscx[0], 1); + break; + + case R685: + release_region(cs->hw.gazel.hscx[0], 0x100); + release_region(cs->hw.gazel.cfg_reg, 0x80); + break; + + case R753: + release_region(cs->hw.gazel.ipac, 0x8); + release_region(cs->hw.gazel.cfg_reg, 0x80); + break; + + case R742: + release_region(cs->hw.gazel.ipac, 8); + break; + } +} + +static int +reset_gazel(struct IsdnCardState *cs) +{ + long flags; + unsigned long plxcntrl, addr = cs->hw.gazel.cfg_reg; + + switch (cs->subtyp) { + case R647: + save_flags(flags); + cli(); + writereg(addr, 0, 0); + HZDELAY(10); + writereg(addr, 0, 1); + HZDELAY(2); + restore_flags(flags); + break; + case R685: + plxcntrl = inl(addr + PLX_CNTRL); + plxcntrl |= (RESET_9050 + RESET_GAZEL); + outl(plxcntrl, addr + PLX_CNTRL); + plxcntrl &= ~(RESET_9050 + RESET_GAZEL); + HZDELAY(4); + outl(plxcntrl, addr + PLX_CNTRL); + HZDELAY(10); + outb(INT_ISAC_EN + INT_HSCX_EN + INT_PCI_EN, addr + PLX_INCSR); + break; + case R753: + plxcntrl = inl(addr + PLX_CNTRL); + plxcntrl |= (RESET_9050 + RESET_GAZEL); + outl(plxcntrl, addr + PLX_CNTRL); + plxcntrl &= ~(RESET_9050 + RESET_GAZEL); + WriteISAC(cs, IPAC_POTA2 - 0x80, 0x20); + HZDELAY(4); + outl(plxcntrl, addr + PLX_CNTRL); + HZDELAY(10); + WriteISAC(cs, IPAC_POTA2 - 0x80, 0x00); + WriteISAC(cs, IPAC_ACFG - 0x80, 0xff); + WriteISAC(cs, IPAC_AOE - 0x80, 0x0); + WriteISAC(cs, IPAC_MASK - 0x80, 0xff); + WriteISAC(cs, IPAC_CONF - 0x80, 0x1); + outb(INT_IPAC_EN + INT_PCI_EN, addr + PLX_INCSR); + WriteISAC(cs, IPAC_MASK - 0x80, 0xc0); + break; + case R742: + WriteISAC(cs, IPAC_POTA2 - 0x80, 0x20); + HZDELAY(4); + WriteISAC(cs, IPAC_POTA2 - 0x80, 0x00); + WriteISAC(cs, IPAC_ACFG - 0x80, 0xff); + WriteISAC(cs, IPAC_AOE - 0x80, 0x0); + WriteISAC(cs, IPAC_MASK - 0x80, 0xff); + WriteISAC(cs, IPAC_CONF - 0x80, 0x1); + WriteISAC(cs, IPAC_MASK - 0x80, 0xc0); + break; + } + return (0); +} + +static int +Gazel_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_gazel(cs); + return (0); + case CARD_RELEASE: + release_io_gazel(cs); + return (0); + case CARD_INIT: + inithscxisac(cs, 1); + if ((cs->subtyp==R647)||(cs->subtyp==R685)) { + int i; + for (i=0;i<(2+MAX_WAITING_CALLS);i++) { + cs->bcs[i].hw.hscx.tsaxr0 = 0x1f; + cs->bcs[i].hw.hscx.tsaxr1 = 0x23; + } + } + return (0); + case CARD_TEST: + return (0); + } + return (0); +} + +static int +reserve_regions(struct IsdnCard *card, struct IsdnCardState *cs) +{ + unsigned int i, base = 0, adr = 0, len = 0; + long flags; + + save_flags(flags); + cli(); + + switch (cs->subtyp) { + case R647: + base = cs->hw.gazel.hscx[0]; + for (i = 0x0000; i < 0xC000; i += 0x1000) { + if (check_region(adr = (i + base), len = 16)) + goto error; + } + if (check_region(adr = (0xC000 + base), len = 1)) + goto error; + + for (i = 0x0000; i < 0xC000; i += 0x1000) + request_region(i + base, 16, "gazel"); + request_region(0xC000 + base, 1, "gazel"); + + break; + + case R685: + if (check_region(adr = cs->hw.gazel.hscx[0], len = 0x100)) + goto error; + if (check_region(adr = cs->hw.gazel.cfg_reg, len = 0x80)) + goto error; + + request_region(cs->hw.gazel.hscx[0], 0x100, "gazel"); + request_region(cs->hw.gazel.cfg_reg, 0x80, "gazel"); + break; + + case R753: + if (check_region(adr = cs->hw.gazel.ipac, len = 0x8)) + goto error; + if (check_region(adr = cs->hw.gazel.cfg_reg, len = 0x80)) + goto error; + + request_region(cs->hw.gazel.ipac, 0x8, "gazel"); + request_region(cs->hw.gazel.cfg_reg, 0x80, "gazel"); + break; + + case R742: + if (check_region(adr = cs->hw.gazel.ipac, len = 0x8)) + goto error; + request_region(cs->hw.gazel.ipac, 0x8, "gazel"); + break; + } + + restore_flags(flags); + return 0; + + error: + restore_flags(flags); + printk(KERN_WARNING "Gazel: %s io ports 0x%x-0x%x already in use\n", + CardType[cs->typ], adr, adr + len); + return 1; +} + +static int +setup_gazelisa(struct IsdnCard *card, struct IsdnCardState *cs) +{ + printk(KERN_INFO "Gazel: ISA PnP card automatic recognition\n"); + // we got an irq parameter, assume it is an ISA card + // R742 decodes address even in not started... + // R647 returns FF if not present or not started + // eventually needs improvment + if (readreg_ipac(card->para[1], IPAC_ID) == 1) + cs->subtyp = R742; + else + cs->subtyp = R647; + + cs->hw.gazel.cfg_reg = card->para[1] + 0xC000; + cs->hw.gazel.ipac = card->para[1]; + cs->hw.gazel.isac = card->para[1] + 0x8000; + cs->hw.gazel.hscx[0] = card->para[1]; + cs->hw.gazel.hscx[1] = card->para[1] + 0x4000; + cs->irq = card->para[0]; + cs->hw.gazel.isacfifo = cs->hw.gazel.isac; + cs->hw.gazel.hscxfifo[0] = cs->hw.gazel.hscx[0]; + cs->hw.gazel.hscxfifo[1] = cs->hw.gazel.hscx[1]; + + switch (cs->subtyp) { + case R647: + printk(KERN_INFO "Gazel: Card ISA R647/R648 found\n"); + cs->dc.isac.adf2 = 0x87; + printk(KERN_INFO + "Gazel: config irq:%d isac:0x%X cfg:0x%X\n", + cs->irq, cs->hw.gazel.isac, cs->hw.gazel.cfg_reg); + printk(KERN_INFO + "Gazel: hscx A:0x%X hscx B:0x%X\n", + cs->hw.gazel.hscx[0], cs->hw.gazel.hscx[1]); + + break; + case R742: + printk(KERN_INFO "Gazel: Card ISA R742 found\n"); + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + printk(KERN_INFO + "Gazel: config irq:%d ipac:0x%X\n", + cs->irq, cs->hw.gazel.ipac); + break; + } + + return (0); +} + +#ifdef COMPAT_HAS_NEW_PCI +static struct pci_dev *dev_tel __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif + +static int +setup_gazelpci(struct IsdnCardState *cs) +{ + u_int pci_ioaddr0 = 0, pci_ioaddr1 = 0; + u_char pci_irq = 0, found; + u_int nbseek, seekcard; + + printk(KERN_WARNING "Gazel: PCI card automatic recognition\n"); + + found = 0; +#ifdef COMPAT_HAS_NEW_PCI + if (!pci_present()) { + printk(KERN_WARNING "Gazel: No PCI bus present\n"); + return 1; + } +#endif + seekcard = GAZEL_R685; + for (nbseek = 0; nbseek < 3; nbseek++) { +#ifdef COMPAT_HAS_NEW_PCI + if ((dev_tel = pci_find_device(GAZEL_MANUFACTURER, seekcard, dev_tel))) { + + pci_irq = dev_tel->irq; + pci_ioaddr0 = get_pcibase(dev_tel, 1); + pci_ioaddr1 = get_pcibase(dev_tel, 2); + found = 1; + } +#else + for (; pci_index < 0xff; pci_index++) { + u_char pci_bus, pci_device_fn; + + if (pcibios_find_device(GAZEL_MANUFACTURER, seekcard, + pci_index, &pci_bus, &pci_device_fn) + != PCIBIOS_SUCCESSFUL) + 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_ioaddr0); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_2, &pci_ioaddr1); + found = 1; + break; + } +#endif /* COMPAT_HAS_NEW_PCI */ + if (found) + break; + else { + switch (seekcard) { + case GAZEL_R685: + seekcard = GAZEL_R753; + break; + case GAZEL_R753: + seekcard = GAZEL_DJINN_ITOO; + break; + } +#ifndef COMPAT_HAS_NEW_PCI + pci_index = 0; +#endif + } + } + if (!found) { + printk(KERN_WARNING "Gazel: No PCI card found\n"); + return (1); + } + if (!pci_irq) { + printk(KERN_WARNING "Gazel: No IRQ for PCI card found\n"); + return 1; + } + cs->hw.gazel.pciaddr[0] = pci_ioaddr0; + cs->hw.gazel.pciaddr[1] = pci_ioaddr1; + + pci_ioaddr1 &= 0xfffe; + cs->hw.gazel.cfg_reg = pci_ioaddr0 & 0xfffe; + cs->hw.gazel.ipac = pci_ioaddr1; + cs->hw.gazel.isac = pci_ioaddr1 + 0x80; + cs->hw.gazel.hscx[0] = pci_ioaddr1; + cs->hw.gazel.hscx[1] = pci_ioaddr1 + 0x40; + cs->hw.gazel.isacfifo = cs->hw.gazel.isac; + cs->hw.gazel.hscxfifo[0] = cs->hw.gazel.hscx[0]; + cs->hw.gazel.hscxfifo[1] = cs->hw.gazel.hscx[1]; + cs->irq = pci_irq; + cs->irq_flags |= SA_SHIRQ; + + switch (seekcard) { + case GAZEL_R685: + printk(KERN_INFO "Gazel: Card PCI R685 found\n"); + cs->subtyp = R685; + cs->dc.isac.adf2 = 0x87; + printk(KERN_INFO + "Gazel: config irq:%d isac:0x%X cfg:0x%X\n", + cs->irq, cs->hw.gazel.isac, cs->hw.gazel.cfg_reg); + printk(KERN_INFO + "Gazel: hscx A:0x%X hscx B:0x%X\n", + cs->hw.gazel.hscx[0], cs->hw.gazel.hscx[1]); + break; + case GAZEL_R753: + case GAZEL_DJINN_ITOO: + printk(KERN_INFO "Gazel: Card PCI R753 found\n"); + cs->subtyp = R753; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + printk(KERN_INFO + "Gazel: config irq:%d ipac:0x%X cfg:0x%X\n", + cs->irq, cs->hw.gazel.ipac, cs->hw.gazel.cfg_reg); + break; + } + + return (0); +} + +__initfunc(int + setup_gazel(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + u_char val; + + strcpy(tmp, gazel_revision); + printk(KERN_INFO "Gazel: Driver Revision %s\n", HiSax_getrev(tmp)); + + if (cs->typ != ISDN_CTYPE_GAZEL) + return (0); + + if (card->para[0]) { + if (setup_gazelisa(card, cs)) + return (0); + } else { + +#if CONFIG_PCI + if (setup_gazelpci(cs)) + return (0); +#else + printk(KERN_WARNING "Gazel: Card PCI requested and NO_PCI_BIOS, unable to config\n"); + return (0); +#endif /* CONFIG_PCI */ + } + + if (reserve_regions(card, cs)) { + return (0); + } + if (reset_gazel(cs)) { + printk(KERN_WARNING "Gazel: wrong IRQ\n"); + release_io_gazel(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 = &Gazel_card_msg; + + switch (cs->subtyp) { + case R647: + case R685: + cs->irq_func = &gazel_interrupt; + ISACVersion(cs, "Gazel:"); + if (HscxVersion(cs, "Gazel:")) { + printk(KERN_WARNING + "Gazel: wrong HSCX versions check IO address\n"); + release_io_gazel(cs); + return (0); + } + break; + case R742: + case R753: + cs->irq_func = &gazel_interrupt_ipac; + val = ReadISAC(cs, IPAC_ID - 0x80); + printk(KERN_INFO "Gazel: IPAC version %x\n", val); + break; + } + + return (1); +} diff --git a/drivers/isdn/hisax/hfc_2bds0.c b/drivers/isdn/hisax/hfc_2bds0.c index 4e734b2c6fa1..cbb64fbab98f 100644 --- a/drivers/isdn/hisax/hfc_2bds0.c +++ b/drivers/isdn/hisax/hfc_2bds0.c @@ -1,4 +1,4 @@ -/* $Id: hfc_2bds0.c,v 1.8 1998/11/15 23:54:40 keil Exp $ +/* $Id: hfc_2bds0.c,v 1.9 1999/07/01 08:11:35 keil Exp $ * * specific routines for CCD's HFC 2BDS0 * @@ -6,6 +6,9 @@ * * * $Log: hfc_2bds0.c,v $ + * Revision 1.9 1999/07/01 08:11:35 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.8 1998/11/15 23:54:40 keil * changes from 2.0 * @@ -264,6 +267,7 @@ static struct sk_buff } 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); @@ -281,7 +285,7 @@ static struct sk_buff sti(); debugl1(cs, "RFIFO BUSY error"); printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); skb = NULL; } else { cli(); @@ -297,7 +301,7 @@ static struct sk_buff bcs->channel, chksum, stat); if (stat) { debugl1(cs, "FIFO CRC error"); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); skb = NULL; } } @@ -386,7 +390,7 @@ hfc_fill_fifo(struct BCState *bcs) if (bcs->st->lli.l1writewakeup && (PACKET_NOACK != bcs->tx_skb->pkt_type)) bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len); - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; } WaitForBusy(cs); @@ -588,7 +592,7 @@ close_2bs0(struct BCState *bcs) discard_queue(&bcs->rqueue); discard_queue(&bcs->squeue); if (bcs->tx_skb) { - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); } @@ -642,7 +646,7 @@ hfcd_bh(struct IsdnCardState *cs) } #endif if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) { - switch (cs->ph_state) { + switch (cs->dc.hfcd.ph_state) { case (0): l1_msg(cs, HW_RESET | INDICATION, NULL); break; @@ -733,6 +737,7 @@ int receive_dmsg(struct IsdnCardState *cs) 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(); @@ -747,7 +752,7 @@ int receive_dmsg(struct IsdnCardState *cs) sti(); debugl1(cs, "RFIFO D BUSY error"); printk(KERN_WARNING "HFC DFIFO channel BUSY Error\n"); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); skb = NULL; } else { cli(); @@ -763,7 +768,7 @@ int receive_dmsg(struct IsdnCardState *cs) chksum, stat); if (stat) { debugl1(cs, "FIFO CRC error"); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); skb = NULL; } else { skb_queue_tail(&cs->rq, skb); @@ -860,7 +865,7 @@ hfc_fill_dfifo(struct IsdnCardState *cs) cli(); WaitNoBusy(cs); ReadReg(cs, HFCD_DATA, HFCD_FIFO | HFCD_F1_INC | HFCD_SEND); - dev_kfree_skb(cs->tx_skb); + idev_kfree_skb(cs->tx_skb, FREE_WRITE); cs->tx_skb = NULL; sti(); WaitForBusy(cs); @@ -895,9 +900,9 @@ hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val) if (val & 0x40) { /* TE state machine irq */ exval = cs->readisac(cs, HFCD_STATES) & 0xf; if (cs->debug & L1_DEB_ISAC) - debugl1(cs, "ph_state chg %d->%d", cs->ph_state, + debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcd.ph_state, exval); - cs->ph_state = exval; + cs->dc.hfcd.ph_state = exval; sched_event_D(cs, D_L1STATECHANGE); val &= ~0x40; } @@ -984,7 +989,7 @@ hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val) 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) { if (cs->tx_skb->len) { if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { hfc_fill_dfifo(cs); @@ -994,10 +999,11 @@ hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val) } goto afterXPR; } else { - dev_kfree_skb(cs->tx_skb); + idev_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)) { @@ -1166,8 +1172,8 @@ hfc_dbusy_timer(struct IsdnCardState *cs) #endif } -unsigned int __init -*init_send_hfcd(int cnt) +__initfunc(unsigned int +*init_send_hfcd(int cnt)) { int i, *send; @@ -1181,8 +1187,8 @@ unsigned int __init return(send); } -void __init -init2bds0(struct IsdnCardState *cs) +__initfunc(void +init2bds0(struct IsdnCardState *cs)) { cs->setstack_d = setstack_hfcd; cs->dbusytimer.function = (void *) hfc_dbusy_timer; diff --git a/drivers/isdn/hisax/hfc_2bs0.c b/drivers/isdn/hisax/hfc_2bs0.c index 13225cb7af12..86515e17d273 100644 --- a/drivers/isdn/hisax/hfc_2bs0.c +++ b/drivers/isdn/hisax/hfc_2bs0.c @@ -1,4 +1,4 @@ -/* $Id: hfc_2bs0.c,v 1.8 1998/11/15 23:54:43 keil Exp $ +/* $Id: hfc_2bs0.c,v 1.9 1999/07/01 08:11:36 keil Exp $ * specific routines for CCD's HFC 2BS0 * @@ -6,6 +6,9 @@ * * * $Log: hfc_2bs0.c,v $ + * Revision 1.9 1999/07/01 08:11:36 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.8 1998/11/15 23:54:43 keil * changes from 2.0 * @@ -219,6 +222,7 @@ hfc_empty_fifo(struct BCState *bcs, int count) 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); @@ -229,7 +233,7 @@ hfc_empty_fifo(struct BCState *bcs, int count) 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); + idev_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)); @@ -247,7 +251,7 @@ hfc_empty_fifo(struct BCState *bcs, int count) bcs->channel, chksum, stat); if (stat) { debugl1(cs, "FIFO CRC error"); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); skb = NULL; } WaitNoBusy(cs); @@ -321,7 +325,7 @@ hfc_fill_fifo(struct BCState *bcs) bcs->tx_cnt -= count; if (PACKET_NOACK == bcs->tx_skb->pkt_type) count = -1; - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; WaitForBusy(cs); WaitNoBusy(cs); @@ -519,7 +523,7 @@ close_hfcstate(struct BCState *bcs) discard_queue(&bcs->rqueue); discard_queue(&bcs->squeue); if (bcs->tx_skb) { - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); } @@ -555,8 +559,8 @@ setstack_hfc(struct PStack *st, struct BCState *bcs) return (0); } -void __init -init_send(struct BCState *bcs) +__initfunc(void +init_send(struct BCState *bcs)) { int i; @@ -569,8 +573,8 @@ init_send(struct BCState *bcs) bcs->hw.hfc.send[i] = 0x1fff; } -void __init -inithfc(struct IsdnCardState *cs) +__initfunc(void +inithfc(struct IsdnCardState *cs)) { init_send(&cs->bcs[0]); init_send(&cs->bcs[1]); diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c new file mode 100644 index 000000000000..2d972f3fe625 --- /dev/null +++ b/drivers/isdn/hisax/hfc_pci.c @@ -0,0 +1,1584 @@ +/* $Id: hfc_pci.c,v 1.13 1999/08/11 21:01:28 keil Exp $ + + * hfc_pci.c low level driver for CCD´s hfc-pci based cards + * + * Author Werner Cornelius (werner@isdn4linux.de) + * based on existing driver for CCD hfc ISA cards + * + * Copyright 1999 by Werner Cornelius (werner@isdn4linux.de) + * Copyright 1999 by Karsten Keil (keil@isdn4linux.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: hfc_pci.c,v $ + * Revision 1.13 1999/08/11 21:01:28 keil + * new PCI codefix + * + * Revision 1.12 1999/08/10 16:01:58 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.11 1999/08/09 19:13:32 werner + * moved constant pci ids to pci id table + * + * Revision 1.10 1999/08/08 10:17:34 werner + * added new PCI vendor and card ids for Manufacturer 0x1043 + * + * Revision 1.9 1999/08/07 21:09:10 werner + * Fixed another memcpy problem in fifo handling. + * Thanks for debugging aid by Olaf Kordwittenborg. + * + * Revision 1.8 1999/07/23 14:25:15 werner + * Some smaller bug fixes and prepared support for GCI/IOM bus + * + * Revision 1.7 1999/07/14 21:24:20 werner + * fixed memcpy problem when using E-channel feature + * + * Revision 1.6 1999/07/13 21:08:08 werner + * added echo channel logging feature. + * + * Revision 1.5 1999/07/12 21:05:10 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.4 1999/07/04 21:51:39 werner + * Changes to solve problems with irq sharing and smp machines + * Thanks to Karsten Keil and Alex Holden for giving aid with + * testing and debugging + * + * Revision 1.3 1999/07/01 09:43:19 keil + * removed additional schedules in timeouts + * + * Revision 1.2 1999/07/01 08:07:51 keil + * Initial version + * + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_pci.h" +#include "isdnl1.h" +#include +#ifndef COMPAT_HAS_NEW_PCI +#include +#endif +#include + +extern const char *CardType[]; + +static const char *hfcpci_revision = "$Revision: 1.13 $"; + +static const int CCD_VENDOR_IDS[] = { + 0x1043, /* Asuscom */ + 0x1051, /* Motorola MC145575 */ + 0x1397, /* CCD and Billion */ + 0, +}; + +static const int CCD_DEVICE_IDS[] = { + 0x675, /* Asuscom */ + 0x100, /* Motorola MC145575 */ + 0x2BD0, /* CCD and Billion */ + 0, +}; + + +#if CONFIG_PCI +/*****************************/ +/* release D- and B-channels */ +/*****************************/ +void +releasehfcpci(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; + } +} + +/******************************************/ +/* free hardware resources used by driver */ +/******************************************/ +void +release_io_hfcpci(struct IsdnCardState *cs) +{ +#if CONFIG_PCI + pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, 0); /* disabe memory mapped ports + busmaster */ +#endif /* CONFIG_PCI */ + releasehfcpci(cs); + del_timer(&cs->hw.hfcpci.timer); + kfree(cs->hw.hfcpci.share_start); + cs->hw.hfcpci.share_start = NULL; + vfree(cs->hw.hfcpci.pci_io); +} + +/********************************************************************************/ +/* function called to reset the HFC PCI chip. A complete software reset of chip */ +/* and fifos is done. */ +/********************************************************************************/ +static void +reset_hfcpci(struct IsdnCardState *cs) +{ + long flags; + + pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO); /* enable memory mapped ports, disable busmaster */ + printk(KERN_INFO "HFC_PCI: resetting card\n"); + pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO + PCI_ENA_MASTER); /* enable memory ports + busmaster */ + Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((30 * HZ) / 1000); /* Timeout 30ms */ + Write_hfc(cs, HFCPCI_CIRM, 0); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */ + if (Read_hfc(cs, HFCPCI_STATUS) & 2) + printk(KERN_WARNING "HFC-PCI init bit busy\n"); + + cs->hw.hfcpci.fifo_en = 0x30; /* only D fifos enabled */ + Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en); + + cs->hw.hfcpci.trm = 0; /* no echo connect */ + Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm); + + Write_hfc(cs, HFCPCI_CLKDEL, 0x0e); /* ST-Bit delay for TE-Mode */ + cs->hw.hfcpci.sctrl_e = HFCPCI_AUTO_AWAKE; + Write_hfc(cs, HFCPCI_SCTRL_E, cs->hw.hfcpci.sctrl_e); /* S/T Auto awake */ + cs->hw.hfcpci.bswapped = 0; /* no exchange */ + cs->hw.hfcpci.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER; + Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt); + + cs->hw.hfcpci.int_m2 = HFCPCI_IRQ_ENABLE; + cs->hw.hfcpci.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC | + HFCPCI_INTS_L1STATE | HFCPCI_CLTIMER; + Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2); + + /* Clear already pending ints */ + if (Read_hfc(cs, HFCPCI_INT_S1)); + if (Read_hfc(cs, HFCPCI_INT_S2)); + + Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 2); /* HFC ST 2 */ + udelay(10); + Write_hfc(cs, HFCPCI_STATES, 2); /* HFC ST 2 */ + cs->hw.hfcpci.mst_m = HFCPCI_MASTER; /* HFC Master Mode */ + + Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); + cs->hw.hfcpci.sctrl = 0; + Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl); + cs->hw.hfcpci.sctrl_r = 0; + Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r); + + /* Init GCI/IOM2 in master mode */ + /* Slots 0 and 1 are set for B-chan 1 and 2 */ + /* D- and monitor/CI channel are not enabled */ + /* STIO1 is used as output for data, B1+B2 from ST->IOM+HFC */ + /* STIO2 is used as data input, B1+B2 from IOM->ST */ + /* ST B-channel send disabled -> continous 1s */ + /* The IOM slots are always enabled */ + cs->hw.hfcpci.conn = 0x36; /* set data flow directions */ + Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn); + Write_hfc(cs, HFCPCI_B1_SSL, 0x80); /* B1-Slot 0 STIO1 out enabled */ + Write_hfc(cs, HFCPCI_B2_SSL, 0x81); /* B2-Slot 1 STIO1 out enabled */ + Write_hfc(cs, HFCPCI_B1_RSL, 0x80); /* B1-Slot 0 STIO2 in enabled */ + Write_hfc(cs, HFCPCI_B2_RSL, 0x81); /* B2-Slot 1 STIO2 in enabled */ + restore_flags(flags); +} + +/***************************************************/ +/* Timer function called when kernel timer expires */ +/***************************************************/ +static void +hfcpci_Timer(struct IsdnCardState *cs) +{ + cs->hw.hfcpci.timer.expires = jiffies + 75; + /* WD RESET */ +/* WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcpci.ctmt | 0x80); + add_timer(&cs->hw.hfcpci.timer); + */ +} + + +/*********************************/ +/* schedule a new D-channel task */ +/*********************************/ +static void +sched_event_D_pci(struct IsdnCardState *cs, int event) +{ + test_and_set_bit(event, &cs->event); + queue_task(&cs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +/*********************************/ +/* schedule a new b_channel task */ +/*********************************/ +static void +hfcpci_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +/************************************************/ +/* select a b-channel entry matching and active */ +/************************************************/ +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); +} + +/*********************************************/ +/* read a complete B-frame out of the buffer */ +/*********************************************/ +static struct sk_buff +* +hfcpci_empty_fifo(struct BCState *bcs, bzfifo_type * bz, u_char * bdata, int count) +{ + u_char *ptr, *ptr1, new_f2; + struct sk_buff *skb; + struct IsdnCardState *cs = bcs->cs; + int flags, total, maxlen, new_z2; + z_type *zp; + + save_flags(flags); + sti(); + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hfcpci_empty_fifo"); + zp = &bz->za[bz->f2]; /* point to Z-Regs */ + new_z2 = zp->z2 + count; /* new position in fifo */ + if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + new_f2 = (bz->f2 + 1) & MAX_B_FRAMES; + if ((count > HSCX_BUFMAX + 3) || (count < 4) || + (*(bdata + (zp->z1 - B_SUB_VAL)))) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfcpci_empty_fifo: incoming packet invalid length %d or crc", count); + bz->za[new_f2].z2 = new_z2; + bz->f2 = new_f2; /* next buffer */ + skb = NULL; + } else if (!(skb = dev_alloc_skb(count - 3))) + printk(KERN_WARNING "HFCPCI: receive out of memory\n"); + else { + SET_SKB_FREE(skb); + total = count; + count -= 3; + ptr = skb_put(skb, count); + + if (zp->z2 + count <= B_FIFO_SIZE + B_SUB_VAL) + maxlen = count; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2; /* maximum */ + + ptr1 = bdata + (zp->z2 - B_SUB_VAL); /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + count -= maxlen; + + if (count) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, count); /* rest */ + } + bz->za[new_f2].z2 = new_z2; + bz->f2 = new_f2; /* next buffer */ + + } + restore_flags(flags); + return (skb); +} + +/*******************************/ +/* D-channel receive procedure */ +/*******************************/ +static +int +receive_dmsg(struct IsdnCardState *cs) +{ + struct sk_buff *skb; + int maxlen; + int rcnt, total; + int count = 5; + u_char *ptr, *ptr1; + dfifo_type *df; + z_type *zp; + + df = &((fifo_area *) (cs->hw.hfcpci.fifos))->d_chan.d_rx; + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + debugl1(cs, "rec_dmsg blocked"); + return (1); + } + while (((df->f1 & D_FREG_MASK) != (df->f2 & D_FREG_MASK)) && count--) { + zp = &df->za[df->f2 & D_FREG_MASK]; + rcnt = zp->z1 - zp->z2; + if (rcnt < 0) + rcnt += D_FIFO_SIZE; + rcnt++; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)", + df->f1, df->f2, zp->z1, zp->z2, rcnt); + + if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) || + (df->data[zp->z1])) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "empty_fifo hfcpci paket inv. len %d or crc %d", rcnt, df->data[zp->z1]); + df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1); /* next buffer */ + df->za[df->f2 & D_FREG_MASK].z2 = (zp->z2 + rcnt) & (D_FIFO_SIZE - 1); + } else if ((skb = dev_alloc_skb(rcnt - 3))) { + SET_SKB_FREE(skb); + total = rcnt; + rcnt -= 3; + ptr = skb_put(skb, rcnt); + + if (zp->z2 + rcnt <= D_FIFO_SIZE) + maxlen = rcnt; /* complete transfer */ + else + maxlen = D_FIFO_SIZE - zp->z2; /* maximum */ + + ptr1 = df->data + zp->z2; /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + rcnt -= maxlen; + + if (rcnt) { /* rest remaining */ + ptr += maxlen; + ptr1 = df->data; /* start of buffer */ + memcpy(ptr, ptr1, rcnt); /* rest */ + } + df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1); /* next buffer */ + df->za[df->f2 & D_FREG_MASK].z2 = (zp->z2 + total) & (D_FIFO_SIZE - 1); + + skb_queue_tail(&cs->rq, skb); + sched_event_D_pci(cs, D_RCVBUFREADY); + } else + printk(KERN_WARNING "HFC-PCI: D receive out of memory\n"); + } + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + return (1); +} + +/**********************************/ +/* B-channel main receive routine */ +/**********************************/ +void +main_rec_hfcpci(struct BCState *bcs) +{ + long flags; + struct IsdnCardState *cs = bcs->cs; + int rcnt; + int receive, count = 5; + struct sk_buff *skb; + bzfifo_type *bz; + u_char *bdata; + z_type *zp; + + + save_flags(flags); + if ((bcs->channel) && (!cs->hw.hfcpci.bswapped)) { + bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2; + bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b2; + } else { + bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b1; + bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b1; + } + Begin: + count--; + cli(); + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + debugl1(cs, "rec_data %d blocked", bcs->channel); + restore_flags(flags); + return; + } + sti(); + if (bz->f1 != bz->f2) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci rec %d f1(%d) f2(%d)", + bcs->channel, bz->f1, bz->f2); + zp = &bz->za[bz->f2]; + + rcnt = zp->z1 - zp->z2; + if (rcnt < 0) + rcnt += B_FIFO_SIZE; + rcnt++; + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci rec %d z1(%x) z2(%x) cnt(%d)", + bcs->channel, zp->z1, zp->z2, rcnt); + if ((skb = hfcpci_empty_fifo(bcs, bz, bdata, rcnt))) { + cli(); + skb_queue_tail(&bcs->rqueue, skb); + sti(); + hfcpci_sched_event(bcs, B_RCVBUFREADY); + } + rcnt = bz->f1 - bz->f2; + if (rcnt < 0) + rcnt += MAX_B_FRAMES + 1; + 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; +} + +/**************************/ +/* D-channel send routine */ +/**************************/ +static void +hfcpci_fill_dfifo(struct IsdnCardState *cs) +{ + long flags; + int fcnt; + int count, new_z1, maxlen; + dfifo_type *df; + u_char *src, *dst, new_f1; + + if (!cs->tx_skb) + return; + if (cs->tx_skb->len <= 0) + return; + + df = &((fifo_area *) (cs->hw.hfcpci.fifos))->d_chan.d_tx; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci_fill_Dfifo f1(%d) f2(%d) z1(f1)(%x)", + df->f1, df->f2, + df->za[df->f1 & D_FREG_MASK].z1); + fcnt = df->f1 - df->f2; /* frame count actually buffered */ + if (fcnt < 0) + fcnt += (MAX_D_FRAMES + 1); /* if wrap around */ + if (fcnt > (MAX_D_FRAMES - 1)) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci_fill_Dfifo more as 14 frames"); + return; + } + /* now determine free bytes in FIFO buffer */ + count = df->za[df->f1 & D_FREG_MASK].z2 - df->za[df->f1 & D_FREG_MASK].z1; + if (count <= 0) + count += D_FIFO_SIZE; /* count now contains available bytes */ + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci_fill_Dfifo count(%ld/%d)", + cs->tx_skb->len, count); + if (count < cs->tx_skb->len) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci_fill_Dfifo no fifo mem"); + return; + } + count = cs->tx_skb->len; /* get frame len */ + new_z1 = (df->za[df->f1 & D_FREG_MASK].z1 + count) & (D_FIFO_SIZE - 1); + new_f1 = ((df->f1 + 1) & D_FREG_MASK) | (D_FREG_MASK + 1); + src = cs->tx_skb->data; /* source pointer */ + dst = df->data + df->za[df->f1 & D_FREG_MASK].z1; + maxlen = D_FIFO_SIZE - df->za[df->f1 & D_FREG_MASK].z1; /* end fifo */ + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = df->data; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + save_flags(flags); + cli(); + df->za[new_f1 & D_FREG_MASK].z1 = new_z1; /* for next buffer */ + df->za[df->f1 & D_FREG_MASK].z1 = new_z1; /* new pos actual buffer */ + df->f1 = new_f1; /* next frame */ + restore_flags(flags); + + idev_kfree_skb(cs->tx_skb, FREE_WRITE); + cs->tx_skb = NULL; + return; +} + +/**************************/ +/* B-channel send routine */ +/**************************/ +static void +hfcpci_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + int flags, maxlen, fcnt; + int count, new_z1; + bzfifo_type *bz; + u_char *bdata; + u_char new_f1, *src, *dst; + + if (!bcs->tx_skb) + return; + if (bcs->tx_skb->len <= 0) + return; + + save_flags(flags); + sti(); + + if ((bcs->channel) && (!cs->hw.hfcpci.bswapped)) { + bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b2; + bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txdat_b2; + } else { + bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b1; + bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txdat_b1; + } + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci_fill_fifo %d f1(%d) f2(%d) z1(f1)(%x)", + bcs->channel, bz->f1, bz->f2, + bz->za[bz->f1].z1); + + fcnt = bz->f1 - bz->f2; /* frame count actually buffered */ + if (fcnt < 0) + fcnt += (MAX_B_FRAMES + 1); /* if wrap around */ + if (fcnt > (MAX_B_FRAMES - 1)) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci_fill_Bfifo more as 14 frames"); + restore_flags(flags); + return; + } + /* now determine free bytes in FIFO buffer */ + count = bz->za[bz->f1].z2 - bz->za[bz->f1].z1; + if (count <= 0) + count += B_FIFO_SIZE; /* count now contains available bytes */ + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci_fill_fifo %d count(%ld/%d),%lx", + bcs->channel, bcs->tx_skb->len, + count, current->state); + + if (count < bcs->tx_skb->len) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci_fill_fifo no fifo mem"); + restore_flags(flags); + return; + } + count = bcs->tx_skb->len; /* get frame len */ + new_z1 = bz->za[bz->f1].z1 + count; /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + + new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES); + src = bcs->tx_skb->data; /* source pointer */ + dst = bdata + (bz->za[bz->f1].z1 - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - bz->za[bz->f1].z1; /* end fifo */ + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + bcs->tx_cnt -= bcs->tx_skb->len; + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len); + + cli(); + bz->za[new_f1].z1 = new_z1; /* for next buffer */ + bz->f1 = new_f1; /* next frame */ + restore_flags(flags); + + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + return; +} + +/***********************/ +/* set/reset echo mode */ +/***********************/ +int hfcpci_set_echo(struct IsdnCardState *cs, int i) +{ int flags; + + if (cs->chanlimit > 1) + return(-EINVAL); + + save_flags(flags); + cli(); + if (i) { + cs->logecho = 1; + cs->hw.hfcpci.trm |= 0x20; /* enable echo chan */ + cs->hw.hfcpci.int_m1 |= HFCPCI_INTS_B2REC; + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2RX; + } + else { + cs->logecho = 0; + cs->hw.hfcpci.trm &= ~0x20; /* enable echo chan */ + cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_B2REC; + cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2RX; + } + cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA; + cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA; + cs->hw.hfcpci.conn |= 0x10; /* B2-IOM -> B2-ST */ + cs->hw.hfcpci.ctmt &= ~2; + Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt); + Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r); + Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl); + Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn); + Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm); + Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en); + Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + restore_flags(flags); + return(0); +} /* hfcpci_set_echo */ + +/*****************************/ +/* E-channel receive routine */ +/*****************************/ +static void receive_emsg(struct IsdnCardState *cs) +{ + long flags; + int rcnt; + int receive, count = 5; + bzfifo_type *bz; + u_char *bdata; + z_type *zp; + u_char *ptr, *ptr1, new_f2; + int total, maxlen, new_z2; + u_char e_buffer[256]; + + save_flags(flags); + bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2; + bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b2; + Begin: + count--; + cli(); + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + debugl1(cs, "echo_rec_data blocked"); + restore_flags(flags); + return; + } + sti(); + if (bz->f1 != bz->f2) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci e_rec f1(%d) f2(%d)", + bz->f1, bz->f2); + zp = &bz->za[bz->f2]; + + rcnt = zp->z1 - zp->z2; + if (rcnt < 0) + rcnt += B_FIFO_SIZE; + rcnt++; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci e_rec z1(%x) z2(%x) cnt(%d)", + zp->z1, zp->z2, rcnt); + new_z2 = zp->z2 + rcnt; /* new position in fifo */ + if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + new_f2 = (bz->f2 + 1) & MAX_B_FRAMES; + if ((rcnt > 256 + 3) || (count < 4) || + (*(bdata + (zp->z1 - B_SUB_VAL)))) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfcpci_empty_echan: incoming packet invalid length %d or crc", rcnt); + bz->za[new_f2].z2 = new_z2; + bz->f2 = new_f2; /* next buffer */ + } else { + total = rcnt; + rcnt -= 3; + ptr = e_buffer; + + if (zp->z2 <= B_FIFO_SIZE + B_SUB_VAL) + maxlen = rcnt; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2; /* maximum */ + + ptr1 = bdata + (zp->z2 - B_SUB_VAL); /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + rcnt -= maxlen; + + if (rcnt) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, rcnt); /* rest */ + } + bz->za[new_f2].z2 = new_z2; + bz->f2 = new_f2; /* next buffer */ + if (cs->debug & DEB_DLOG_HEX) { + ptr = cs->dlog; + if ((total - 3) < MAX_DLOG_SPACE / 3 - 10) { + *ptr++ = 'E'; + *ptr++ = 'C'; + *ptr++ = 'H'; + *ptr++ = 'O'; + *ptr++ = ':'; + ptr += QuickHex(ptr, e_buffer, total - 3); + ptr--; + *ptr++ = '\n'; + *ptr = 0; + HiSax_putstatus(cs, NULL, cs->dlog); + } else + HiSax_putstatus(cs, "LogEcho: ", "warning Frame too big (%d)", total - 3); + } + + } + + rcnt = bz->f1 - bz->f2; + if (rcnt < 0) + rcnt += MAX_B_FRAMES + 1; + 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; +} /* receive_emsg */ + +/*********************/ +/* Interrupt handler */ +/*********************/ +static void +hfcpci_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char exval; + struct BCState *bcs; + int count = 15; + long flags; + u_char val, stat; + + if (!cs) { + printk(KERN_WARNING "HFC-PCI: Spurious interrupt!\n"); + return; + } + if (HFCPCI_ANYINT & (stat = Read_hfc(cs, HFCPCI_STATUS))) { + val = Read_hfc(cs, HFCPCI_INT_S1); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFC-PCI: stat(%02x) s1(%02x)", stat, val); + } else + return; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFC-PCI irq %x %s", val, + test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ? + "locked" : "unlocked"); + val &= cs->hw.hfcpci.int_m1; + if (val & 0x40) { /* TE state machine irq */ + exval = Read_hfc(cs, HFCPCI_STATES) & 0xf; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcpci.ph_state, + exval); + cs->dc.hfcpci.ph_state = exval; + sched_event_D_pci(cs, D_L1STATECHANGE); + val &= ~0x40; + } + while (val) { + save_flags(flags); + cli(); + if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + cs->hw.hfcpci.int_s1 |= val; + restore_flags(flags); + return; + } + if (cs->hw.hfcpci.int_s1 & 0x18) { + exval = val; + val = cs->hw.hfcpci.int_s1; + cs->hw.hfcpci.int_s1 = exval; + } + if (val & 0x08) { + if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1:0))) { + if (cs->debug) + debugl1(cs, "hfcpci spurious 0x08 IRQ"); + } else + main_rec_hfcpci(bcs); + } + if (val & 0x10) { + if (cs->logecho) + receive_emsg(cs); + else + if (!(bcs = Sel_BCS(cs, 1))) { + if (cs->debug) + debugl1(cs, "hfcpci spurious 0x10 IRQ"); + } else + main_rec_hfcpci(bcs); + } + if (val & 0x01) { + if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1:0))) { + if (cs->debug) + debugl1(cs, "hfcpci spurious 0x01 IRQ"); + } else { + if (bcs->tx_skb) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "fill_data %d blocked", bcs->channel); + } else { + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "fill_data %d blocked", bcs->channel); + } else { + hfcpci_sched_event(bcs, B_XMTBUFREADY); + } + } + } + } + if (val & 0x02) { + if (!(bcs = Sel_BCS(cs, 1))) { + if (cs->debug) + debugl1(cs, "hfcpci spurious 0x02 IRQ"); + } else { + if (bcs->tx_skb) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "fill_data %d blocked", bcs->channel); + } else { + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "fill_data %d blocked", bcs->channel); + } else { + hfcpci_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_pci(cs, D_CLEARBUSY); + if (cs->tx_skb) { + if (cs->tx_skb->len) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + debugl1(cs, "hfcpci_fill_dfifo irq blocked"); + } + goto afterXPR; + } else { + idev_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)) { + hfcpci_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + debugl1(cs, "hfcpci_fill_dfifo irq blocked"); + } + } else + sched_event_D_pci(cs, D_XMTBUFREADY); + } + afterXPR: + if (cs->hw.hfcpci.int_s1 && count--) { + val = cs->hw.hfcpci.int_s1; + cs->hw.hfcpci.int_s1 = 0; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFC-PCI irq %x loop %d", val, 15 - count); + } else + val = 0; + restore_flags(flags); + } +} + +/********************************************************************/ +/* timer callback for D-chan busy resolution. Currently no function */ +/********************************************************************/ +static void +hfcpci_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 +} + +/*************************************/ +/* Layer 1 D-channel hardware access */ +/*************************************/ +static void +HFCPCI_l1hw(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + struct sk_buff *skb = arg; + + switch (pr) { + case (PH_DATA | REQUEST): + if (cs->debug & DEB_DLOG_HEX) + LogFrame(cs, skb->data, skb->len); + if (cs->debug & DEB_DLOG_VERBOSE) + dlogframe(cs, skb, 0); + 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 { + 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)) { + hfcpci_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "hfcpci_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->debug & DEB_DLOG_HEX) + LogFrame(cs, skb->data, skb->len); + if (cs->debug & DEB_DLOG_VERBOSE) + dlogframe(cs, skb, 0); + 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)) { + hfcpci_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "hfcpci_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): + Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3); /* HFC ST 3 */ + udelay(6); + Write_hfc(cs, HFCPCI_STATES, 3); /* HFC ST 2 */ + cs->hw.hfcpci.mst_m |= HFCPCI_MASTER; + Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); + Write_hfc(cs, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION); + l1_msg(cs, HW_POWERUP | CONFIRM, NULL); + break; + case (HW_ENABLE | REQUEST): + Write_hfc(cs, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION); + break; + case (HW_DEACTIVATE | REQUEST): + cs->hw.hfcpci.mst_m &= ~HFCPCI_MASTER; + Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); + break; + case (HW_INFO3 | REQUEST): + cs->hw.hfcpci.mst_m |= HFCPCI_MASTER; + Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.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) + debugl1(cs, "hfcpci_l1hw unknown pr %4x", pr); + break; + } +} + +/***********************************************/ +/* called during init setting l1 stack pointer */ +/***********************************************/ +void +setstack_hfcpci(struct PStack *st, struct IsdnCardState *cs) +{ + st->l1.l1hw = HFCPCI_l1hw; +} + +/**************************************/ +/* send B-channel data if not blocked */ +/**************************************/ +static void +hfcpci_send_data(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "send_data %d blocked", bcs->channel); +} + +/***************************************************************/ +/* activate/deactivate hardware for selected channels and mode */ +/***************************************************************/ +void +mode_hfcpci(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + int flags; + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HFCPCI bchannel mode %d bchan %d/%d", + mode, bc, bcs->channel); + bcs->mode = mode; + bcs->channel = bc; + if (cs->chanlimit > 1) { + cs->hw.hfcpci.bswapped = 0; /* B1 and B2 normal mode */ + cs->hw.hfcpci.sctrl_e &= ~0x80; + } + else { + if (bc) { + cs->hw.hfcpci.bswapped = 1; /* B1 and B2 exchanged */ + cs->hw.hfcpci.sctrl_e |= 0x80; + bc = 0; /* B1 controller used */ + } + else { + cs->hw.hfcpci.bswapped = 0; /* B1 and B2 normal mode */ + cs->hw.hfcpci.sctrl_e &= ~0x80; + } + } + save_flags(flags); + cli(); + switch (mode) { + case (L1_MODE_NULL): + if (bc) { + cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA; + cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA; + cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2; + cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC); + } else { + cs->hw.hfcpci.sctrl &= ~SCTRL_B1_ENA; + cs->hw.hfcpci.sctrl_r &= ~SCTRL_B1_ENA; + cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B1; + cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC); + } + break; + case (L1_MODE_TRANS): + if (bc) { + cs->hw.hfcpci.ctmt |= 2; + cs->hw.hfcpci.conn &= ~0x18; + cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA; + cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA; + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2; + cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC); + } else { + cs->hw.hfcpci.ctmt |= 1; + cs->hw.hfcpci.conn &= ~0x03; + cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA; + cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA; + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1; + cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC); + } + break; + case (L1_MODE_HDLC): + if (bc) { + cs->hw.hfcpci.ctmt &= ~2; + cs->hw.hfcpci.conn &= ~0x18; + cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA; + cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA; + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2; + cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC); + } else { + cs->hw.hfcpci.ctmt &= ~1; + cs->hw.hfcpci.conn &= ~0x3; + cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA; + cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA; + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1; + cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC); + } + break; + case (L1_MODE_EXTRN): + if (bc) { + cs->hw.hfcpci.conn |= 0x10; + cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA; + cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA; + cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2; + cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC); + } else { + cs->hw.hfcpci.conn |= 0x02; + cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA; + cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA; + cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B1; + cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC); + } + break; + } + Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + restore_flags(flags); + Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en); + Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl); + Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r); + Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt); + Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn); +} + +/******************************/ +/* Layer2 -> Layer 1 Transfer */ +/******************************/ +static void +hfcpci_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->tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->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->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->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->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_hfcpci(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); + mode_hfcpci(st->l1.bcs, 0, st->l1.bc); + st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + break; + } +} + +/******************************************/ +/* deactivate B-channel access and queues */ +/******************************************/ +static void +close_hfcpci(struct BCState *bcs) +{ + mode_hfcpci(bcs, 0, bcs->channel); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + discard_queue(&bcs->rqueue); + discard_queue(&bcs->squeue); + if (bcs->tx_skb) { + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +/*************************************/ +/* init B-channel queues and control */ +/*************************************/ +static int +open_hfcpcistate(struct IsdnCardState *cs, struct BCState *bcs) +{ + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->tx_cnt = 0; + return (0); +} + +/*********************************/ +/* inits the stack for B-channel */ +/*********************************/ +static int +setstack_2b(struct PStack *st, struct BCState *bcs) +{ + bcs->channel = st->l1.bc; + if (open_hfcpcistate(st->l1.hardware, bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = hfcpci_l2l1; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + +/***************************/ +/* handle L1 state changes */ +/***************************/ +static void +hfcpci_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->dc.hfcpci.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); +} + + +/*************************************/ +/* Alloc memory send data for queues */ +/*************************************/ +__initfunc(unsigned int + *init_send_hfcpci(int cnt)) +{ + int i, *send; + + if (!(send = kmalloc(cnt * sizeof(unsigned int), GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for hfcpci.send\n"); + return (NULL); + } + for (i = 0; i < cnt; i++) + send[i] = 0x1fff; + return (send); +} + +/********************************/ +/* called for card init message */ +/********************************/ +__initfunc(void + inithfcpci(struct IsdnCardState *cs)) +{ + cs->setstack_d = setstack_hfcpci; + cs->dbusytimer.function = (void *) hfcpci_dbusy_timer; + cs->dbusytimer.data = (long) cs; + init_timer(&cs->dbusytimer); + cs->tqueue.routine = (void *) (void *) hfcpci_bh; +#if 0 + if (!cs->hw.hfcpci.send) + cs->hw.hfcpci.send = init_send_hfcpci(16); +#endif + if (!cs->bcs[0].hw.hfc.send) + cs->bcs[0].hw.hfc.send = init_send_hfcpci(32); + if (!cs->bcs[1].hw.hfc.send) + cs->bcs[1].hw.hfc.send = init_send_hfcpci(32); + cs->BC_Send_Data = &hfcpci_send_data; + cs->bcs[0].BC_SetStack = setstack_2b; + cs->bcs[1].BC_SetStack = setstack_2b; + cs->bcs[0].BC_Close = close_hfcpci; + cs->bcs[1].BC_Close = close_hfcpci; + mode_hfcpci(cs->bcs, 0, 0); + mode_hfcpci(cs->bcs + 1, 0, 1); +} + + + +/*******************************************/ +/* handle card messages from control layer */ +/*******************************************/ +static int +hfcpci_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + long flags; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFCPCI: card_msg %x", mt); + switch (mt) { + case CARD_RESET: + reset_hfcpci(cs); + return (0); + case CARD_RELEASE: + release_io_hfcpci(cs); + return (0); + case CARD_INIT: + inithfcpci(cs); + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((80 * HZ) / 1000); /* Timeout 80ms */ + /* now switch timer interrupt off */ + cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + /* reinit mode reg */ + Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); + restore_flags(flags); + return (0); + case CARD_TEST: + return (0); + } + return (0); +} + + +/* this variable is used as card index when more than one cards are present */ +#ifdef COMPAT_HAS_NEW_PCI +static struct pci_dev *dev_hfcpci __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif + +#endif /* CONFIG_PCI */ + +__initfunc(int + setup_hfcpci(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + int i; +#ifdef COMPAT_HAS_NEW_PCI + struct pci_dev *tmp_hfcpci = NULL; +#endif + + strcpy(tmp, hfcpci_revision); + printk(KERN_INFO "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp)); +#if CONFIG_PCI + cs->hw.hfcpci.int_s1 = 0; +#if 0 + cs->hw.hfcpci.send = NULL; +#endif + cs->bcs[0].hw.hfc.send = NULL; + cs->bcs[1].hw.hfc.send = NULL; + cs->dc.hfcpci.ph_state = 0; + cs->hw.hfcpci.fifo = 255; + if (cs->typ == ISDN_CTYPE_HFC_PCI) { +#ifdef COMPAT_HAS_NEW_PCI + if (!pci_present()) { + printk(KERN_ERR "HFC-PCI: no PCI bus present\n"); + return (0); + } + i = 0; + while (CCD_VENDOR_IDS[i]) { + tmp_hfcpci = pci_find_device(CCD_VENDOR_IDS[i], + CCD_DEVICE_IDS[i], + dev_hfcpci); + if (tmp_hfcpci) break; + i++; + } + + if (tmp_hfcpci) { + dev_hfcpci = tmp_hfcpci; /* old device */ + cs->hw.hfcpci.pci_bus = dev_hfcpci->bus->number; + cs->hw.hfcpci.pci_device_fn = dev_hfcpci->devfn; + cs->irq = dev_hfcpci->irq; + if (!cs->irq) { + printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n"); + return (0); + } + cs->hw.hfcpci.pci_io = (char *) get_pcibase(dev_hfcpci, 1); + } else { + printk(KERN_WARNING "HFC-PCI: No PCI card found\n"); + return (0); + } +#else + for (; pci_index < 255; pci_index++) { + unsigned char irq; + + i = 0; + while (CCD_VENDOR_IDS[i]) { + if (pcibios_find_device(CCD_VENDOR_IDS[i], + CCD_DEVICE_IDS[i], pci_index, + &cs->hw.hfcpci.pci_bus, &cs->hw.hfcpci.pci_device_fn) == 0) + break; + i++; + } + if (!CCD_VENDOR_IDS[i]) + continue; + + pcibios_read_config_byte(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, + PCI_INTERRUPT_LINE, &irq); + cs->irq = irq; + + pcibios_read_config_dword(cs->hw.hfcpci.pci_bus, + cs->hw.hfcpci.pci_device_fn, PCI_BASE_ADDRESS_1, + (void *) &cs->hw.hfcpci.pci_io); + break; + } + if (pci_index == 255) { + printk(KERN_WARNING "HFC-PCI: No card found\n"); + return (0); + } + pci_index++; +#endif /* COMPAT_HAS_NEW_PCI */ + if (!cs->hw.hfcpci.pci_io) { + printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n"); + return (0); + } + /* Allocate memory for FIFOS */ + /* Because the HFC-PCI needs a 32K physical alignment, we */ + /* need to allocate the double mem and align the address */ + if (!((void *) cs->hw.hfcpci.share_start = kmalloc(65536, GFP_KERNEL))) { + printk(KERN_WARNING "HFC-PCI: Error allocating memory for FIFO!\n"); + return 0; + } + (ulong) cs->hw.hfcpci.fifos = + (((ulong) cs->hw.hfcpci.share_start) & ~0x7FFF) + 0x8000; + pcibios_write_config_dword(cs->hw.hfcpci.pci_bus, + cs->hw.hfcpci.pci_device_fn, 0x80, + (u_int) virt_to_bus(cs->hw.hfcpci.fifos)); + cs->hw.hfcpci.pci_io = ioremap((ulong) cs->hw.hfcpci.pci_io, 256); + printk(KERN_INFO + "HFC-PCI: defined at mem %#x fifo %#x(%#x) IRQ %d HZ %d\n", + (u_int) cs->hw.hfcpci.pci_io, + (u_int) cs->hw.hfcpci.fifos, + (u_int) virt_to_bus(cs->hw.hfcpci.fifos), + cs->irq, HZ); + pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO); /* enable memory mapped ports, disable busmaster */ + cs->hw.hfcpci.int_m2 = 0; /* disable alle interrupts */ + cs->hw.hfcpci.int_m1 = 0; + Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2); + /* At this point the needed PCI config is done */ + /* fifos are still not enabled */ + } else + return (0); /* no valid card type */ + + + cs->readisac = NULL; + cs->writeisac = NULL; + cs->readisacfifo = NULL; + cs->writeisacfifo = NULL; + cs->BC_Read_Reg = NULL; + cs->BC_Write_Reg = NULL; + cs->irq_func = &hfcpci_interrupt; + cs->irq_flags |= SA_SHIRQ; + + cs->hw.hfcpci.timer.function = (void *) hfcpci_Timer; + cs->hw.hfcpci.timer.data = (long) cs; + init_timer(&cs->hw.hfcpci.timer); + + reset_hfcpci(cs); + cs->cardmsg = &hfcpci_card_msg; + return (1); +#else + printk(KERN_WARNING "HFC-PCI: NO_PCI_BIOS\n"); + return (0); +#endif /* CONFIG_PCI */ +} diff --git a/drivers/isdn/hisax/hfc_pci.h b/drivers/isdn/hisax/hfc_pci.h new file mode 100644 index 000000000000..9f5124523234 --- /dev/null +++ b/drivers/isdn/hisax/hfc_pci.h @@ -0,0 +1,252 @@ +/* $Id: hfc_pci.h,v 1.5 1999/08/09 19:13:34 werner Exp $ + + * specific defines for CCD's HFC 2BDS0 PCI chips + * + * Author Werner Cornelius (werner@isdn4linux.de) + * + * Copyright 1999 by Werner Cornelius (werner@isdn4linux.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: hfc_pci.h,v $ + * Revision 1.5 1999/08/09 19:13:34 werner + * moved constant pci ids to pci id table + * + * Revision 1.4 1999/08/08 10:17:33 werner + * added new PCI vendor and card ids for Manufacturer 0x1043 + * + * Revision 1.3 1999/07/14 12:39:34 werner + * Added changes for echo handling. + * + * Revision 1.2 1999/07/01 08:07:52 keil + * Initial version + * + * + * + */ + + +/* defines for PCI config */ + +#define PCI_ENA_MEMIO 0x02 +#define PCI_ENA_MASTER 0x04 + + +/* GCI/IOM bus monitor registers */ + +#define HCFPCI_C_I 0x08 +#define HFCPCI_TRxR 0x0C +#define HFCPCI_MON1_D 0x28 +#define HFCPCI_MON2_D 0x2C + + +/* GCI/IOM bus timeslot registers */ + +#define HFCPCI_B1_SSL 0x80 +#define HFCPCI_B2_SSL 0x84 +#define HFCPCI_AUX1_SSL 0x88 +#define HFCPCI_AUX2_SSL 0x8C +#define HFCPCI_B1_RSL 0x90 +#define HFCPCI_B2_RSL 0x94 +#define HFCPCI_AUX1_RSL 0x98 +#define HFCPCI_AUX2_RSL 0x9C + +/* GCI/IOM bus data registers */ + +#define HFCPCI_B1_D 0xA0 +#define HFCPCI_B2_D 0xA4 +#define HFCPCI_AUX1_D 0xA8 +#define HFCPCI_AUX2_D 0xAC + +/* GCI/IOM bus configuration registers */ + +#define HFCPCI_MST_EMOD 0xB4 +#define HFCPCI_MST_MODE 0xB8 +#define HFCPCI_CONNECT 0xBC + + +/* Interrupt and status registers */ + +#define HFCPCI_FIFO_EN 0x44 +#define HFCPCI_TRM 0x48 +#define HFCPCI_B_MODE 0x4C +#define HFCPCI_CHIP_ID 0x58 +#define HFCPCI_CIRM 0x60 +#define HFCPCI_CTMT 0x64 +#define HFCPCI_INT_M1 0x68 +#define HFCPCI_INT_M2 0x6C +#define HFCPCI_INT_S1 0x78 +#define HFCPCI_INT_S2 0x7C +#define HFCPCI_STATUS 0x70 + +/* S/T section registers */ + +#define HFCPCI_STATES 0xC0 +#define HFCPCI_SCTRL 0xC4 +#define HFCPCI_SCTRL_E 0xC8 +#define HFCPCI_SCTRL_R 0xCC +#define HFCPCI_SQ 0xD0 +#define HFCPCI_CLKDEL 0xDC +#define HFCPCI_B1_REC 0xF0 +#define HFCPCI_B1_SEND 0xF0 +#define HFCPCI_B2_REC 0xF4 +#define HFCPCI_B2_SEND 0xF4 +#define HFCPCI_D_REC 0xF8 +#define HFCPCI_D_SEND 0xF8 +#define HFCPCI_E_REC 0xFC + + +/* bits in status register (READ) */ +#define HFCPCI_PCI_PROC 0x02 +#define HFCPCI_NBUSY 0x04 +#define HFCPCI_TIMER_ELAP 0x10 +#define HFCPCI_STATINT 0x20 +#define HFCPCI_FRAMEINT 0x40 +#define HFCPCI_ANYINT 0x80 + +/* bits in CTMT (Write) */ +#define HFCPCI_CLTIMER 0x80 +#define HFCPCI_TIM3_125 0x00 +#define HFCPCI_TIM25 0x10 +#define HFCPCI_TIM50 0x14 +#define HFCPCI_TIM400 0x18 +#define HFCPCI_TIM800 0x1C +#define HFCPCI_AUTO_TIMER 0x20 +#define HFCPCI_TRANSB2 0x02 +#define HFCPCI_TRANSB1 0x01 + +/* bits in CIRM (Write) */ +#define HFCPCI_AUX_MSK 0x07 +#define HFCPCI_RESET 0x08 +#define HFCPCI_B1_REV 0x40 +#define HFCPCI_B2_REV 0x80 + +/* bits in INT_M1 and INT_S1 */ +#define HFCPCI_INTS_B1TRANS 0x01 +#define HFCPCI_INTS_B2TRANS 0x02 +#define HFCPCI_INTS_DTRANS 0x04 +#define HFCPCI_INTS_B1REC 0x08 +#define HFCPCI_INTS_B2REC 0x10 +#define HFCPCI_INTS_DREC 0x20 +#define HFCPCI_INTS_L1STATE 0x40 +#define HFCPCI_INTS_TIMER 0x80 + +/* bits in INT_M2 */ +#define HFCPCI_PROC_TRANS 0x01 +#define HFCPCI_GCI_I_CHG 0x02 +#define HFCPCI_GCI_MON_REC 0x04 +#define HFCPCI_IRQ_ENABLE 0x08 +#define HFCPCI_PMESEL 0x80 + +/* bits in STATES */ +#define HFCPCI_STATE_MSK 0x0F +#define HFCPCI_LOAD_STATE 0x10 +#define HFCPCI_ACTIVATE 0x20 +#define HFCPCI_DO_ACTION 0x40 +#define HFCPCI_NT_G2_G3 0x80 + +/* bits in HFCD_MST_MODE */ +#define HFCPCI_MASTER 0x01 +#define HFCPCI_SLAVE 0x00 +/* remaining bits are for codecs control */ + +/* bits in HFCD_SCTRL */ +#define SCTRL_B1_ENA 0x01 +#define SCTRL_B2_ENA 0x02 +#define SCTRL_MODE_TE 0x00 +#define SCTRL_MODE_NT 0x04 +#define SCTRL_LOW_PRIO 0x08 +#define SCTRL_SQ_ENA 0x10 +#define SCTRL_TEST 0x20 +#define SCTRL_NONE_CAP 0x40 +#define SCTRL_PWR_DOWN 0x80 + +/* bits in SCTRL_E */ +#define HFCPCI_AUTO_AWAKE 0x01 +#define HFCPCI_DBIT_1 0x04 +#define HFCPCI_IGNORE_COL 0x08 +#define HFCPCI_CHG_B1_B2 0x80 + +/****************************/ +/* bits in FIFO_EN register */ +/****************************/ +#define HFCPCI_FIFOEN_B1 0x03 +#define HFCPCI_FIFOEN_B2 0x0C +#define HFCPCI_FIFOEN_DTX 0x10 +#define HFCPCI_FIFOEN_B2RX 0x08 + + +/***********************************/ +/* definitions of fifo memory area */ +/***********************************/ +#define MAX_D_FRAMES 15 +#define MAX_B_FRAMES 31 +#define B_SUB_VAL 0x200 +#define B_FIFO_SIZE (0x2000 - B_SUB_VAL) +#define D_FIFO_SIZE 512 +#define D_FREG_MASK 0xF + +typedef struct { + unsigned short z1; /* Z1 pointer 16 Bit */ + unsigned short z2; /* Z2 pointer 16 Bit */ + } z_type; + +typedef struct { + u_char data[D_FIFO_SIZE]; /* FIFO data space */ + u_char fill1[0x20A0-D_FIFO_SIZE]; /* reserved, do not use */ + u_char f1,f2; /* f pointers */ + u_char fill2[0x20C0-0x20A2]; /* reserved, do not use */ + z_type za[MAX_D_FRAMES+1]; /* mask index with D_FREG_MASK for access */ + u_char fill3[0x4000-0x2100]; /* align 16K */ + } dfifo_type; + +typedef struct { + z_type za[MAX_B_FRAMES+1]; /* only range 0x0..0x1F allowed */ + u_char f1,f2; /* f pointers */ + u_char fill[0x2100-0x2082]; /* alignment */ + } bzfifo_type; + + +typedef union { + struct { + dfifo_type d_tx; /* D-send channel */ + dfifo_type d_rx; /* D-receive channel */ + } d_chan; + struct { + u_char fill1[0x200]; + u_char txdat_b1[B_FIFO_SIZE]; + bzfifo_type txbz_b1; + + bzfifo_type txbz_b2; + u_char txdat_b2[B_FIFO_SIZE]; + + u_char fill2[D_FIFO_SIZE]; + + u_char rxdat_b1[B_FIFO_SIZE]; + bzfifo_type rxbz_b1; + + bzfifo_type rxbz_b2; + u_char rxdat_b2[B_FIFO_SIZE]; + } b_chans; + u_char fill[32768]; + } fifo_area; + + +#define Write_hfc(a,b,c) (*(((u_char *)a->hw.hfcpci.pci_io)+b) = c) +#define Read_hfc(a,b) (*(((u_char *)a->hw.hfcpci.pci_io)+b)) + +extern void main_irq_hcpci(struct BCState *bcs); +extern void inithfcpci(struct IsdnCardState *cs); +extern void releasehfcpci(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/hfcscard.c b/drivers/isdn/hisax/hfcscard.c new file mode 100644 index 000000000000..edd3836431cb --- /dev/null +++ b/drivers/isdn/hisax/hfcscard.c @@ -0,0 +1,206 @@ +/* $Id: hfcscard.c,v 1.4 1999/08/09 18:59:59 keil Exp $ + + * hfcscard.c low level stuff for hfcs based cards (Teles3c, ACER P10) + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * + * $Log: hfcscard.c,v $ + * Revision 1.4 1999/08/09 18:59:59 keil + * Fix S0 init - Thanks to Stefan Gybas + * + * Revision 1.3 1999/07/12 21:05:12 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.2 1999/07/01 08:16:03 keil + * teles3c ---> hfcscard + * + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_2bds0.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +static const char *hfcs_revision = "$Revision: 1.4 $"; + +static void +hfcs_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat; + + if (!cs) { + printk(KERN_WARNING "HFCS: 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) + debugl1(cs, "HFCS: stat(%02x) s1(%02x)", stat, val); + hfc2bds0_interrupt(cs, val); + } else { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFCS: irq_no_irq stat(%02x)", stat); + } +} + +static void +hfcs_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_hfcs(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_hfcs(struct IsdnCardState *cs) +{ + long flags; + + printk(KERN_INFO "HFCS: resetting card\n"); + cs->hw.hfcD.cirm = HFCD_RESET; + if (cs->typ == ISDN_CTYPE_TELES3C) + cs->hw.hfcD.cirm |= 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; + schedule_timeout((30*HZ)/1000); + cs->hw.hfcD.cirm = 0; + if (cs->typ == ISDN_CTYPE_TELES3C) + 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; + schedule_timeout((10*HZ)/1000); + if (cs->typ == ISDN_CTYPE_TELES3C) + cs->hw.hfcD.cirm |= HFCD_INTB; + else if (cs->typ == ISDN_CTYPE_ACERP10) + cs->hw.hfcD.cirm |= HFCD_INTA; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); + 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 = HFCD_MASTER; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m); /* 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 +hfcs_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + long flags; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFCS: card_msg %x", mt); + switch (mt) { + case CARD_RESET: + reset_hfcs(cs); + return(0); + case CARD_RELEASE: + release_io_hfcs(cs); + return(0); + case CARD_INIT: + cs->hw.hfcD.timer.expires = jiffies + 75; + add_timer(&cs->hw.hfcD.timer); + init2bds0(cs); + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((80*HZ)/1000); + 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_hfcs(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, hfcs_revision); + printk(KERN_INFO "HiSax: HFC-S driver Rev. %s\n", HiSax_getrev(tmp)); + 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.dfifosize = 512; + cs->dc.hfcd.ph_state = 0; + cs->hw.hfcD.fifo = 255; + if (cs->typ == ISDN_CTYPE_TELES3C) { + cs->hw.hfcD.bfifosize = 1024 + 512; + } else if (cs->typ == ISDN_CTYPE_ACERP10) { + cs->hw.hfcD.bfifosize = 7*1024 + 512; + } else + return (0); + 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, "HFCS isdn"); + } + printk(KERN_INFO + "HFCS: defined at 0x%x IRQ %d HZ %d\n", + cs->hw.hfcD.addr, + cs->irq, HZ); + if (cs->typ == ISDN_CTYPE_TELES3C) { + /* 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); + } else if (cs->typ == ISDN_CTYPE_ACERP10) { + /* Acer P10 IO ADR is 0x300 */ + outb(0x00, cs->hw.hfcD.addr); + outb(0x57, cs->hw.hfcD.addr | 1); + } + set_cs_func(cs); + cs->hw.hfcD.timer.function = (void *) hfcs_Timer; + cs->hw.hfcD.timer.data = (long) cs; + init_timer(&cs->hw.hfcD.timer); + reset_hfcs(cs); + cs->cardmsg = &hfcs_card_msg; + cs->irq_func = &hfcs_interrupt; + return (1); +} diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h index 36cd82ee12d3..14c201386a02 100644 --- a/drivers/isdn/hisax/hisax.h +++ b/drivers/isdn/hisax/hisax.h @@ -1,8 +1,28 @@ -/* $Id: hisax.h,v 2.26 1998/11/15 23:54:45 keil Exp $ +/* $Id: hisax.h,v 2.33 1999/08/05 20:43:16 keil Exp $ * Basic declarations, defines and prototypes * * $Log: hisax.h,v $ + * Revision 2.33 1999/08/05 20:43:16 keil + * ISAR analog modem support + * + * Revision 2.31 1999/07/21 14:46:11 keil + * changes from EICON certification + * + * Revision 2.30 1999/07/14 12:38:38 werner + * Added changes for echo channel handling + * + * Revision 2.29 1999/07/12 21:05:14 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 2.28 1999/07/05 23:51:46 werner + * Allow limiting of available HiSax B-chans per card. Controlled by hisaxctrl + * hisaxctrl id 10 + * + * Revision 2.27 1999/07/01 08:11:38 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.26 1998/11/15 23:54:45 keil * changes from 2.0 * @@ -111,8 +131,7 @@ #include #include #include -#include -#include +#include #define REQUEST 0 #define CONFIRM 1 @@ -131,7 +150,6 @@ #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 @@ -168,16 +186,21 @@ #define CC_SETUP_COMPL 0x0330 #define CC_PROCEEDING 0x0340 #define CC_ALERTING 0x0344 +#define CC_PROGRESS 0x0348 #define CC_CONNECT 0x0350 #define CC_CHARGE 0x0354 +#define CC_NOTIFY 0x0358 #define CC_DISCONNECT 0x0360 #define CC_RELEASE 0x0368 #define CC_SUSPEND 0x0370 +#define CC_PROCEED_SEND 0x0374 +#define CC_REDIR 0x0378 #define CC_T303 0x0383 #define CC_T304 0x0384 #define CC_T305 0x0385 #define CC_T308_1 0x0388 -#define CC_T308_2 0x0389 +#define CC_T308_2 0x038A +#define CC_T309 0x0309 #define CC_T310 0x0390 #define CC_T313 0x0393 #define CC_T318 0x0398 @@ -188,12 +211,22 @@ #define CC_RESUME_ERR 0x03E3 #define CC_CONNECT_ERR 0x03E4 #define CC_RELEASE_ERR 0x03E5 -#define CC_DLRL 0x03F0 #define CC_RESTART 0x03F4 +#define CC_TDSS1_IO 0x13F4 /* DSS1 IO user timer */ + +/* define maximum number of possible waiting incoming calls */ +#define MAX_WAITING_CALLS 2 #ifdef __KERNEL__ +/* include only l3dss1 specific process structures, but no other defines */ +#ifdef CONFIG_HISAX_EURO + #define l3dss1_process + #include "l3dss1.h" + #undef l3dss1_process +#endif CONFIG_HISAX_EURO + #define MAX_DFRAME_LEN 260 #define MAX_DFRAME_LEN_L1 300 #define HSCX_BUFMAX 4096 @@ -294,16 +327,17 @@ struct Layer1 { #define FLG_ESTAB_PEND 13 #define FLG_PTP 14 #define FLG_FIXED_TEI 15 +#define FLG_L2BLOCK 16 struct Layer2 { int tei; int sap; int maxlen; unsigned int flag; - int vs, va, vr; + unsigned int vs, va, vr; int rc; - int window; - int sow; + unsigned int window; + unsigned int sow; struct sk_buff *windowar[MAX_WINDOW]; struct sk_buff_head i_queue; struct sk_buff_head ui_queue; @@ -319,8 +353,10 @@ struct Layer2 { struct Layer3 { void (*l3l4) (struct PStack *, int, void *); + void (*l3ml3) (struct PStack *, int, void *); void (*l3l2) (struct PStack *, int, void *); struct FsmInst l3m; + struct FsmTimer l3m_timer; struct sk_buff_head squeue; struct l3_process *proc; struct l3_process *global; @@ -331,6 +367,7 @@ struct Layer3 { struct LLInterface { void (*l4l3) (struct PStack *, int, void *); + int (*l4l3_proto) (struct PStack *, isdn_ctrl *); void *userdata; void (*l1writewakeup) (struct PStack *, int); void (*l2writewakeup) (struct PStack *, int); @@ -345,16 +382,17 @@ struct Management { void (*layer) (struct PStack *, int, void *); }; +#define NO_CAUSE 254 struct Param { - int cause; - int loc; + u_char cause; + u_char loc; + u_char diag[6]; int bchannel; - setup_parm setup; /* from isdnif.h numbers and Serviceindicator */ - int chargeinfo; /* Charge Info - only for 1tr6 in - * the moment - */ + int chargeinfo; int spv; /* SPV Flag */ + setup_parm setup; /* from isdnif.h numbers and Serviceindicator */ + u_char moderate; /* transfer mode and rate (bearer octet 4) */ }; @@ -366,6 +404,14 @@ struct PStack { struct LLInterface lli; struct Management ma; int protocol; /* EDSS1 or 1TR6 */ + + /* protocol specific data fields */ + union + { u_char uuuu; /* only as dummy */ +#ifdef CONFIG_HISAX_EURO + dss1_stk_priv dss1; /* private dss1 data */ +#endif CONFIG_HISAX_EURO + } prot; }; struct l3_process { @@ -378,6 +424,15 @@ struct l3_process { struct Channel *chan; struct PStack *st; struct l3_process *next; + ulong redir_result; + + /* protocol specific data fields */ + union + { u_char uuuu; /* only when euro not defined, avoiding empty union */ +#ifdef CONFIG_HISAX_EURO + dss1_proc_priv dss1; /* private dss1 data */ +#endif CONFIG_HISAX_EURO + } prot; }; struct hscx_hw { @@ -385,6 +440,8 @@ struct hscx_hw { int rcvidx; int count; /* Current skb sent count */ u_char *rcvbuf; /* B-Channel receive Buffer */ + u_char tsaxr0; + u_char tsaxr1; }; struct isar_reg { @@ -402,6 +459,7 @@ struct isar_hw { int txcnt; int mml; u_char *rcvbuf; /* B-Channel receive Buffer */ + u_char conmsg[16]; struct isar_reg *reg; }; @@ -469,11 +527,15 @@ struct amd7930_hw { #define BC_FLG_NOFRAME 4 #define BC_FLG_HALF 5 #define BC_FLG_EMPTY 6 +#define BC_FLG_ORIG 7 #define L1_MODE_NULL 0 #define L1_MODE_TRANS 1 #define L1_MODE_HDLC 2 +#define L1_MODE_EXTRN 3 #define L1_MODE_MODEM 7 +#define L1_MODE_V32 8 +#define L1_MODE_FAX 9 struct BCState { int channel; @@ -486,6 +548,7 @@ struct BCState { struct sk_buff_head squeue; /* B-Channel send Queue */ struct PStack *st; u_char *blog; + u_char *conmsg; struct timer_list transbusy; struct tq_struct tqueue; int event; @@ -578,7 +641,8 @@ struct ix1_hw { }; struct diva_hw { - unsigned int cfg_reg; + unsigned long cfg_reg; + unsigned long pci_cfg; unsigned int ctrl; unsigned int isac_adr; unsigned int isac; @@ -647,6 +711,31 @@ struct njet_hw { unsigned char last_is0; }; +struct hfcPCI_hw { + unsigned char cirm; + unsigned char ctmt; + unsigned char conn; + unsigned char mst_m; + unsigned char int_m1; + unsigned char int_m2; + unsigned char int_s1; + unsigned char sctrl; + unsigned char sctrl_r; + unsigned char sctrl_e; + unsigned char trm; + unsigned char stat; + unsigned char fifo; + unsigned char fifo_en; + unsigned char bswapped; + /* unsigned int *send; */ + unsigned char pci_bus; + unsigned char pci_device_fn; + unsigned char *pci_io; /* start of PCI IO memory */ + void *share_start; /* shared memory for Fifos start */ + void *fifos; /* FIFO memory */ + struct timer_list timer; +}; + struct hfcD_hw { unsigned int addr; unsigned int bfifosize; @@ -668,25 +757,118 @@ struct hfcD_hw { struct timer_list timer; }; -#define HW_IOM1 0 -#define HW_IPAC 1 -#define HW_ISAR 2 -#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 isurf_hw { + unsigned int reset; + unsigned int isac; + unsigned int isar; + struct isar_reg isar_r; +}; + +struct saphir_hw { + unsigned int cfg_reg; + unsigned int ale; + unsigned int isac; + unsigned int hscx; + struct timer_list timer; +}; + +struct bkm_hw { + unsigned int base; + /* A4T stuff */ + unsigned int isac_adr; + unsigned int isac_ale; + unsigned int jade_adr; + unsigned int jade_ale; + /* Scitel Quadro stuff */ + unsigned int plx_adr; + unsigned int data_adr; +}; + +struct gazel_hw { + unsigned int cfg_reg; + unsigned int pciaddr[2]; + signed int ipac; + signed int isac; + signed int hscx[2]; + signed int isacfifo; + signed int hscxfifo[2]; + unsigned char timeslot; + unsigned char iom2; +}; + +#ifdef CONFIG_HISAX_TESTEMU +struct te_hw { + unsigned char *sfifo; + unsigned char *sfifo_w; + unsigned char *sfifo_r; + unsigned char *sfifo_e; + int sfifo_cnt; + unsigned int stat; +#ifdef COMPAT_HAS_NEW_WAITQ + wait_queue_head_t rwaitq; + wait_queue_head_t swaitq; +#else + struct wait_queue *rwaitq; + struct wait_queue *swaitq; +#endif +}; +#endif + +struct arcofi_msg { + struct arcofi_msg *next; + u_char receive; + u_char len; + u_char msg[10]; +}; + +struct isac_chip { + int ph_state; + u_char *mon_tx; + u_char *mon_rx; + int mon_txp; + int mon_txc; + int mon_rxp; + struct arcofi_msg *arcofi_list; + struct timer_list arcofitimer; +#ifdef COMPAT_HAS_NEW_WAITQ + wait_queue_head_t arcofi_wait; +#else + struct wait_queue *arcofi_wait; +#endif + u_char arcofi_bc; + u_char arcofi_state; + u_char mocr; + u_char adf2; +}; + +struct hfcd_chip { + int ph_state; +}; + +struct hfcpci_chip { + int ph_state; +}; + +#define HW_IOM1 0 +#define HW_IPAC 1 +#define HW_ISAR 2 +#define FLG_TWO_DCHAN 4 +#define FLG_L1_DBUSY 5 +#define FLG_DBUSY_TIMER 6 +#define FLG_LOCK_ATOMIC 7 +#define FLG_ARCOFI_TIMER 8 +#define FLG_ARCOFI_ERROR 9 struct IsdnCardState { unsigned char typ; unsigned char subtyp; int protocol; unsigned int irq; + unsigned long irq_flags; int HW_Flags; int *busy_flag; + int chanlimit; /* limited number of B-chans to use */ + int logecho; /* log echo if supported by card */ union { struct elsa_hw elsa; struct teles0_hw teles0; @@ -701,7 +883,15 @@ struct IsdnCardState { struct mic_hw mic; struct njet_hw njet; struct hfcD_hw hfcD; + struct hfcPCI_hw hfcpci; struct ix1_hw niccy; + struct isurf_hw isurf; + struct saphir_hw saphir; +#ifdef CONFIG_HISAX_TESTEMU + struct te_hw te; +#endif + struct bkm_hw ax; + struct gazel_hw gazel; } hw; int myid; isdn_if iif; @@ -717,9 +907,21 @@ struct IsdnCardState { 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]; + void (*setstack_d) (struct PStack *, struct IsdnCardState *); + void (*DC_Close) (struct IsdnCardState *); + void (*irq_func) (int, void *, struct pt_regs *); + struct Channel channel[2+MAX_WAITING_CALLS]; + struct BCState bcs[2+MAX_WAITING_CALLS]; struct PStack *stlist; + struct sk_buff_head rq, sq; /* D-channel queues */ + int cardnr; + char *dlog; + int debug; + union { + struct isac_chip isac; + struct hfcd_chip hfcd; + struct hfcpci_chip hfcpci; + } dc; u_char *rcvbuf; int rcvidx; struct sk_buff *tx_skb; @@ -727,18 +929,6 @@ struct IsdnCardState { int event; struct tq_struct tqueue; struct timer_list dbusytimer; - struct sk_buff_head rq, sq; /* D-channel queues */ - int ph_state; - int cardnr; - char *dlog; - int debug; - 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 @@ -776,18 +966,33 @@ struct IsdnCardState { #define ISDN_CTYPE_A1_PCMCIA 26 #define ISDN_CTYPE_FRITZPCI 27 #define ISDN_CTYPE_SEDLBAUER_FAX 28 +#define ISDN_CTYPE_ISURF 29 +#define ISDN_CTYPE_ACERP10 30 +#define ISDN_CTYPE_HSTSAPHIR 31 +#define ISDN_CTYPE_BKM_A4T 32 +#define ISDN_CTYPE_SCT_QUADRO 33 +#define ISDN_CTYPE_GAZEL 34 +#define ISDN_CTYPE_HFC_PCI 35 +#define ISDN_CTYPE_COUNT 35 -#define ISDN_CTYPE_COUNT 28 #ifdef ISDN_CHIP_ISAC #undef ISDN_CHIP_ISAC #endif +#ifndef __initfunc +#define __initfunc(__arginit) __arginit +#endif + +#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) +#define CARD_TELES0 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -796,8 +1001,7 @@ struct IsdnCardState { #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_COMPAQ_ISA) +#define CARD_TELES3 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -806,7 +1010,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_TELESPCI -#define CARD_TELESPCI (1<< ISDN_CTYPE_TELESPCI) +#define CARD_TELESPCI 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -815,7 +1019,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_AVM_A1 -#define CARD_AVM_A1 (1<< ISDN_CTYPE_A1) +#define CARD_AVM_A1 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -824,7 +1028,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_AVM_A1_PCMCIA -#define CARD_AVM_A1_PCMCIA (1<< ISDN_CTYPE_A1_PCMCIA) +#define CARD_AVM_A1_PCMCIA 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -833,7 +1037,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_FRITZPCI -#define CARD_FRITZPCI (1<< ISDN_CTYPE_FRITZPCI) +#define CARD_FRITZPCI 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -842,8 +1046,7 @@ struct IsdnCardState { #endif #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) +#define CARD_ELSA 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -855,9 +1058,8 @@ struct IsdnCardState { #define CARD_ELSA 0 #endif - #ifdef CONFIG_HISAX_IX1MICROR2 -#define CARD_IX1MICROR2 (1 << ISDN_CTYPE_IX1MICROR2) +#define CARD_IX1MICROR2 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -866,7 +1068,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_DIEHLDIVA -#define CARD_DIEHLDIVA (1 << ISDN_CTYPE_DIEHLDIVA) +#define CARD_DIEHLDIVA 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -875,7 +1077,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_ASUSCOM -#define CARD_ASUSCOM (1 << ISDN_CTYPE_ASUSCOM) +#define CARD_ASUSCOM 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -884,7 +1086,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_TELEINT -#define CARD_TELEINT (1 << ISDN_CTYPE_TELEINT) +#define CARD_TELEINT 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -893,7 +1095,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_SEDLBAUER -#define CARD_SEDLBAUER (1 << ISDN_CTYPE_SEDLBAUER) | (1 << ISDN_CTYPE_SEDLBAUER_PCMCIA) | ( 1 << ISDN_CTYPE_SEDLBAUER_FAX) +#define CARD_SEDLBAUER 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -902,7 +1104,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_SPORTSTER -#define CARD_SPORTSTER (1 << ISDN_CTYPE_SPORTSTER) +#define CARD_SPORTSTER 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -911,7 +1113,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_MIC -#define CARD_MIC (1 << ISDN_CTYPE_MIC) +#define CARD_MIC 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -920,7 +1122,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_NETJET -#define CARD_NETJET (1 << ISDN_CTYPE_NETJET) +#define CARD_NETJET 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -928,20 +1130,27 @@ struct IsdnCardState { #define CARD_NETJET 0 #endif -#ifdef CONFIG_HISAX_TELES3C -#define CARD_TELES3C (1<< ISDN_CTYPE_TELES3C) +#ifdef CONFIG_HISAX_HFCS +#define CARD_HFCS 1 +#else +#define CARD_HFCS 0 +#endif + +#ifdef CONFIG_HISAX_HFC_PCI +#define CARD_HFC_PCI 1 +extern int hfcpci_set_echo(struct IsdnCardState *, int); #else -#define CARD_TELES3C 0 +#define CARD_HFC_PCI 0 #endif #ifdef CONFIG_HISAX_AMD7930 -#define CARD_AMD7930 (1 << ISDN_CTYPE_AMD7930) +#define CARD_AMD7930 1 #else #define CARD_AMD7930 0 #endif #ifdef CONFIG_HISAX_NICCY -#define CARD_NICCY (1 << ISDN_CTYPE_NICCY) +#define CARD_NICCY 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -949,8 +1158,17 @@ struct IsdnCardState { #define CARD_NICCY 0 #endif +#ifdef CONFIG_HISAX_ISURF +#define CARD_ISURF 1 +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_ISURF 0 +#endif + #ifdef CONFIG_HISAX_S0BOX -#define CARD_S0BOX (1 << ISDN_CTYPE_S0BOX) +#define CARD_S0BOX 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -958,12 +1176,50 @@ struct IsdnCardState { #define CARD_S0BOX 0 #endif -#define SUPORTED_CARDS (CARD_TELES0 | CARD_TELES3 | CARD_AVM_A1 | CARD_ELSA \ - | CARD_IX1MICROR2 | CARD_DIEHLDIVA | CARD_ASUSCOM \ - | CARD_TELEINT | CARD_SEDLBAUER | CARD_SPORTSTER \ - | CARD_MIC | CARD_NETJET | CARD_TELES3C | CARD_AMD7930 \ - | CARD_AVM_A1_PCMCIA | CARD_FRITZPCI\ - | CARD_NICCY | CARD_S0BOX | CARD_TELESPCI) +#ifdef CONFIG_HISAX_HSTSAPHIR +#define CARD_HSTSAPHIR 1 +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_HSTSAPHIR 0 +#endif + +#ifdef CONFIG_HISAX_TESTEMU +#define CARD_TESTEMU 1 +#define ISDN_CTYPE_TESTEMU 99 +#undef ISDN_CTYPE_COUNT +#define ISDN_CTYPE_COUNT ISDN_CTYPE_TESTEMU +#else +#define CARD_TESTEMU 0 +#endif + +#ifdef CONFIG_HISAX_BKM_A4T +#define CARD_BKM_A4T 1 +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_BKM_A4T 0 +#endif + +#ifdef CONFIG_HISAX_SCT_QUADRO +#define CARD_SCT_QUADRO 1 +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_SCT_QUADRO 0 +#endif + +#ifdef CONFIG_HISAX_GAZEL +#define CARD_GAZEL 1 +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_GAZEL 0 +#endif #define TEI_PER_CARD 0 diff --git a/drivers/isdn/hisax/hscx.c b/drivers/isdn/hisax/hscx.c index 980dc76e8a66..290c89f5059b 100644 --- a/drivers/isdn/hisax/hscx.c +++ b/drivers/isdn/hisax/hscx.c @@ -1,4 +1,4 @@ -/* $Id: hscx.c,v 1.16 1998/11/15 23:54:48 keil Exp $ +/* $Id: hscx.c,v 1.17 1999/07/01 08:11:41 keil Exp $ * hscx.c HSCX specific routines * @@ -6,6 +6,9 @@ * * * $Log: hscx.c,v $ + * Revision 1.17 1999/07/01 08:11:41 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.16 1998/11/15 23:54:48 keil * changes from 2.0 * @@ -110,12 +113,12 @@ modehscx(struct BCState *bcs, int mode, int bc) if (bc == 0) { cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, - test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : 0x2f); + test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : bcs->hw.hscx.tsaxr0); cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, - test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : 0x2f); + test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : bcs->hw.hscx.tsaxr0); } else { - cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0x3); - cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0x3); + cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, bcs->hw.hscx.tsaxr1); + cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, bcs->hw.hscx.tsaxr1); } switch (mode) { case (L1_MODE_NULL): @@ -216,7 +219,7 @@ close_hscxstate(struct BCState *bcs) discard_queue(&bcs->rqueue); discard_queue(&bcs->squeue); if (bcs->tx_skb) { - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); } @@ -301,6 +304,10 @@ inithscx(struct IsdnCardState *cs)) cs->bcs[1].BC_Close = close_hscxstate; cs->bcs[0].hw.hscx.hscx = 0; cs->bcs[1].hw.hscx.hscx = 1; + cs->bcs[0].hw.hscx.tsaxr0 = 0x2f; + cs->bcs[0].hw.hscx.tsaxr1 = 3; + cs->bcs[1].hw.hscx.tsaxr0 = 0x2f; + cs->bcs[1].hw.hscx.tsaxr1 = 3; modehscx(cs->bcs, 0, 0); modehscx(cs->bcs + 1, 0, 0); } diff --git a/drivers/isdn/hisax/hscx_irq.c b/drivers/isdn/hisax/hscx_irq.c index 217d241c31e0..ecc1e825e52a 100644 --- a/drivers/isdn/hisax/hscx_irq.c +++ b/drivers/isdn/hisax/hscx_irq.c @@ -1,4 +1,4 @@ -/* $Id: hscx_irq.c,v 1.11 1998/11/15 23:54:49 keil Exp $ +/* $Id: hscx_irq.c,v 1.12 1999/07/01 08:11:42 keil Exp $ * hscx_irq.c low level b-channel stuff for Siemens HSCX * @@ -7,6 +7,9 @@ * This is an include file for fast inline IRQ stuff * * $Log: hscx_irq.c,v $ + * Revision 1.12 1999/07/01 08:11:42 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.11 1998/11/15 23:54:49 keil * changes from 2.0 * @@ -42,8 +45,6 @@ * */ -#include - static inline void waitforCEC(struct IsdnCardState *cs, int hscx) @@ -203,6 +204,7 @@ hscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx) 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); } @@ -218,6 +220,7 @@ hscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx) 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); } @@ -234,7 +237,7 @@ hscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx) if (bcs->st->lli.l1writewakeup && (PACKET_NOACK != bcs->tx_skb->pkt_type)) bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count); - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->hw.hscx.count = 0; bcs->tx_skb = NULL; } diff --git a/drivers/isdn/hisax/isac.c b/drivers/isdn/hisax/isac.c index a239fdfad902..6a130ed01001 100644 --- a/drivers/isdn/hisax/isac.c +++ b/drivers/isdn/hisax/isac.c @@ -1,4 +1,4 @@ -/* $Id: isac.c,v 1.18 1998/11/15 23:54:51 keil Exp $ +/* $Id: isac.c,v 1.22 1999/08/09 19:04:40 keil Exp $ * isac.c ISAC specific routines * @@ -9,6 +9,19 @@ * ../../../Documentation/isdn/HiSax.cert * * $Log: isac.c,v $ + * Revision 1.22 1999/08/09 19:04:40 keil + * Fix race condition - Thanks to Christer Weinigel + * + * Revision 1.21 1999/07/12 21:05:17 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.20 1999/07/09 08:23:06 keil + * Fix ISAC lost TX IRQ handling + * + * Revision 1.19 1999/07/01 08:11:43 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.18 1998/11/15 23:54:51 keil * changes from 2.0 * @@ -69,6 +82,7 @@ #define __NO_VERSION__ #include "hisax.h" #include "isac.h" +#include "arcofi.h" #include "isdnl1.h" #include @@ -100,7 +114,7 @@ ph_command(struct IsdnCardState *cs, unsigned int command) static void isac_new_ph(struct IsdnCardState *cs) { - switch (cs->ph_state) { + switch (cs->dc.isac.ph_state) { case (ISAC_IND_RS): case (ISAC_IND_EI): ph_command(cs, ISAC_CMD_DUI); @@ -154,14 +168,10 @@ isac_bh(struct IsdnCardState *cs) 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); + arcofi_fsm(cs, ARCOFI_RX_END, NULL); if (test_and_clear_bit(D_TX_MON1, &cs->event)) - test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags); + arcofi_fsm(cs, ARCOFI_TX_END, NULL); } void @@ -226,7 +236,6 @@ isac_fill_fifo(struct IsdnCardState *cs) 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); @@ -234,6 +243,7 @@ isac_fill_fifo(struct IsdnCardState *cs) init_timer(&cs->dbusytimer); cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000); add_timer(&cs->dbusytimer); + restore_flags(flags); if (cs->debug & L1_DEB_ISAC_FIFO) { char *t = cs->dlog; @@ -283,6 +293,7 @@ isac_interrupt(struct IsdnCardState *cs, u_char val) 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); } @@ -310,7 +321,7 @@ isac_interrupt(struct IsdnCardState *cs, u_char val) isac_fill_fifo(cs); goto afterXPR; } else { - dev_kfree_skb(cs->tx_skb); + idev_kfree_skb(cs->tx_skb, FREE_WRITE); cs->tx_cnt = 0; cs->tx_skb = NULL; } @@ -327,9 +338,9 @@ isac_interrupt(struct IsdnCardState *cs, u_char val) if (cs->debug & L1_DEB_ISAC) debugl1(cs, "ISAC CIR0 %02X", exval ); if (exval & 2) { - cs->ph_state = (exval >> 2) & 0xf; + cs->dc.isac.ph_state = (exval >> 2) & 0xf; if (cs->debug & L1_DEB_ISAC) - debugl1(cs, "ph_state change %x", cs->ph_state); + debugl1(cs, "ph_state change %x", cs->dc.isac.ph_state); isac_sched_event(cs, D_L1STATECHANGE); } if (exval & 1) { @@ -347,127 +358,147 @@ isac_interrupt(struct IsdnCardState *cs, u_char val) exval = cs->readisac(cs, ISAC_EXIR); if (cs->debug & L1_DEB_WARN) debugl1(cs, "ISAC EXIR %02x", exval); - if (exval & 0x04) { + if (exval & 0x80) { /* XMR */ + debugl1(cs, "ISAC XMR"); + printk(KERN_WARNING "HiSax: ISAC XMR\n"); + } + if (exval & 0x40) { /* XDU */ + debugl1(cs, "ISAC XDU"); + printk(KERN_WARNING "HiSax: ISAC XDU\n"); + 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) { /* Restart frame */ + skb_push(cs->tx_skb, cs->tx_cnt); + cs->tx_cnt = 0; + isac_fill_fifo(cs); + } else { + printk(KERN_WARNING "HiSax: ISAC XDU no skb\n"); + debugl1(cs, "ISAC XDU no skb"); + } + } + if (exval & 0x04) { /* MOS */ v1 = cs->readisac(cs, ISAC_MOSR); if (cs->debug & L1_DEB_MONITOR) debugl1(cs, "ISAC MOSR %02x", v1); #if ARCOFI_USE if (v1 & 0x08) { - if (!cs->mon_rx) { - if (!(cs->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (!cs->dc.isac.mon_rx) { + if (!(cs->dc.isac.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); + cs->dc.isac.mocr &= 0xf0; + cs->dc.isac.mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); goto afterMONR0; } else - cs->mon_rxp = 0; + cs->dc.isac.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->dc.isac.mon_rxp >= MAX_MON_FRAME) { + cs->dc.isac.mocr &= 0xf0; + cs->dc.isac.mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + cs->dc.isac.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); + cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp++] = cs->readisac(cs, ISAC_MOR0); if (cs->debug & L1_DEB_MONITOR) - debugl1(cs, "ISAC MOR0 %02x", cs->mon_rx[cs->mon_rxp -1]); - if (cs->mon_rxp == 1) { - cs->mocr |= 0x04; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); + debugl1(cs, "ISAC MOR0 %02x", cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp -1]); + if (cs->dc.isac.mon_rxp == 1) { + cs->dc.isac.mocr |= 0x04; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); } } afterMONR0: if (v1 & 0x80) { - if (!cs->mon_rx) { - if (!(cs->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (!cs->dc.isac.mon_rx) { + if (!(cs->dc.isac.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); + cs->dc.isac.mocr &= 0x0f; + cs->dc.isac.mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); goto afterMONR1; } else - cs->mon_rxp = 0; + cs->dc.isac.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->dc.isac.mon_rxp >= MAX_MON_FRAME) { + cs->dc.isac.mocr &= 0x0f; + cs->dc.isac.mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + cs->dc.isac.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); + cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp++] = cs->readisac(cs, ISAC_MOR1); if (cs->debug & L1_DEB_MONITOR) - debugl1(cs, "ISAC MOR1 %02x", cs->mon_rx[cs->mon_rxp -1]); - cs->mocr |= 0x40; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); + debugl1(cs, "ISAC MOR1 %02x", cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp -1]); + cs->dc.isac.mocr |= 0x40; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.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); + cs->dc.isac.mocr &= 0xf0; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + cs->dc.isac.mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + isac_sched_event(cs, D_RX_MON0); } 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); + cs->dc.isac.mocr &= 0x0f; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + cs->dc.isac.mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + isac_sched_event(cs, D_RX_MON1); } if (v1 & 0x02) { - if ((!cs->mon_tx) || (cs->mon_txc && - (cs->mon_txp >= cs->mon_txc) && + if ((!cs->dc.isac.mon_tx) || (cs->dc.isac.mon_txc && + (cs->dc.isac.mon_txp >= cs->dc.isac.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); + cs->dc.isac.mocr &= 0xf0; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + cs->dc.isac.mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + if (cs->dc.isac.mon_txc && + (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc)) + isac_sched_event(cs, D_TX_MON0); goto AfterMOX0; } - if (cs->mon_txc && (cs->mon_txp >= cs->mon_txc)) { - test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags); + if (cs->dc.isac.mon_txc && (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc)) { + isac_sched_event(cs, D_TX_MON0); goto AfterMOX0; } cs->writeisac(cs, ISAC_MOX0, - cs->mon_tx[cs->mon_txp++]); + cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]); if (cs->debug & L1_DEB_MONITOR) - debugl1(cs, "ISAC %02x -> MOX0", cs->mon_tx[cs->mon_txp -1]); + debugl1(cs, "ISAC %02x -> MOX0", cs->dc.isac.mon_tx[cs->dc.isac.mon_txp -1]); } AfterMOX0: if (v1 & 0x20) { - if ((!cs->mon_tx) || (cs->mon_txc && - (cs->mon_txp >= cs->mon_txc) && + if ((!cs->dc.isac.mon_tx) || (cs->dc.isac.mon_txc && + (cs->dc.isac.mon_txp >= cs->dc.isac.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); + cs->dc.isac.mocr &= 0x0f; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + cs->dc.isac.mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + if (cs->dc.isac.mon_txc && + (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc)) + isac_sched_event(cs, D_TX_MON1); goto AfterMOX1; } - if (cs->mon_txc && (cs->mon_txp >= cs->mon_txc)) { - test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags); + if (cs->dc.isac.mon_txc && (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc)) { + isac_sched_event(cs, D_TX_MON1); goto AfterMOX1; } cs->writeisac(cs, ISAC_MOX1, - cs->mon_tx[cs->mon_txp++]); + cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]); if (cs->debug & L1_DEB_MONITOR) - debugl1(cs, "ISAC %02x -> MOX1", cs->mon_tx[cs->mon_txp -1]); + debugl1(cs, "ISAC %02x -> MOX1", cs->dc.isac.mon_tx[cs->dc.isac.mon_txp -1]); } AfterMOX1: #endif @@ -535,9 +566,9 @@ ISAC_l1hw(struct PStack *st, int pr, void *arg) 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)) + if ((cs->dc.isac.ph_state == ISAC_IND_EI) || + (cs->dc.isac.ph_state == ISAC_IND_DR) || + (cs->dc.isac.ph_state == ISAC_IND_RS)) ph_command(cs, ISAC_CMD_TIM); else ph_command(cs, ISAC_CMD_RS); @@ -576,7 +607,7 @@ ISAC_l1hw(struct PStack *st, int pr, void *arg) discard_queue(&cs->rq); discard_queue(&cs->sq); if (cs->tx_skb) { - dev_kfree_skb(cs->tx_skb); + idev_kfree_skb(cs->tx_skb, FREE_WRITE); cs->tx_skb = NULL; } if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) @@ -597,27 +628,50 @@ setstack_isac(struct PStack *st, struct IsdnCardState *cs) st->l1.l1hw = ISAC_l1hw; } +void +DC_Close_isac(struct IsdnCardState *cs) { + if (cs->dc.isac.mon_rx) { + kfree(cs->dc.isac.mon_rx); + cs->dc.isac.mon_rx = NULL; + } + if (cs->dc.isac.mon_tx) { + kfree(cs->dc.isac.mon_tx); + cs->dc.isac.mon_tx = NULL; + } +} + static void dbusy_timer_handler(struct IsdnCardState *cs) { struct PStack *stptr; - int val; + int rbch, star; 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; + rbch = cs->readisac(cs, ISAC_RBCH); + star = cs->readisac(cs, ISAC_STAR); + if (cs->debug) + debugl1(cs, "D-Channel Busy RBCH %02x STAR %02x", + rbch, star); + if (rbch & ISAC_RBCH_XAC) { /* 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; + } + } else { + /* discard frame; reset transceiver */ + test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags); + if (cs->tx_skb) { + idev_kfree_skb(cs->tx_skb, FREE_WRITE); + cs->tx_cnt = 0; + cs->tx_skb = NULL; + } else { + printk(KERN_WARNING "HiSax: ISAC D-Channel Busy no skb\n"); + debugl1(cs, "D-Channel Busy no skb"); + } + cs->writeisac(cs, ISAC_CMDR, 0x01); /* Transmitter reset */ + cs->irq_func(cs->irq, cs, NULL); } } } @@ -627,11 +681,14 @@ initisac(struct IsdnCardState *cs)) { cs->tqueue.routine = (void *) (void *) isac_bh; cs->setstack_d = setstack_isac; + cs->DC_Close = DC_Close_isac; + cs->dc.isac.mon_tx = NULL; + cs->dc.isac.mon_rx = NULL; 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; + cs->dc.isac.mocr = 0xaa; if (test_bit(HW_IOM1, &cs->HW_Flags)) { /* IOM 1 Mode */ cs->writeisac(cs, ISAC_ADF2, 0x0); @@ -641,7 +698,9 @@ initisac(struct IsdnCardState *cs)) cs->writeisac(cs, ISAC_MODE, 0xc9); } else { /* IOM 2 Mode */ - cs->writeisac(cs, ISAC_ADF2, 0x80); + if (!cs->dc.isac.adf2) + cs->dc.isac.adf2 = 0x80; + cs->writeisac(cs, ISAC_ADF2, cs->dc.isac.adf2); cs->writeisac(cs, ISAC_SQXR, 0x2f); cs->writeisac(cs, ISAC_SPCR, 0x00); cs->writeisac(cs, ISAC_STCR, 0x70); @@ -672,7 +731,7 @@ clear_pending_isac_ints(struct IsdnCardState *cs)) } val = cs->readisac(cs, ISAC_CIR0); debugl1(cs, "ISAC CIR0 %x", val); - cs->ph_state = (val >> 2) & 0xf; + cs->dc.isac.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/isar.c b/drivers/isdn/hisax/isar.c index ba9247a9f5a2..05fb7fe9cb95 100644 --- a/drivers/isdn/hisax/isar.c +++ b/drivers/isdn/hisax/isar.c @@ -1,4 +1,4 @@ -/* $Id: isar.c,v 1.2 1998/11/15 23:54:53 keil Exp $ +/* $Id: isar.c,v 1.4 1999/08/05 20:43:18 keil Exp $ * isar.c ISAR (Siemens PSB 7110) specific routines * @@ -6,6 +6,12 @@ * * * $Log: isar.c,v $ + * Revision 1.4 1999/08/05 20:43:18 keil + * ISAR analog modem support + * + * Revision 1.3 1999/07/01 08:11:45 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.2 1998/11/15 23:54:53 keil * changes from 2.0 * @@ -425,7 +431,9 @@ isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs) cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); break; case L1_MODE_TRANS: + case L1_MODE_V32: if ((skb = dev_alloc_skb(ireg->clsb))) { + SET_SKB_FREE(skb); rcv_mbox(cs, ireg, (u_char *)skb_put(skb, ireg->clsb)); skb_queue_tail(&bcs->rqueue, skb); isar_sched_event(bcs, B_RCVBUFREADY); @@ -459,6 +467,7 @@ isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs) } else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx-2))) printk(KERN_WARNING "ISAR: receive out of memory\n"); else { + SET_SKB_FREE(skb); memcpy(skb_put(skb, bcs->hw.isar.rcvidx-2), bcs->hw.isar.rcvbuf, bcs->hw.isar.rcvidx-2); skb_queue_tail(&bcs->rqueue, skb); @@ -512,6 +521,7 @@ isar_fill_fifo(struct BCState *bcs) printk(KERN_ERR"isar_fill_fifo wrong mode 0\n"); break; case L1_MODE_TRANS: + case L1_MODE_V32: if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA, 0, count, ptr)) { if (cs->debug) @@ -557,7 +567,7 @@ send_frames(struct BCState *bcs) if (bcs->st->lli.l1writewakeup && (PACKET_NOACK != bcs->tx_skb->pkt_type)) bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.isar.txcnt); - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->hw.isar.txcnt = 0; bcs->tx_skb = NULL; } @@ -593,6 +603,136 @@ check_send(struct IsdnCardState *cs, u_char rdm) } } +const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4", + "300", "600", "1200", "2400", "4800", "7200", + "9600nt", "9600t", "12000", "14400", "WRONG"}; +const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21", + "Bell103", "V23", "Bell202", "V17", "V29", "V27ter"}; + +static void +isar_pump_status_rsp(struct BCState *bcs, struct isar_reg *ireg) { + struct IsdnCardState *cs = bcs->cs; + u_char ril = ireg->par[0]; + u_char rim; + + if (!test_and_clear_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags)) + return; + if (ril > 14) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "wrong pstrsp ril=%d",ril); + ril = 15; + } + switch(ireg->par[1]) { + case 0: + rim = 0; + break; + case 0x20: + rim = 2; + break; + case 0x40: + rim = 3; + break; + case 0x41: + rim = 4; + break; + case 0x51: + rim = 5; + break; + case 0x61: + rim = 6; + break; + case 0x71: + rim = 7; + break; + case 0x82: + rim = 8; + break; + case 0x92: + rim = 9; + break; + case 0xa2: + rim = 10; + break; + default: + rim = 1; + break; + } + sprintf(bcs->hw.isar.conmsg,"%s %s", dmril[ril], dmrim[rim]); + bcs->conmsg = bcs->hw.isar.conmsg; + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump strsp %s", bcs->conmsg); +} + +static void +isar_pump_status_ev(struct BCState *bcs, u_char devt) { + struct IsdnCardState *cs = bcs->cs; + u_char dps = SET_DPS(bcs->hw.isar.dpath); + + switch(devt) { + case PSEV_10MS_TIMER: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev TIMER"); + break; + case PSEV_CON_ON: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CONNECT"); + l1_msg_b(bcs->st, PH_ACTIVATE | REQUEST, NULL); + break; + case PSEV_CON_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev NO CONNECT"); + sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + l1_msg_b(bcs->st, PH_DEACTIVATE | REQUEST, NULL); + break; + case PSEV_V24_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev V24 OFF"); + break; + case PSEV_CTS_ON: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CTS ON"); + break; + case PSEV_CTS_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CTS OFF"); + break; + case PSEV_DCD_ON: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CARRIER ON"); + test_and_set_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags); + sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + break; + case PSEV_DCD_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CARRIER OFF"); + break; + case PSEV_DSR_ON: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev DSR ON"); +// sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, 0xCF, 0, NULL); + break; + case PSEV_DSR_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev DSR_OFF"); + break; + case PSEV_REM_RET: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev REMOTE RETRAIN"); + break; + case PSEV_REM_REN: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev REMOTE RENEGOTIATE"); + break; + case PSEV_GSTN_CLR: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev GSTN CLEAR", devt); + break; + default: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "unknown pump stev %x", devt); + break; + } +} static char debbuf[64]; @@ -628,10 +768,31 @@ isar_int_main(struct IsdnCardState *cs) if (cs->debug & L1_DEB_WARN) debugl1(cs, "Buffer STEV dpath%d msb(%x)", ireg->iis>>6, ireg->cmsb); + case ISAR_IIS_PSTEV: + if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) { + rcv_mbox(cs, ireg, (u_char *)ireg->par); + isar_pump_status_ev(bcs, ireg->cmsb); + } else { + debugl1(cs, "isar spurious IIS_PSTEV %x/%x/%x", + ireg->iis, ireg->cmsb, ireg->clsb); + printk(KERN_WARNING"isar spurious IIS_PSTEV %x/%x/%x\n", + ireg->iis, ireg->cmsb, ireg->clsb); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } break; - case ISAR_IIS_DIAG: case ISAR_IIS_PSTRSP: - case ISAR_IIS_PSTEV: + if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) { + rcv_mbox(cs, ireg, (u_char *)ireg->par); + isar_pump_status_rsp(bcs, ireg); + } else { + debugl1(cs, "isar spurious IIS_PSTRSP %x/%x/%x", + ireg->iis, ireg->cmsb, ireg->clsb); + printk(KERN_WARNING"isar spurious IIS_PSTRSP %x/%x/%x\n", + ireg->iis, ireg->cmsb, ireg->clsb); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } + break; + case ISAR_IIS_DIAG: case ISAR_IIS_BSTRSP: case ISAR_IIS_IOM2RSP: rcv_mbox(cs, ireg, (u_char *)ireg->par); @@ -659,7 +820,8 @@ void setup_pump(struct BCState *bcs) { struct IsdnCardState *cs = bcs->cs; u_char dps = SET_DPS(bcs->hw.isar.dpath); - + u_char ctrl, param[6]; + switch (bcs->mode) { case L1_MODE_NULL: case L1_MODE_TRANS: @@ -670,6 +832,44 @@ setup_pump(struct BCState *bcs) { bcs->hw.isar.dpath); } break; + case L1_MODE_V32: + ctrl = PMOD_DATAMODEM; + if (test_bit(BC_FLG_ORIG, &bcs->Flag)) { + ctrl |= PCTRL_ORIG; + param[5] = PV32P6_CTN; + } else { + param[5] = PV32P6_ATN; + } + param[0] = 11; /* 11 db */ +// param[1] = PV32P2_V22A | PV32P2_V22B | PV32P2_V21; + param[1] = PV32P2_V22A; +// param[2] = PV32P3_AMOD | PV32P3_V32B; + param[2] = PV32P3_AMOD; + param[3] = PV32P4_48; + param[4] = PV32P5_48; +// param[3] = PV32P4_UT144; +// param[4] = PV32P5_UT144; + if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param)) { + if (cs->debug) + debugl1(cs, "isar pump datamodem cfg dp%d failed", + bcs->hw.isar.dpath); + } + break; + case L1_MODE_FAX: + ctrl = PMOD_FAX; + if (test_bit(BC_FLG_ORIG, &bcs->Flag)) { + ctrl |= PCTRL_ORIG; + param[1] = PFAXP2_CTN; + } else { + param[1] = PFAXP2_ATN; + } + param[0] = 8; /* 8 db */ + if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param)) { + if (cs->debug) + debugl1(cs, "isar pump faxmodem cfg dp%d failed", + bcs->hw.isar.dpath); + } + break; } if (!sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL)) { if (cs->debug) @@ -682,6 +882,7 @@ void setup_sart(struct BCState *bcs) { struct IsdnCardState *cs = bcs->cs; u_char dps = SET_DPS(bcs->hw.isar.dpath); + u_char ctrl, param[2]; switch (bcs->mode) { case L1_MODE_NULL: @@ -701,7 +902,17 @@ setup_sart(struct BCState *bcs) { case L1_MODE_HDLC: if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1, "\0")) { if (cs->debug) - debugl1(cs, "isar sart binary dp%d failed", + debugl1(cs, "isar sart hdlc dp%d failed", + bcs->hw.isar.dpath); + } + break; + case L1_MODE_V32: + ctrl = SMODE_V14 | SCTRL_HDMC_BOTH; + param[0] = S_P1_CHS_8; + param[1] = S_P2_BFT_DEF; + if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, ctrl, 2, param)) { + if (cs->debug) + debugl1(cs, "isar sart v14 dp%d failed", bcs->hw.isar.dpath); } break; @@ -717,18 +928,22 @@ void setup_iom2(struct BCState *bcs) { struct IsdnCardState *cs = bcs->cs; u_char dps = SET_DPS(bcs->hw.isar.dpath); - u_char cmsb = 0, msg[5] = {0x10,0,0,0,0}; + u_char cmsb = IOM_CTRL_ENA, msg[5] = {IOM_P1_TXD,0,0,0,0}; + if (bcs->channel) + msg[1] = msg[3] = 1; switch (bcs->mode) { case L1_MODE_NULL: + cmsb = 0; /* dummy slot */ msg[1] = msg[3] = bcs->hw.isar.dpath + 2; break; case L1_MODE_TRANS: case L1_MODE_HDLC: - cmsb = 0x80; - if (bcs->channel) - msg[1] = msg[3] = 1; + break; + case L1_MODE_V32: + case L1_MODE_FAX: + cmsb |= IOM_CTRL_ALAW | IOM_CTRL_RCV; break; } if (!sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg)) { @@ -763,7 +978,18 @@ modeisar(struct BCState *bcs, int mode, int bc) &bcs->hw.isar.reg->Flags)) bcs->hw.isar.dpath = 1; else { - printk(KERN_ERR"isar modeisar both pathes in use\n"); + printk(KERN_WARNING"isar modeisar both pathes in use\n"); + return(1); + } + break; + case L1_MODE_V32: + /* only datapath 1 */ + if (!test_and_set_bit(ISAR_DP1_USE, + &bcs->hw.isar.reg->Flags)) + bcs->hw.isar.dpath = 1; + else { + printk(KERN_WARNING"isar modeisar analog funktions only with DP1\n"); + debugl1(cs, "isar modeisar analog funktions only with DP1"); return(1); } break; @@ -774,8 +1000,8 @@ modeisar(struct BCState *bcs, int mode, int bc) bcs->hw.isar.dpath, bcs->mode, mode, bc); bcs->mode = mode; setup_pump(bcs); - setup_sart(bcs); setup_iom2(bcs); + setup_sart(bcs); if (bcs->mode == L1_MODE_NULL) { /* Clear resources */ if (bcs->hw.isar.dpath == 1) @@ -853,8 +1079,24 @@ isar_l2l1(struct PStack *st, int pr, void *arg) break; case (PH_ACTIVATE | REQUEST): test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); - modeisar(st->l1.bcs, st->l1.mode, st->l1.bc); - l1_msg_b(st, pr, arg); + st->l1.bcs->hw.isar.conmsg[0] = 0; + if (test_bit(FLG_ORIG, &st->l2.flag)) + test_and_set_bit(BC_FLG_ORIG, &st->l1.bcs->Flag); + else + test_and_clear_bit(BC_FLG_ORIG, &st->l1.bcs->Flag); + switch(st->l1.mode) { + case L1_MODE_TRANS: + case L1_MODE_HDLC: + if (modeisar(st->l1.bcs, st->l1.mode, st->l1.bc)) + l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg); + else + l1_msg_b(st, PH_ACTIVATE | REQUEST, arg); + break; + case L1_MODE_V32: + if (modeisar(st->l1.bcs, st->l1.mode, st->l1.bc)) + l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg); + break; + } break; case (PH_DEACTIVATE | REQUEST): l1_msg_b(st, pr, arg); @@ -882,7 +1124,7 @@ close_isarstate(struct BCState *bcs) discard_queue(&bcs->rqueue); discard_queue(&bcs->squeue); if (bcs->tx_skb) { - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); if (bcs->cs->debug & L1_DEB_HSCX) diff --git a/drivers/isdn/hisax/isar.h b/drivers/isdn/hisax/isar.h index de892f813f9c..c17875263901 100644 --- a/drivers/isdn/hisax/isar.h +++ b/drivers/isdn/hisax/isar.h @@ -1,10 +1,16 @@ -/* $Id: isar.h,v 1.2 1998/11/15 23:54:54 keil Exp $ +/* $Id: isar.h,v 1.4 1999/08/05 20:43:20 keil Exp $ * isar.h ISAR (Siemens PSB 7110) specific defines * * Author Karsten Keil (keil@isdn4linux.de) * * * $Log: isar.h,v $ + * Revision 1.4 1999/08/05 20:43:20 keil + * ISAR analog modem support + * + * Revision 1.3 1999/07/01 08:11:46 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.2 1998/11/15 23:54:54 keil * changes from 2.0 * @@ -36,8 +42,10 @@ #define ISAR_HIS_P12CFG 0x24 #define ISAR_HIS_SARTCFG 0x25 #define ISAR_HIS_PUMPCFG 0x26 +#define ISAR_HIS_PUMPCTRL 0x2a #define ISAR_HIS_IOM2CFG 0x27 #define ISAR_HIS_IOM2REQ 0x07 +#define ISAR_HIS_IOM2CTRL 0x2b #define ISAR_HIS_BSTREQ 0x0c #define ISAR_HIS_PSTREQ 0x0e #define ISAR_HIS_SDATA 0x20 @@ -66,12 +74,97 @@ #define ISAR_DP1_USE 1 #define ISAR_DP2_USE 2 +#define ISAR_RATE_REQ 3 +#define PMOD_DISABLE 0 +#define PMOD_FAX 1 +#define PMOD_DATAMODEM 2 +#define PMOD_HALFDUPLEX 3 +#define PMOD_V110 4 +#define PMOD_DTMF 5 +#define PMOD_DTMF_TRANS 6 #define PMOD_BYPASS 7 +#define PCTRL_ORIG 0x80 +#define PV32P2_V23R 0x40 +#define PV32P2_V22A 0x20 +#define PV32P2_V22B 0x10 +#define PV32P2_V22C 0x08 +#define PV32P2_V21 0x02 +#define PV32P2_BEL 0x01 + +#define PV32P3_AMOD 0x80 +#define PV32P3_V32B 0x02 +#define PV32P4_48 0x05 +#define PV32P5_48 0x11 +#define PV32P4_UT48 0x0d +#define PV32P5_UT48 0x11 +#define PV32P4_96 0x03 +#define PV32P5_96 0x11 +#define PV32P4_UT96 0x0f +#define PV32P5_UT96 0x11 +#define PV32P4_B96 0x0b +#define PV32P5_B96 0x91 +#define PV32P4_UTB96 0x0f +#define PV32P5_UTB96 0xd1 +#define PV32P4_120 0x09 +#define PV32P5_120 0xb1 +#define PV32P4_UT120 0x0f +#define PV32P5_UT120 0xf1 +#define PV32P4_144 0x09 +#define PV32P5_144 0x99 +#define PV32P4_UT144 0x0f +#define PV32P5_UT144 0xf9 +#define PV32P6_CTN 0x01 +#define PV32P6_ATN 0x02 +#define PFAXP2_CTN 0x01 +#define PFAXP2_ATN 0x04 + +#define PSEV_10MS_TIMER 0x02 +#define PSEV_CON_ON 0x18 +#define PSEV_CON_OFF 0x19 +#define PSEV_V24_OFF 0x20 +#define PSEV_CTS_ON 0x21 +#define PSEV_CTS_OFF 0x22 +#define PSEV_DCD_ON 0x23 +#define PSEV_DCD_OFF 0x24 +#define PSEV_DSR_ON 0x25 +#define PSEV_DSR_OFF 0x26 +#define PSEV_REM_RET 0xcc +#define PSEV_REM_REN 0xcd +#define PSEV_GSTN_CLR 0xd4 + +#define PCTRL_LOC_RET 0xcf +#define PCTRL_LOC_REN 0xce + #define SMODE_DISABLE 0 +#define SMODE_V14 2 #define SMODE_HDLC 3 #define SMODE_BINARY 4 +#define SMODE_FSK_V14 5 + +#define SCTRL_HDMC_BOTH 0x00 +#define SCTRL_HDMC_DTX 0x80 +#define SCTRL_HDMC_DRX 0x40 +#define S_P1_OVSP 0x40 +#define S_P1_SNP 0x20 +#define S_P1_EOP 0x10 +#define S_P1_EDP 0x08 +#define S_P1_NSB 0x04 +#define S_P1_CHS_8 0x03 +#define S_P1_CHS_7 0x02 +#define S_P1_CHS_6 0x01 +#define S_P1_CHS_5 0x00 + +#define S_P2_BFT_DEF 30 + +#define IOM_CTRL_ENA 0x80 +#define IOM_CTRL_NOPCM 0x00 +#define IOM_CTRL_ALAW 0x02 +#define IOM_CTRL_ULAW 0x04 +#define IOM_CTRL_RCV 0x01 + +#define IOM_P1_TXD 0x10 #define HDLC_FED 0x40 #define HDLC_FSD 0x20 diff --git a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c index c8f88a1627a4..68813448084a 100644 --- a/drivers/isdn/hisax/isdnl1.c +++ b/drivers/isdn/hisax/isdnl1.c @@ -1,4 +1,4 @@ -/* $Id: isdnl1.c,v 2.31 1998/11/15 23:54:56 keil Exp $ +/* $Id: isdnl1.c,v 2.34 1999/07/09 13:50:15 keil Exp $ * isdnl1.c common low level stuff for Siemens Chipsetbased isdn cards * based on the teles driver from Jan den Ouden @@ -15,6 +15,15 @@ * * * $Log: isdnl1.c,v $ + * Revision 2.34 1999/07/09 13:50:15 keil + * remove unused variable + * + * Revision 2.33 1999/07/09 13:34:33 keil + * remove debug code + * + * Revision 2.32 1999/07/01 08:11:47 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.31 1998/11/15 23:54:56 keil * changes from 2.0 * @@ -120,9 +129,10 @@ * */ -const char *l1_revision = "$Revision: 2.31 $"; +const char *l1_revision = "$Revision: 2.34 $"; #define __NO_VERSION__ +#include #include "hisax.h" #include "isdnl1.h" @@ -298,9 +308,18 @@ DChannel_proc_rcv(struct IsdnCardState *cs) Logl2Frame(cs, skb, "PH_DATA", 1); #endif stptr = cs->stlist; + if (skb->len<3) { + debugl1(cs, "D-channel frame too short(%d)",skb->len); + idev_kfree_skb(skb, FREE_READ); + return; + } + if ((skb->data[0] & 1) || !(skb->data[1] &1)) { + debugl1(cs, "D-channel frame wrong EA0/EA1"); + idev_kfree_skb(skb, FREE_READ); + return; + } sapi = skb->data[0] >> 2; tei = skb->data[1] >> 1; - if (cs->debug & DEB_DLOG_HEX) LogFrame(cs, skb->data, skb->len); if (cs->debug & DEB_DLOG_VERBOSE) @@ -323,7 +342,7 @@ DChannel_proc_rcv(struct IsdnCardState *cs) stptr = stptr->next; } } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } else if (sapi == CTRL_SAPI) { /* sapi 0 */ found = 0; while (stptr != NULL) @@ -334,7 +353,7 @@ DChannel_proc_rcv(struct IsdnCardState *cs) } else stptr = stptr->next; if (!found) - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } } } diff --git a/drivers/isdn/hisax/isdnl2.c b/drivers/isdn/hisax/isdnl2.c index ccef2682e6aa..24d468da2528 100644 --- a/drivers/isdn/hisax/isdnl2.c +++ b/drivers/isdn/hisax/isdnl2.c @@ -1,4 +1,4 @@ -/* $Id: isdnl2.c,v 2.16 1998/11/15 23:55:01 keil Exp $ +/* $Id: isdnl2.c,v 2.19 1999/08/05 20:40:26 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -11,6 +11,15 @@ * Fritz Elfert * * $Log: isdnl2.c,v $ + * Revision 2.19 1999/08/05 20:40:26 keil + * Fix interlayer communication + * + * Revision 2.18 1999/07/21 14:46:16 keil + * changes from EICON certification + * + * Revision 2.17 1999/07/01 08:11:50 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.16 1998/11/15 23:55:01 keil * changes from 2.0 * @@ -71,7 +80,7 @@ #include "hisax.h" #include "isdnl2.h" -const char *l2_revision = "$Revision: 2.16 $"; +const char *l2_revision = "$Revision: 2.19 $"; static void l2m_debug(struct FsmInst *fi, char *fmt, ...); @@ -106,7 +115,7 @@ static char *strL2State[] = enum { EV_L2_UI, - EV_L2_SABMX, + EV_L2_SABME, EV_L2_DISC, EV_L2_DM, EV_L2_UA, @@ -116,22 +125,25 @@ enum { EV_L2_DL_DATA, EV_L2_ACK_PULL, EV_L2_DL_UNIT_DATA, - EV_L2_DL_ESTABLISH, - EV_L2_DL_RELEASE, + EV_L2_DL_ESTABLISH_REQ, + EV_L2_DL_RELEASE_REQ, EV_L2_MDL_ASSIGN, EV_L2_MDL_REMOVE, EV_L2_MDL_ERROR, EV_L1_DEACTIVATE, EV_L2_T200, EV_L2_T203, + EV_L2_SET_OWN_BUSY, + EV_L2_CLEAR_OWN_BUSY, + EV_L2_FRAME_ERROR, }; -#define L2_EVENT_COUNT (EV_L2_T203+1) +#define L2_EVENT_COUNT (EV_L2_FRAME_ERROR+1) static char *strL2Event[] = { "EV_L2_UI", - "EV_L2_SABMX", + "EV_L2_SABME", "EV_L2_DISC", "EV_L2_DM", "EV_L2_UA", @@ -141,18 +153,34 @@ static char *strL2Event[] = "EV_L2_DL_DATA", "EV_L2_ACK_PULL", "EV_L2_DL_UNIT_DATA", - "EV_L2_DL_ESTABLISH", - "EV_L2_DL_RELEASE", + "EV_L2_DL_ESTABLISH_REQ", + "EV_L2_DL_RELEASE_REQ", "EV_L2_MDL_ASSIGN", "EV_L2_MDL_REMOVE", "EV_L2_MDL_ERROR", "EV_L1_DEACTIVATE", "EV_L2_T200", "EV_L2_T203", + "EV_L2_SET_OWN_BUSY", + "EV_L2_CLEAR_OWN_BUSY", + "EV_L2_FRAME_ERROR", }; static int l2addrsize(struct Layer2 *l2); +static void +set_peer_busy(struct Layer2 *l2) { + test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + if (skb_queue_len(&l2->i_queue) || skb_queue_len(&l2->ui_queue)) + test_and_set_bit(FLG_L2BLOCK, &l2->flag); +} + +static void +clear_peer_busy(struct Layer2 *l2) { + if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag)) + test_and_clear_bit(FLG_L2BLOCK, &l2->flag); +} + static void InitWin(struct Layer2 *l2) { @@ -162,30 +190,45 @@ InitWin(struct Layer2 *l2) l2->windowar[i] = NULL; } -static void -ReleaseWin(struct Layer2 *l2) +static int +freewin1(struct Layer2 *l2) { int i, cnt = 0; for (i = 0; i < MAX_WINDOW; i++) { if (l2->windowar[i]) { cnt++; - dev_kfree_skb(l2->windowar[i]); + idev_kfree_skb(l2->windowar[i], FREE_WRITE); l2->windowar[i] = NULL; } } - if (cnt) + return cnt; +} + +inline void +freewin(struct PStack *st) +{ + freewin1(&st->l2); +} + +static void +ReleaseWin(struct Layer2 *l2) +{ + int cnt; + + if((cnt = freewin1(l2))) printk(KERN_WARNING "isdl2 freed %d skbuffs in release\n", cnt); } -inline int +inline unsigned int cansend(struct PStack *st) { - int p1; + unsigned int p1; - p1 = st->l2.vs - st->l2.va; - if (p1 < 0) - p1 += (test_bit(FLG_MOD128, &st->l2.flag) ? 128 : 8); + if(test_bit(FLG_MOD128, &st->l2.flag)) + p1 = (st->l2.vs - st->l2.va) % 128; + else + p1 = (st->l2.vs - st->l2.va) % 8; return ((p1 < st->l2.window) && !test_bit(FLG_PEER_BUSY, &st->l2.flag)); } @@ -195,7 +238,7 @@ clear_exception(struct Layer2 *l2) 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); + clear_peer_busy(l2); } inline int @@ -244,98 +287,197 @@ enqueue_super(struct PStack *st, #define enqueue_ui(a, b) enqueue_super(a, b) inline int -IsUI(u_char * data, int ext) +IsUI(u_char * data) { return ((data[0] & 0xef) == UI); } inline int -IsUA(u_char * data, int ext) +IsUA(u_char * data) { return ((data[0] & 0xef) == UA); } inline int -IsDM(u_char * data, int ext) +IsDM(u_char * data) { return ((data[0] & 0xef) == DM); } inline int -IsDISC(u_char * data, int ext) +IsDISC(u_char * data) { return ((data[0] & 0xef) == DISC); } inline int -IsRR(u_char * data, int ext) +IsRR(u_char * data, struct PStack *st) { - if (ext) + if (test_bit(FLG_MOD128, &st->l2.flag)) return (data[0] == RR); else return ((data[0] & 0xf) == 1); } inline int -IsSFrame(u_char * data, int ext) +IsSFrame(u_char * data, struct PStack *st) { register u_char d = *data; - if (!ext) + if (!test_bit(FLG_MOD128, &st->l2.flag)) d &= 0xf; return(((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c)); } inline int -IsSABMX(u_char * data, int ext) +IsSABME(u_char * data, struct PStack *st) { u_char d = data[0] & ~0x10; - return (ext ? d == SABME : d == SABM); + return (test_bit(FLG_MOD128, &st->l2.flag) ? d == SABME : d == SABM); } inline int -IsREJ(u_char * data, int ext) +IsREJ(u_char * data, struct PStack *st) { - return (ext ? data[0] == REJ : (data[0] & 0xf) == REJ); + return (test_bit(FLG_MOD128, &st->l2.flag) ? data[0] == REJ : (data[0] & 0xf) == REJ); } inline int -IsFRMR(u_char * data, int ext) +IsFRMR(u_char * data) { return ((data[0] & 0xef) == FRMR); } inline int -IsRNR(u_char * data, int ext) +IsRNR(u_char * data, struct PStack *st) { - return (ext ? data[0] == RNR : (data[0] & 0xf) == RNR); + return (test_bit(FLG_MOD128, &st->l2.flag) ? data[0] == RNR : (data[0] & 0xf) == RNR); } -static int -legalnr(struct PStack *st, int nr) +int +iframe_error(struct PStack *st, struct sk_buff *skb) { - struct Layer2 *l2 = &st->l2; - int lnr, lvs; + int i = l2addrsize(&st->l2) + (test_bit(FLG_MOD128, &st->l2.flag) ? 1 : 0); + int rsp = *skb->data & 0x2; + + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; + + if (rsp) + return 'L'; + + + if (skb->len <= i) + return 'N'; + + if ((skb->len - i) > st->l2.maxlen) + return 'O'; - 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); + + return 0; +} + +int +super_error(struct PStack *st, struct sk_buff *skb) +{ + if (skb->len != l2addrsize(&st->l2) + + (test_bit(FLG_MOD128, &st->l2.flag) ? 2 : 1)) + return 'N'; + + return 0; +} + +int +unnum_error(struct PStack *st, struct sk_buff *skb, int wantrsp) +{ + int rsp = (*skb->data & 0x2) >> 1; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; + + if (rsp != wantrsp) + return 'L'; + + if (skb->len != l2addrsize(&st->l2) + 1) + return 'N'; + + return 0; +} + +int +UI_error(struct PStack *st, struct sk_buff *skb) +{ + int rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; + + if (rsp) + return 'L'; + + if (skb->len > st->l2.maxlen + l2addrsize(&st->l2) + 1) + return 'O'; + + return 0; +} + +int +FRMR_error(struct PStack *st, struct sk_buff *skb) +{ + int headers = l2addrsize(&st->l2) + 1; + u_char *datap = skb->data + headers; + int rsp = *skb->data & 0x2; + + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; + + if (!rsp) + return 'L'; + + if (test_bit(FLG_MOD128, &st->l2.flag)) { + if (skb->len < headers + 5) + return 'N'; + else + l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x %2x %2x", + datap[0], datap[1], datap[2], + datap[3], datap[4]); + } else { + if (skb->len < headers + 3) + return 'N'; + else + l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x", + datap[0], datap[1], datap[2]); + } + + return 0; +} + +static unsigned int +legalnr(struct PStack *st, unsigned int nr) +{ + struct Layer2 *l2 = &st->l2; + + if(test_bit(FLG_MOD128, &l2->flag)) + return ((nr - l2->va) % 128) <= ((l2->vs - l2->va) % 128); + else + return ((nr - l2->va) % 8) <= ((l2->vs - l2->va) % 8); } static void -setva(struct PStack *st, int nr) +setva(struct PStack *st, unsigned 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); + (l2->va)++; + if(test_bit(FLG_MOD128, &l2->flag)) + l2->va %= 128; + else + l2->va %= 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]); + idev_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)) @@ -356,6 +498,7 @@ send_uframe(struct PStack *st, u_char cmd, u_char cr) printk(KERN_WARNING "isdl2 can't alloc sbbuff for send_uframe\n"); return; } + SET_SKB_FREE(skb); memcpy(skb_put(skb, i), tmp, i); enqueue_super(st, skb); } @@ -369,7 +512,7 @@ get_PollFlag(struct PStack * st, struct sk_buff * skb) inline void FreeSkb(struct sk_buff *skb) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } @@ -383,6 +526,48 @@ get_PollFlagFree(struct PStack *st, struct sk_buff *skb) return (PF); } +inline void +start_t200(struct PStack *st, int i) +{ + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, i); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); +} + +inline void +restart_t200(struct PStack *st, int i) +{ + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, i); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); +} + +inline void +stop_t200(struct PStack *st, int i) +{ + if(test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, i); +} + +inline void +st5_dl_release_l2l3(struct PStack *st) +{ + int pr; + + if(test_and_clear_bit(FLG_PEND_REL, &st->l2.flag)) + pr = DL_RELEASE | CONFIRM; + else + pr = DL_RELEASE | INDICATION; + + st->l2.l2l3(st, pr, NULL); +} + +inline void +lapb_dl_release_l2l3(struct PStack *st, int f) +{ + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + st->l2.l2l3(st, DL_RELEASE | f, NULL); +} + static void establishlink(struct FsmInst *fi) { @@ -394,56 +579,91 @@ establishlink(struct FsmInst *fi) 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); + restart_t200(st, 1); + test_and_clear_bit(FLG_PEND_REL, &st->l2.flag); + freewin(st); FsmChangeState(fi, ST_L2_5); } static void -l2_mdl_error(struct FsmInst *fi, int event, void *arg) +l2_mdl_error_ua(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; - } + if (get_PollFlagFree(st, skb)) + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'C'); + else + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'D'); } static void -l2_dl_establish(struct FsmInst *fi, int event, void *arg) +l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg) { + struct sk_buff *skb = arg; struct PStack *st = fi->userdata; - int state = fi->state; - - if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) { - FsmChangeState(fi, ST_L2_4); + 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_set_bit(FLG_L3_INIT, &st->l2.flag); - } else { - FsmChangeState(fi, ST_L2_3); - if (state == ST_L2_1) - st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); + } +} + +static void +l2_st8_mdl_error_dm(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct PStack *st = fi->userdata; + + 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); +} + +static void +l2_go_st3(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L2_3); +} + +static void +l2_mdl_assign(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L2_3); + st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL); +} + +static void +l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&st->l2.ui_queue, skb); + FsmChangeState(fi, ST_L2_2); + st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL); +} + +static void +l2_queue_ui(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&st->l2.ui_queue, skb); } static void -l2_send_ui(struct PStack *st) +tx_ui(struct PStack *st) { struct sk_buff *skb; u_char header[MAX_HEADER_LEN]; @@ -458,18 +678,13 @@ l2_send_ui(struct PStack *st) } static void -l2_put_ui(struct FsmInst *fi, int event, void *arg) +l2_send_ui(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; 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); - } - if (fi->state > ST_L2_3) - l2_send_ui(st); + tx_ui(st); } static void @@ -479,11 +694,12 @@ l2_got_ui(struct FsmInst *fi, int event, void *arg) struct sk_buff *skb = arg; 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); + st->l2.l2l3(st, DL_UNIT_DATA | INDICATION, skb); +/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * in states 1-3 for broadcast + */ + + } static void @@ -491,277 +707,244 @@ 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); + establishlink(fi); test_and_set_bit(FLG_L3_INIT, &st->l2.flag); } static void -l2_dl_release(struct FsmInst *fi, int event, void *arg) +l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.i_queue); + test_and_set_bit(FLG_L3_INIT, &st->l2.flag); + test_and_clear_bit(FLG_PEND_REL, &st->l2.flag); +} + +static void +l2_l3_reestablish(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); + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &st->l2.flag); +} + +static void +l2_release(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); +} + +static void +l2_pend_rel(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + test_and_set_bit(FLG_PEND_REL, &st->l2.flag); +} + +static void +l2_disconnect(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.i_queue); + freewin(st); FsmChangeState(fi, ST_L2_6); st->l2.rc = 0; send_uframe(st, DISC | 0x10, CMD); FsmDelTimer(&st->l2.t203, 1); - FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 2); - test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + restart_t200(st, 2); +} + +static void +l2_start_multi(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + send_uframe(st, UA | get_PollFlagFree(st, skb), RSP); + + clear_exception(&st->l2); + st->l2.vs = 0; + st->l2.va = 0; + st->l2.vr = 0; + st->l2.sow = 0; + FsmChangeState(fi, ST_L2_7); + FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3); + + st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL); } static void -l2_got_SABMX(struct FsmInst *fi, int event, void *arg) +l2_send_UA(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - int est = 1, state, rsp; - u_char PollFlag; + + send_uframe(st, UA | get_PollFlagFree(st, skb), RSP); +} + +static void +l2_send_DM(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + send_uframe(st, DM | get_PollFlagFree(st, skb), RSP); +} + +static void +l2_restart_multi(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + int est = 0, state; 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 ((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_queue(&st->l2.i_queue); - est = 1; - } else - est = 0; + + send_uframe(st, UA | get_PollFlagFree(st, skb), RSP); + + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'F'); + + if (st->l2.vs != st->l2.va) { + discard_queue(&st->l2.i_queue); + est = 1; } + clear_exception(&st->l2); st->l2.vs = 0; st->l2.va = 0; st->l2.vr = 0; st->l2.sow = 0; FsmChangeState(fi, ST_L2_7); - if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) - FsmDelTimer(&st->l2.t200, 2); + stop_t200(st, 3); FsmRestartTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3); if (est) 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_PULL | REQUEST, NULL); + if ((ST_L2_7==state) || (ST_L2_8 == state)) + if (skb_queue_len(&st->l2.i_queue) && cansend(st)) + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); +} + +static void +l2_stop_multi(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + FsmChangeState(fi, ST_L2_4); + FsmDelTimer(&st->l2.t203, 3); + stop_t200(st, 4); + + send_uframe(st, UA | get_PollFlagFree(st, skb), RSP); + + discard_queue(&st->l2.i_queue); + freewin(st); + lapb_dl_release_l2l3(st, INDICATION); +} + +static void +l2_connected(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + int pr=-1; + + if (!get_PollFlag(st, skb)) { + l2_mdl_error_ua(fi, event, arg); + return; + } + FreeSkb(skb); + + if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag)) + l2_disconnect(fi, event, arg); + + 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; + } + + stop_t200(st, 5); + + st->l2.vr = 0; + st->l2.vs = 0; + st->l2.va = 0; + st->l2.sow = 0; + FsmChangeState(fi, ST_L2_7); + FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 4); + + if (pr != -1) + st->l2.l2l3(st, pr, NULL); + + if (skb_queue_len(&st->l2.i_queue) && cansend(st)) + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); +} + +static void +l2_released(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + if (!get_PollFlag(st, skb)) { + l2_mdl_error_ua(fi, event, arg); + return; + } + FreeSkb(skb); + + stop_t200(st, 6); + lapb_dl_release_l2l3(st, CONFIRM); + FsmChangeState(fi, ST_L2_4); } static void -l2_got_disconn(struct FsmInst *fi, int event, void *arg) +l2_reestablish(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - u_char PollFlag, cmd = UA; - int state, rel = 1, cst = 1, rsp; - - 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 ((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 (!get_PollFlagFree(st, skb)) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &st->l2.flag); } } - static void -l2_got_ua(struct FsmInst *fi, int event, void *arg) +l2_st5_dm_release(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - int pr=-1; - u_char PollFlag; - int state,rsp; - - 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_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)) { + if (get_PollFlagFree(st, skb)) { + stop_t200(st, 7); + if (!test_bit(FLG_L3_INIT, &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); + st5_dl_release_l2l3(st); FsmChangeState(fi, ST_L2_4); } } static void -l2_got_dm(struct FsmInst *fi, int event, void *arg) +l2_st6_dm_release(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - u_char PollFlag; - int state,rsp; - - 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_7) || (fi->state == ST_L2_8)) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E'); - establishlink(fi); - } - } else { - switch (fi->state) { - case ST_L2_8: - establishlink(fi); - case ST_L2_7: - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B'); - break; - case ST_L2_4: - break; - case ST_L2_5: - if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) - FsmDelTimer(&st->l2.t200, 2); - discard_queue(&st->l2.i_queue); - if (test_bit(FLG_LAPB, &st->l2.flag)) - st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); - st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); - FsmChangeState(fi, ST_L2_4); - break; - case ST_L2_6: - if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) - FsmDelTimer(&st->l2.t200, 2); - 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); - break; - } + if (get_PollFlagFree(st, skb)) { + stop_t200(st, 8); + lapb_dl_release_l2l3(st, CONFIRM); + FsmChangeState(fi, ST_L2_4); } } @@ -784,6 +967,7 @@ enquiry_cr(struct PStack *st, u_char typ, u_char cr, u_char pf) printk(KERN_WARNING "isdl2 can't alloc sbbuff for enquiry_cr\n"); return; } + SET_SKB_FREE(skb); memcpy(skb_put(skb, i), tmp, i); enqueue_super(st, skb); } @@ -806,8 +990,7 @@ transmit_enquiry(struct PStack *st) 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); + start_t200(st, 9); } @@ -818,42 +1001,42 @@ nrerrorrecovery(struct FsmInst *fi) st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'J'); establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); } static void -invoke_retransmission(struct PStack *st, int nr) +invoke_retransmission(struct PStack *st, unsigned int nr) { struct Layer2 *l2 = &st->l2; - int p1; - long flags; + unsigned int p1; 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); + (l2->vs)--; + if(test_bit(FLG_MOD128, &l2->flag)) { + l2->vs %= 128; + p1 = (l2->vs - l2->va) % 128; + } else { + l2->vs %= 8; + p1 = (l2->vs - l2->va) % 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) +l2_st7_got_super(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - int PollFlag, nr, rsp, typ = RR; + int PollFlag, rsp, typ = RR; + unsigned int nr; struct Layer2 *l2 = &st->l2; rsp = *skb->data & 0x2; @@ -861,72 +1044,77 @@ l2_got_st7_super(struct FsmInst *fi, int event, void *arg) rsp = !rsp; skb_pull(skb, l2addrsize(l2)); - if (IsRNR(skb->data, test_bit(FLG_MOD128, &l2->flag))) { - test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + if (IsRNR(skb->data, st)) { + set_peer_busy(l2); typ = RNR; } else - test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); - if (IsREJ(skb->data, test_bit(FLG_MOD128, &l2->flag))) + clear_peer_busy(l2); + if (IsREJ(skb->data, st)) 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 { - if (skb->len >2) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); - establishlink(fi); - } - FreeSkb(skb); - return; - } + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; } else { - 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; - } + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; } FreeSkb(skb); - if ((!rsp) && PollFlag) - enquiry_response(st); - if (rsp && PollFlag) - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'A'); + if (PollFlag) { + if (rsp) + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'A'); + else + enquiry_response(st); + } 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); + stop_t200(st, 10); 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); + stop_t200(st, 11); 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(typ != RR) FsmDelTimer(&st->l2.t203, 9); + restart_t200(st, 12); } 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.l2l3(st, DL_FLUSH | INDICATION, NULL); - } +static void +l2_feed_i_if_reest(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0); + if (!test_bit(FLG_L3_INIT, &st->l2.flag)) + skb_queue_tail(&st->l2.i_queue, skb); + else + FreeSkb(skb); +} + +static void +l2_feed_i_pull(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0); + skb_queue_tail(&st->l2.i_queue, skb); + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); } static void @@ -937,10 +1125,7 @@ l2_feed_iqueue(struct FsmInst *fi, int event, void *arg) 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); + skb_queue_tail(&st->l2.i_queue, skb); } static void @@ -949,54 +1134,30 @@ l2_got_iframe(struct FsmInst *fi, int event, void *arg) struct PStack *st = fi->userdata; struct sk_buff *skb = arg; struct Layer2 *l2 = &(st->l2); - int PollFlag, ns, nr, i, rsp; - - rsp = *skb->data & 0x2; - if (test_bit(FLG_ORIG, &l2->flag)) - rsp = !rsp; + int PollFlag, ns, i; + unsigned int nr; - if (rsp) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); - FreeSkb(skb); - establishlink(fi); - return; - } i = l2addrsize(l2); if (test_bit(FLG_MOD128, &l2->flag)) { - if (skb->len <= (i + 1)) { - FreeSkb(skb); - 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 { - 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 (test_bit(FLG_OWN_BUSY, &l2->flag)) { FreeSkb(skb); - enquiry_response(st); + if(PollFlag) enquiry_response(st); } else if (l2->vr == ns) { - l2->vr = (l2->vr + 1) % (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + (l2->vr)++; + if(test_bit(FLG_MOD128, &l2->flag)) + l2->vr %= 128; + else + l2->vr %= 8; test_and_clear_bit(FLG_REJEXC, &l2->flag); + if (PollFlag) enquiry_response(st); else @@ -1016,19 +1177,15 @@ l2_got_iframe(struct FsmInst *fi, int event, void *arg) } if (legalnr(st, nr)) { - setva(st, nr); if (!test_bit(FLG_PEER_BUSY, &st->l2.flag) && (fi->state == ST_L2_7)) { if (nr == st->l2.vs) { - if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) - FsmDelTimer(&st->l2.t200, 10); + stop_t200(st, 13); 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 if (nr != st->l2.va) + restart_t200(st, 14); } + setva(st, nr); } else { nrerrorrecovery(fi); return; @@ -1053,7 +1210,7 @@ l2_got_tei(struct FsmInst *fi, int event, void *arg) } else FsmChangeState(fi, ST_L2_4); if (skb_queue_len(&st->l2.ui_queue)) - l2_send_ui(st); + tx_ui(st); } static void @@ -1071,7 +1228,7 @@ l2_st5_tout_200(struct FsmInst *fi, int event, void *arg) 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); + st5_dl_release_l2l3(st); } else { st->l2.rc++; FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); @@ -1090,10 +1247,9 @@ l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) 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); 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); + lapb_dl_release_l2l3(st, CONFIRM); } else { st->l2.rc++; FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, @@ -1103,7 +1259,7 @@ l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) } static void -l2_st78_tout_200(struct FsmInst *fi, int event, void *arg) +l2_st7_tout_200(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; @@ -1113,12 +1269,28 @@ l2_st78_tout_200(struct FsmInst *fi, int event, void *arg) 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); + st->l2.rc = 0; + FsmChangeState(fi, ST_L2_8); + + transmit_enquiry(st); + st->l2.rc++; +} + +static void +l2_st8_tout_200(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + return; } + test_and_clear_bit(FLG_T200_RUN, &st->l2.flag); if (st->l2.rc == st->l2.N200) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'I'); establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); } else { transmit_enquiry(st); st->l2.rc++; @@ -1147,7 +1319,9 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) struct sk_buff *skb, *oskb; struct Layer2 *l2 = &st->l2; u_char header[MAX_HEADER_LEN]; - int p1, i; + int i; + int unsigned p1; + long flags; if (!cansend(st)) return; @@ -1156,14 +1330,17 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) if (!skb) return; - p1 = l2->vs - l2->va; - if (p1 < 0) - p1 += test_bit(FLG_MOD128, &l2->flag) ? 128 : 8; + save_flags(flags); + cli(); + if(test_bit(FLG_MOD128, &l2->flag)) + p1 = (l2->vs - l2->va) % 128; + else + p1 = (l2->vs - l2->va) % 8; p1 = (p1 + l2->sow) % l2->window; if (l2->windowar[p1]) { printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n", p1); - dev_kfree_skb(l2->windowar[p1]); + idev_kfree_skb(l2->windowar[p1], FREE_WRITE); } l2->windowar[p1] = skb_clone(skb, GFP_ATOMIC); @@ -1177,6 +1354,8 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) header[i++] = (l2->vr << 5) | (l2->vs << 1); l2->vs = (l2->vs + 1) % 8; } + restore_flags(flags); + p1 = skb->data - skb->head; if (p1 >= i) memcpy(skb_push(skb, i), header, i); @@ -1185,6 +1364,7 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) "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); @@ -1200,74 +1380,58 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) } static void -l2_got_st8_super(struct FsmInst *fi, int event, void *arg) +l2_st8_got_super(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - int PollFlag, nr, rsp, rnr = 0; + int PollFlag, rsp, rnr = 0; + unsigned int nr; struct Layer2 *l2 = &st->l2; rsp = *skb->data & 0x2; if (test_bit(FLG_ORIG, &l2->flag)) rsp = !rsp; + skb_pull(skb, l2addrsize(l2)); - if (IsRNR(skb->data, test_bit(FLG_MOD128, &l2->flag))) { - test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + if (IsRNR(skb->data, st)) { + set_peer_busy(l2); rnr = 1; } else - test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); + clear_peer_busy(l2); + 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; - } + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; } else { - 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; - } + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; } FreeSkb(skb); if (rsp && PollFlag) { 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 + restart_t200(st, 15); + } else { + stop_t200(st, 16); FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 5); + setva(st, nr); + } invoke_retransmission(st, nr); FsmChangeState(fi, ST_L2_7); if (skb_queue_len(&l2->i_queue) && cansend(st)) st->l2.l2l1(st, PH_PULL | REQUEST, NULL); - else if (fi->userint & LC_FLUSH_WAIT) { - fi->userint &= ~LC_FLUSH_WAIT; - st->l2.l2l3(st, DL_FLUSH | INDICATION, NULL); - } - } + } else + nrerrorrecovery(fi); } else { if (!rsp && PollFlag) enquiry_response(st); if (legalnr(st, nr)) { setva(st, nr); - } + } else + nrerrorrecovery(fi); } } @@ -1278,22 +1442,9 @@ l2_got_FRMR(struct FsmInst *fi, int event, void *arg) struct sk_buff *skb = arg; 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 - l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x %2x %2x", - skb->data[0], skb->data[1], skb->data[2], - skb->data[3], skb->data[4]); - } else { - if (skb->len < 3) - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); - else - l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x", - skb->data[0], skb->data[1], skb->data[2]); - } + if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */ - (IsUA(skb->data, 0) && (fi->state == ST_L2_7))) { + (IsUA(skb->data) && (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); @@ -1301,6 +1452,53 @@ l2_got_FRMR(struct FsmInst *fi, int event, void *arg) FreeSkb(skb); } +static void +l2_st24_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.ui_queue); + st->l2.tei = -1; + FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.ui_queue); + st->l2.tei = -1; + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.i_queue); + discard_queue(&st->l2.ui_queue); + freewin(st); + st->l2.tei = -1; + stop_t200(st, 17); + st5_dl_release_l2l3(st); + FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.ui_queue); + st->l2.tei = -1; + stop_t200(st, 18); + st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); + FsmChangeState(fi, ST_L2_1); +} + static void l2_tei_remove(struct FsmInst *fi, int event, void *arg) { @@ -1308,109 +1506,156 @@ l2_tei_remove(struct FsmInst *fi, int event, void *arg) discard_queue(&st->l2.i_queue); discard_queue(&st->l2.ui_queue); + freewin(st); st->l2.tei = -1; - if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) - FsmDelTimer(&st->l2.t200, 18); + stop_t200(st, 17); FsmDelTimer(&st->l2.t203, 19); - if (fi->state != ST_L2_4) - st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); FsmChangeState(fi, ST_L2_1); } static void -l2_persistant_da(struct FsmInst *fi, int event, void *arg) +l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - int rel = DL_RELEASE | INDICATION; - 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); + if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag)) + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); +} + +static void +l2_st5_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.i_queue); + discard_queue(&st->l2.ui_queue); + freewin(st); + stop_t200(st, 19); + st5_dl_release_l2l3(st); + FsmChangeState(fi, ST_L2_4); +} + +static void +l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.ui_queue); + stop_t200(st, 20); + st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); + FsmChangeState(fi, ST_L2_4); +} + +static void +l2_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.i_queue); + discard_queue(&st->l2.ui_queue); + freewin(st); + stop_t200(st, 19); 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; + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + FsmChangeState(fi, ST_L2_4); +} + +static void +l2_set_own_busy(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + if(!test_and_set_bit(FLG_OWN_BUSY, &st->l2.flag)) { + enquiry_cr(st, RNR, RSP, 0); + test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag); } - test_and_clear_bit(FLG_PEND_REL, &st->l2.flag); - test_and_clear_bit(FLG_L1_ACTIV, &st->l2.flag); +} + +static void +l2_clear_own_busy(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + if(!test_and_clear_bit(FLG_OWN_BUSY, &st->l2.flag)) { + enquiry_cr(st, RR, RSP, 0); + test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag); + } +} + +static void +l2_frame_error(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + st->ma.layer(st, MDL_ERROR | INDICATION, arg); +} + +static void +l2_frame_error_reest(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + st->ma.layer(st, MDL_ERROR | INDICATION, arg); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); } static struct FsmNode L2FnList[] HISAX_INITDATA = { - {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_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_1, EV_L2_DL_ESTABLISH_REQ, l2_mdl_assign}, + {ST_L2_2, EV_L2_DL_ESTABLISH_REQ, l2_go_st3}, + {ST_L2_4, EV_L2_DL_ESTABLISH_REQ, l2_establish}, + {ST_L2_5, EV_L2_DL_ESTABLISH_REQ, l2_discard_i_setl3}, + {ST_L2_7, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, + {ST_L2_8, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, + {ST_L2_4, EV_L2_DL_RELEASE_REQ, l2_release}, + {ST_L2_5, EV_L2_DL_RELEASE_REQ, l2_pend_rel}, + {ST_L2_7, EV_L2_DL_RELEASE_REQ, l2_disconnect}, + {ST_L2_8, EV_L2_DL_RELEASE_REQ, l2_disconnect}, + {ST_L2_5, EV_L2_DL_DATA, l2_feed_i_if_reest}, + {ST_L2_7, EV_L2_DL_DATA, l2_feed_i_pull}, {ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue}, - {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_DL_UNIT_DATA, l2_queue_ui_assign}, + {ST_L2_2, EV_L2_DL_UNIT_DATA, l2_queue_ui}, + {ST_L2_3, EV_L2_DL_UNIT_DATA, l2_queue_ui}, + {ST_L2_4, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {ST_L2_5, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {ST_L2_6, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {ST_L2_7, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {ST_L2_8, EV_L2_DL_UNIT_DATA, l2_send_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_2, EV_L2_MDL_ERROR, l2_st24_tei_remove}, + {ST_L2_3, EV_L2_MDL_ERROR, l2_st3_tei_remove}, + {ST_L2_4, EV_L2_MDL_REMOVE, l2_st24_tei_remove}, + {ST_L2_5, EV_L2_MDL_REMOVE, l2_st5_tei_remove}, + {ST_L2_6, EV_L2_MDL_REMOVE, l2_st6_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_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_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_got_dm}, - {ST_L2_8, EV_L2_DM, l2_got_dm}, + {ST_L2_4, EV_L2_SABME, l2_start_multi}, + {ST_L2_5, EV_L2_SABME, l2_send_UA}, + {ST_L2_6, EV_L2_SABME, l2_send_DM}, + {ST_L2_7, EV_L2_SABME, l2_restart_multi}, + {ST_L2_8, EV_L2_SABME, l2_restart_multi}, + {ST_L2_4, EV_L2_DISC, l2_send_DM}, + {ST_L2_5, EV_L2_DISC, l2_send_DM}, + {ST_L2_6, EV_L2_DISC, l2_send_UA}, + {ST_L2_7, EV_L2_DISC, l2_stop_multi}, + {ST_L2_8, EV_L2_DISC, l2_stop_multi}, + {ST_L2_4, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_5, EV_L2_UA, l2_connected}, + {ST_L2_6, EV_L2_UA, l2_released}, + {ST_L2_7, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_8, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_4, EV_L2_DM, l2_reestablish}, + {ST_L2_5, EV_L2_DM, l2_st5_dm_release}, + {ST_L2_6, EV_L2_DM, l2_st6_dm_release}, + {ST_L2_7, EV_L2_DM, l2_mdl_error_dm}, + {ST_L2_8, EV_L2_DM, l2_st8_mdl_error_dm}, {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}, @@ -1421,22 +1666,31 @@ static struct FsmNode L2FnList[] HISAX_INITDATA = {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_7, EV_L2_SUPER, l2_got_st7_super}, - {ST_L2_8, EV_L2_SUPER, l2_got_st8_super}, + {ST_L2_7, EV_L2_SUPER, l2_st7_got_super}, + {ST_L2_8, EV_L2_SUPER, l2_st8_got_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_st78_tout_200}, - {ST_L2_8, EV_L2_T200, l2_st78_tout_200}, + {ST_L2_7, EV_L2_T200, l2_st7_tout_200}, + {ST_L2_8, EV_L2_T200, l2_st8_tout_200}, {ST_L2_7, EV_L2_T203, l2_st7_tout_203}, {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_L2_SET_OWN_BUSY, l2_set_own_busy}, + {ST_L2_8, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, + {ST_L2_7, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, + {ST_L2_8, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, + {ST_L2_4, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_5, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_6, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_7, EV_L2_FRAME_ERROR, l2_frame_error_reest}, + {ST_L2_8, EV_L2_FRAME_ERROR, l2_frame_error_reest}, + {ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistant_da}, + {ST_L2_2, EV_L1_DEACTIVATE, l2_st24_tei_remove}, + {ST_L2_3, EV_L1_DEACTIVATE, l2_st3_tei_remove}, + {ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistant_da}, + {ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistant_da}, + {ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistant_da}, {ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da}, {ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da}, }; @@ -1449,6 +1703,7 @@ isdnl2_l1l2(struct PStack *st, int pr, void *arg) struct sk_buff *skb = arg; u_char *datap; int ret = 1, len; + int c = 0; switch (pr) { case (PH_DATA | INDICATION): @@ -1457,36 +1712,46 @@ isdnl2_l1l2(struct PStack *st, int pr, void *arg) if (skb->len > len) datap += len; else { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FsmEvent(&st->l2.l2m, EV_L2_FRAME_ERROR, (void *) 'N'); FreeSkb(skb); return; } - if (!(*datap & 1)) /* I-Frame */ - ret = FsmEvent(&st->l2.l2m, EV_L2_I, skb); - else if (IsSFrame(datap, test_bit(FLG_MOD128, &st->l2.flag))) - 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, test_bit(FLG_MOD128, &st->l2.flag))) - ret = FsmEvent(&st->l2.l2m, EV_L2_SABMX, skb); - else if (IsUA(datap, test_bit(FLG_MOD128, &st->l2.flag))) - ret = FsmEvent(&st->l2.l2m, EV_L2_UA, skb); - else if (IsDISC(datap, test_bit(FLG_MOD128, &st->l2.flag))) - ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, skb); - 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 { - ret = 1; - if ((st->l2.l2m.state == ST_L2_7) || - (st->l2.l2m.state == ST_L2_8)) - establishlink(&st->l2.l2m); - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); + if (!(*datap & 1)) { /* I-Frame */ + if(!(c = iframe_error(st, skb))) + ret = FsmEvent(&st->l2.l2m, EV_L2_I, skb); + } else if (IsSFrame(datap, st)) { /* S-Frame */ + if(!(c = super_error(st, skb))) + ret = FsmEvent(&st->l2.l2m, EV_L2_SUPER, skb); + } else if (IsUI(datap)) { + if(!(c = UI_error(st, skb))) + ret = FsmEvent(&st->l2.l2m, EV_L2_UI, skb); + } else if (IsSABME(datap, st)) { + if(!(c = unnum_error(st, skb, CMD))) + ret = FsmEvent(&st->l2.l2m, EV_L2_SABME, skb); + } else if (IsUA(datap)) { + if(!(c = unnum_error(st, skb, RSP))) + ret = FsmEvent(&st->l2.l2m, EV_L2_UA, skb); + } else if (IsDISC(datap)) { + if(!(c = unnum_error(st, skb, CMD))) + ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, skb); + } else if (IsDM(datap)) { + if(!(c = unnum_error(st, skb, RSP))) + ret = FsmEvent(&st->l2.l2m, EV_L2_DM, skb); + } else if (IsFRMR(datap)) { + if(!(c = FRMR_error(st,skb))) + ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, skb); + } else { + FsmEvent(&st->l2.l2m, EV_L2_FRAME_ERROR, (void *) 'L'); + FreeSkb(skb); + ret = 0; } - if (ret) { + if(c) { FreeSkb(skb); + FsmEvent(&st->l2.l2m, EV_L2_FRAME_ERROR, (void *) c); + ret = 0; } + if (ret) + FreeSkb(skb); break; case (PH_PULL | CONFIRM): FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg); @@ -1501,7 +1766,7 @@ isdnl2_l1l2(struct PStack *st, int pr, void *arg) 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); + FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH_REQ, arg); break; case (PH_DEACTIVATE | INDICATION): case (PH_DEACTIVATE | CONFIRM): @@ -1520,19 +1785,19 @@ isdnl2_l3l2(struct PStack *st, int pr, void *arg) switch (pr) { case (DL_DATA | REQUEST): if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg)) { - dev_kfree_skb((struct sk_buff *) arg); + idev_kfree_skb((struct sk_buff *) arg, FREE_READ); } break; case (DL_UNIT_DATA | REQUEST): if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg)) { - dev_kfree_skb((struct sk_buff *) arg); + idev_kfree_skb((struct sk_buff *) arg, FREE_READ); } break; 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); + FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH_REQ, arg); } } else { if (test_bit(FLG_LAPD, &st->l2.flag) || @@ -1546,10 +1811,7 @@ isdnl2_l3l2(struct PStack *st, int pr, void *arg) 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 (DL_FLUSH | REQUEST): - (&st->l2.l2m)->userint |= LC_FLUSH_WAIT; + FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE_REQ, arg); break; case (MDL_ASSIGN | REQUEST): FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg); @@ -1566,7 +1828,7 @@ isdnl2_l3l2(struct PStack *st, int pr, void *arg) void releasestack_isdnl2(struct PStack *st) { - FsmDelTimer(&st->l2.t200, 15); + FsmDelTimer(&st->l2.t200, 21); FsmDelTimer(&st->l2.t203, 16); discard_queue(&st->l2.i_queue); discard_queue(&st->l2.ui_queue); diff --git a/drivers/isdn/hisax/isdnl3.c b/drivers/isdn/hisax/isdnl3.c index 4511251e7f0c..458afe920984 100644 --- a/drivers/isdn/hisax/isdnl3.c +++ b/drivers/isdn/hisax/isdnl3.c @@ -1,4 +1,4 @@ -/* $Id: isdnl3.c,v 2.8 1998/11/15 23:55:04 keil Exp $ +/* $Id: isdnl3.c,v 2.10 1999/07/21 14:46:19 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -11,6 +11,12 @@ * Fritz Elfert * * $Log: isdnl3.c,v $ + * Revision 2.10 1999/07/21 14:46:19 keil + * changes from EICON certification + * + * Revision 2.9 1999/07/01 08:11:53 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.8 1998/11/15 23:55:04 keil * changes from 2.0 * @@ -62,7 +68,7 @@ #include "isdnl3.h" #include -const char *l3_revision = "$Revision: 2.8 $"; +const char *l3_revision = "$Revision: 2.10 $"; static struct Fsm l3fsm = @@ -71,6 +77,7 @@ struct Fsm l3fsm = enum { ST_L3_LC_REL, ST_L3_LC_ESTAB_WAIT, + ST_L3_LC_REL_DELAY, ST_L3_LC_REL_WAIT, ST_L3_LC_ESTAB, }; @@ -81,6 +88,7 @@ static char *strL3State[] = { "ST_L3_LC_REL", "ST_L3_LC_ESTAB_WAIT", + "ST_L3_LC_REL_DELAY", "ST_L3_LC_REL_WAIT", "ST_L3_LC_ESTAB", }; @@ -92,9 +100,10 @@ enum { EV_RELEASE_REQ, EV_RELEASE_CNF, EV_RELEASE_IND, + EV_TIMEOUT, }; -#define L3_EVENT_COUNT (EV_RELEASE_IND+1) +#define L3_EVENT_COUNT (EV_TIMEOUT+1) static char *strL3Event[] = { @@ -104,6 +113,7 @@ static char *strL3Event[] = "EV_RELEASE_REQ", "EV_RELEASE_CNF", "EV_RELEASE_IND", + "EV_TIMEOUT", }; static void @@ -142,7 +152,14 @@ findie(u_char * p, int size, u_char ie, int wanted_set) else { if (codeset == wanted_set) { if (*p == ie) - return (p); + { /* improved length check (Werner Cornelius) */ + if ((pend - p) < 2) + return(NULL); + if (*(p+1) > (pend - (p+2))) + return(NULL); + return (p); + } + if (*p > ie) return (NULL); } @@ -158,15 +175,15 @@ findie(u_char * p, int size, u_char ie, int wanted_set) int getcallref(u_char * p) { - int l, m = 1, cr = 0; + int l, cr = 0; + p++; /* prot discr */ + if (*p & 0xfe) /* wrong callref BRI only 1 octet*/ + return(-2); l = 0xf & *p++; /* callref length */ if (!l) /* dummy CallRef */ return(-1); - while (l--) { - cr += m * (*p++); - m *= 8; - } + cr = *p++; return (cr); } @@ -186,8 +203,9 @@ 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); + l3_debug(pc->st, "newstate cr %d %d --> %d", + pc->callref & 0x7F, + pc->state, state); pc->state = state; } @@ -242,6 +260,7 @@ l3_alloc_skb(int len) printk(KERN_WARNING "HiSax: No skb for D-channel\n"); return (NULL); } + SET_SKB_FREE(skb); skb_reserve(skb, MAX_HEADER_LEN); return (skb); } @@ -253,10 +272,17 @@ no_l3_proto(struct PStack *st, int pr, void *arg) HiSax_putstatus(st->l1.hardware, "L3", "no D protocol"); if (skb) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } } +static int +no_l3_proto_spec(struct PStack *st, isdn_ctrl *ic) +{ + printk(KERN_WARNING "HiSax: no specific protocol handler for proto %lu\n",ic->arg & 0xFF); + return(-1); +} + #ifdef CONFIG_HISAX_EURO extern void setstack_dss1(struct PStack *st); #endif @@ -300,7 +326,7 @@ struct l3_process np->next = p; } p->next = NULL; - p->debug = L3_DEB_WARN; + p->debug = st->l3.debug; p->callref = cr; p->state = 0; p->chan = NULL; @@ -323,8 +349,19 @@ release_l3_process(struct l3_process *p) StopAllL3Timer(p); if (pp) pp->next = np->next; - else - p->st->l3.proc = np->next; + else if (!(p->st->l3.proc = np->next) && + !test_bit(FLG_PTP, &p->st->l2.flag)) { + if (p->debug) + l3_debug(p->st, "release_l3_process: last process"); + if (!skb_queue_len(&p->st->l3.squeue)) { + if (p->debug) + l3_debug(p->st, "release_l3_process: release link"); + FsmEvent(&p->st->l3.l3m, EV_RELEASE_REQ, NULL); + } else { + if (p->debug) + l3_debug(p->st, "release_l3_process: not release link"); + } + } kfree(p); return; } @@ -335,6 +372,17 @@ release_l3_process(struct l3_process *p) l3_debug(p->st, "HiSax internal L3 error CR(%d) not in list", p->callref); }; +static void +l3ml3p(struct PStack *st, int pr) +{ + struct l3_process *p = st->l3.proc; + + while (p) { + st->l3.l3ml3(st, pr, p); + p = p->next; + } +} + void setstack_l3dc(struct PStack *st, struct Channel *chanp) { @@ -349,7 +397,9 @@ setstack_l3dc(struct PStack *st, struct Channel *chanp) st->l3.l3m.userdata = st; st->l3.l3m.userint = 0; st->l3.l3m.printdebug = l3m_debug; + FsmInitTimer(&st->l3.l3m, &st->l3.l3m_timer); strcpy(st->l3.debug_id, "L3DC "); + st->lli.l4l3_proto = no_l3_proto_spec; #ifdef CONFIG_HISAX_EURO if (st->protocol == ISDN_PTYPE_EURO) { @@ -369,10 +419,12 @@ setstack_l3dc(struct PStack *st, struct Channel *chanp) if (st->protocol == ISDN_PTYPE_LEASED) { st->lli.l4l3 = no_l3_proto; st->l2.l2l3 = no_l3_proto; + st->l3.l3ml3 = no_l3_proto; printk(KERN_INFO "HiSax: Leased line mode\n"); } else { st->lli.l4l3 = no_l3_proto; st->l2.l2l3 = no_l3_proto; + st->l3.l3ml3 = no_l3_proto; sprintf(tmp, "protocol %s not supported", (st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" : (st->protocol == ISDN_PTYPE_EURO) ? "euro" : @@ -398,6 +450,7 @@ releasestack_isdnl3(struct PStack *st) kfree(st->l3.global); st->l3.global = NULL; } + FsmDelTimer(&st->l3.l3m_timer, 54); discard_queue(&st->l3.squeue); } @@ -418,6 +471,8 @@ setstack_l3bc(struct PStack *st, struct Channel *chanp) st->lli.l4l3 = isdnl3_trans; } +#define DREL_TIMER_VALUE 40000 + static void lc_activate(struct FsmInst *fi, int event, void *arg) { @@ -432,12 +487,49 @@ lc_connect(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; + int dequeued = 0; FsmChangeState(fi, ST_L3_LC_ESTAB); while ((skb = skb_dequeue(&st->l3.squeue))) { st->l3.l3l2(st, DL_DATA | REQUEST, skb); + dequeued++; } - st->l3.l3l4(st, DL_ESTABLISH | INDICATION, NULL); + if ((!st->l3.proc) && dequeued) { + if (st->l3.debug) + l3_debug(st, "lc_connect: release link"); + FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL); + } else + l3ml3p(st, DL_ESTABLISH | INDICATION); +} + +static void +lc_connected(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + int dequeued = 0; + + FsmDelTimer(&st->l3.l3m_timer, 51); + FsmChangeState(fi, ST_L3_LC_ESTAB); + while ((skb = skb_dequeue(&st->l3.squeue))) { + st->l3.l3l2(st, DL_DATA | REQUEST, skb); + dequeued++; + } + if ((!st->l3.proc) && dequeued) { + if (st->l3.debug) + l3_debug(st, "lc_connected: release link"); + FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL); + } else + l3ml3p(st, DL_ESTABLISH | CONFIRM); +} + +static void +lc_start_delay(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L3_LC_REL_DELAY); + FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 50); } static void @@ -445,11 +537,15 @@ 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 + if (test_bit(FLG_L2BLOCK, &st->l2.flag)) { + if (st->l3.debug) + l3_debug(st, "lc_release_req: l2 blocked"); + /* restart release timer */ + FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 51); + } else { FsmChangeState(fi, ST_L3_LC_REL_WAIT); - st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL); + st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL); + } } static void @@ -457,23 +553,38 @@ lc_release_ind(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; + FsmDelTimer(&st->l3.l3m_timer, 52); FsmChangeState(fi, ST_L3_LC_REL); discard_queue(&st->l3.squeue); - st->l3.l3l4(st, DL_RELEASE | INDICATION, NULL); + l3ml3p(st, DL_RELEASE | INDICATION); } +static void +lc_release_cnf(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L3_LC_REL); + discard_queue(&st->l3.squeue); + l3ml3p(st, DL_RELEASE | CONFIRM); +} + + /* *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_ESTABLISH_CNF, lc_connected}, + {ST_L3_LC_ESTAB_WAIT, EV_RELEASE_REQ, lc_start_delay}, {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_ESTAB, EV_RELEASE_REQ, lc_start_delay}, + {ST_L3_LC_REL_DELAY, EV_RELEASE_IND, lc_release_ind}, + {ST_L3_LC_REL_DELAY, EV_ESTABLISH_REQ, lc_connected}, + {ST_L3_LC_REL_DELAY, EV_TIMEOUT, lc_release_req}, + {ST_L3_LC_REL_WAIT, EV_RELEASE_CNF, lc_release_cnf}, {ST_L3_LC_REL_WAIT, EV_ESTABLISH_REQ, lc_activate}, }; /* *INDENT-ON* */ diff --git a/drivers/isdn/hisax/isdnl3.h b/drivers/isdn/hisax/isdnl3.h index a0e518f8cb2c..c7050f5f13f9 100644 --- a/drivers/isdn/hisax/isdnl3.h +++ b/drivers/isdn/hisax/isdnl3.h @@ -1,6 +1,12 @@ -/* $Id: isdnl3.h,v 2.3 1998/11/15 23:55:06 keil Exp $ +/* $Id: isdnl3.h,v 2.5 1999/07/25 16:18:32 keil Exp $ * $Log: isdnl3.h,v $ + * Revision 2.5 1999/07/25 16:18:32 keil + * Fix Suspend/Resume + * + * Revision 2.4 1999/07/01 08:11:54 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.3 1998/11/15 23:55:06 keil * changes from 2.0 * @@ -31,14 +37,16 @@ */ #define SBIT(state) (1< +#include "hisax.h" +#include "isac.h" +#include "isar.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +static const char *ISurf_revision = "$Revision: 1.3 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ISURF_ISAR_RESET 1 +#define ISURF_ISAC_RESET 2 +#define ISURF_ISAR_EA 4 +#define ISURF_ARCOFI_RESET 8 +#define ISURF_RESET (ISURF_ISAR_RESET | ISURF_ISAC_RESET | ISURF_ARCOFI_RESET) + +#define ISURF_ISAR_OFFSET 0 +#define ISURF_ISAC_OFFSET 0x100 + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readb(cs->hw.isurf.isac + offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writeb(value, cs->hw.isurf.isac + offset); mb(); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + register int i; + for (i = 0; i < size; i++) + data[i] = readb(cs->hw.isurf.isac); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + register int i; + for (i = 0; i < size; i++){ + writeb(data[i], cs->hw.isurf.isac);mb(); + } +} + +/* ISAR access routines + * mode = 0 access with IRQ on + * mode = 1 access with IRQ off + * mode = 2 access with IRQ off and using last offset + */ + +static u_char +ReadISAR(struct IsdnCardState *cs, int mode, u_char offset) +{ + return(readb(cs->hw.isurf.isar + offset)); +} + +static void +WriteISAR(struct IsdnCardState *cs, int mode, u_char offset, u_char value) +{ + writeb(value, cs->hw.isurf.isar + offset);mb(); +} + +static void +isurf_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val; + int cnt = 5; + + if (!cs) { + printk(KERN_WARNING "ISurf: Spurious interrupt!\n"); + return; + } + + val = readb(cs->hw.isurf.isar + ISAR_IRQBIT); + Start_ISAR: + if (val & ISAR_IRQSTA) + isar_int_main(cs); + val = readb(cs->hw.isurf.isac + ISAC_ISTA); + Start_ISAC: + if (val) + isac_interrupt(cs, val); + val = readb(cs->hw.isurf.isar + ISAR_IRQBIT); + if ((val & ISAR_IRQSTA) && --cnt) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "ISAR IntStat after IntRoutine"); + goto Start_ISAR; + } + val = readb(cs->hw.isurf.isac + ISAC_ISTA); + if (val && --cnt) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (!cnt) + printk(KERN_WARNING "ISurf IRQ LOOP\n"); + + writeb(0, cs->hw.isurf.isar + ISAR_IRQBIT); mb(); + writeb(0xFF, cs->hw.isurf.isac + ISAC_MASK);mb(); + writeb(0, cs->hw.isurf.isac + ISAC_MASK);mb(); + writeb(ISAR_IRQMSK, cs->hw.isurf.isar + ISAR_IRQBIT); mb(); +} + +void +release_io_isurf(struct IsdnCardState *cs) +{ + release_region(cs->hw.isurf.reset, 1); +} + +static void +reset_isurf(struct IsdnCardState *cs, u_char chips) +{ + long flags; + + printk(KERN_INFO "ISurf: resetting card\n"); + + byteout(cs->hw.isurf.reset, chips); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10*HZ)/1000); + byteout(cs->hw.isurf.reset, ISURF_ISAR_EA); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10*HZ)/1000); + restore_flags(flags); +} + +static int +ISurf_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_isurf(cs, ISURF_RESET); + return(0); + case CARD_RELEASE: + release_io_isurf(cs); + return(0); + case CARD_INIT: + clear_pending_isac_ints(cs); + writeb(0, cs->hw.isurf.isar+ISAR_IRQBIT);mb(); + initisac(cs); + initisar(cs); + /* Reenable ISAC IRQ */ + cs->writeisac(cs, ISAC_MASK, 0); + /* RESET Receiver and Transmitter */ + cs->writeisac(cs, ISAC_CMDR, 0x41); + return(0); + case CARD_TEST: + return(0); + case CARD_LOAD_FIRM: + if (isar_load_firmware(cs, arg)) + return(1); + ll_run(cs); + reset_isurf(cs, ISURF_ISAR_EA | ISURF_ISAC_RESET | + ISURF_ARCOFI_RESET); + initisac(cs); + cs->writeisac(cs, ISAC_MASK, 0); + cs->writeisac(cs, ISAC_CMDR, 0x41); + return(0); + } + return(0); +} + +__initfunc(int +setup_isurf(struct IsdnCard *card)) +{ + int ver; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, ISurf_revision); + printk(KERN_INFO "HiSax: ISurf driver Rev. %s\n", HiSax_getrev(tmp)); + + if (cs->typ != ISDN_CTYPE_ISURF) + return(0); + if (card->para[1] && card->para[2]) { + cs->hw.isurf.reset = card->para[1]; + cs->hw.isurf.isar = card->para[2] + ISURF_ISAR_OFFSET; + cs->hw.isurf.isac = card->para[2] + ISURF_ISAC_OFFSET; + cs->irq = card->para[0]; + } else { + printk(KERN_WARNING "HiSax: %s port/mem not set\n", + CardType[card->typ]); + return (0); + } + if (check_region(cs->hw.isurf.reset, 1)) { + printk(KERN_WARNING + "HiSax: %s config port %x already in use\n", + CardType[card->typ], + cs->hw.isurf.reset); + return (0); + } else { + request_region(cs->hw.isurf.reset, 1, "isurf isdn"); + } + + printk(KERN_INFO + "ISurf: defined at 0x%x 0x%x IRQ %d\n", + cs->hw.isurf.reset, + cs->hw.isurf.isar, + cs->irq); + + cs->cardmsg = &ISurf_card_msg; + cs->irq_func = &isurf_interrupt; + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->bcs[0].hw.isar.reg = &cs->hw.isurf.isar_r; + cs->bcs[1].hw.isar.reg = &cs->hw.isurf.isar_r; + reset_isurf(cs, ISURF_RESET); + test_and_set_bit(HW_ISAR, &cs->HW_Flags); + ISACVersion(cs, "ISurf:"); + cs->BC_Read_Reg = &ReadISAR; + cs->BC_Write_Reg = &WriteISAR; + cs->BC_Send_Data = &isar_fill_fifo; + ver = ISARVersion(cs, "ISurf:"); + if (ver < 0) { + printk(KERN_WARNING + "ISurf: wrong ISAR version (ret = %d)\n", ver); + release_io_isurf(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/ix1_micro.c b/drivers/isdn/hisax/ix1_micro.c index 732e2b8a71b3..cb94d5bb392c 100644 --- a/drivers/isdn/hisax/ix1_micro.c +++ b/drivers/isdn/hisax/ix1_micro.c @@ -1,4 +1,4 @@ -/* $Id: ix1_micro.c,v 2.7 1998/04/15 16:44:31 keil Exp $ +/* $Id: ix1_micro.c,v 2.8 1999/07/12 21:05:19 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,10 @@ * Beat Doebeli * * $Log: ix1_micro.c,v $ + * Revision 2.8 1999/07/12 21:05:19 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * * Revision 2.7 1998/04/15 16:44:31 keil * new init code * @@ -84,7 +88,7 @@ #include "isdnl1.h" extern const char *CardType[]; -const char *ix1_revision = "$Revision: 2.7 $"; +const char *ix1_revision = "$Revision: 2.8 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -199,7 +203,7 @@ static void ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; if (!cs) { printk(KERN_WARNING "IX1: Spurious interrupt!\n"); @@ -207,16 +211,12 @@ ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs) } val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40); Start_HSCX: - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA); Start_ISAC: - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40); if (val) { if (cs->debug & L1_DEB_HSCX) @@ -229,16 +229,12 @@ ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs) debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } - if (stat & 1) { - 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) { - 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); - } + 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.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0); + 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); } void @@ -276,9 +272,6 @@ ix1_card_msg(struct IsdnCardState *cs, int mt, void *arg) 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); @@ -289,8 +282,8 @@ ix1_card_msg(struct IsdnCardState *cs, int mt, void *arg) } -int __init -setup_ix1micro(struct IsdnCard *card) +__initfunc(int +setup_ix1micro(struct IsdnCard *card)) { struct IsdnCardState *cs = card->cs; char tmp[64]; @@ -331,6 +324,7 @@ setup_ix1micro(struct IsdnCard *card) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &ix1_card_msg; + cs->irq_func = &ix1micro_interrupt; ISACVersion(cs, "ix1-Micro:"); if (HscxVersion(cs, "ix1-Micro:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/jade.c b/drivers/isdn/hisax/jade.c new file mode 100644 index 000000000000..5699f28a76dd --- /dev/null +++ b/drivers/isdn/hisax/jade.c @@ -0,0 +1,326 @@ +/* $Id: jade.c,v 1.2 1999/07/01 08:07:57 keil Exp $ + * + * jade.c JADE stuff (derived from original hscx.c) + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * $Log: jade.c,v $ + * Revision 1.2 1999/07/01 08:07:57 keil + * Initial version + * + * + */ + + +#define __NO_VERSION__ +#include "hisax.h" +#include "hscx.h" +#include "jade.h" +#include "isdnl1.h" +#include + + +HISAX_INITFUNC(int +JadeVersion(struct IsdnCardState *cs, char *s)) +{ + int ver,i; + int to = 50; + cs->BC_Write_Reg(cs, -1, 0x50, 0x19); + i=0; + while (to) { + udelay(1); + ver = cs->BC_Read_Reg(cs, -1, 0x60); + to--; + if (ver) + break; + if (!to) { + printk(KERN_INFO "%s JADE version not obtainable\n", s); + return (0); + } + } + /* Wait for the JADE */ + udelay(10); + /* Read version */ + ver = cs->BC_Read_Reg(cs, -1, 0x60); + printk(KERN_INFO "%s JADE version: %d\n", s, ver); + return (1); +} + +/* Write to indirect accessible jade register set */ +static void +jade_write_indirect(struct IsdnCardState *cs, u_char reg, u_char value) +{ + int to = 50; + long flags; + u_char ret; + save_flags(flags); + cli(); + /* Write the data */ + cs->BC_Write_Reg(cs, -1, COMM_JADE+1, value); + /* Say JADE we wanna write indirect reg 'reg' */ + cs->BC_Write_Reg(cs, -1, COMM_JADE, reg); + to = 50; + /* Wait for RDY goes high */ + while (to) { + udelay(1); + ret = cs->BC_Read_Reg(cs, -1, COMM_JADE); + to--; + if (ret & 1) + /* Got acknowledge */ + break; + if (!to) { + restore_flags(flags); + printk(KERN_INFO "Can not see ready bit from JADE DSP (reg=0x%X, value=0x%X)\n", reg, value); + return; + } + } + restore_flags(flags); +} + + + +void +modejade(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + int jade = bcs->hw.hscx.hscx; + + if (cs->debug & L1_DEB_HSCX) { + char tmp[40]; + sprintf(tmp, "jade %c mode %d ichan %d", + 'A' + jade, mode, bc); + debugl1(cs, tmp); + } + bcs->mode = mode; + bcs->channel = bc; + + cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (mode == L1_MODE_TRANS ? jadeMODE_TMO:0x00)); + cs->BC_Write_Reg(cs, jade, jade_HDLC_CCR0, (jadeCCR0_PU|jadeCCR0_ITF)); + cs->BC_Write_Reg(cs, jade, jade_HDLC_CCR1, 0x00); + + jade_write_indirect(cs, jade_HDLC1SERRXPATH, 0x08); + jade_write_indirect(cs, jade_HDLC2SERRXPATH, 0x08); + jade_write_indirect(cs, jade_HDLC1SERTXPATH, 0x00); + jade_write_indirect(cs, jade_HDLC2SERTXPATH, 0x00); + + cs->BC_Write_Reg(cs, jade, jade_HDLC_XCCR, 0x07); + cs->BC_Write_Reg(cs, jade, jade_HDLC_RCCR, 0x07); + + if (bc == 0) { + cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAX, 0x00); + cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAR, 0x00); + } else { + cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAX, 0x04); + cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAR, 0x04); + } + switch (mode) { + case (L1_MODE_NULL): + cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, jadeMODE_TMO); + break; + case (L1_MODE_TRANS): + cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (jadeMODE_TMO|jadeMODE_RAC|jadeMODE_XAC)); + break; + case (L1_MODE_HDLC): + cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (jadeMODE_RAC|jadeMODE_XAC)); + break; + } + if (mode) { + cs->BC_Write_Reg(cs, jade, jade_HDLC_RCMD, (jadeRCMD_RRES|jadeRCMD_RMC)); + cs->BC_Write_Reg(cs, jade, jade_HDLC_XCMD, jadeXCMD_XRES); + /* Unmask ints */ + cs->BC_Write_Reg(cs, jade, jade_HDLC_IMR, 0xF8); + } + else + /* Mask ints */ + cs->BC_Write_Reg(cs, jade, jade_HDLC_IMR, 0x00); +} + +void +jade_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void +jade_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->tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->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->tx_skb) { + printk(KERN_WARNING "jade_l2l1: this shouldn't happen\n"); + break; + } + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->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->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); + modejade(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); + modejade(st->l1.bcs, 0, st->l1.bc); + st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + break; + } +} + +void +close_jadestate(struct BCState *bcs) +{ + modejade(bcs, 0, bcs->channel); + 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; + } + if (bcs->blog) { + kfree(bcs->blog); + bcs->blog = NULL; + } + discard_queue(&bcs->rqueue); + discard_queue(&bcs->squeue); + if (bcs->tx_skb) { + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +static int +open_jadestate(struct IsdnCardState *cs, struct BCState *bcs) +{ + 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"); + test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); + return (1); + } + if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for bcs->blog\n"); + test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); + kfree(bcs->hw.hscx.rcvbuf); + bcs->hw.hscx.rcvbuf = NULL; + return (2); + } + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->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_jade(struct PStack *st, struct BCState *bcs) +{ + bcs->channel = st->l1.bc; + if (open_jadestate(st->l1.hardware, bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = jade_l2l1; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + +HISAX_INITFUNC(void +clear_pending_jade_ints(struct IsdnCardState *cs)) +{ + int val; + char tmp[64]; + + cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR, 0x00); + cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0x00); + + val = cs->BC_Read_Reg(cs, 1, jade_HDLC_ISR); + sprintf(tmp, "jade B ISTA %x", val); + debugl1(cs, tmp); + val = cs->BC_Read_Reg(cs, 0, jade_HDLC_ISR); + sprintf(tmp, "jade A ISTA %x", val); + debugl1(cs, tmp); + val = cs->BC_Read_Reg(cs, 1, jade_HDLC_STAR); + sprintf(tmp, "jade B STAR %x", val); + debugl1(cs, tmp); + val = cs->BC_Read_Reg(cs, 0, jade_HDLC_STAR); + sprintf(tmp, "jade A STAR %x", val); + debugl1(cs, tmp); + /* Unmask ints */ + cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR, 0xF8); + cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0xF8); +} + +HISAX_INITFUNC(void +initjade(struct IsdnCardState *cs)) +{ + cs->bcs[0].BC_SetStack = setstack_jade; + cs->bcs[1].BC_SetStack = setstack_jade; + cs->bcs[0].BC_Close = close_jadestate; + cs->bcs[1].BC_Close = close_jadestate; + cs->bcs[0].hw.hscx.hscx = 0; + cs->bcs[1].hw.hscx.hscx = 1; + + /* Stop DSP audio tx/rx */ + jade_write_indirect(cs, 0x11, 0x0f); + jade_write_indirect(cs, 0x17, 0x2f); + + /* Transparent Mode, RxTx inactive, No Test, No RFS/TFS */ + cs->BC_Write_Reg(cs, 0, jade_HDLC_MODE, jadeMODE_TMO); + cs->BC_Write_Reg(cs, 1, jade_HDLC_MODE, jadeMODE_TMO); + /* Power down, 1-Idle, RxTx least significant bit first */ + cs->BC_Write_Reg(cs, 0, jade_HDLC_CCR0, 0x00); + cs->BC_Write_Reg(cs, 1, jade_HDLC_CCR0, 0x00); + /* Mask all interrupts */ + cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR, 0x00); + cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0x00); + /* Setup host access to hdlc controller */ + jade_write_indirect(cs, jade_HDLCCNTRACCESS, (jadeINDIRECT_HAH1|jadeINDIRECT_HAH2)); + /* Unmask HDLC int (don´t forget DSP int later on)*/ + cs->BC_Write_Reg(cs, -1,jade_INT, (jadeINT_HDLC1|jadeINT_HDLC2)); + + /* once again TRANSPARENT */ + modejade(cs->bcs, 0, 0); + modejade(cs->bcs + 1, 0, 0); +} + diff --git a/drivers/isdn/hisax/jade.h b/drivers/isdn/hisax/jade.h new file mode 100644 index 000000000000..bcbe6b29181a --- /dev/null +++ b/drivers/isdn/hisax/jade.h @@ -0,0 +1,137 @@ +/* $Id: jade.h,v 1.2 1999/07/01 08:07:58 keil Exp $ + * jade.h JADE specific defines + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * + * $Log: jade.h,v $ + * Revision 1.2 1999/07/01 08:07:58 keil + * Initial version + * + * + */ + +/* All Registers original Siemens Spec */ +#ifndef __JADE_H__ +#define __JADE_H__ + +/* Special registers for access to indirect accessible JADE regs */ +#define DIRECT_IO_JADE 0x0000 /* Jade direct io access area */ +#define COMM_JADE 0x0040 /* Jade communication area */ + +/********************************************************************/ +/* JADE-HDLC registers */ +/********************************************************************/ +#define jade_HDLC_RFIFO 0x00 /* R */ +#define jade_HDLC_XFIFO 0x00 /* W */ + +#define jade_HDLC_STAR 0x20 /* R */ + #define jadeSTAR_XDOV 0x80 + #define jadeSTAR_XFW 0x40 /* Does not work*/ + #define jadeSTAR_XCEC 0x20 + #define jadeSTAR_RCEC 0x10 + #define jadeSTAR_BSY 0x08 + #define jadeSTAR_RNA 0x04 + #define jadeSTAR_STR 0x02 + #define jadeSTAR_STX 0x01 + +#define jade_HDLC_XCMD 0x20 /* W */ + #define jadeXCMD_XF 0x80 + #define jadeXCMD_XME 0x40 + #define jadeXCMD_XRES 0x20 + #define jadeXCMD_STX 0x01 + +#define jade_HDLC_RSTA 0x21 /* R */ + #define jadeRSTA_VFR 0x80 + #define jadeRSTA_RDO 0x40 + #define jadeRSTA_CRC 0x20 + #define jadeRSTA_RAB 0x10 + #define jadeRSTA_MASK 0xF0 + +#define jade_HDLC_MODE 0x22 /* RW*/ + #define jadeMODE_TMO 0x80 + #define jadeMODE_RAC 0x40 + #define jadeMODE_XAC 0x20 + #define jadeMODE_TLP 0x10 + #define jadeMODE_ERFS 0x02 + #define jadeMODE_ETFS 0x01 + +#define jade_HDLC_RBCH 0x24 /* R */ + +#define jade_HDLC_RBCL 0x25 /* R */ +#define jade_HDLC_RCMD 0x25 /* W */ + #define jadeRCMD_RMC 0x80 + #define jadeRCMD_RRES 0x40 + #define jadeRCMD_RMD 0x20 + #define jadeRCMD_STR 0x02 + +#define jade_HDLC_CCR0 0x26 /* RW*/ + #define jadeCCR0_PU 0x80 + #define jadeCCR0_ITF 0x40 + #define jadeCCR0_C32 0x20 + #define jadeCCR0_CRL 0x10 + #define jadeCCR0_RCRC 0x08 + #define jadeCCR0_XCRC 0x04 + #define jadeCCR0_RMSB 0x02 + #define jadeCCR0_XMSB 0x01 + +#define jade_HDLC_CCR1 0x27 /* RW*/ + #define jadeCCR1_RCS0 0x80 + #define jadeCCR1_RCONT 0x40 + #define jadeCCR1_RFDIS 0x20 + #define jadeCCR1_XCS0 0x10 + #define jadeCCR1_XCONT 0x08 + #define jadeCCR1_XFDIS 0x04 + +#define jade_HDLC_TSAR 0x28 /* RW*/ +#define jade_HDLC_TSAX 0x29 /* RW*/ +#define jade_HDLC_RCCR 0x2A /* RW*/ +#define jade_HDLC_XCCR 0x2B /* RW*/ + +#define jade_HDLC_ISR 0x2C /* R */ +#define jade_HDLC_IMR 0x2C /* W */ + #define jadeISR_RME 0x80 + #define jadeISR_RPF 0x40 + #define jadeISR_RFO 0x20 + #define jadeISR_XPR 0x10 + #define jadeISR_XDU 0x08 + #define jadeISR_ALLS 0x04 + +#define jade_INT 0x75 + #define jadeINT_HDLC1 0x02 + #define jadeINT_HDLC2 0x01 + #define jadeINT_DSP 0x04 +#define jade_INTR 0x70 + +/********************************************************************/ +/* Indirect accessible JADE registers of common interest */ +/********************************************************************/ +#define jade_CHIPVERSIONNR 0x00 /* Does not work*/ + +#define jade_HDLCCNTRACCESS 0x10 + #define jadeINDIRECT_HAH1 0x02 + #define jadeINDIRECT_HAH2 0x01 + +#define jade_HDLC1SERRXPATH 0x1D +#define jade_HDLC1SERTXPATH 0x1E +#define jade_HDLC2SERRXPATH 0x1F +#define jade_HDLC2SERTXPATH 0x20 + #define jadeINDIRECT_SLIN1 0x10 + #define jadeINDIRECT_SLIN0 0x08 + #define jadeINDIRECT_LMOD1 0x04 + #define jadeINDIRECT_LMOD0 0x02 + #define jadeINDIRECT_HHR 0x01 + #define jadeINDIRECT_HHX 0x01 + +#define jade_RXAUDIOCH1CFG 0x11 +#define jade_RXAUDIOCH2CFG 0x14 +#define jade_TXAUDIOCH1CFG 0x17 +#define jade_TXAUDIOCH2CFG 0x1A + +extern int JadeVersion(struct IsdnCardState *cs, char *s); +extern void jade_sched_event(struct BCState *bcs, int event); +extern void modejade(struct BCState *bcs, int mode, int bc); +extern void clear_pending_jade_ints(struct IsdnCardState *cs); +extern void initjade(struct IsdnCardState *cs); + +#endif /* __JADE_H__ */ diff --git a/drivers/isdn/hisax/jade_irq.c b/drivers/isdn/hisax/jade_irq.c new file mode 100644 index 000000000000..9cfcc7d6db91 --- /dev/null +++ b/drivers/isdn/hisax/jade_irq.c @@ -0,0 +1,247 @@ +/* $Id: jade_irq.c,v 1.2 1999/07/01 08:07:59 keil Exp $ + * + * jade_irq.c Low level JADE IRQ stuff (derived from original hscx_irq.c) + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * $Log: jade_irq.c,v $ + * Revision 1.2 1999/07/01 08:07:59 keil + * Initial version + * + * + */ + +static inline void +waitforCEC(struct IsdnCardState *cs, int jade, int reg) +{ + int to = 50; + int mask = (reg == jade_HDLC_XCMD ? jadeSTAR_XCEC : jadeSTAR_RCEC); + while ((READJADE(cs, jade, jade_HDLC_STAR) & mask) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: waitforCEC (jade) timeout\n"); +} + + +static inline void +waitforXFW(struct IsdnCardState *cs, int jade) +{ + /* Does not work on older jade versions, don't care */ +} + +static inline void +WriteJADECMDR(struct IsdnCardState *cs, int jade, int reg, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + waitforCEC(cs, jade, reg); + WRITEJADE(cs, jade, reg, data); + restore_flags(flags); +} + + + +static void +jade_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, "jade_empty_fifo"); + + if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "jade_empty_fifo: incoming packet too large"); + WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_RCMD, jadeRCMD_RMC); + bcs->hw.hscx.rcvidx = 0; + return; + } + ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx; + bcs->hw.hscx.rcvidx += count; + save_flags(flags); + cli(); + READJADEFIFO(cs, bcs->hw.hscx.hscx, ptr, count); + WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_RCMD, jadeRCMD_RMC); + restore_flags(flags); + if (cs->debug & L1_DEB_HSCX_FIFO) { + char *t = bcs->blog; + + t += sprintf(t, "jade_empty_fifo %c cnt %d", + bcs->hw.hscx.hscx ? 'B' : 'A', count); + QuickHex(t, ptr, count); + debugl1(cs, bcs->blog); + } +} + +static void +jade_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + int more, count; + int fifo_size = 32; + u_char *ptr; + long flags; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "jade_fill_fifo"); + + if (!bcs->tx_skb) + return; + if (bcs->tx_skb->len <= 0) + return; + + more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0; + if (bcs->tx_skb->len > fifo_size) { + more = !0; + count = fifo_size; + } else + count = bcs->tx_skb->len; + + waitforXFW(cs, bcs->hw.hscx.hscx); + save_flags(flags); + cli(); + ptr = bcs->tx_skb->data; + skb_pull(bcs->tx_skb, count); + bcs->tx_cnt -= count; + bcs->hw.hscx.count += count; + WRITEJADEFIFO(cs, bcs->hw.hscx.hscx, ptr, count); + WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_XCMD, more ? jadeXCMD_XF : (jadeXCMD_XF|jadeXCMD_XME)); + restore_flags(flags); + if (cs->debug & L1_DEB_HSCX_FIFO) { + char *t = bcs->blog; + + t += sprintf(t, "jade_fill_fifo %c cnt %d", + bcs->hw.hscx.hscx ? 'B' : 'A', count); + QuickHex(t, ptr, count); + debugl1(cs, bcs->blog); + } +} + + +static inline void +jade_interrupt(struct IsdnCardState *cs, u_char val, u_char jade) +{ + u_char r; + struct BCState *bcs = cs->bcs + jade; + struct sk_buff *skb; + int fifo_size = 32; + int count; + int i_jade = (int) jade; /* To satisfy the compiler */ + + if (!test_bit(BC_FLG_INIT, &bcs->Flag)) + return; + + if (val & 0x80) { /* RME */ + r = READJADE(cs, i_jade, jade_HDLC_RSTA); + if ((r & 0xf0) != 0xa0) { + if (!(r & 0x80)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "JADE %s invalid frame", (jade ? "B":"A")); + if ((r & 0x40) && bcs->mode) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "JADE %c RDO mode=%d", 'A'+jade, bcs->mode); + if (!(r & 0x20)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "JADE %c CRC error", 'A'+jade); + WriteJADECMDR(cs, jade, jade_HDLC_RCMD, jadeRCMD_RMC); + } else { + count = READJADE(cs, i_jade, jade_HDLC_RBCL) & 0x1F; + if (count == 0) + count = fifo_size; + jade_empty_fifo(bcs, count); + if ((count = bcs->hw.hscx.rcvidx - 1) > 0) { + if (cs->debug & L1_DEB_HSCX_FIFO) + debugl1(cs, "HX Frame %d", count); + if (!(skb = dev_alloc_skb(count))) + printk(KERN_WARNING "JADE %s receive out of memory\n", (jade ? "B":"A")); + 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; + jade_sched_event(bcs, B_RCVBUFREADY); + } + if (val & 0x40) { /* RPF */ + jade_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; + jade_sched_event(bcs, B_RCVBUFREADY); + } + } + if (val & 0x10) { /* XPR */ + if (bcs->tx_skb) { + if (bcs->tx_skb->len) { + jade_fill_fifo(bcs); + return; + } else { + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->hw.hscx.count = 0; + bcs->tx_skb = NULL; + } + } + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + bcs->hw.hscx.count = 0; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + jade_fill_fifo(bcs); + } else { + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + jade_sched_event(bcs, B_XMTBUFREADY); + } + } +} + +static inline void +jade_int_main(struct IsdnCardState *cs, u_char val, int jade) +{ + struct BCState *bcs; + bcs = cs->bcs + jade; + + if (val & jadeISR_RFO) { + /* handled with RDO */ + val &= ~jadeISR_RFO; + } + if (val & jadeISR_XDU) { + /* relevant in HDLC mode only */ + /* don't reset XPR here */ + if (bcs->mode == 1) + jade_fill_fifo(bcs); + else { + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame. + */ + if (bcs->tx_skb) { + skb_push(bcs->tx_skb, bcs->hw.hscx.count); + bcs->tx_cnt += bcs->hw.hscx.count; + bcs->hw.hscx.count = 0; + } + WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_XCMD, jadeXCMD_XRES); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "JADE %c EXIR %x Lost TX", 'A'+jade, val); + } + } + if (val & (jadeISR_RME|jadeISR_RPF|jadeISR_XPR)) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "JADE %c interrupt %x", 'A'+jade, val); + jade_interrupt(cs, val, jade); + } +} diff --git a/drivers/isdn/hisax/l3_1tr6.c b/drivers/isdn/hisax/l3_1tr6.c index db6ceca7615b..ba8d95dbdb77 100644 --- a/drivers/isdn/hisax/l3_1tr6.c +++ b/drivers/isdn/hisax/l3_1tr6.c @@ -1,4 +1,4 @@ -/* $Id: l3_1tr6.c,v 2.8 1998/11/15 23:55:08 keil Exp $ +/* $Id: l3_1tr6.c,v 2.9 1999/07/01 08:11:55 keil Exp $ * German 1TR6 D-channel protocol * @@ -10,6 +10,9 @@ * * * $Log: l3_1tr6.c,v $ + * Revision 2.9 1999/07/01 08:11:55 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.8 1998/11/15 23:55:08 keil * changes from 2.0 * @@ -56,7 +59,7 @@ #include extern char *HiSax_getrev(const char *revision); -const char *l3_1tr6_revision = "$Revision: 2.8 $"; +const char *l3_1tr6_revision = "$Revision: 2.9 $"; #define MsgHead(ptr, cref, mty, dis) \ *ptr++ = dis; \ @@ -91,14 +94,14 @@ l3_1tr6_invalid(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); l3_1tr6_release_req(pc, 0, NULL); } static void l3_1tr6_error(struct l3_process *pc, u_char *msg, struct sk_buff *skb) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); if (pc->st->l3.debug & L3_DEB_WARN) l3_debug(pc->st, msg); l3_1tr6_release_req(pc, 0, NULL); @@ -248,7 +251,7 @@ l3_1tr6_setup(struct l3_process *pc, u_char pr, void *arg) if ((FAC_SPV == p[3]) || (FAC_Activate == p[3])) pc->para.spv = 1; } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); /* Signal all services, linklevel takes care of Service-Indicator */ if (bcfound) { @@ -287,7 +290,7 @@ l3_1tr6_setup_ack(struct l3_process *pc, u_char pr, void *arg) l3_1tr6_error(pc, "missing setup_ack WE0_chanID", skb); return; } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); L3AddTimer(&pc->timer, T304, CC_T304); pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc); } @@ -318,7 +321,7 @@ l3_1tr6_call_sent(struct l3_process *pc, u_char pr, void *arg) l3_1tr6_error(pc, "missing call sent WE0_chanID", skb); return; } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); L3AddTimer(&pc->timer, T310, CC_T310); newl3state(pc, 3); pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc); @@ -329,7 +332,7 @@ l3_1tr6_alert(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); L3DelTimer(&pc->timer); /* T304 */ newl3state(pc, 4); pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc); @@ -360,7 +363,7 @@ l3_1tr6_info(struct l3_process *pc, u_char pr, void *arg) } } else if (pc->st->l3.debug & L3_DEB_CHARGE) l3_debug(pc->st, "charging info not found"); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } @@ -369,7 +372,7 @@ l3_1tr6_info_s2(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } static void @@ -383,7 +386,7 @@ l3_1tr6_connect(struct l3_process *pc, u_char pr, void *arg) return; } newl3state(pc, 10); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); pc->para.chargeinfo = 0; pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc); } @@ -407,11 +410,11 @@ l3_1tr6_rel(struct l3_process *pc, u_char pr, void *arg) pc->para.loc = 0; } } else { - pc->para.cause = -1; + pc->para.cause = NO_CAUSE; l3_1tr6_error(pc, "missing REL cause", skb); return; } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); StopAllL3Timer(pc); newl3state(pc, 0); l3_1TR6_message(pc, MT_N1_REL_ACK, PROTO_DIS_N1); @@ -424,10 +427,10 @@ l3_1tr6_rel_ack(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); StopAllL3Timer(pc); newl3state(pc, 0); - pc->para.cause = -1; + pc->para.cause = NO_CAUSE; pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc); release_l3_process(pc); } @@ -475,13 +478,13 @@ l3_1tr6_disc(struct l3_process *pc, u_char pr, void *arg) } else { if (pc->st->l3.debug & L3_DEB_WARN) l3_debug(pc->st, "cause not found"); - pc->para.cause = -1; + pc->para.cause = NO_CAUSE; } if (!findie(skb->data, skb->len, WE6_date, 6)) { l3_1tr6_error(pc, "missing connack date", skb); return; } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); newl3state(pc, 12); pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc); } @@ -496,7 +499,7 @@ l3_1tr6_connect_ack(struct l3_process *pc, u_char pr, void *arg) l3_1tr6_error(pc, "missing connack date", skb); return; } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); newl3state(pc, 10); pc->para.chargeinfo = 0; L3DelTimer(&pc->timer); @@ -567,6 +570,9 @@ l3_1tr6_disconnect_req(struct l3_process *pc, u_char pr, void *arg) case 0x10: clen = 0; break; + case 0x11: + cause = CAUSE_UserBusy; + break; case 0x15: cause = CAUSE_CallRejected; break; @@ -620,7 +626,7 @@ l3_1tr6_t305(struct l3_process *pc, u_char pr, void *arg) u_char clen = 1; L3DelTimer(&pc->timer); - if (pc->para.cause > 0) + if (pc->para.cause != NO_CAUSE) cause = pc->para.cause; /* Map DSS1 causes */ switch (cause & 0x7f) { @@ -679,6 +685,24 @@ l3_1tr6_t308_2(struct l3_process *pc, u_char pr, void *arg) pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc); release_l3_process(pc); } + +static void +l3_1tr6_dl_reset(struct l3_process *pc, u_char pr, void *arg) +{ + pc->para.cause = CAUSE_LocalProcErr; + l3_1tr6_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); +} + +static void +l3_1tr6_dl_release(struct l3_process *pc, u_char pr, void *arg) +{ + newl3state(pc, 0); + pc->para.cause = 0x1b; /* Destination out of order */ + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + release_l3_process(pc); +} + /* *INDENT-OFF* */ static struct stateentry downstl[] = { @@ -689,8 +713,6 @@ static struct stateentry downstl[] = CC_DISCONNECT | REQUEST, l3_1tr6_disconnect_req}, {SBIT(12), CC_RELEASE | REQUEST, l3_1tr6_release_req}, - {ALL_STATES, - CC_DLRL | REQUEST, l3_1tr6_reset}, {SBIT(6), CC_IGNORE | REQUEST, l3_1tr6_reset}, {SBIT(6), @@ -751,11 +773,22 @@ static struct stateentry datastln1[] = {SBIT(19), MT_N1_REL_ACK, l3_1tr6_rel_ack} }; -/* *INDENT-ON* */ #define DATASTLN1_LEN \ (sizeof(datastln1) / sizeof(struct stateentry)) +static struct stateentry manstatelist[] = +{ + {SBIT(2), + DL_ESTABLISH | INDICATION, l3_1tr6_dl_reset}, + {ALL_STATES, + DL_RELEASE | INDICATION, l3_1tr6_dl_release}, +}; + +#define MANSLLEN \ + (sizeof(manstatelist) / sizeof(struct stateentry)) +/* *INDENT-ON* */ + static void up1tr6(struct PStack *st, int pr, void *arg) { @@ -778,20 +811,20 @@ up1tr6(struct PStack *st, int pr, void *arg) } if (skb->len < 4) { if (st->l3.debug & L3_DEB_PROTERR) { - sprintf(tmp, "up1tr6 len only %ld", skb->len); + sprintf(tmp, "up1tr6 len only %d", skb->len); l3_debug(st, tmp); } - dev_kfree_skb(skb); + idev_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", + sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d", (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", skb->data[0], skb->len); l3_debug(st, tmp); } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } if (skb->data[1] != 1) { @@ -799,13 +832,13 @@ up1tr6(struct PStack *st, int pr, void *arg) sprintf(tmp, "up1tr6 CR len not 1"); l3_debug(st, tmp); } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } cr = skb->data[2]; mt = skb->data[3]; if (skb->data[0] == PROTO_DIS_N0) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "up1tr6%s N0 mt %x unhandled", (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", mt); @@ -820,11 +853,11 @@ up1tr6(struct PStack *st, int pr, void *arg) sprintf(tmp, "up1tr6 no roc mem"); l3_debug(st, tmp); } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } } else { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } } else if ((mt == MT_N1_REL) || (mt == MT_N1_REL_ACK) || @@ -832,7 +865,7 @@ up1tr6(struct PStack *st, int pr, void *arg) (mt == MT_N1_REG_ACK) || (mt == MT_N1_REG_REJ) || (mt == MT_N1_SUSP_ACK) || (mt == MT_N1_RES_REJ) || (mt == MT_N1_INFO)) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } else { if (!(proc = new_l3_process(st, cr))) { @@ -840,7 +873,7 @@ up1tr6(struct PStack *st, int pr, void *arg) sprintf(tmp, "up1tr6 no roc mem"); l3_debug(st, tmp); } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } mt = MT_N1_INVALID; @@ -851,7 +884,7 @@ up1tr6(struct PStack *st, int pr, void *arg) ((1 << proc->state) & datastln1[i].state)) break; if (i == DATASTLN1_LEN) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "up1tr6%sstate %d mt %x unhandled", (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", @@ -879,7 +912,7 @@ down1tr6(struct PStack *st, int pr, void *arg) struct Channel *chan; char tmp[80]; - if (((DL_ESTABLISH | REQUEST)== pr) || ((DL_RELEASE | REQUEST)== pr)) { + if ((DL_ESTABLISH | REQUEST)== pr) { l3_msg(st, pr, NULL); return; } else if ((CC_SETUP | REQUEST) == pr) { @@ -918,6 +951,34 @@ down1tr6(struct PStack *st, int pr, void *arg) } } +static void +man1tr6(struct PStack *st, int pr, void *arg) +{ + int i; + struct l3_process *proc = arg; + + if (!proc) { + printk(KERN_ERR "HiSax man1tr6 without proc pr=%04x\n", pr); + return; + } + for (i = 0; i < MANSLLEN; i++) + if ((pr == manstatelist[i].primitive) && + ((1 << proc->state) & manstatelist[i].state)) + break; + if (i == MANSLLEN) { + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "cr %d man1tr6 state %d prim %d unhandled", + proc->callref & 0x7f, proc->state, pr); + } + } else { + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "cr %d man1tr6 state %d prim %d", + proc->callref & 0x7f, proc->state, pr); + } + manstatelist[i].rout(proc, pr, arg); + } +} + void setstack_1tr6(struct PStack *st) { @@ -925,6 +986,7 @@ setstack_1tr6(struct PStack *st) st->lli.l4l3 = down1tr6; st->l2.l2l3 = up1tr6; + st->l3.l3ml3 = man1tr6; st->l3.N303 = 0; strcpy(tmp, l3_1tr6_revision); diff --git a/drivers/isdn/hisax/l3dss1.c b/drivers/isdn/hisax/l3dss1.c index 0808d32a0b59..1c541033f0fd 100644 --- a/drivers/isdn/hisax/l3dss1.c +++ b/drivers/isdn/hisax/l3dss1.c @@ -1,4 +1,4 @@ -/* $Id: l3dss1.c,v 2.12 1998/11/15 23:55:10 keil Exp $ +/* $Id: l3dss1.c,v 2.18 1999/08/11 20:54:39 keil Exp $ * EURO/DSS1 D-channel protocol * @@ -13,6 +13,21 @@ * Fritz Elfert * * $Log: l3dss1.c,v $ + * Revision 2.18 1999/08/11 20:54:39 keil + * High layer compatibility is valid in SETUP + * + * Revision 2.17 1999/07/25 16:18:25 keil + * Fix Suspend/Resume + * + * Revision 2.16 1999/07/21 14:46:23 keil + * changes from EICON certification + * + * Revision 2.14 1999/07/09 08:30:08 keil + * cosmetics + * + * Revision 2.13 1999/07/01 08:11:58 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.12 1998/11/15 23:55:10 keil * changes from 2.0 * @@ -72,29 +87,227 @@ #include extern char *HiSax_getrev(const char *revision); -const char *dss1_revision = "$Revision: 2.12 $"; +const char *dss1_revision = "$Revision: 2.18 $"; + +#define EXT_BEARER_CAPS 1 #define MsgHead(ptr, cref, mty) \ *ptr++ = 0x8; \ - *ptr++ = 0x1; \ - *ptr++ = cref^0x80; \ + if (cref == -1) { \ + *ptr++ = 0x0; \ + } else { \ + *ptr++ = 0x1; \ + *ptr++ = cref^0x80; \ + } \ *ptr++ = mty -#if HISAX_DE_AOC +/**********************************************/ +/* get a new invoke id for remote operations. */ +/* Only a return value != 0 is valid */ +/**********************************************/ +static unsigned char new_invoke_id(struct PStack *p) +{ + unsigned char retval; + int flags,i; + + i = 32; /* maximum search depth */ + + save_flags(flags); + cli(); + + retval = p->prot.dss1.last_invoke_id + 1; /* try new id */ + while ((i) && (p->prot.dss1.invoke_used[retval >> 3] == 0xFF)) { + p->prot.dss1.last_invoke_id = (retval & 0xF8) + 8; + i--; + } + if (i) { + while (p->prot.dss1.invoke_used[retval >> 3] & (1 << (retval & 7))) + retval++; + } else + retval = 0; + p->prot.dss1.last_invoke_id = retval; + p->prot.dss1.invoke_used[retval >> 3] |= (1 << (retval & 7)); + restore_flags(flags); + + return(retval); +} /* new_invoke_id */ + +/*************************/ +/* free a used invoke id */ +/*************************/ +static void free_invoke_id(struct PStack *p, unsigned char id) +{ int flags; + + if (!id) return; /* 0 = invalid value */ + + save_flags(flags); + cli(); + p->prot.dss1.invoke_used[id >> 3] &= ~(1 << (id & 7)); + restore_flags(flags); +} /* free_invoke_id */ + + +/**********************************************************/ +/* create a new l3 process and fill in dss1 specific data */ +/**********************************************************/ +static struct l3_process +*dss1_new_l3_process(struct PStack *st, int cr) +{ struct l3_process *proc; + + if (!(proc = new_l3_process(st, cr))) + return(NULL); + + proc->prot.dss1.invoke_id = 0; + proc->prot.dss1.remote_operation = 0; + proc->prot.dss1.uus1_data[0] = '\0'; + + return(proc); +} /* dss1_new_l3_process */ + +/************************************************/ +/* free a l3 process and all dss1 specific data */ +/************************************************/ +static void +dss1_release_l3_process(struct l3_process *p) +{ + free_invoke_id(p->st,p->prot.dss1.invoke_id); + release_l3_process(p); +} /* dss1_release_l3_process */ + +/********************************************************/ +/* search a process with invoke id id and dummy callref */ +/********************************************************/ +static struct l3_process * +l3dss1_search_dummy_proc(struct PStack *st, int id) +{ struct l3_process *pc = st->l3.proc; /* start of processes */ + + if (!id) return(NULL); + + while (pc) + { if ((pc->callref == -1) && (pc->prot.dss1.invoke_id == id)) + return(pc); + pc = pc->next; + } + return(NULL); +} /* l3dss1_search_dummy_proc */ + +/*******************************************************************/ +/* called when a facility message with a dummy callref is received */ +/* and a return result is delivered. id specifies the invoke id. */ +/*******************************************************************/ +static void +l3dss1_dummy_return_result(struct PStack *st, int id, u_char *p, u_char nlen) +{ isdn_ctrl ic; + struct IsdnCardState *cs; + struct l3_process *pc = NULL; + + if ((pc = l3dss1_search_dummy_proc(st, id))) + { L3DelTimer(&pc->timer); /* remove timer */ + + cs = pc->st->l1.hardware; + ic.driver = cs->myid; + ic.command = ISDN_STAT_PROT; + ic.arg = DSS1_STAT_INVOKE_RES; + ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id; + ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id; + ic.parm.dss1_io.proc = pc->prot.dss1.proc; + ic.parm.dss1_io.timeout= 0; + ic.parm.dss1_io.datalen = nlen; + ic.parm.dss1_io.data = p; + free_invoke_id(pc->st, pc->prot.dss1.invoke_id); + pc->prot.dss1.invoke_id = 0; /* reset id */ + + cs->iif.statcallb(&ic); + dss1_release_l3_process(pc); + } + else + l3_debug(st, "dummy return result id=0x%x result len=%d",id,nlen); +} /* l3dss1_dummy_return_result */ + +/*******************************************************************/ +/* called when a facility message with a dummy callref is received */ +/* and a return error is delivered. id specifies the invoke id. */ +/*******************************************************************/ +static void +l3dss1_dummy_error_return(struct PStack *st, int id, ulong error) +{ isdn_ctrl ic; + struct IsdnCardState *cs; + struct l3_process *pc = NULL; + + if ((pc = l3dss1_search_dummy_proc(st, id))) + { L3DelTimer(&pc->timer); /* remove timer */ + + cs = pc->st->l1.hardware; + ic.driver = cs->myid; + ic.command = ISDN_STAT_PROT; + ic.arg = DSS1_STAT_INVOKE_ERR; + ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id; + ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id; + ic.parm.dss1_io.proc = pc->prot.dss1.proc; + ic.parm.dss1_io.timeout= error; + ic.parm.dss1_io.datalen = 0; + ic.parm.dss1_io.data = NULL; + free_invoke_id(pc->st, pc->prot.dss1.invoke_id); + pc->prot.dss1.invoke_id = 0; /* reset id */ + + cs->iif.statcallb(&ic); + dss1_release_l3_process(pc); + } + else + l3_debug(st, "dummy return error id=0x%x error=0x%lx",id,error); +} /* l3dss1_error_return */ + +/*******************************************************************/ +/* called when a facility message with a dummy callref is received */ +/* and a invoke is delivered. id specifies the invoke id. */ +/*******************************************************************/ +static void +l3dss1_dummy_invoke(struct PStack *st, int cr, int id, + int ident, u_char *p, u_char nlen) +{ isdn_ctrl ic; + struct IsdnCardState *cs; + + l3_debug(st, "dummy invoke %s id=0x%x ident=0x%x datalen=%d", + (cr == -1) ? "local" : "broadcast",id,ident,nlen); + if (cr >= -1) return; /* ignore local data */ + + cs = st->l1.hardware; + ic.driver = cs->myid; + ic.command = ISDN_STAT_PROT; + ic.arg = DSS1_STAT_INVOKE_BRD; + ic.parm.dss1_io.hl_id = id; + ic.parm.dss1_io.ll_id = 0; + ic.parm.dss1_io.proc = ident; + ic.parm.dss1_io.timeout= 0; + ic.parm.dss1_io.datalen = nlen; + ic.parm.dss1_io.data = p; + + cs->iif.statcallb(&ic); +} /* l3dss1_dummy_invoke */ + static void -l3dss1_parse_facility(struct l3_process *pc, u_char * p) +l3dss1_parse_facility(struct PStack *st, struct l3_process *pc, + int cr, u_char * p) { int qd_len = 0; + unsigned char nlen = 0, ilen, cp_tag; + int ident, id; + ulong err_ret; + + if (pc) + st = pc->st; /* valid Stack */ + else + if ((!st) || (cr >= 0)) return; /* neither pc nor st specified */ p++; qd_len = *p++; if (qd_len == 0) { - l3_debug(pc->st, "qd_len == 0"); + l3_debug(st, "qd_len == 0"); return; } if ((*p & 0x1F) != 0x11) { /* Service discriminator, supplementary service */ - l3_debug(pc->st, "supplementary service != 0x11"); + l3_debug(st, "supplementary service != 0x11"); return; } while (qd_len > 0 && !(*p & 0x80)) { /* extension ? */ @@ -102,72 +315,92 @@ l3dss1_parse_facility(struct l3_process *pc, u_char * p) qd_len--; } if (qd_len < 2) { - l3_debug(pc->st, "qd_len < 2"); + l3_debug(st, "qd_len < 2"); return; } p++; qd_len--; if ((*p & 0xE0) != 0xA0) { /* class and form */ - l3_debug(pc->st, "class and form != 0xA0"); + l3_debug(st, "class and form != 0xA0"); return; } - switch (*p & 0x1F) { /* component tag */ - case 1: /* invoke */ - { - unsigned char nlen = 0, 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--; - } + + cp_tag = *p & 0x1F; /* remember tag value */ + p++; + qd_len--; + if (qd_len < 1) + { l3_debug(st, "qd_len < 1"); + return; + } + if (*p & 0x80) + { /* length format indefinite or limited */ + nlen = *p++ & 0x7F; /* number of len bytes or indefinite */ + if ((qd_len-- < ((!nlen) ? 3 : (1 + nlen))) || + (nlen > 1)) + { l3_debug(st, "length format error or not implemented"); + return; + } + if (nlen == 1) + { nlen = *p++; /* complete length */ + qd_len--; + } + else + { qd_len -= 2; /* trailing null bytes */ + if ((*(p+qd_len)) || (*(p+qd_len+1))) + { l3_debug(st,"length format indefinite error"); + return; + } + nlen = qd_len; + } + } + else + { nlen = *p++; + qd_len--; + } + if (qd_len < nlen) + { l3_debug(st, "qd_len < nlen"); + return; + } + qd_len -= nlen; + + if (nlen < 2) + { l3_debug(st, "nlen < 2"); + return; + } + if (*p != 0x02) + { /* invoke identifier tag */ + l3_debug(st, "invoke identifier tag !=0x02"); + return; + } + p++; + nlen--; + if (*p & 0x80) + { /* length format */ + l3_debug(st, "invoke id length format 2"); + return; + } + ilen = *p++; + nlen--; + if (ilen > nlen || ilen == 0) + { l3_debug(st, "ilen > nlen || ilen == 0"); + return; + } + nlen -= ilen; + id = 0; + while (ilen > 0) + { id = (id << 8) | (*p++ & 0xFF); /* invoke identifier */ + ilen--; + } + + switch (cp_tag) { /* component tag */ + case 1: /* invoke */ if (nlen < 2) { - l3_debug(pc->st, "nlen < 2 22"); + l3_debug(st, "nlen < 2 22"); return; } if (*p != 0x02) { /* operation value */ - l3_debug(pc->st, "operation value !=0x02"); + l3_debug(st, "operation value !=0x02"); return; } p++; @@ -175,7 +408,7 @@ l3dss1_parse_facility(struct l3_process *pc, u_char * p) ilen = *p++; nlen--; if (ilen > nlen || ilen == 0) { - l3_debug(pc->st, "ilen > nlen || ilen == 0 22"); + l3_debug(st, "ilen > nlen || ilen == 0 22"); return; } nlen -= ilen; @@ -185,11 +418,18 @@ l3dss1_parse_facility(struct l3_process *pc, u_char * p) ilen--; } + if (!pc) + { l3dss1_dummy_invoke(st, cr, id, ident, p, nlen); + return; + } +#if HISAX_DE_AOC + { + #define FOO1(s,a,b) \ while(nlen > 1) { \ int ilen = p[1]; \ if(nlen < ilen+2) { \ - l3_debug(pc->st, "FOO1 nlen < ilen+2"); \ + l3_debug(st, "FOO1 nlen < ilen+2"); \ return; \ } \ nlen -= ilen+2; \ @@ -203,8 +443,6 @@ l3dss1_parse_facility(struct l3_process *pc, u_char * p) } switch (ident) { - default: - break; case 0x22: /* during */ FOO1("1A", 0x30, FOO1("1C", 0xA1, FOO1("1D", 0x30, FOO1("1E", 0x02, ( { ident = 0; @@ -215,14 +453,14 @@ l3dss1_parse_facility(struct l3_process *pc, u_char * p) } if (ident > pc->para.chargeinfo) { pc->para.chargeinfo = ident; - pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc); + st->l3.l3l4(st, CC_CHARGE | INDICATION, pc); } - if (pc->st->l3.debug & L3_DEB_CHARGE) { + if (st->l3.debug & L3_DEB_CHARGE) { if (*(p + 2) == 0) { - l3_debug(pc->st, "charging info during %d", pc->para.chargeinfo); + l3_debug(st, "charging info during %d", pc->para.chargeinfo); } else { - l3_debug(pc->st, "charging info final %d", pc->para.chargeinfo); + l3_debug(st, "charging info final %d", pc->para.chargeinfo); } } } @@ -238,113 +476,553 @@ l3dss1_parse_facility(struct l3_process *pc, u_char * p) } if (ident > pc->para.chargeinfo) { pc->para.chargeinfo = ident; - pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc); + st->l3.l3l4(st, CC_CHARGE | INDICATION, pc); } - if (pc->st->l3.debug & L3_DEB_CHARGE) { - l3_debug(pc->st, "charging info final %d", pc->para.chargeinfo); + if (st->l3.debug & L3_DEB_CHARGE) { + l3_debug(st, "charging info final %d", pc->para.chargeinfo); } } )))))) break; + default: + l3_debug(st, "invoke break invalid ident %02x",ident); + break; } #undef FOO1 } +#else not HISAX_DE_AOC + l3_debug(st, "invoke break"); +#endif not HISAX_DE_AOC break; case 2: /* return result */ - l3_debug(pc->st, "return result break"); + /* if no process available handle separately */ + if (!pc) + { if (cr == -1) + l3dss1_dummy_return_result(st, id, p, nlen); + return; + } + if ((pc->prot.dss1.invoke_id) && (pc->prot.dss1.invoke_id == id)) + { /* Diversion successfull */ + free_invoke_id(st,pc->prot.dss1.invoke_id); + pc->prot.dss1.remote_result = 0; /* success */ + pc->prot.dss1.invoke_id = 0; + pc->redir_result = pc->prot.dss1.remote_result; + st->l3.l3l4(st, CC_REDIR | INDICATION, pc); } /* Diversion successfull */ + else + l3_debug(st,"return error unknown identifier"); break; case 3: /* return error */ - l3_debug(pc->st, "return error break"); + err_ret = 0; + if (nlen < 2) + { l3_debug(st, "return error nlen < 2"); + return; + } + if (*p != 0x02) + { /* result tag */ + l3_debug(st, "invoke error tag !=0x02"); + return; + } + p++; + nlen--; + if (*p > 4) + { /* length format */ + l3_debug(st, "invoke return errlen > 4 "); + return; + } + ilen = *p++; + nlen--; + if (ilen > nlen || ilen == 0) + { l3_debug(st, "error return ilen > nlen || ilen == 0"); + return; + } + nlen -= ilen; + while (ilen > 0) + { err_ret = (err_ret << 8) | (*p++ & 0xFF); /* error value */ + ilen--; + } + /* if no process available handle separately */ + if (!pc) + { if (cr == -1) + l3dss1_dummy_error_return(st, id, err_ret); + return; + } + if ((pc->prot.dss1.invoke_id) && (pc->prot.dss1.invoke_id == id)) + { /* Deflection error */ + free_invoke_id(st,pc->prot.dss1.invoke_id); + pc->prot.dss1.remote_result = err_ret; /* result */ + pc->prot.dss1.invoke_id = 0; + pc->redir_result = pc->prot.dss1.remote_result; + st->l3.l3l4(st, CC_REDIR | INDICATION, pc); + } /* Deflection error */ + else + l3_debug(st,"return result unknown identifier"); break; default: - l3_debug(pc->st, "default break"); + l3_debug(st, "facility default break tag=0x%02x",cp_tag); break; } } + +static void +l3dss1_message(struct l3_process *pc, u_char mt) +{ + struct sk_buff *skb; + u_char *p; + + if (!(skb = l3_alloc_skb(4))) + return; + p = skb_put(skb, 4); + MsgHead(p, pc->callref, mt); + l3_msg(pc->st, DL_DATA | REQUEST, skb); +} + +static void +l3dss1_message_cause(struct l3_process *pc, u_char mt, u_char cause) +{ + struct sk_buff *skb; + u_char tmp[16]; + u_char *p = tmp; + int l; + + MsgHead(p, pc->callref, mt); + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = cause | 0x80; + + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + l3_msg(pc->st, DL_DATA | REQUEST, skb); +} + +static void +l3dss1_status_send(struct l3_process *pc, u_char pr, void *arg) +{ + u_char tmp[16]; + u_char *p = tmp; + int l; + struct sk_buff *skb; + + MsgHead(p, pc->callref, MT_STATUS); + + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = pc->para.cause | 0x80; + + *p++ = IE_CALL_STATE; + *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_msg_without_setup(struct l3_process *pc, u_char pr, void *arg) +{ + /* 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: /* invalid callreference */ + case 88: /* incomp destination */ + case 96: /* mandory IE missing */ + case 100: /* invalid IE contents */ + case 101: /* 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 l3dss1_msg_without_setup wrong cause %d\n", + pc->para.cause); + 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); + dss1_release_l3_process(pc); +} + +static int ie_ALERTING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, + IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_HLC, IE_USER_USER, -1}; +static int ie_CALL_PROCEEDING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, + IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_HLC, -1}; +static int ie_CONNECT[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, + IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_DATE, IE_CONNECT_PN, + IE_CONNECT_SUB, IE_LLC, IE_HLC, IE_USER_USER, -1}; +static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, -1}; +static int ie_DISCONNECT[] = {IE_CAUSE | IE_MANDATORY, IE_FACILITY, + IE_PROGRESS, IE_DISPLAY, IE_USER_USER, -1}; +static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD, + IE_CALLED_PN, -1}; +static int ie_NOTIFY[] = {IE_BEARER, IE_NOTIFY | IE_MANDATORY, IE_DISPLAY, -1}; +static int ie_PROGRESS[] = {IE_BEARER, IE_CAUSE, IE_FACILITY, IE_PROGRESS | + IE_MANDATORY, IE_DISPLAY, IE_HLC, IE_USER_USER, -1}; +static int ie_RELEASE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY, IE_DISPLAY, IE_USER_USER, -1}; +/* a RELEASE_COMPLETE with errors don't require special actions +static int ie_RELEASE_COMPLETE[] = {IE_CAUSE | IE_MANDATORY_1, IE_DISPLAY, IE_USER_USER, -1}; +*/ +static int ie_RESUME_ACKNOWLEDGE[] = {IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, + IE_DISPLAY, -1}; +static int ie_RESUME_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; +static int ie_SETUP[] = {IE_COMPLETE, IE_BEARER | IE_MANDATORY, + IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, IE_PROGRESS, + IE_NET_FAC, IE_DISPLAY, IE_KEYPAD, IE_CALLING_PN, + IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_LLC, IE_HLC, + IE_USER_USER, -1}; +static int ie_SETUP_ACKNOWLEDGE[] = {IE_CHANNEL_ID | IE_MANDATORY, IE_FACILITY, + IE_PROGRESS, IE_DISPLAY, -1}; +static int ie_STATUS[] = {IE_CAUSE | IE_MANDATORY, IE_CALL_STATE | + IE_MANDATORY, IE_DISPLAY, -1}; +static int ie_STATUS_ENQUIRY[] = {IE_DISPLAY, -1}; +static int ie_SUSPEND_ACKNOWLEDGE[] = {IE_DISPLAY, IE_FACILITY, -1}; +static int ie_SUSPEND_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; +/* not used + * static int ie_CONGESTION_CONTROL[] = {IE_CONGESTION | IE_MANDATORY, + * IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; + * static int ie_USER_INFORMATION[] = {IE_MORE_DATA, IE_USER_USER | IE_MANDATORY, -1}; + * static int ie_RESTART[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_RESTART_IND | + * IE_MANDATORY, -1}; + */ +static int ie_FACILITY[] = {IE_FACILITY | IE_MANDATORY, IE_DISPLAY, -1}; +static int comp_required[] = {1,2,3,5,6,7,9,10,11,14,15,-1}; +static int l3_valid_states[] = {0,1,2,3,4,6,7,8,9,10,11,12,15,17,19,25,-1}; + +struct ie_len { + int ie; + int len; +}; + +static +struct ie_len max_ie_len[] = { + {IE_SEGMENT, 4}, + {IE_BEARER, 12}, + {IE_CAUSE, 32}, + {IE_CALL_ID, 10}, + {IE_CALL_STATE, 3}, + {IE_CHANNEL_ID, 34}, + {IE_FACILITY, 255}, + {IE_PROGRESS, 4}, + {IE_NET_FAC, 255}, + {IE_NOTIFY, 3}, + {IE_DISPLAY, 82}, + {IE_DATE, 8}, + {IE_KEYPAD, 34}, + {IE_SIGNAL, 3}, + {IE_INFORATE, 6}, + {IE_E2E_TDELAY, 11}, + {IE_TDELAY_SEL, 5}, + {IE_PACK_BINPARA, 3}, + {IE_PACK_WINSIZE, 4}, + {IE_PACK_SIZE, 4}, + {IE_CUG, 7}, + {IE_REV_CHARGE, 3}, + {IE_CALLING_PN, 24}, + {IE_CALLING_SUB, 23}, + {IE_CALLED_PN, 24}, + {IE_CALLED_SUB, 23}, + {IE_REDIR_NR, 255}, + {IE_TRANS_SEL, 255}, + {IE_RESTART_IND, 3}, + {IE_LLC, 18}, + {IE_HLC, 5}, + {IE_USER_USER, 131}, + {-1,0}, +}; + +static int +getmax_ie_len(u_char ie) { + int i = 0; + while (max_ie_len[i].ie != -1) { + if (max_ie_len[i].ie == ie) + return(max_ie_len[i].len); + i++; + } + return(255); +} + +static int +ie_in_set(struct l3_process *pc, u_char ie, int *checklist) { + int ret = 1; + + while (*checklist != -1) { +#if 0 + if (pc->debug & L3_DEB_CHECK) + l3_debug(pc->st, "ie_in_set ie(%x) cl(%x)", + ie, *checklist); #endif + if ((*checklist & 0xff) == ie) { + if (ie & 0x80) + return(-ret); + else + return(ret); + } + ret++; + checklist++; + } + return(0); +} static int -l3dss1_check_messagetype_validity(int mt) +check_infoelements(struct l3_process *pc, struct sk_buff *skb, int *checklist) +{ + int *cl = checklist; + u_char mt; + u_char *p, ie; + int l, newpos, oldpos; + int err_seq = 0, err_len = 0, err_compr = 0, err_ureg = 0; + + p = skb->data; + /* skip cr */ + p++; + l = (*p++) & 0xf; + p += l; + mt = *p++; + oldpos = 0; +/* shift codeset procedure not implemented in the moment */ + while ((p - skb->data) < skb->len) { + if ((newpos = ie_in_set(pc, *p, cl))) { + if (newpos > 0) { + if (newpos < oldpos) + err_seq++; + else + oldpos = newpos; + } + } else { + if (ie_in_set(pc, *p, comp_required)) + err_compr++; + else + err_ureg++; + } + ie = *p++; + if (ie & 0x80) { + l = 1; + } else { + l = *p++; + p += l; + l += 2; + } + if (l > getmax_ie_len(ie)) + err_len++; + } + if (err_compr | err_ureg | err_len | err_seq) { + if (pc->debug & L3_DEB_CHECK) + l3_debug(pc->st, "check_infoelements mt %x %d/%d/%d/%d", + mt, err_compr, err_ureg, err_len, err_seq); + if (err_compr) + return(ERR_IE_COMPREHENSION); + if (err_ureg) + return(ERR_IE_UNRECOGNIZED); + if (err_len) + return(ERR_IE_LENGTH); + if (err_seq) + return(ERR_IE_SEQUENCE); + } + return(0); +} + +/* verify if a message type exists and contain no IE error */ +static int +l3dss1_check_messagetype_validity(struct l3_process *pc, int mt, void *arg) { -/* verify if a message type exists */ switch (mt) { case MT_ALERTING: case MT_CALL_PROCEEDING: case MT_CONNECT: case MT_CONNECT_ACKNOWLEDGE: + case MT_DISCONNECT: + case MT_INFORMATION: + case MT_FACILITY: + case MT_NOTIFY: case MT_PROGRESS: + case MT_RELEASE: + case MT_RELEASE_COMPLETE: 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: + if (pc->debug & L3_DEB_CHECK) + l3_debug(pc->st, "l3dss1_check_messagetype_validity mt(%x) OK", mt); + break; + case MT_RESUME: /* RESUME only in user->net */ + case MT_SUSPEND: /* SUSPEND only in user->net */ + default: + if (pc->debug & (L3_DEB_CHECK | L3_DEB_WARN)) + l3_debug(pc->st, "l3dss1_check_messagetype_validity mt(%x) fail", mt); + pc->para.cause = 97; + l3dss1_status_send(pc, 0, NULL); return(1); + } + return(0); +} + +static void +l3dss1_std_ie_err(struct l3_process *pc, int ret) { + + if (pc->debug & L3_DEB_CHECK) + l3_debug(pc->st, "check_infoelements ret %d", ret); + switch(ret) { + case 0: + break; + case ERR_IE_COMPREHENSION: + pc->para.cause = 96; + l3dss1_status_send(pc, 0, NULL); + break; + case ERR_IE_UNRECOGNIZED: + pc->para.cause = 99; + l3dss1_status_send(pc, 0, NULL); + break; + case ERR_IE_LENGTH: + pc->para.cause = 100; + l3dss1_status_send(pc, 0, NULL); + break; + case ERR_IE_SEQUENCE: default: - return(0); + break; } +} + +static int +l3dss1_get_channel_id(struct l3_process *pc, struct sk_buff *skb) { + u_char *p; + + p = skb->data; + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + p++; + if (*p != 1) { /* len for BRI = 1 */ + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "wrong chid len %d", *p); + return (-2); + } + p++; + if (*p & 0x60) { /* only base rate interface */ + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "wrong chid %x", *p); + return (-3); + } + return(*p & 0x3); + } else + return(-1); +} + +static int +l3dss1_get_cause(struct l3_process *pc, struct sk_buff *skb) { + u_char l, i=0; + u_char *p; + + p = skb->data; + pc->para.cause = 31; + pc->para.loc = 0; + if ((p = findie(p, skb->len, IE_CAUSE, 0))) { + p++; + l = *p++; + if (l>30) + return(1); + if (l) { + pc->para.loc = *p++; + l--; + } else { + return(2); + } + if (l && !(pc->para.loc & 0x80)) { + l--; + p++; /* skip recommendation */ + } + if (l) { + pc->para.cause = *p++; + l--; + if (!(pc->para.cause & 0x80)) + return(3); + } else + return(4); + while (l && (i<6)) { + pc->para.diag[i++] = *p++; + l--; + } + } else + return(-1); return(0); } static void -l3dss1_message(struct l3_process *pc, u_char mt) +l3dss1_msg_with_uus(struct l3_process *pc, u_char cmd) { struct sk_buff *skb; - u_char *p; + u_char tmp[16+40]; + u_char *p = tmp; + int l; - if (!(skb = l3_alloc_skb(4))) + MsgHead(p, pc->callref, cmd); + + if (pc->prot.dss1.uus1_data[0]) + { *p++ = IE_USER_USER; /* UUS info element */ + *p++ = strlen(pc->prot.dss1.uus1_data) + 1; + *p++ = 0x04; /* IA5 chars */ + strcpy(p,pc->prot.dss1.uus1_data); + p += strlen(pc->prot.dss1.uus1_data); + pc->prot.dss1.uus1_data[0] = '\0'; + } + + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) return; - p = skb_put(skb, 4); - MsgHead(p, pc->callref, mt); + memcpy(skb_put(skb, l), tmp, l); l3_msg(pc->st, DL_DATA | REQUEST, skb); -} +} /* l3dss1_msg_with_uus */ static void l3dss1_release_req(struct l3_process *pc, u_char pr, void *arg) { StopAllL3Timer(pc); newl3state(pc, 19); - l3dss1_message(pc, MT_RELEASE); + if (!pc->prot.dss1.uus1_data[0]) + l3dss1_message(pc, MT_RELEASE); + else + l3dss1_msg_with_uus(pc, MT_RELEASE); L3AddTimer(&pc->timer, T308, CC_T308_1); } static void l3dss1_release_cmpl(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; struct sk_buff *skb = arg; - int cause = -1; + int ret; - p = skb->data; - pc->para.loc = 0; - if ((p = findie(p, skb->len, IE_CAUSE, 0))) { - p++; - if (*p++ == 2) - pc->para.loc = *p++; - cause = *p & 0x7f; - } - dev_kfree_skb(skb); + if ((ret = l3dss1_get_cause(pc, skb))>0) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "RELCMPL get_cause ret(%d)",ret); + } else if (ret < 0) + pc->para.cause = NO_CAUSE; StopAllL3Timer(pc); - pc->para.cause = cause; newl3state(pc, 0); pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc); - release_l3_process(pc); + dss1_release_l3_process(pc); } #if EXT_BEARER_CAPS @@ -574,9 +1252,8 @@ DecodeSI2(struct sk_buff *skb) break; case 0x08: // if (p[5] == 0x02) // sync. Bitratenadaption - - return DecodeSyncParams(176, p[5]); // V.120 - + if (p[1] > 3) + return DecodeSyncParams(176, p[5]); // V.120 break; } } @@ -594,6 +1271,7 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, u_char tmp[128]; u_char *p = tmp; u_char channel = 0; + u_char send_keypad; u_char screen = 0x80; u_char *teln; u_char *msn; @@ -603,13 +1281,17 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, MsgHead(p, pc->callref, MT_SETUP); + teln = pc->para.setup.phone; + send_keypad = (strchr(teln,'*') || strchr(teln,'#')) ? 1 : 0; /* * Set Bearer Capability, Map info from 1TR6-convention to EDSS1 */ #if HISAX_EURO_SENDCOMPLETE - *p++ = 0xa1; /* complete indicator */ + if (!send_keypad) + *p++ = 0xa1; /* complete indicator */ #endif - switch (pc->para.setup.si1) { + if (!send_keypad) + switch (pc->para.setup.si1) { case 1: /* Telephony */ *p++ = 0x4; /* BC-IE-code */ *p++ = 0x3; /* Length */ @@ -625,12 +1307,26 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, *p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inform. */ *p++ = 0x90; /* Circuit-Mode 64kbps */ break; - } + } + else { *p++ = 0x4; /* assumptions for bearer services with keypad */ + *p++ = 0x3; + *p++ = 0x80; + *p++ = 0x90; + *p++ = 0xa3; + *p++ = 0x18; /* no specific channel */ + *p++ = 0x01; + *p++ = 0x83; + *p++ = 0x2C; /* IE keypad */ + *p++ = strlen(teln); + while (*teln) + *p++ = (*teln++) & 0x7F; + } + + /* * What about info2? Mapping to High-Layer-Compatibility? */ - teln = pc->para.setup.phone; - if (*teln) { + if ((*teln) && (!send_keypad)) { /* parse number for special things */ if (!isdigit(*teln)) { switch (0x5f & *teln) { @@ -650,7 +1346,8 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, case 'D': screen = 0x80; break; - default: + + default: if (pc->debug & L3_DEB_WARN) l3_debug(pc->st, "Wrong MSN Code"); break; @@ -703,24 +1400,33 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, } 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; - } + + if (!send_keypad) { + *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; + } + } #if EXT_BEARER_CAPS - if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) { // sync. Bitratenadaption, V.110/X.30 + if (send_keypad) { /* special handling independant of si2 */ + *p++ = 0x7c; + *p++ = 0x03; + *p++ = 0x80; + *p++ = 0x90; + *p++ = 0xa3; + } else if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) { // sync. Bitratenadaption, V.110/X.30 *p++ = 0x7c; *p++ = 0x04; @@ -767,74 +1473,136 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, static void l3dss1_call_proc(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; struct sk_buff *skb = arg; - + int id, ret; + + if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) { + if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer with wrong chid %x", id); + pc->para.cause = 100; + l3dss1_status_send(pc, pr, NULL); + return; + } + pc->para.bchannel = id; + } else if (1 == pc->state) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer wrong chid (ret %d)", id); + if (id == -1) + pc->para.cause = 96; + else + pc->para.cause = 100; + l3dss1_status_send(pc, pr, NULL); + return; + } + /* Now we are on none mandatory IEs */ + ret = check_infoelements(pc, skb, ie_CALL_PROCEEDING); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + return; + } 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, "setup answer without bchannel"); - } else if (pc->debug & L3_DEB_WARN) - l3_debug(pc->st, "setup answer without bchannel"); - dev_kfree_skb(skb); newl3state(pc, 3); L3AddTimer(&pc->timer, T310, CC_T310); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, ret); pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc); } static void l3dss1_setup_ack(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; struct sk_buff *skb = arg; - + int id, ret; + + if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) { + if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer with wrong chid %x", id); + pc->para.cause = 100; + l3dss1_status_send(pc, pr, NULL); + return; + } + pc->para.bchannel = id; + } else { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer wrong chid (ret %d)", id); + if (id == -1) + pc->para.cause = 96; + else + pc->para.cause = 100; + l3dss1_status_send(pc, pr, NULL); + return; + } + /* Now we are on none mandatory IEs */ + ret = check_infoelements(pc, skb, ie_SETUP_ACKNOWLEDGE); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + return; + } 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, "setup answer without bchannel"); - } else if (pc->debug & L3_DEB_WARN) - l3_debug(pc->st, "setup answer without bchannel"); - dev_kfree_skb(skb); newl3state(pc, 2); L3AddTimer(&pc->timer, T304, CC_T304); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, ret); pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc); } static void l3dss1_disconnect(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; struct sk_buff *skb = arg; - int cause = -1; + u_char *p; + int ret; + u_char cause = 0; StopAllL3Timer(pc); - p = skb->data; - pc->para.loc = 0; - if ((p = findie(p, skb->len, IE_CAUSE, 0))) { - p++; - if (*p++ == 2) - pc->para.loc = *p++; - cause = *p & 0x7f; - } - dev_kfree_skb(skb); + if ((ret = l3dss1_get_cause(pc, skb))) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "DISC get_cause ret(%d)", ret); + if (ret < 0) + cause = 96; + else if (ret > 0) + cause = 100; + } + if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) + l3dss1_parse_facility(pc->st, pc, pc->callref, p); + ret = check_infoelements(pc, skb, ie_DISCONNECT); + if (ERR_IE_COMPREHENSION == ret) + cause = 96; + else if ((!cause) && (ERR_IE_UNRECOGNIZED == ret)) + cause = 99; + ret = pc->state; newl3state(pc, 12); - pc->para.cause = cause; - pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc); + if (cause) + newl3state(pc, 19); + if (11 != ret) + pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc); + else if (!cause) + l3dss1_release_req(pc, pr, NULL); + if (cause) { + l3dss1_message_cause(pc, MT_RELEASE, cause); + L3AddTimer(&pc->timer, T308, CC_T308_1); + } } static void l3dss1_connect(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; + int ret; - dev_kfree_skb(skb); + ret = check_infoelements(pc, skb, ie_CONNECT); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + return; + } L3DelTimer(&pc->timer); /* T310 */ newl3state(pc, 10); pc->para.chargeinfo = 0; + /* here should inserted COLP handling KKe */ + if (ret) + l3dss1_std_ie_err(pc, ret); pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc); } @@ -842,143 +1610,142 @@ static void l3dss1_alerting(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; + int ret; - dev_kfree_skb(skb); + ret = check_infoelements(pc, skb, ie_ALERTING); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + return; + } L3DelTimer(&pc->timer); /* T304 */ newl3state(pc, 4); + if (ret) + l3dss1_std_ie_err(pc, ret); pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc); } -static void -l3dss1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg) -{ - /* 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; + u_char *p; int bcfound = 0; char tmp[80]; struct sk_buff *skb = arg; + int id; + int err = 0; - /* ETS 300-104 1.3.4 and 1.3.5 - * we need to detect unknown inform. element from 0 to 7 + /* + * Bearer Capabilities */ p = skb->data; - 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); + /* only the first occurence 'll be detected ! */ + if ((p = findie(p, skb->len, 0x04, 0))) { + if ((p[1] < 2) || (p[1] > 11)) + err = 1; + else { + pc->para.setup.si2 = 0; + switch (p[2] & 0x7f) { + case 0x00: /* Speech */ + case 0x10: /* 3.1 Khz audio */ + pc->para.setup.si1 = 1; + break; + case 0x08: /* Unrestricted digital information */ + pc->para.setup.si1 = 7; +/* JIM, 05.11.97 I wanna set service indicator 2 */ +#if EXT_BEARER_CAPS + pc->para.setup.si2 = DecodeSI2(skb); +#endif + break; + case 0x09: /* Restricted digital information */ + pc->para.setup.si1 = 2; + break; + case 0x11: + /* Unrestr. digital information with + * tones/announcements ( or 7 kHz audio + */ + pc->para.setup.si1 = 3; + break; + case 0x18: /* Video */ + pc->para.setup.si1 = 4; + break; + default: + err = 2; + break; + } + switch (p[3] & 0x7f) { + case 0x40: /* packed mode */ + pc->para.setup.si1 = 8; + break; + case 0x10: /* 64 kbit */ + case 0x11: /* 2*64 kbit */ + case 0x13: /* 384 kbit */ + case 0x15: /* 1536 kbit */ + case 0x17: /* 1920 kbit */ + pc->para.moderate = p[3] & 0x7f; + break; + default: + err = 3; + break; + } + } + if (pc->debug & L3_DEB_SI) + l3_debug(pc->st, "SI=%d, AI=%d", + pc->para.setup.si1, pc->para.setup.si2); + if (err) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup with wrong bearer(l=%d:%x,%x)", + p[1], p[2], p[3]); + pc->para.cause = 100; + l3dss1_msg_without_setup(pc, pr, NULL); + return; + } + } else { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without bearer capabilities"); + /* ETS 300-104 1.3.3 */ + pc->para.cause = 96; l3dss1_msg_without_setup(pc, pr, NULL); return; } /* * Channel Identification */ - p = skb->data; - if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { - pc->para.bchannel = p[2] & 0x3; - if (pc->para.bchannel) + if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) { + if ((pc->para.bchannel = id)) { + if ((3 == id) && (0x10 == pc->para.moderate)) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup with wrong chid %x", + id); + pc->para.cause = 100; + l3dss1_msg_without_setup(pc, pr, NULL); + return; + } bcfound++; - 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, call waiting"); + bcfound++; + } } else { if (pc->debug & L3_DEB_WARN) - l3_debug(pc->st, "setup without bchannel"); - pc->para.cause = 0x60; - dev_kfree_skb(skb); + l3_debug(pc->st, "setup with wrong chid ret %d", id); + if (id == -1) + pc->para.cause = 96; + else + pc->para.cause = 100; l3dss1_msg_without_setup(pc, pr, NULL); return; } - /* - * Bearer Capabilities - */ - p = skb->data; - if ((p = findie(p, skb->len, 0x04, 0))) { - pc->para.setup.si2 = 0; - switch (p[2] & 0x1f) { - case 0x00: - /* Speech */ - case 0x10: - /* 3.1 Khz audio */ - pc->para.setup.si1 = 1; - break; - case 0x08: - /* Unrestricted digital information */ - pc->para.setup.si1 = 7; -/* JIM, 05.11.97 I wanna set service indicator 2 */ -#if 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 */ - pc->para.setup.si1 = 2; - break; - case 0x11: - /* Unrestr. digital information with tones/announcements */ - pc->para.setup.si1 = 3; - break; - case 0x18: - /* Video */ - pc->para.setup.si1 = 4; - break; - default: - pc->para.setup.si1 = 0; - } - } 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); + /* Now we are on none mandatory IEs */ +#if 1 +/* !!!!!! this check seems to be a problem ? info bugfix to Karsten */ + err = check_infoelements(pc, skb, ie_SETUP); + if (ERR_IE_COMPREHENSION == err) { + pc->para.cause = 96; l3dss1_msg_without_setup(pc, pr, NULL); return; } - +#endif p = skb->data; if ((p = findie(p, skb->len, 0x70, 0))) iecpy(pc->para.setup.eazmsn, p, 1); @@ -1020,8 +1787,8 @@ l3dss1_setup(struct l3_process *pc, u_char pr, void *arg) } else if (pc->debug & L3_DEB_WARN) l3_debug(pc->st, "wrong calling subaddress"); } - dev_kfree_skb(skb); +#if 0 if (bcfound) { if ((pc->para.setup.si1 != 7) && (pc->debug & L3_DEB_WARN)) { l3_debug(pc->st, "non-digital call: %s -> %s", @@ -1029,53 +1796,38 @@ l3dss1_setup(struct l3_process *pc, u_char pr, void *arg) } if ((pc->para.setup.si1 != 7) && test_bit(FLG_PTP, &pc->st->l2.flag)) { - pc->para.cause = 0x58; + pc->para.cause = 88; /* incompatible destination */ 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); + dss1_release_l3_process(pc); +#else + newl3state(pc, 6); + if (err) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, err); + pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc); +#endif } static void l3dss1_reset(struct l3_process *pc, u_char pr, void *arg) { - release_l3_process(pc); -} - -static void -l3dss1_setup_rsp(struct l3_process *pc, u_char pr, - void *arg) -{ - newl3state(pc, 8); - l3dss1_message(pc, MT_CONNECT); - L3DelTimer(&pc->timer); - L3AddTimer(&pc->timer, T313, CC_T313); -} - -static void -l3dss1_connect_ack(struct l3_process *pc, u_char pr, void *arg) -{ - struct sk_buff *skb = arg; - - dev_kfree_skb(skb); - newl3state(pc, 10); - L3DelTimer(&pc->timer); - pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc); + dss1_release_l3_process(pc); } static void l3dss1_disconnect_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; - u_char tmp[16]; + u_char tmp[16+40]; u_char *p = tmp; int l; - u_char cause = 0x10; + u_char cause = 16; - if (pc->para.cause > 0) + if (pc->para.cause != NO_CAUSE) cause = pc->para.cause; StopAllL3Timer(pc); @@ -1087,6 +1839,15 @@ l3dss1_disconnect_req(struct l3_process *pc, u_char pr, void *arg) *p++ = 0x80; *p++ = cause | 0x80; + if (pc->prot.dss1.uus1_data[0]) + { *p++ = IE_USER_USER; /* UUS info element */ + *p++ = strlen(pc->prot.dss1.uus1_data) + 1; + *p++ = 0x04; /* IA5 chars */ + strcpy(p,pc->prot.dss1.uus1_data); + p += strlen(pc->prot.dss1.uus1_data); + pc->prot.dss1.uus1_data[0] = '\0'; + } + l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; @@ -1096,6 +1857,40 @@ l3dss1_disconnect_req(struct l3_process *pc, u_char pr, void *arg) L3AddTimer(&pc->timer, T305, CC_T305); } +static void +l3dss1_setup_rsp(struct l3_process *pc, u_char pr, + void *arg) +{ + if (!pc->para.bchannel) + { if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "D-chan connect for waiting call"); + l3dss1_disconnect_req(pc, pr, arg); + return; + } + newl3state(pc, 8); + l3dss1_message(pc, MT_CONNECT); + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T313, CC_T313); +} + +static void +l3dss1_connect_ack(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + + ret = check_infoelements(pc, skb, ie_CONNECT_ACKNOWLEDGE); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + return; + } + newl3state(pc, 10); + L3DelTimer(&pc->timer); + if (ret) + l3dss1_std_ie_err(pc, ret); + pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc); +} + static void l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg) { @@ -1103,9 +1898,9 @@ l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg) u_char tmp[16]; u_char *p = tmp; int l; - u_char cause = 0x95; + u_char cause = 21; - if (pc->para.cause > 0) + if (pc->para.cause != NO_CAUSE) cause = pc->para.cause; MsgHead(p, pc->callref, MT_RELEASE_COMPLETE); @@ -1113,7 +1908,7 @@ l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg) *p++ = IE_CAUSE; *p++ = 0x2; *p++ = 0x80; - *p++ = cause; + *p++ = cause | 0x80; l = p - tmp; if (!(skb = l3_alloc_skb(l))) @@ -1122,38 +1917,41 @@ l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg) 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); + dss1_release_l3_process(pc); } static void l3dss1_release(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; struct sk_buff *skb = arg; - int cause = -1; + u_char *p; + int ret, cause=0; - p = skb->data; - if ((p = findie(p, skb->len, IE_CAUSE, 0))) { - p++; - if (*p++ == 2) - pc->para.loc = *p++; - cause = *p & 0x7f; - } - p = skb->data; - if ((p = findie(p, skb->len, IE_FACILITY, 0))) { -#if HISAX_DE_AOC - l3dss1_parse_facility(pc, p); -#else - p = NULL; -#endif - } - dev_kfree_skb(skb); StopAllL3Timer(pc); - pc->para.cause = cause; - l3dss1_message(pc, MT_RELEASE_COMPLETE); + if ((ret = l3dss1_get_cause(pc, skb))>0) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "REL get_cause ret(%d)", ret); + } else if (ret<0) + pc->para.cause = NO_CAUSE; + if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) { + l3dss1_parse_facility(pc->st, pc, pc->callref, p); + } + if ((ret<0) && (pc->state != 11)) + cause = 96; + else if (ret>0) + cause = 100; + ret = check_infoelements(pc, skb, ie_RELEASE); + if (ERR_IE_COMPREHENSION == ret) + cause = 96; + else if ((ERR_IE_UNRECOGNIZED == ret) && (!cause)) + cause = 99; + if (cause) + l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause); + else + l3dss1_message(pc, MT_RELEASE_COMPLETE); pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); newl3state(pc, 0); - release_l3_process(pc); + dss1_release_l3_process(pc); } static void @@ -1161,66 +1959,360 @@ l3dss1_alert_req(struct l3_process *pc, u_char pr, void *arg) { newl3state(pc, 7); - l3dss1_message(pc, MT_ALERTING); + if (!pc->prot.dss1.uus1_data[0]) + l3dss1_message(pc, MT_ALERTING); + else + l3dss1_msg_with_uus(pc, MT_ALERTING); } static void -l3dss1_status_enq(struct l3_process *pc, u_char pr, void *arg) +l3dss1_proceed_req(struct l3_process *pc, u_char pr, + void *arg) { - u_char tmp[16]; - u_char *p = tmp; - int l; - struct sk_buff *skb = arg; + newl3state(pc, 9); + l3dss1_message(pc, MT_CALL_PROCEEDING); + pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc); +} - dev_kfree_skb(skb); +/********************************************/ +/* deliver a incoming display message to HL */ +/********************************************/ +static void +l3dss1_deliver_display(struct l3_process *pc, int pr, u_char *infp) +{ u_char len; + isdn_ctrl ic; + struct IsdnCardState *cs; + char *p; + + if (*infp++ != IE_DISPLAY) return; + if ((len = *infp++) > 80) return; /* total length <= 82 */ + if (!pc->chan) return; + + p = ic.parm.display; + while (len--) + *p++ = *infp++; + *p = '\0'; + ic.command = ISDN_STAT_DISPLAY; + cs = pc->st->l1.hardware; + ic.driver = cs->myid; + ic.arg = pc->chan->chan; + cs->iif.statcallb(&ic); +} /* l3dss1_deliver_display */ - MsgHead(p, pc->callref, MT_STATUS); - *p++ = IE_CAUSE; - *p++ = 0x2; - *p++ = 0x80; - *p++ = 0x9E; /* answer status enquire */ +static void +l3dss1_progress(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int err = 0; + u_char *p; - *p++ = 0x14; /* CallState */ - *p++ = 0x1; - *p++ = pc->state & 0x3f; + if ((p = findie(skb->data, skb->len, IE_PROGRESS, 0))) { + if (p[1] != 2) { + err = 1; + pc->para.cause = 100; + } else if (p[2] & 0x60) { + switch (p[2]) { + case 0x80: + case 0x81: + case 0x82: + case 0x84: + case 0x85: + case 0x87: + case 0x8a: + switch (p[3]) { + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x88: + break; + default: + err = 2; + pc->para.cause = 100; + break; + } + break; + default: + err = 3; + pc->para.cause = 100; + break; + } + } + } else { + pc->para.cause = 96; + err = 4; + } + if (err) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "progress error %d", err); + l3dss1_status_send(pc, pr, NULL); + return; + } + /* Now we are on none mandatory IEs */ + err = check_infoelements(pc, skb, ie_PROGRESS); + if (err) + l3dss1_std_ie_err(pc, err); + if (ERR_IE_COMPREHENSION != err) + pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc); +} - l = p - tmp; - if (!(skb = l3_alloc_skb(l))) +static void +l3dss1_notify(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int err = 0; + u_char *p; + + if ((p = findie(skb->data, skb->len, IE_NOTIFY, 0))) { + if (p[1] != 1) { + err = 1; + pc->para.cause = 100; + } else { + switch (p[2]) { + case 0x80: + case 0x81: + case 0x82: + break; + default: + pc->para.cause = 100; + err = 2; + break; + } + } + } else { + pc->para.cause = 96; + err = 3; + } + if (err) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "notify error %d", err); + l3dss1_status_send(pc, pr, NULL); return; - memcpy(skb_put(skb, l), tmp, l); - l3_msg(pc->st, DL_DATA | REQUEST, skb); + } + /* Now we are on none mandatory IEs */ + err = check_infoelements(pc, skb, ie_NOTIFY); + if (err) + l3dss1_std_ie_err(pc, err); + if (ERR_IE_COMPREHENSION != err) + pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc); } static void -l3dss1_status_req(struct l3_process *pc, u_char pr, void *arg) +l3dss1_status_enq(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; + int ret; struct sk_buff *skb = arg; - dev_kfree_skb(skb); + ret = check_infoelements(pc, skb, ie_STATUS_ENQUIRY); + l3dss1_std_ie_err(pc, ret); +// KKe 19.7.99 test eicon +// idev_kfree_skb(skb, FREE_READ); + pc->para.cause = 30; /* response to STATUS_ENQUIRY */ + l3dss1_status_send(pc, pr, NULL); +} - MsgHead(p, pc->callref, MT_STATUS); +static void +l3dss1_information(struct l3_process *pc, u_char pr, void *arg) +{ + int ret; + struct sk_buff *skb = arg; - *p++ = IE_CAUSE; - *p++ = 0x2; - *p++ = 0x80; - *p++ = 0x62 | 0x80; /* status sending */ + ret = check_infoelements(pc, skb, ie_INFORMATION); + l3dss1_std_ie_err(pc, ret); +} - *p++ = 0x14; /* CallState */ - *p++ = 0x1; - *p++ = pc->state & 0x3f; +/******************************/ +/* handle deflection requests */ +/******************************/ +static void l3dss1_redir_req(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb; + u_char tmp[128]; + u_char *p = tmp; + u_char *subp; + u_char len_phone = 0; + u_char len_sub = 0; + int l; + + + strcpy(pc->prot.dss1.uus1_data,pc->chan->setup.eazmsn); /* copy uus element if available */ + if (!pc->chan->setup.phone[0]) + { pc->para.cause = -1; + l3dss1_disconnect_req(pc,pr,arg); /* disconnect immediately */ + return; + } /* only uus */ + + if (pc->prot.dss1.invoke_id) + free_invoke_id(pc->st,pc->prot.dss1.invoke_id); + + if (!(pc->prot.dss1.invoke_id = new_invoke_id(pc->st))) + return; + + MsgHead(p, pc->callref, MT_FACILITY); + + for (subp = pc->chan->setup.phone; (*subp) && (*subp != '.'); subp++) len_phone++; /* len of phone number */ + if (*subp++ == '.') len_sub = strlen(subp) + 2; /* length including info subadress element */ + + *p++ = 0x1c; /* Facility info element */ + *p++ = len_phone + len_sub + 2 + 2 + 8 + 3 + 3; /* length of element */ + *p++ = 0x91; /* remote operations protocol */ + *p++ = 0xa1; /* invoke component */ + + *p++ = len_phone + len_sub + 2 + 2 + 8 + 3; /* length of data */ + *p++ = 0x02; /* invoke id tag, integer */ + *p++ = 0x01; /* length */ + *p++ = pc->prot.dss1.invoke_id; /* invoke id */ + *p++ = 0x02; /* operation value tag, integer */ + *p++ = 0x01; /* length */ + *p++ = 0x0D; /* Call Deflect */ + + *p++ = 0x30; /* sequence phone number */ + *p++ = len_phone + 2 + 2 + 3 + len_sub; /* length */ + + *p++ = 0x30; /* Deflected to UserNumber */ + *p++ = len_phone+2+len_sub; /* length */ + *p++ = 0x80; /* NumberDigits */ + *p++ = len_phone; /* length */ + for (l = 0; l < len_phone; l++) + *p++ = pc->chan->setup.phone[l]; + + if (len_sub) + { *p++ = 0x04; /* called party subadress */ + *p++ = len_sub - 2; + while (*subp) *p++ = *subp++; + } + + *p++ = 0x01; /* screening identifier */ + *p++ = 0x01; + *p++ = pc->chan->setup.screen; l = p - tmp; - if (!(skb = l3_alloc_skb(l))) - return; + if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - l3_msg(pc->st, DL_DATA | REQUEST, skb); -} + + l3_msg(pc->st, DL_DATA | REQUEST, skb); +} /* l3dss1_redir_req */ + +/********************************************/ +/* handle deflection request in early state */ +/********************************************/ +static void l3dss1_redir_req_early(struct l3_process *pc, u_char pr, void *arg) +{ + l3dss1_proceed_req(pc,pr,arg); + l3dss1_redir_req(pc,pr,arg); +} /* l3dss1_redir_req_early */ + +/***********************************************/ +/* handle special commands for this protocol. */ +/* Examples are call independant services like */ +/* remote operations with dummy callref. */ +/***********************************************/ +static int l3dss1_cmd_global(struct PStack *st, isdn_ctrl *ic) +{ u_char id; + u_char temp[265]; + u_char *p = temp; + int i, l, proc_len; + struct sk_buff *skb; + struct l3_process *pc = NULL; + + switch (ic->arg) + { case DSS1_CMD_INVOKE: + if (ic->parm.dss1_io.datalen < 0) return(-2); /* invalid parameter */ + + for (proc_len = 1, i = ic->parm.dss1_io.proc >> 8; i; i++) + i = i >> 8; /* add one byte */ + l = ic->parm.dss1_io.datalen + proc_len + 8; /* length excluding ie header */ + if (l > 255) + return(-2); /* too long */ + + if (!(id = new_invoke_id(st))) + return(0); /* first get a invoke id -> return if no available */ + + i = -1; + MsgHead(p, i, MT_FACILITY); /* build message head */ + *p++ = 0x1C; /* Facility IE */ + *p++ = l; /* length of ie */ + *p++ = 0x91; /* remote operations */ + *p++ = 0xA1; /* invoke */ + *p++ = l - 3; /* length of invoke */ + *p++ = 0x02; /* invoke id tag */ + *p++ = 0x01; /* length is 1 */ + *p++ = id; /* invoke id */ + *p++ = 0x02; /* operation */ + *p++ = proc_len; /* length of operation */ + + for (i = proc_len; i; i--) + *p++ = (ic->parm.dss1_io.proc >> (i-1)) & 0xFF; + memcpy(p, ic->parm.dss1_io.data, ic->parm.dss1_io.datalen); /* copy data */ + l = (p - temp) + ic->parm.dss1_io.datalen; /* total length */ + + if (ic->parm.dss1_io.timeout > 0) + if (!(pc = dss1_new_l3_process(st, -1))) + { free_invoke_id(st, id); + return(-2); + } + pc->prot.dss1.ll_id = ic->parm.dss1_io.ll_id; /* remember id */ + pc->prot.dss1.proc = ic->parm.dss1_io.proc; /* and procedure */ + + if (!(skb = l3_alloc_skb(l))) + { free_invoke_id(st, id); + if (pc) dss1_release_l3_process(pc); + return(-2); + } + memcpy(skb_put(skb, l), temp, l); + + if (pc) + { pc->prot.dss1.invoke_id = id; /* remember id */ + L3AddTimer(&pc->timer, ic->parm.dss1_io.timeout, CC_TDSS1_IO | REQUEST); + } + + l3_msg(st, DL_DATA | REQUEST, skb); + ic->parm.dss1_io.hl_id = id; /* return id */ + return(0); + + case DSS1_CMD_INVOKE_ABORT: + if ((pc = l3dss1_search_dummy_proc(st, ic->parm.dss1_io.hl_id))) + { L3DelTimer(&pc->timer); /* remove timer */ + dss1_release_l3_process(pc); + return(0); + } + else + { l3_debug(st, "l3dss1_cmd_global abort unknown id"); + return(-2); + } + break; + + default: + l3_debug(st, "l3dss1_cmd_global unknown cmd 0x%lx", ic->arg); + return(-1); + } /* switch ic-> arg */ + return(-1); +} /* l3dss1_cmd_global */ + +static void +l3dss1_io_timer(struct l3_process *pc) +{ isdn_ctrl ic; + struct IsdnCardState *cs = pc->st->l1.hardware; + + L3DelTimer(&pc->timer); /* remove timer */ + + ic.driver = cs->myid; + ic.command = ISDN_STAT_PROT; + ic.arg = DSS1_STAT_INVOKE_ERR; + ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id; + ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id; + ic.parm.dss1_io.proc = pc->prot.dss1.proc; + ic.parm.dss1_io.timeout= -1; + ic.parm.dss1_io.datalen = 0; + ic.parm.dss1_io.data = NULL; + free_invoke_id(pc->st, pc->prot.dss1.invoke_id); + pc->prot.dss1.invoke_id = 0; /* reset id */ + + cs->iif.statcallb(&ic); + + dss1_release_l3_process(pc); +} /* l3dss1_io_timer */ static void l3dss1_release_ind(struct l3_process *pc, u_char pr, void *arg) @@ -1241,12 +2333,17 @@ l3dss1_release_ind(struct l3_process *pc, u_char pr, void *arg) */ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); newl3state(pc, 0); - release_l3_process(pc); + dss1_release_l3_process(pc); } else { pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc); } } +static void +l3dss1_dummy(struct l3_process *pc, u_char pr, void *arg) +{ +} + static void l3dss1_t303(struct l3_process *pc, u_char pr, void *arg) { @@ -1256,8 +2353,9 @@ l3dss1_t303(struct l3_process *pc, u_char pr, void *arg) l3dss1_setup_req(pc, pr, arg); } else { L3DelTimer(&pc->timer); + l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, 102); pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc); - release_l3_process(pc); + dss1_release_l3_process(pc); } } @@ -1265,7 +2363,7 @@ static void l3dss1_t304(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->para.cause = 0xE6; + pc->para.cause = 102; l3dss1_disconnect_req(pc, pr, NULL); pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); @@ -1278,18 +2376,18 @@ l3dss1_t305(struct l3_process *pc, u_char pr, void *arg) u_char *p = tmp; int l; struct sk_buff *skb; - u_char cause = 0x90; + u_char cause = 16; L3DelTimer(&pc->timer); - if (pc->para.cause > 0) - cause = pc->para.cause | 0x80; + if (pc->para.cause != NO_CAUSE) + cause = pc->para.cause; MsgHead(p, pc->callref, MT_RELEASE); *p++ = IE_CAUSE; *p++ = 0x2; *p++ = 0x80; - *p++ = cause; + *p++ = cause | 0x80; l = p - tmp; if (!(skb = l3_alloc_skb(l))) @@ -1304,7 +2402,7 @@ static void l3dss1_t310(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->para.cause = 0xE6; + pc->para.cause = 102; l3dss1_disconnect_req(pc, pr, NULL); pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); } @@ -1313,7 +2411,7 @@ static void l3dss1_t313(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->para.cause = 0xE6; + pc->para.cause = 102; l3dss1_disconnect_req(pc, pr, NULL); pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc); } @@ -1332,14 +2430,14 @@ l3dss1_t308_2(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc); - release_l3_process(pc); + dss1_release_l3_process(pc); } static void l3dss1_t318(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->para.cause = 0x66; /* Timer expiry */ + pc->para.cause = 102; /* Timer expiry */ pc->para.loc = 0; /* local */ pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc); newl3state(pc, 19); @@ -1351,7 +2449,7 @@ static void l3dss1_t319(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->para.cause = 0x66; /* Timer expiry */ + pc->para.cause = 102; /* Timer expiry */ pc->para.loc = 0; /* local */ pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc); newl3state(pc, 10); @@ -1361,69 +2459,80 @@ static void l3dss1_restart(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->st->l3.l3l4(pc->st, CC_DLRL | INDICATION, pc); - release_l3_process(pc); + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + dss1_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))) { + int ret; + u_char cause = 0, callState = 0; + + if ((ret = l3dss1_get_cause(pc, skb))) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "STATUS get_cause ret(%d)",ret); + if (ret < 0) + cause = 96; + else if (ret > 0) + cause = 100; + } + if ((p = findie(skb->data, skb->len, IE_CALL_STATE, 0))) { p++; if (1 == *p++) { callState = *p; - t += sprintf(t, "peer state %x", *p); + if (!ie_in_set(pc, *p, l3_valid_states)) + cause = 100; } else - t += sprintf(t, "peer state len error"); + cause = 100; } else - sprintf(t, "no peer state"); - l3_debug(pc->st, tmp); - if (((cause & 0x7f) == 0x6f) && (callState == 0)) { + cause = 96; + if (!cause) { /* no error before */ + ret = check_infoelements(pc, skb, ie_STATUS); + if (ERR_IE_COMPREHENSION == ret) + cause = 96; + else if (ERR_IE_UNRECOGNIZED == ret) + cause = 99; + } + if (cause) { + u_char tmp; + + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "STATUS error(%d/%d)",ret,cause); + tmp = pc->para.cause; + pc->para.cause = cause; + l3dss1_status_send(pc, 0, NULL); + if (cause == 99) + pc->para.cause = tmp; + else + return; + } + cause = pc->para.cause; + if (((cause & 0x7f) == 111) && (callState == 0)) { /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... - * if received MT_STATUS with cause == 0x6f and call + * if received MT_STATUS with cause == 111 and call * state == 0, then we must set down layer 3 */ - l3dss1_release_ind(pc, pr, arg); - } else - dev_kfree_skb(skb); + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + newl3state(pc, 0); + dss1_release_l3_process(pc); + } } 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))) { -#if HISAX_DE_AOC - l3dss1_parse_facility(pc, p); -#else - p = NULL; -#endif + int ret; + + ret = check_infoelements(pc, skb, ie_FACILITY); + l3dss1_std_ie_err(pc, ret); + { + u_char *p; + if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) + l3dss1_parse_facility(pc->st, pc, pc->callref, p); } } @@ -1438,14 +2547,14 @@ l3dss1_suspend_req(struct l3_process *pc, u_char pr, void *arg) MsgHead(p, pc->callref, MT_SUSPEND); - *p++ = IE_CALLID; + *p++ = IE_CALL_ID; l = *msg++; if (l && (l <= 10)) { /* Max length 10 octets */ *p++ = l; for (i = 0; i < l; i++) *p++ = *msg++; } else { - l3_debug(pc->st, "SUS wrong CALLID len %d", l); + l3_debug(pc->st, "SUS wrong CALL_ID len %d", l); return; } l = p - tmp; @@ -1461,34 +2570,45 @@ static void l3dss1_suspend_ack(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; + int ret; L3DelTimer(&pc->timer); newl3state(pc, 0); - dev_kfree_skb(skb); - pc->para.cause = -1; + pc->para.cause = NO_CAUSE; pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc); - release_l3_process(pc); + /* We don't handle suspend_ack for IE errors now */ + if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE))) + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "SUSPACK check ie(%d)",ret); + dss1_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; + int ret; - 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; + if ((ret = l3dss1_get_cause(pc, skb))) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "SUSP_REJ get_cause ret(%d)",ret); + if (ret < 0) + pc->para.cause = 96; + else + pc->para.cause = 100; + l3dss1_status_send(pc, pr, NULL); + return; + } + ret = check_infoelements(pc, skb, ie_SUSPEND_REJECT); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + return; } - dev_kfree_skb(skb); - pc->para.cause = cause; + L3DelTimer(&pc->timer); pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc); newl3state(pc, 10); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, ret); } static void @@ -1502,14 +2622,14 @@ l3dss1_resume_req(struct l3_process *pc, u_char pr, void *arg) MsgHead(p, pc->callref, MT_RESUME); - *p++ = IE_CALLID; + *p++ = IE_CALL_ID; l = *msg++; if (l && (l <= 10)) { /* Max length 10 octets */ *p++ = l; for (i = 0; i < l; i++) *p++ = *msg++; } else { - l3_debug(pc->st, "RES wrong CALLID len %d", l); + l3_debug(pc->st, "RES wrong CALL_ID len %d", l); return; } l = p - tmp; @@ -1518,48 +2638,70 @@ l3dss1_resume_req(struct l3_process *pc, u_char pr, void *arg) memcpy(skb_put(skb, l), tmp, l); l3_msg(pc->st, DL_DATA | REQUEST, skb); newl3state(pc, 17); - L3AddTimer(&pc->timer, T319, CC_T319); + L3AddTimer(&pc->timer, T318, CC_T318); } static void l3dss1_resume_ack(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; struct sk_buff *skb = arg; - + int id, ret; + + if ((id = l3dss1_get_channel_id(pc, skb)) > 0) { + if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "resume ack with wrong chid %x", id); + pc->para.cause = 100; + l3dss1_status_send(pc, pr, NULL); + return; + } + pc->para.bchannel = id; + } else if (1 == pc->state) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "resume ack without chid (ret %d)", id); + pc->para.cause = 96; + l3dss1_status_send(pc, pr, NULL); + return; + } + ret = check_infoelements(pc, skb, ie_RESUME_ACKNOWLEDGE); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + return; + } 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); pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc); newl3state(pc, 10); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, ret); } 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; + int ret; - 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; + if ((ret = l3dss1_get_cause(pc, skb))) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "RES_REJ get_cause ret(%d)",ret); + if (ret < 0) + pc->para.cause = 96; + else + pc->para.cause = 100; + l3dss1_status_send(pc, pr, NULL); + return; } - dev_kfree_skb(skb); - pc->para.cause = cause; - newl3state(pc, 0); + ret = check_infoelements(pc, skb, ie_RESUME_REJECT); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + return; + } + L3DelTimer(&pc->timer); pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc); - release_l3_process(pc); + newl3state(pc, 0); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, ret); + dss1_release_l3_process(pc); } static void @@ -1589,7 +2731,6 @@ l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg) if (pc->st->l3.debug) l3_debug(pc->st, "Restart for channel %d", chan); } - dev_kfree_skb(skb); newl3state(pc, 2); up = pc->st->l3.proc; while (up) { @@ -1616,6 +2757,41 @@ l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg) newl3state(pc, 0); l3_msg(pc->st, DL_DATA | REQUEST, skb); } + +static void +l3dss1_dl_reset(struct l3_process *pc, u_char pr, void *arg) +{ + pc->para.cause = 0x29; /* Temporary failure */ + l3dss1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); +} + +static void +l3dss1_dl_release(struct l3_process *pc, u_char pr, void *arg) +{ + newl3state(pc, 0); + pc->para.cause = 0x1b; /* Destination out of order */ + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + release_l3_process(pc); +} + +static void +l3dss1_dl_reestablish(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T309, CC_T309); + l3_msg(pc->st, DL_ESTABLISH | REQUEST, NULL); +} + +static void +l3dss1_dl_reest_status(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + + pc->para.cause = 0x1F; /* normal, unspecified */ + l3dss1_status_send(pc, 0, NULL); +} + /* *INDENT-OFF* */ static struct stateentry downstatelist[] = { @@ -1623,12 +2799,10 @@ static struct stateentry downstatelist[] = 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), + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10), CC_DISCONNECT | REQUEST, l3dss1_disconnect_req}, {SBIT(12), CC_RELEASE | REQUEST, l3dss1_release_req}, - {ALL_STATES, - CC_DLRL | REQUEST, l3dss1_reset}, {ALL_STATES, CC_RESTART | REQUEST, l3dss1_restart}, {SBIT(6), @@ -1636,11 +2810,21 @@ static struct stateentry downstatelist[] = {SBIT(6), CC_REJECT | REQUEST, l3dss1_reject_req}, {SBIT(6), + CC_PROCEED_SEND | REQUEST, l3dss1_proceed_req}, + {SBIT(6) | SBIT(9), CC_ALERTING | REQUEST, l3dss1_alert_req}, - {SBIT(6) | SBIT(7), + {SBIT(6) | SBIT(7) | SBIT(9), CC_SETUP | RESPONSE, l3dss1_setup_rsp}, {SBIT(10), CC_SUSPEND | REQUEST, l3dss1_suspend_req}, + {SBIT(6), + CC_PROCEED_SEND | REQUEST, l3dss1_proceed_req}, + {SBIT(7) | SBIT(9), + CC_REDIR | REQUEST, l3dss1_redir_req}, + {SBIT(6), + CC_REDIR | REQUEST, l3dss1_redir_req_early}, + {SBIT(9) | SBIT(25), + CC_DISCONNECT | REQUEST, l3dss1_disconnect_req}, {SBIT(1), CC_T303, l3dss1_t303}, {SBIT(2), @@ -1659,6 +2843,8 @@ static struct stateentry downstatelist[] = CC_T308_1, l3dss1_t308_1}, {SBIT(19), CC_T308_2, l3dss1_t308_2}, + {SBIT(10), + CC_T309, l3dss1_dl_release}, }; #define DOWNSLLEN \ @@ -1674,37 +2860,37 @@ static struct stateentry datastatelist[] = MT_STATUS, l3dss1_release_ind}, {ALL_STATES, MT_STATUS, l3dss1_status}, - {SBIT(0) | SBIT(6), + {SBIT(0), MT_SETUP, l3dss1_setup}, + {SBIT(6) | SBIT(7), + MT_SETUP, l3dss1_dummy}, {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), + {SBIT(2) | SBIT(3), MT_ALERTING, l3dss1_alerting}, - {SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), - MT_ALERTING, l3dss1_status_req}, + {SBIT(2) | SBIT(3), + MT_PROGRESS, l3dss1_progress}, + {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | + SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), + MT_INFORMATION, l3dss1_information}, + {SBIT(10) | SBIT(11) | SBIT(15), + MT_NOTIFY, l3dss1_notify}, {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(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), MT_RELEASE_COMPLETE, l3dss1_release_cmpl}, - {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(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(25), MT_RELEASE, l3dss1_release}, {SBIT(19), MT_RELEASE, l3dss1_release_ind}, - {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | SBIT(15), + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(25), MT_DISCONNECT, l3dss1_disconnect}, - {SBIT(11), - MT_DISCONNECT, l3dss1_release_req}, +// {SBIT(11), +// MT_DISCONNECT, l3dss1_release_req}, + {SBIT(19), + MT_DISCONNECT, l3dss1_dummy}, {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), @@ -1715,8 +2901,6 @@ static struct stateentry datastatelist[] = 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}, }; #define DATASLLEN \ @@ -1734,26 +2918,56 @@ static struct stateentry globalmes_list[] = }; #define GLOBALM_LEN \ (sizeof(globalmes_list) / sizeof(struct stateentry)) + +static struct stateentry manstatelist[] = +{ + {SBIT(2), + DL_ESTABLISH | INDICATION, l3dss1_dl_reset}, + {SBIT(10), + DL_ESTABLISH | CONFIRM, l3dss1_dl_reest_status}, + {SBIT(10), + DL_RELEASE | INDICATION, l3dss1_dl_reestablish}, + {ALL_STATES, + DL_RELEASE | INDICATION, l3dss1_dl_release}, +}; + +#define MANSLLEN \ + (sizeof(manstatelist) / sizeof(struct stateentry)) /* *INDENT-ON* */ static void global_handler(struct PStack *st, int mt, struct sk_buff *skb) { + u_char tmp[16]; + u_char *p = tmp; + int l; int i; struct l3_process *proc = st->l3.global; + proc->callref = skb->data[2]; /* cr flag */ for (i = 0; i < GLOBALM_LEN; i++) if ((mt == globalmes_list[i].primitive) && ((1 << proc->state) & globalmes_list[i].state)) break; if (i == GLOBALM_LEN) { - dev_kfree_skb(skb); if (st->l3.debug & L3_DEB_STATE) { l3_debug(st, "dss1 global state %d mt %x unhandled", proc->state, mt); } - return; + MsgHead(p, proc->callref, MT_STATUS); + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = 81 |0x80; /* invalid cr */ + *p++ = 0x14; /* CallState */ + *p++ = 0x1; + *p++ = proc->state & 0x3f; + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + l3_msg(proc->st, DL_DATA | REQUEST, skb); } else { if (st->l3.debug & L3_DEB_STATE) { l3_debug(st, "dss1 global %d mt %x", @@ -1768,6 +2982,7 @@ dss1up(struct PStack *st, int pr, void *arg) { int i, mt, cr, cause, callState; char *ptr; + u_char *p; struct sk_buff *skb = arg; struct l3_process *proc; @@ -1782,23 +2997,57 @@ dss1up(struct PStack *st, int pr, void *arg) l3_msg(st, pr, arg); return; break; + default: + printk(KERN_ERR "HiSax dss1up unknown pr=%04x\n", pr); + return; + } + if (skb->len < 3) { + l3_debug(st, "dss1up frame too short(%d)", skb->len); + idev_kfree_skb(skb, FREE_READ); + return; } + if (skb->data[0] != PROTO_DIS_EURO) { if (st->l3.debug & L3_DEB_PROTERR) { l3_debug(st, "dss1up%sunexpected discriminator %x message len %d", (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", skb->data[0], skb->len); } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } cr = getcallref(skb->data); + if (skb->len < ((skb->data[1] & 0x0f) + 3)) { + l3_debug(st, "dss1up frame too short(%d)", skb->len); + idev_kfree_skb(skb, FREE_READ); + return; + } mt = skb->data[skb->data[1] + 2]; - if (!cr) { /* Global CallRef */ - global_handler(st, mt, skb); + if (st->l3.debug & L3_DEB_STATE) + l3_debug(st, "dss1up cr %d", cr); + if (cr == -2) { /* wrong Callref */ + if (st->l3.debug & L3_DEB_WARN) + l3_debug(st, "dss1up wrong Callref"); + idev_kfree_skb(skb, FREE_READ); return; } else if (cr == -1) { /* Dummy Callref */ - dev_kfree_skb(skb); + if (mt == MT_FACILITY) + if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) { + l3dss1_parse_facility(st, NULL, + (pr == (DL_DATA | INDICATION)) ? -1 : -2, p); + idev_kfree_skb(skb, FREE_READ); + return; + } + if (st->l3.debug & L3_DEB_WARN) + l3_debug(st, "dss1up dummy Callref (no facility msg or ie)"); + idev_kfree_skb(skb, FREE_READ); + return; + } else if ((((skb->data[1] & 0x0f) == 1) && (0==(cr & 0x7f))) || + (((skb->data[1] & 0x0f) == 2) && (0==(cr & 0x7fff)))) { /* Global CallRef */ + if (st->l3.debug & L3_DEB_STATE) + l3_debug(st, "dss1up Global CallRef"); + global_handler(st, mt, skb); + idev_kfree_skb(skb, FREE_READ); return; } else if (!(proc = getl3proc(st, cr))) { /* No transaction process exist, that means no call with @@ -1806,12 +3055,19 @@ dss1up(struct PStack *st, int pr, void *arg) */ if (mt == MT_SETUP) { /* Setup creates a new transaction process */ - if (!(proc = new_l3_process(st, cr))) { + if (skb->data[2] & 0x80) { + /* Setup with wrong CREF flag */ + if (st->l3.debug & L3_DEB_STATE) + l3_debug(st, "dss1up wrong CRef flag"); + idev_kfree_skb(skb, FREE_READ); + return; + } + if (!(proc = dss1_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); + idev_kfree_skb(skb, FREE_READ); return; } } else if (mt == MT_STATUS) { @@ -1829,62 +3085,60 @@ dss1up(struct PStack *st, int pr, void *arg) ptr++; callState = *ptr; } - if (callState == 0) { - /* ETS 300-104 part 2.4.1 - * if setup has not been made and a message type - * MT_STATUS is received with call state == 0, - * we must send nothing - */ - dev_kfree_skb(skb); - return; - } else { + /* ETS 300-104 part 2.4.1 + * if setup has not been made and a message type + * MT_STATUS is received with call state == 0, + * we must send nothing + */ + if (callState != 0) { /* ETS 300-104 part 2.4.2 * if setup has not been made and a message type * MT_STATUS is received with call state != 0, * we must send MT_RELEASE_COMPLETE cause 101 */ - dev_kfree_skb(skb); - if ((proc = new_l3_process(st, cr))) { - proc->para.cause = 0x65; /* 101 */ + if ((proc = dss1_new_l3_process(st, cr))) { + proc->para.cause = 101; l3dss1_msg_without_setup(proc, 0, NULL); } - return; } + idev_kfree_skb(skb, FREE_READ); + return; } else if (mt == MT_RELEASE_COMPLETE) { - dev_kfree_skb(skb); + idev_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); - if ((proc = new_l3_process(st, cr))) { - proc->para.cause = 0x51; /* 81 */ + idev_kfree_skb(skb, FREE_READ); + if ((proc = dss1_new_l3_process(st, cr))) { + proc->para.cause = 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 ;-) */ } + if (l3dss1_check_messagetype_validity(proc, mt, skb)) { + idev_kfree_skb(skb, FREE_READ); + return; + } + if ((p = findie(skb->data, skb->len, IE_DISPLAY, 0)) != NULL) + l3dss1_deliver_display(proc, pr, p); /* Display IE included */ for (i = 0; i < DATASLLEN; i++) if ((mt == datastatelist[i].primitive) && ((1 << proc->state) & datastatelist[i].state)) break; if (i == DATASLLEN) { - dev_kfree_skb(skb); if (st->l3.debug & L3_DEB_STATE) { - l3_debug(st, "dss1up%sstate %d mt %x unhandled", + l3_debug(st, "dss1up%sstate %d mt %#x unhandled", (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", proc->state, mt); } - return; + if ((MT_RELEASE_COMPLETE != mt) && (MT_RELEASE != mt)) { + proc->para.cause = 101; + l3dss1_status_send(proc, pr, skb); + } } else { if (st->l3.debug & L3_DEB_STATE) { l3_debug(st, "dss1up%sstate %d mt %x", @@ -1893,6 +3147,8 @@ dss1up(struct PStack *st, int pr, void *arg) } datastatelist[i].rout(proc, pr, skb); } + idev_kfree_skb(skb, FREE_READ); + return; } static void @@ -1902,14 +3158,14 @@ dss1down(struct PStack *st, int pr, void *arg) struct l3_process *proc; struct Channel *chan; - if (((DL_ESTABLISH | REQUEST) == pr) || ((DL_RELEASE | REQUEST) == pr)) { + if ((DL_ESTABLISH | 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))) { + if ((proc = dss1_new_l3_process(st, cr))) { proc->chan = chan; chan->proc = proc; proc->para.setup = chan->setup; @@ -1922,32 +3178,75 @@ dss1down(struct PStack *st, int pr, void *arg) printk(KERN_ERR "HiSax dss1down without proc pr=%04x\n", pr); return; } + + if ( pr == (CC_TDSS1_IO | REQUEST)) { + l3dss1_io_timer(proc); /* timer expires */ + return; + } + for (i = 0; i < DOWNSLLEN; i++) if ((pr == downstatelist[i].primitive) && ((1 << proc->state) & downstatelist[i].state)) break; if (i == DOWNSLLEN) { if (st->l3.debug & L3_DEB_STATE) { - l3_debug(st, "dss1down state %d prim %d unhandled", + l3_debug(st, "dss1down state %d prim %#x unhandled", proc->state, pr); } } else { if (st->l3.debug & L3_DEB_STATE) { - l3_debug(st, "dss1down state %d prim %d", + l3_debug(st, "dss1down state %d prim %#x", proc->state, pr); } downstatelist[i].rout(proc, pr, arg); } } +static void +dss1man(struct PStack *st, int pr, void *arg) +{ + int i; + struct l3_process *proc = arg; + + if (!proc) { + printk(KERN_ERR "HiSax dss1man without proc pr=%04x\n", pr); + return; + } + for (i = 0; i < MANSLLEN; i++) + if ((pr == manstatelist[i].primitive) && + ((1 << proc->state) & manstatelist[i].state)) + break; + if (i == MANSLLEN) { + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "cr %d dss1man state %d prim %#x unhandled", + proc->callref & 0x7f, proc->state, pr); + } + } else { + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "cr %d dss1man state %d prim %#x", + proc->callref & 0x7f, proc->state, pr); + } + manstatelist[i].rout(proc, pr, arg); + } +} + void setstack_dss1(struct PStack *st) { char tmp[64]; + int i; st->lli.l4l3 = dss1down; + st->lli.l4l3_proto = l3dss1_cmd_global; st->l2.l2l3 = dss1up; + st->l3.l3ml3 = dss1man; st->l3.N303 = 1; + st->prot.dss1.last_invoke_id = 0; + st->prot.dss1.invoke_used[0] = 1; /* Bit 0 must always be set to 1 */ + i = 1; + while (i < 32) + st->prot.dss1.invoke_used[i++] = 0; + 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 { @@ -1957,6 +3256,8 @@ setstack_dss1(struct PStack *st) st->l3.global->debug = L3_DEB_WARN; st->l3.global->st = st; st->l3.global->N303 = 1; + st->l3.global->prot.dss1.invoke_id = 0; + L3InitTimer(st->l3.global, &st->l3.global->timer); } strcpy(tmp, dss1_revision); diff --git a/drivers/isdn/hisax/l3dss1.h b/drivers/isdn/hisax/l3dss1.h index 10e612482975..268b5376face 100644 --- a/drivers/isdn/hisax/l3dss1.h +++ b/drivers/isdn/hisax/l3dss1.h @@ -1,8 +1,11 @@ -/* $Id: l3dss1.h,v 1.6 1998/03/19 13:18:50 keil Exp $ +/* $Id: l3dss1.h,v 1.7 1999/07/01 08:12:02 keil Exp $ * * DSS1 (Euro) D-channel protocol defines * * $Log: l3dss1.h,v $ + * Revision 1.7 1999/07/01 08:12:02 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.6 1998/03/19 13:18:50 keil * Start of a CAPI like interface for supplementary Service * first service: SUSPEND @@ -25,10 +28,14 @@ * * */ + +#ifndef l3dss1_process + #define T303 4000 #define T304 30000 #define T305 30000 #define T308 4000 +#define T309 40000 #define T310 30000 #define T313 4000 #define T318 4000 @@ -38,39 +45,99 @@ * 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_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_SEGMENT 0x00 +#define IE_BEARER 0x04 +#define IE_CAUSE 0x08 +#define IE_CALL_ID 0x10 +#define IE_CALL_STATE 0x14 +#define IE_CHANNEL_ID 0x18 +#define IE_FACILITY 0x1c +#define IE_PROGRESS 0x1e +#define IE_NET_FAC 0x20 +#define IE_NOTIFY 0x27 +#define IE_DISPLAY 0x28 +#define IE_DATE 0x29 +#define IE_KEYPAD 0x2c +#define IE_SIGNAL 0x34 +#define IE_INFORATE 0x40 +#define IE_E2E_TDELAY 0x42 +#define IE_TDELAY_SEL 0x43 +#define IE_PACK_BINPARA 0x44 +#define IE_PACK_WINSIZE 0x45 +#define IE_PACK_SIZE 0x46 +#define IE_CUG 0x47 +#define IE_REV_CHARGE 0x4a +#define IE_CONNECT_PN 0x4c +#define IE_CONNECT_SUB 0x4d +#define IE_CALLING_PN 0x6c +#define IE_CALLING_SUB 0x6d +#define IE_CALLED_PN 0x70 +#define IE_CALLED_SUB 0x71 +#define IE_REDIR_NR 0x74 +#define IE_TRANS_SEL 0x78 +#define IE_RESTART_IND 0x79 +#define IE_LLC 0x7c +#define IE_HLC 0x7d +#define IE_USER_USER 0x7e +#define IE_ESCAPE 0x7f +#define IE_SHIFT 0x90 +#define IE_MORE_DATA 0xa0 +#define IE_COMPLETE 0xa1 +#define IE_CONGESTION 0xb0 +#define IE_REPEAT 0xd0 + +#define IE_MANDATORY 0x0100 +/* mandatory not in every case */ +#define IE_MANDATORY_1 0x0200 + +#define ERR_IE_COMPREHENSION 1 +#define ERR_IE_UNRECOGNIZED -1 +#define ERR_IE_LENGTH -2 +#define ERR_IE_SEQUENCE -3 + +#else /* only l3dss1_process */ + +/* l3dss1 specific data in l3 process */ +typedef struct + { unsigned char invoke_id; /* used invoke id in remote ops, 0 = not active */ + ulong ll_id; /* remebered ll id */ + u_char remote_operation; /* handled remote operation, 0 = not active */ + int proc; /* rememered procedure */ + ulong remote_result; /* result of remote operation for statcallb */ + char uus1_data[35]; /* data send during alerting or disconnect */ + } dss1_proc_priv; -#define MT_INVALID 0xff +/* l3dss1 specific data in protocol stack */ +typedef struct + { unsigned char last_invoke_id; /* last used value for invoking */ + unsigned char invoke_used[32]; /* 256 bits for 256 values */ + } dss1_stk_priv; -#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 +#endif /* only l3dss1_process */ diff --git a/drivers/isdn/hisax/lmgr.c b/drivers/isdn/hisax/lmgr.c index d6ef0b63013c..6097fe54797c 100644 --- a/drivers/isdn/hisax/lmgr.c +++ b/drivers/isdn/hisax/lmgr.c @@ -1,4 +1,4 @@ -/* $Id: lmgr.c,v 1.5 1998/11/15 23:55:12 keil Exp $ +/* $Id: lmgr.c,v 1.6 1999/07/01 08:12:04 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * @@ -6,6 +6,9 @@ * Layermanagement module * * $Log: lmgr.c,v $ + * Revision 1.6 1999/07/01 08:12:04 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.5 1998/11/15 23:55:12 keil * changes from 2.0 * @@ -50,7 +53,7 @@ hisax_manager(struct PStack *st, int pr, void *arg) case (MDL_ERROR | INDICATION): Code = (long) arg; HiSax_putstatus(st->l1.hardware, "manager: MDL_ERROR", - "%c %s\n", (char)Code, + " %c %s", (char)Code, test_bit(FLG_LAPD, &st->l2.flag) ? "D-channel" : "B-channel"); if (test_bit(FLG_LAPD, &st->l2.flag)) diff --git a/drivers/isdn/hisax/md5sums.asc b/drivers/isdn/hisax/md5sums.asc index 49888c55d38f..7fe2bbbae56e 100644 --- a/drivers/isdn/hisax/md5sums.asc +++ b/drivers/isdn/hisax/md5sums.asc @@ -2,28 +2,30 @@ # This are valid md5sums for certificated HiSax driver. # The certification is valid only if the md5sums of all files match. -# The certification is valid only for ELSA QuickStep cards in the moment. +# The certification is valid only for ELSA QuickStep cards and +# Eicon Technology Diva 2.01 PCI cards in the moment. # Read ../../../Documentation/isdn/HiSax.cert for more informations. # -a273c532aec063574273ee519975cd9a isac.c -27c5c5bfa2ceabf02e2e6d686b03abde isdnl1.c -8c89ac659d3188ab997fb575da22b566 isdnl2.c -d0fa912aa284b8fd19fed86b65999f6f isdnl3.c -1bce120740b615006286ad9b2d7fcdcb tei.c -8845f88dd17917d9b58badeff1605057 callc.c -f3ec2a634f06074d16167aaba02b6dc1 cert.c -71840ec8189f42b0db86fb38e5e5984c l3dss1.c -1882de8bea921b9ccd98fbe77267aa04 l3_1tr6.c -3bd7af3a11693d028300278744d0da09 elsa.c +d93f31e02c1b153ec04d16f69e5688b3 isac.c +e2a78c07f32c8ca7c88fc9f92a87dfab isdnl1.c +54490c4f46a998ff4ef34287bc262185 isdnl2.c +f4184a50e35e5b568608e6cb7a693319 isdnl3.c +ef70f4269fdc2ca15100f9b776afaa0d tei.c +cf3923304983e9d64cf35bc5a3533f6c callc.c +bf9605b36429898f7be6630034e83230 cert.c +309261e4c36d950db6978440e8bc8342 l3dss1.c +b674eee9314a7cc413971c84003cf1d2 l3_1tr6.c +8f86d92f43ecc42f6457773168cfc114 elsa.c +24cda374da44b57f6a1bb215424267b5 diva.c # end of md5sums -----BEGIN PGP SIGNATURE----- Version: 2.6.3i Charset: noconv -iQCVAwUBNrl5JDpxHvX/mS9tAQHm8wP+Nk64UJ2abdDG/igXZSrwcYhX/Kp7cxt9 -ccYp+aaur+pALA0lxwY3xcLt9u36fCYuTLHAVmQoiC9Vbemj37yzM2rUpz9nkw/7 -D6gLqZs2jxVpAwVVJgp0JwDONKXaRX6Lt2EPD9PTW6vxRWEu0HqGhM5hrtd/o4rV -mC1W7Wj13XM= -=LdhT +iQCVAwUBN7HnvDpxHvX/mS9tAQFANgP+LGuG98lvCv97vN2dc6T/6hZTxFW+WirJ +XMhU5NHoZ+8MISMOVKB7ugsGO9cJI0lUA0sOe8jtPCo5070nF1ZkNsxV/x7WK2dS +RwXfHy6+TAH7qIiBnkP9odB2lib+VFl/nnkkTwsXfVwRCD8bLaagMPv+nAveDoNE +uff0xxXEnJw= +=Wu2M -----END PGP SIGNATURE----- diff --git a/drivers/isdn/hisax/mic.c b/drivers/isdn/hisax/mic.c index 4f147462487f..be65f1101ca0 100644 --- a/drivers/isdn/hisax/mic.c +++ b/drivers/isdn/hisax/mic.c @@ -1,4 +1,4 @@ -/* $Id: mic.c,v 1.7 1998/04/15 16:44:32 keil Exp $ +/* $Id: mic.c,v 1.8 1999/07/12 21:05:20 keil Exp $ * mic.c low level stuff for mic cards * @@ -8,6 +8,10 @@ * * * $Log: mic.c,v $ + * Revision 1.8 1999/07/12 21:05:20 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * * Revision 1.7 1998/04/15 16:44:32 keil * new init code * @@ -40,7 +44,7 @@ extern const char *CardType[]; -const char *mic_revision = "$Revision: 1.7 $"; +const char *mic_revision = "$Revision: 1.8 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -158,7 +162,7 @@ static void mic_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; if (!cs) { printk(KERN_WARNING "mic: Spurious interrupt!\n"); @@ -166,16 +170,12 @@ mic_interrupt(int intno, void *dev_id, struct pt_regs *regs) } val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40); Start_HSCX: - if (val) { + 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) { + 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) @@ -188,16 +188,12 @@ mic_interrupt(int intno, void *dev_id, struct pt_regs *regs) 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); - } + 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.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0x0); + 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); } void @@ -218,9 +214,6 @@ mic_card_msg(struct IsdnCardState *cs, int mt, void *arg) 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); @@ -231,8 +224,8 @@ mic_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init -setup_mic(struct IsdnCard *card) +__initfunc(int +setup_mic(struct IsdnCard *card)) { int bytecnt; struct IsdnCardState *cs = card->cs; @@ -273,6 +266,7 @@ setup_mic(struct IsdnCard *card) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &mic_card_msg; + cs->irq_func = &mic_interrupt; ISACVersion(cs, "mic:"); if (HscxVersion(cs, "mic:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/netjet.c b/drivers/isdn/hisax/netjet.c index 690e0820eeb4..7043959e9db6 100644 --- a/drivers/isdn/hisax/netjet.c +++ b/drivers/isdn/hisax/netjet.c @@ -1,4 +1,4 @@ -/* $Id: netjet.c,v 1.8 1998/11/15 23:55:14 keil Exp $ +/* $Id: netjet.c,v 1.13 1999/08/11 21:01:31 keil Exp $ * netjet.c low level stuff for Traverse Technologie NETJet ISDN cards * @@ -6,8 +6,25 @@ * * Thanks to Traverse Technologie Australia for documents and informations * - * * $Log: netjet.c,v $ + * Revision 1.13 1999/08/11 21:01:31 keil + * new PCI codefix + * + * Revision 1.12 1999/08/10 16:02:00 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.11 1999/08/07 17:32:00 keil + * Asymetric buffers for improved ping times. Interframe spacing + * fix for NJ<->NJ thoughput. Matt Henderson - www.traverse.com.au + * + * + * Revision 1.10 1999/07/12 21:05:22 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.9 1999/07/01 08:12:05 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.8 1998/11/15 23:55:14 keil * changes from 2.0 * @@ -42,12 +59,23 @@ #include "hscx.h" #include "isdnl1.h" #include +#ifndef COMPAT_HAS_NEW_PCI +#include +#endif #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.8 $"; +const char *NETjet_revision = "$Revision: 1.13 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -83,7 +111,8 @@ const char *NETjet_revision = "$Revision: 1.8 $"; #define NETJET_IRQM0_WRITE_1 0x01 #define NETJET_IRQM0_WRITE_2 0x02 -#define NETJET_DMA_SIZE 512 +#define NETJET_DMA_TXSIZE 512 +#define NETJET_DMA_RXSIZE 128 #define HDLC_ZERO_SEARCH 0 #define HDLC_FLAG_SEARCH 1 @@ -211,7 +240,7 @@ mode_tiger(struct BCState *bcs, int mode, int bc) switch (mode) { case (L1_MODE_NULL): fill_mem(bcs, bcs->hw.tiger.send, - NETJET_DMA_SIZE, bc, 0xff); + NETJET_DMA_TXSIZE, bc, 0xff); if (cs->debug & L1_DEB_HSCX) debugl1(cs, "Tiger stat rec %d/%d send %d", bcs->hw.tiger.r_tot, bcs->hw.tiger.r_err, @@ -228,7 +257,7 @@ mode_tiger(struct BCState *bcs, int mode, int bc) break; case (L1_MODE_HDLC): fill_mem(bcs, bcs->hw.tiger.send, - NETJET_DMA_SIZE, bc, 0xff); + NETJET_DMA_TXSIZE, bc, 0xff); bcs->hw.tiger.r_state = HDLC_ZERO_SEARCH; bcs->hw.tiger.r_tot = 0; bcs->hw.tiger.r_bitcnt = 0; @@ -237,14 +266,14 @@ mode_tiger(struct BCState *bcs, int mode, int bc) bcs->hw.tiger.s_tot = 0; if (! cs->hw.njet.dmactrl) { fill_mem(bcs, bcs->hw.tiger.send, - NETJET_DMA_SIZE, !bc, 0xff); + NETJET_DMA_TXSIZE, !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; + bcs->hw.tiger.free = NETJET_DMA_TXSIZE; test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag); break; } @@ -363,6 +392,7 @@ static int make_raw_data(struct BCState *bcs) { s_val |= 0x80; } bcs->hw.tiger.sendbuf[s_cnt++] = s_val; + bcs->hw.tiger.sendbuf[s_cnt++] = 0xff; // NJ<->NJ thoughput bug fix } bcs->hw.tiger.sendcnt = s_cnt; bcs->tx_cnt -= bcs->tx_skb->len; @@ -376,6 +406,7 @@ static void got_frame(struct BCState *bcs, int count) { 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); } @@ -393,7 +424,7 @@ 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; + u_int *pend = bcs->hw.tiger.rec +NETJET_DMA_RXSIZE -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; @@ -548,7 +579,7 @@ static void read_raw(struct BCState *bcs, u_int *buf, int cnt){ static void read_tiger(struct IsdnCardState *cs) { u_int *p; - int cnt = NETJET_DMA_SIZE/2; + int cnt = NETJET_DMA_RXSIZE/2; if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_READ) { debugl1(cs,"tiger warn read double dma %x/%x", @@ -559,7 +590,7 @@ static void read_tiger(struct IsdnCardState *cs) { 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; + p = cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1; else p = cs->bcs[0].hw.tiger.rec + cnt - 1; if (cs->bcs[0].mode == L1_MODE_HDLC) @@ -616,12 +647,12 @@ static void fill_dma(struct BCState *bcs) cnt = bcs->hw.tiger.s_end - p; if (cnt < 2) { p = bcs->hw.tiger.send + 1; - cnt = NETJET_DMA_SIZE/2 - 2; + cnt = NETJET_DMA_TXSIZE/2 - 2; } else { p++; p++; - if (cnt <= (NETJET_DMA_SIZE/2)) - cnt += NETJET_DMA_SIZE/2; + if (cnt <= (NETJET_DMA_TXSIZE/2)) + cnt += NETJET_DMA_TXSIZE/2; cnt--; cnt--; } @@ -674,12 +705,12 @@ static void write_raw(struct BCState *bcs, u_int *buf, int cnt) { if (bcs->st->lli.l1writewakeup && (PACKET_NOACK != bcs->tx_skb->pkt_type)) bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len); - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->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)) + if (bcs->hw.tiger.free > (NETJET_DMA_TXSIZE/2)) test_and_set_bit(BC_FLG_HALF, &bcs->Flag); else { test_and_clear_bit(BC_FLG_HALF, &bcs->Flag); @@ -719,7 +750,7 @@ static void write_raw(struct BCState *bcs, u_int *buf, int cnt) { } static void write_tiger(struct IsdnCardState *cs) { - u_int *p, cnt = NETJET_DMA_SIZE/2; + u_int *p, cnt = NETJET_DMA_TXSIZE/2; if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_WRITE) { debugl1(cs,"tiger warn write double dma %x/%x", @@ -730,7 +761,7 @@ static void write_tiger(struct IsdnCardState *cs) { 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; + p = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1; else p = cs->bcs[0].hw.tiger.send + cnt - 1; if (cs->bcs[0].mode == L1_MODE_HDLC) @@ -811,7 +842,7 @@ close_tigerstate(struct BCState *bcs) discard_queue(&bcs->rqueue); discard_queue(&bcs->squeue); if (bcs->tx_skb) { - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); } @@ -858,45 +889,45 @@ setstack_tiger(struct PStack *st, struct BCState *bcs) } -void __init -inittiger(struct IsdnCardState *cs) +__initfunc(void +inittiger(struct IsdnCardState *cs)) { - if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_SIZE * sizeof(unsigned int), + if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_TXSIZE * 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[0].hw.tiger.s_irq = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE/2 - 1; + cs->bcs[0].hw.tiger.s_end = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 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)); + memset(cs->bcs[0].hw.tiger.send, 0xff, NETJET_DMA_TXSIZE * sizeof(unsigned int)); debugl1(cs, "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)); + (u_int)(cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1)); 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), + if (!(cs->bcs[0].hw.tiger.rec = kmalloc(NETJET_DMA_RXSIZE * sizeof(unsigned int), GFP_KERNEL | GFP_DMA))) { printk(KERN_WARNING "HiSax: No memory for tiger.rec\n"); return; } debugl1(cs, "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)); + (u_int)(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1)); 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)); + memset(cs->bcs[0].hw.tiger.rec, 0xff, NETJET_DMA_RXSIZE * 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), + outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE/2 - 1), cs->hw.njet.base + NETJET_DMA_WRITE_IRQ); - outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1), + outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1), cs->hw.njet.base + NETJET_DMA_WRITE_END); debugl1(cs, "tiger: dmacfg %x/%x pulse=%d", inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR), @@ -1030,9 +1061,6 @@ NETjet_card_msg(struct IsdnCardState *cs, int mt, void *arg) 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); @@ -1046,23 +1074,31 @@ NETjet_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } - - +#ifdef COMPAT_HAS_NEW_PCI static struct pci_dev *dev_netjet __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif -int __init -setup_netjet(struct IsdnCard *card) +__initfunc(int +setup_netjet(struct IsdnCard *card)) { int bytecnt; struct IsdnCardState *cs = card->cs; char tmp[64]; - +#if CONFIG_PCI +#ifndef COMPAT_HAS_NEW_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr, found; +#endif +#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); test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); #if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI if (!pci_present()) { printk(KERN_ERR "Netjet: no PCI bus present\n"); return(0); @@ -1071,22 +1107,57 @@ setup_netjet(struct IsdnCard *card) PCI_NETJET_ID, dev_netjet))) { cs->irq = dev_netjet->irq; if (!cs->irq) { + printk(KERN_WARNING "NETjet: No IRQ for PCI card found\n"); + return(0); + } + cs->hw.njet.base = get_pcibase(dev_netjet, 0) + & PCI_BASE_ADDRESS_IO_MASK; + if (!cs->hw.njet.base) { + printk(KERN_WARNING "NETjet: No IO-Adr for PCI card found\n"); + return(0); + } + } else { + printk(KERN_WARNING "NETjet: No PCI card found\n"); + return(0); + } +#else + 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 + continue; + /* 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); } - cs->hw.njet.base = dev_netjet->resource[0].start & - PCI_BASE_ADDRESS_IO_MASK; - if (!cs->hw.njet.base) { + if (!pci_ioaddr) { printk(KERN_WARNING "NETjet: No IO-Adr for PCI card found\n"); return(0); } - cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA; - cs->hw.njet.isac = cs->hw.njet.base | NETJET_ISAC_OFF; + cs->hw.njet.base = pci_ioaddr & PCI_BASE_ADDRESS_IO_MASK; + cs->irq = pci_irq; +#endif /* COMPAT_HAS_NEW_PCI */ + cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA; + cs->hw.njet.isac = cs->hw.njet.base | NETJET_ISAC_OFF; bytecnt = 256; - } else { - printk(KERN_WARNING "NETjet: No PCI card found\n"); - return(0); - } #else printk(KERN_WARNING "NETjet: NO_PCI_BIOS\n"); printk(KERN_WARNING "NETjet: unable to config NETJET PCI\n"); @@ -1114,6 +1185,8 @@ setup_netjet(struct IsdnCard *card) cs->BC_Write_Reg = &dummywr; cs->BC_Send_Data = &fill_dma; cs->cardmsg = &NETjet_card_msg; + cs->irq_func = &netjet_interrupt; + cs->irq_flags |= SA_SHIRQ; ISACVersion(cs, "NETjet:"); return (1); } diff --git a/drivers/isdn/hisax/niccy.c b/drivers/isdn/hisax/niccy.c index eaed7554fc65..d83287d23287 100644 --- a/drivers/isdn/hisax/niccy.c +++ b/drivers/isdn/hisax/niccy.c @@ -1,4 +1,4 @@ -/* $Id: niccy.c,v 1.4 1998/04/16 19:16:48 keil Exp $ +/* $Id: niccy.c,v 1.8 1999/08/11 21:01:33 keil Exp $ * niccy.c low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and * compatible (SAGEM cybermodem) @@ -8,6 +8,19 @@ * Thanks to Dr. Neuhaus and SAGEM for informations * * $Log: niccy.c,v $ + * Revision 1.8 1999/08/11 21:01:33 keil + * new PCI codefix + * + * Revision 1.7 1999/08/10 16:02:04 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.6 1999/07/12 21:05:23 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.5 1999/07/01 08:12:07 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.4 1998/04/16 19:16:48 keil * need config.h * @@ -27,9 +40,12 @@ #include "hscx.h" #include "isdnl1.h" #include +#ifndef COMPAT_HAS_NEW_PCI +#include +#endif extern const char *CardType[]; -const char *niccy_revision = "$Revision: 1.4 $"; +const char *niccy_revision = "$Revision: 1.8 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -154,7 +170,7 @@ static void niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; if (!cs) { printk(KERN_WARNING "Niccy: Spurious interrupt!\n"); @@ -169,16 +185,12 @@ niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs) } val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40); Start_HSCX: - if (val) { + 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) { + 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) @@ -191,16 +203,12 @@ niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs) 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); - } + 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.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0); + 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); } void @@ -235,8 +243,6 @@ niccy_reset(struct IsdnCardState *cs) static int niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - int imode; - switch (mt) { case CARD_RESET: niccy_reset(cs); @@ -244,14 +250,6 @@ niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg) 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); @@ -261,10 +259,14 @@ niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } +#ifdef COMPAT_HAS_NEW_PCI static struct pci_dev *niccy_dev __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif -int __init -setup_niccy(struct IsdnCard *card) +__initfunc(int +setup_niccy(struct IsdnCard *card)) { struct IsdnCardState *cs = card->cs; char tmp[64]; @@ -304,40 +306,85 @@ setup_niccy(struct IsdnCard *card) } else { #if CONFIG_PCI u_int pci_ioaddr; - +#ifdef COMPAT_HAS_NEW_PCI if (!pci_present()) { printk(KERN_ERR "Niccy: no PCI bus present\n"); return(0); } - cs->subtyp = 0; if ((niccy_dev = pci_find_device(PCI_VENDOR_DR_NEUHAUS, PCI_NICCY_ID, niccy_dev))) { /* get IRQ */ if (!niccy_dev->irq) { printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n"); + return(0); + } + cs->irq = niccy_dev->irq; + if (!get_pcibase(niccy_dev, 0)) { + printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n"); + return(0); + } + cs->hw.niccy.cfg_reg = get_pcibase(niccy_dev, 0) & PCI_BASE_ADDRESS_IO_MASK; + if (!get_pcibase(niccy_dev, 1)) { + printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr = get_pcibase(niccy_dev, 1) & PCI_BASE_ADDRESS_IO_MASK; + cs->subtyp = NICCY_PCI; + } else { + printk(KERN_WARNING "Niccy: No PCI card found\n"); return(0); } - cs->irq = niccy_dev->irq; - if (!niccy_dev->resource[0].start) { +#else + u_char pci_bus, pci_device_fn, pci_irq; + + 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 + continue; + /* 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); } - cs->hw.niccy.cfg_reg = niccy_dev->resource[0].start & PCI_BASE_ADDRESS_IO_MASK; - if (!niccy_dev->resource[1].start) { + 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 = niccy_dev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK; + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->irq = pci_irq; +#endif /* COMPAT_HAS_NEW_PCI */ + cs->irq_flags |= SA_SHIRQ; 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->subtyp = NICCY_PCI; - } else { - printk(KERN_WARNING "Niccy: No PCI card found\n"); - return(0); - } if (check_region((cs->hw.niccy.isac), 4)) { printk(KERN_WARNING "HiSax: %s data port %x-%x already in use\n", @@ -376,6 +423,7 @@ setup_niccy(struct IsdnCard *card) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &niccy_card_msg; + cs->irq_func = &niccy_interrupt; ISACVersion(cs, "Niccy:"); if (HscxVersion(cs, "Niccy:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/s0box.c b/drivers/isdn/hisax/s0box.c index 479880bb33b7..51bc11658416 100644 --- a/drivers/isdn/hisax/s0box.c +++ b/drivers/isdn/hisax/s0box.c @@ -1,4 +1,4 @@ -/* $Id: s0box.c,v 2.1 1998/04/15 16:38:24 keil Exp $ +/* $Id: s0box.c,v 2.2 1999/07/12 21:05:25 keil Exp $ * s0box.c low level stuff for Creatix S0BOX * @@ -13,7 +13,7 @@ #include "isdnl1.h" extern const char *CardType[]; -const char *s0box_revision = "$Revision: 2.1 $"; +const char *s0box_revision = "$Revision: 2.2 $"; static inline void writereg(unsigned int padr, signed int addr, u_char off, u_char val) { @@ -148,9 +148,9 @@ WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) static void s0box_interrupt(int intno, void *dev_id, struct pt_regs *regs) { -#define MAXCOUNT 20 +#define MAXCOUNT 5 struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; int count = 0; if (!cs) { @@ -159,16 +159,12 @@ s0box_interrupt(int intno, void *dev_id, struct pt_regs *regs) } val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA); Start_HSCX: - if (val) { + 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) { + 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) { @@ -184,16 +180,12 @@ s0box_interrupt(int intno, void *dev_id, struct pt_regs *regs) } 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); - } + 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.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0x0); + 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); } void @@ -211,9 +203,6 @@ S0Box_card_msg(struct IsdnCardState *cs, int mt, void *arg) 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; @@ -266,6 +255,7 @@ setup_s0box(struct IsdnCard *card)) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &S0Box_card_msg; + cs->irq_func = &s0box_interrupt; ISACVersion(cs, "S0Box:"); if (HscxVersion(cs, "S0Box:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/saphir.c b/drivers/isdn/hisax/saphir.c new file mode 100644 index 000000000000..b132afc2c51e --- /dev/null +++ b/drivers/isdn/hisax/saphir.c @@ -0,0 +1,326 @@ +/* $Id: saphir.c,v 1.3 1999/07/12 21:05:26 keil Exp $ + + * saphir.c low level stuff for HST Saphir 1 + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * Thanks to HST High Soft Tech GmbH + * + * + * $Log: saphir.c,v $ + * Revision 1.3 1999/07/12 21:05:26 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.2 1999/07/01 08:07:55 keil + * Initial version + * + * + */ + + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; +static char *saphir_rev = "$Revision: 1.3 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ISAC_DATA 0 +#define HSCX_DATA 1 +#define ADDRESS_REG 2 +#define IRQ_REG 3 +#define SPARE_REG 4 +#define RESET_REG 5 + +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.saphir.ale, cs->hw.saphir.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.saphir.ale, cs->hw.saphir.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.saphir.ale, cs->hw.saphir.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx, + offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, + offset + (hscx ? 0x40 : 0), value); +} + +#define READHSCX(cs, nr, reg) readreg(cs->hw.saphir.ale, \ + cs->hw.saphir.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.saphir.ale, \ + cs->hw.saphir.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.saphir.ale, \ + cs->hw.saphir.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.saphir.ale, \ + cs->hw.saphir.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +saphir_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val; + + if (!cs) { + printk(KERN_WARNING "saphir: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) + hscx_int_main(cs, val); + val = readreg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_ISTA); + Start_ISAC: + if (val) + isac_interrupt(cs, val); + val = readreg(cs->hw.saphir.ale, cs->hw.saphir.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.saphir.ale, cs->hw.saphir.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + /* Watchdog */ + if (cs->hw.saphir.timer.function) { + del_timer(&cs->hw.saphir.timer); + cs->hw.saphir.timer.expires = jiffies + 1*HZ; + add_timer(&cs->hw.saphir.timer); + } else + printk(KERN_WARNING "saphir: Spurious timer!\n"); + writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_MASK, 0); + writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK, 0); + writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK + 0x40, 0); +} + +static void +SaphirWatchDog(struct IsdnCardState *cs) +{ + /* 5 sec WatchDog, so read at least every 4 sec */ + cs->readisac(cs, ISAC_RBCH); + del_timer(&cs->hw.saphir.timer); + cs->hw.saphir.timer.expires = jiffies + 1*HZ; + add_timer(&cs->hw.saphir.timer); +} + +void +release_io_saphir(struct IsdnCardState *cs) +{ + long flags; + + save_flags(flags); + cli(); + byteout(cs->hw.saphir.cfg_reg + IRQ_REG, 0xff); + del_timer(&cs->hw.saphir.timer); + cs->hw.saphir.timer.function = NULL; + restore_flags(flags); + if (cs->hw.saphir.cfg_reg) + release_region(cs->hw.saphir.cfg_reg, 6); +} + +static int +saphir_reset(struct IsdnCardState *cs) +{ + long flags; + u_char irq_val; + + switch(cs->irq) { + case 5: irq_val = 0; + break; + case 3: irq_val = 1; + break; + case 11: + irq_val = 2; + break; + case 12: + irq_val = 3; + break; + case 15: + irq_val = 4; + break; + default: + printk(KERN_WARNING "HiSax: saphir wrong IRQ %d\n", + cs->irq); + return (1); + } + byteout(cs->hw.saphir.cfg_reg + IRQ_REG, irq_val); + save_flags(flags); + sti(); + byteout(cs->hw.saphir.cfg_reg + RESET_REG, 1); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((30*HZ)/1000); /* Timeout 30ms */ + byteout(cs->hw.saphir.cfg_reg + RESET_REG, 0); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((30*HZ)/1000); /* Timeout 30ms */ + restore_flags(flags); + byteout(cs->hw.saphir.cfg_reg + IRQ_REG, irq_val); + byteout(cs->hw.saphir.cfg_reg + SPARE_REG, 0x02); + return (0); +} + +static int +saphir_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + saphir_reset(cs); + return(0); + case CARD_RELEASE: + release_io_saphir(cs); + return(0); + case CARD_INIT: + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + + +__initfunc(int +setup_saphir(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, saphir_rev); + printk(KERN_INFO "HiSax: HST Saphir driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_HSTSAPHIR) + return (0); + + /* IO-Ports */ + cs->hw.saphir.cfg_reg = card->para[1]; + cs->hw.saphir.isac = card->para[1] + ISAC_DATA; + cs->hw.saphir.hscx = card->para[1] + HSCX_DATA; + cs->hw.saphir.ale = card->para[1] + ADDRESS_REG; + cs->irq = card->para[0]; + if (check_region((cs->hw.saphir.cfg_reg), 6)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.saphir.cfg_reg, + cs->hw.saphir.cfg_reg + 5); + return (0); + } else + request_region(cs->hw.saphir.cfg_reg,6, "saphir"); + + printk(KERN_INFO + "HiSax: %s config irq:%d io:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.saphir.cfg_reg); + + cs->hw.saphir.timer.function = (void *) SaphirWatchDog; + cs->hw.saphir.timer.data = (long) cs; + init_timer(&cs->hw.saphir.timer); + cs->hw.saphir.timer.expires = jiffies + 4*HZ; + add_timer(&cs->hw.saphir.timer); + if (saphir_reset(cs)) { + release_io_saphir(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 = &saphir_card_msg; + cs->irq_func = &saphir_interrupt; + ISACVersion(cs, "saphir:"); + if (HscxVersion(cs, "saphir:")) { + printk(KERN_WARNING + "saphir: wrong HSCX versions check IO address\n"); + release_io_saphir(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/sedlbauer.c b/drivers/isdn/hisax/sedlbauer.c index 105c9a380725..25303b6e942b 100644 --- a/drivers/isdn/hisax/sedlbauer.c +++ b/drivers/isdn/hisax/sedlbauer.c @@ -1,4 +1,4 @@ -/* $Id: sedlbauer.c,v 1.9 1998/11/15 23:55:20 keil Exp $ +/* $Id: sedlbauer.c,v 1.14 1999/08/11 20:59:22 keil Exp $ * sedlbauer.c low level stuff for Sedlbauer cards * includes support for the Sedlbauer speed star (speed star II), @@ -17,6 +17,23 @@ * Edgar Toernig * * $Log: sedlbauer.c,v $ + * Revision 1.14 1999/08/11 20:59:22 keil + * new PCI codefix + * fix IRQ problem while unload + * + * Revision 1.13 1999/08/10 16:02:08 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.12 1999/08/05 20:43:22 keil + * ISAR analog modem support + * + * Revision 1.11 1999/07/12 21:05:27 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.10 1999/07/01 08:12:09 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.9 1998/11/15 23:55:20 keil * changes from 2.0 * @@ -76,10 +93,13 @@ #include "isar.h" #include "isdnl1.h" #include +#ifndef COMPAT_HAS_NEW_PCI +#include +#endif extern const char *CardType[]; -const char *Sedlbauer_revision = "$Revision: 1.9 $"; +const char *Sedlbauer_revision = "$Revision: 1.14 $"; const char *Sedlbauer_Types[] = {"None", "speed card/win", "speed star", "speed fax+", @@ -291,7 +311,7 @@ static void sedlbauer_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; if (!cs) { printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n"); @@ -307,16 +327,12 @@ sedlbauer_interrupt(int intno, void *dev_id, struct pt_regs *regs) val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40); Start_HSCX: - if (val) { + 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) { + 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) @@ -329,23 +345,19 @@ sedlbauer_interrupt(int intno, void *dev_id, struct pt_regs *regs) 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); - } + 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.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0); + 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); } static void sedlbauer_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char ista, val, icnt = 20; + u_char ista, val, icnt = 5; if (!cs) { printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n"); @@ -392,7 +404,7 @@ sedlbauer_interrupt_isar(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; u_char val; - int cnt = 20; + int cnt = 5; if (!cs) { printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n"); @@ -431,9 +443,11 @@ sedlbauer_interrupt_isar(int intno, void *dev_id, struct pt_regs *regs) void release_io_sedlbauer(struct IsdnCardState *cs) { - int bytecnt = (cs->subtyp == SEDL_SPEED_FAX) ? 16 : 8; + int bytecnt = 8; - if (cs->hw.sedl.bus == SEDL_BUS_PCI) { + if (cs->subtyp == SEDL_SPEED_FAX) { + bytecnt = 16; + } else if (cs->hw.sedl.bus == SEDL_BUS_PCI) { bytecnt = 256; } if (cs->hw.sedl.cfg_reg) @@ -454,10 +468,10 @@ reset_sedlbauer(struct IsdnCardState *cs) save_flags(flags); sti(); current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x0); current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_CONF, 0x0); writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ACFG, 0xff); writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_AOE, 0x0); @@ -469,10 +483,10 @@ reset_sedlbauer(struct IsdnCardState *cs) save_flags(flags); sti(); current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); byteout(cs->hw.sedl.reset_off, 0); /* Reset Off */ current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); restore_flags(flags); } } @@ -486,19 +500,19 @@ Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg) reset_sedlbauer(cs); return(0); case CARD_RELEASE: - release_io_sedlbauer(cs); - return(0); - case CARD_SETIRQ: if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) { - return(request_irq(cs->irq, &sedlbauer_interrupt_isar, - I4L_IRQ_FLAG, "HiSax", cs)); - } else if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) { - return(request_irq(cs->irq, &sedlbauer_interrupt_ipac, - I4L_IRQ_FLAG, "HiSax", cs)); - } else { - return(request_irq(cs->irq, &sedlbauer_interrupt, - I4L_IRQ_FLAG, "HiSax", cs)); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, + ISAR_IRQBIT, 0); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, + ISAC_MASK, 0xFF); + reset_sedlbauer(cs); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, + ISAR_IRQBIT, 0); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, + ISAC_MASK, 0xFF); } + release_io_sedlbauer(cs); + return(0); case CARD_INIT: if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) { clear_pending_isac_ints(cs); @@ -530,11 +544,15 @@ Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg) #ifdef SEDLBAUER_PCI +#ifdef COMPAT_HAS_NEW_PCI +static struct pci_dev *dev_sedl __initdata = NULL; +#else static int pci_index __initdata = 0; #endif +#endif -int __init -setup_sedlbauer(struct IsdnCard *card) +__initfunc(int +setup_sedlbauer(struct IsdnCard *card)) { int bytecnt, ver, val; struct IsdnCardState *cs = card->cs; @@ -569,6 +587,25 @@ setup_sedlbauer(struct IsdnCard *card) /* Probe for Sedlbauer speed pci */ #if SEDLBAUER_PCI #if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI + if (!pci_present()) { + printk(KERN_ERR "FritzPCI: no PCI bus present\n"); + return(0); + } + if ((dev_sedl = pci_find_device(PCI_VENDOR_SEDLBAUER, + PCI_SPEEDPCI_ID, dev_sedl))) { + cs->irq = dev_sedl->irq; + if (!cs->irq) { + printk(KERN_WARNING "Sedlbauer: No IRQ for PCI card found\n"); + return(0); + } + cs->hw.sedl.cfg_reg = get_pcibase(dev_sedl, 0) & + PCI_BASE_ADDRESS_IO_MASK; + } else { + printk(KERN_WARNING "Sedlbauer: No PCI card found\n"); + return(0); + } +#else for (; pci_index < 255; pci_index++) { unsigned char pci_bus, pci_device_fn; unsigned int ioaddr; @@ -589,14 +626,6 @@ setup_sedlbauer(struct IsdnCard *card) printk(KERN_WARNING "Sedlbauer: No IO-Adr for PCI card found\n"); return(0); } - cs->hw.sedl.bus = SEDL_BUS_PCI; - cs->hw.sedl.chip = SEDL_CHIP_IPAC; - cs->subtyp = SEDL_SPEED_PCI; - bytecnt = 256; - byteout(cs->hw.sedl.cfg_reg, 0xff); - byteout(cs->hw.sedl.cfg_reg, 0x00); - byteout(cs->hw.sedl.cfg_reg+ 2, 0xdd); - byteout(cs->hw.sedl.cfg_reg+ 5, 0x02); break; } if (pci_index == 255) { @@ -604,6 +633,16 @@ setup_sedlbauer(struct IsdnCard *card) return(0); } pci_index++; +#endif /* COMPAT_HAS_NEW_PCI */ + cs->irq_flags |= SA_SHIRQ; + cs->hw.sedl.bus = SEDL_BUS_PCI; + cs->hw.sedl.chip = SEDL_CHIP_IPAC; + cs->subtyp = SEDL_SPEED_PCI; + bytecnt = 256; + byteout(cs->hw.sedl.cfg_reg, 0xff); + byteout(cs->hw.sedl.cfg_reg, 0x00); + byteout(cs->hw.sedl.cfg_reg+ 2, 0xdd); + byteout(cs->hw.sedl.cfg_reg+ 5, 0x02); #else printk(KERN_WARNING "Sedlbauer: NO_PCI_BIOS\n"); return (0); @@ -683,6 +722,7 @@ setup_sedlbauer(struct IsdnCard *card) cs->writeisac = &WriteISAC_IPAC; cs->readisacfifo = &ReadISACfifo_IPAC; cs->writeisacfifo = &WriteISACfifo_IPAC; + cs->irq_func = &sedlbauer_interrupt_ipac; val = readreg(cs->hw.sedl.adr,cs->hw.sedl.isac, IPAC_ID); printk(KERN_INFO "Sedlbauer: IPAC version %x\n", val); @@ -702,6 +742,7 @@ setup_sedlbauer(struct IsdnCard *card) cs->bcs[0].hw.isar.reg = &cs->hw.sedl.isar; cs->bcs[1].hw.isar.reg = &cs->hw.sedl.isar; test_and_set_bit(HW_ISAR, &cs->HW_Flags); + cs->irq_func = &sedlbauer_interrupt_isar; ISACVersion(cs, "Sedlbauer:"); @@ -729,6 +770,7 @@ setup_sedlbauer(struct IsdnCard *card) cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_ON; cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_OFF; } + cs->irq_func = &sedlbauer_interrupt; ISACVersion(cs, "Sedlbauer:"); if (HscxVersion(cs, "Sedlbauer:")) { diff --git a/drivers/isdn/hisax/sportster.c b/drivers/isdn/hisax/sportster.c index ec2240b19b1c..0d062dadaf36 100644 --- a/drivers/isdn/hisax/sportster.c +++ b/drivers/isdn/hisax/sportster.c @@ -1,4 +1,4 @@ -/* $Id: sportster.c,v 1.7 1998/11/15 23:55:22 keil Exp $ +/* $Id: sportster.c,v 1.9 1999/07/12 21:05:29 keil Exp $ * sportster.c low level stuff for USR Sportster internal TA * @@ -7,6 +7,13 @@ * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation * * $Log: sportster.c,v $ + * Revision 1.9 1999/07/12 21:05:29 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.8 1999/07/01 08:12:10 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.7 1998/11/15 23:55:22 keil * changes from 2.0 * @@ -36,7 +43,7 @@ #include "isdnl1.h" extern const char *CardType[]; -const char *sportster_revision = "$Revision: 1.7 $"; +const char *sportster_revision = "$Revision: 1.9 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -171,11 +178,11 @@ reset_sportster(struct IsdnCardState *cs) save_flags(flags); sti(); current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); 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; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); restore_flags(flags); } @@ -189,9 +196,6 @@ Sportster_card_msg(struct IsdnCardState *cs, int mt, void *arg) 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 */ @@ -204,8 +208,8 @@ Sportster_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init -get_io_range(struct IsdnCardState *cs) +__initfunc(int +get_io_range(struct IsdnCardState *cs)) { int i, j, adr; @@ -230,8 +234,8 @@ get_io_range(struct IsdnCardState *cs) } } -int __init -setup_sportster(struct IsdnCard *card) +__initfunc(int +setup_sportster(struct IsdnCard *card)) { struct IsdnCardState *cs = card->cs; char tmp[64]; @@ -282,6 +286,7 @@ setup_sportster(struct IsdnCard *card) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &Sportster_card_msg; + cs->irq_func = &sportster_interrupt; ISACVersion(cs, "Sportster:"); if (HscxVersion(cs, "Sportster:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/tei.c b/drivers/isdn/hisax/tei.c index c9d94fe4dc79..f21284bece2c 100644 --- a/drivers/isdn/hisax/tei.c +++ b/drivers/isdn/hisax/tei.c @@ -1,4 +1,4 @@ -/* $Id: tei.c,v 2.11 1998/11/15 23:55:24 keil Exp $ +/* $Id: tei.c,v 2.13 1999/07/21 14:46:28 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -11,6 +11,12 @@ * Fritz Elfert * * $Log: tei.c,v $ + * Revision 2.13 1999/07/21 14:46:28 keil + * changes from EICON certification + * + * Revision 2.12 1999/07/01 08:12:11 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.11 1998/11/15 23:55:24 keil * changes from 2.0 * @@ -72,7 +78,7 @@ #include "isdnl2.h" #include -const char *tei_revision = "$Revision: 2.11 $"; +const char *tei_revision = "$Revision: 2.13 $"; #define ID_REQUEST 1 #define ID_ASSIGNED 2 @@ -163,6 +169,7 @@ put_tei_msg(struct PStack *st, u_char m_id, unsigned int ri, u_char tei) printk(KERN_WARNING "HiSax: No skb for TEI manager\n"); return; } + SET_SKB_FREE(skb); bp = skb_put(skb, 3); bp[0] = (TEI_SAPI << 2); bp[1] = (GROUP_TEI << 1) | 0x1; @@ -225,6 +232,25 @@ tei_id_assign(struct FsmInst *fi, int event, void *arg) } } +static void +tei_id_test_dup(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *ost, *st = fi->userdata; + struct sk_buff *skb = arg; + int tei, ri; + + ri = ((unsigned int) skb->data[1] << 8) + skb->data[2]; + tei = skb->data[4] >> 1; + if (st->ma.debug) + st->ma.tei_m.printdebug(&st->ma.tei_m, + "foreign identity assign ri %d tei %d", ri, tei); + if ((ost = findtei(st, tei))) { /* same tei is in use */ + st->ma.tei_m.printdebug(&st->ma.tei_m, + "possible duplicate assignment tei %d", tei); + FsmEvent(&ost->ma.tei_m, EV_VERIFY, NULL); + } +} + static void tei_id_denied(struct FsmInst *fi, int event, void *arg) { @@ -345,7 +371,7 @@ tei_l1l2(struct PStack *st, int pr, void *arg) int mt; if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } @@ -353,8 +379,8 @@ tei_l1l2(struct PStack *st, int pr, void *arg) if (skb->len < 3) { st->ma.tei_m.printdebug(&st->ma.tei_m, "short mgr frame %ld/3", skb->len); - } else if (((skb->data[0] >> 2) != TEI_SAPI) || - ((skb->data[1] >> 1) != GROUP_TEI)) { + } else if ((skb->data[0] != ((TEI_SAPI << 2) | 2)) || + (skb->data[1] != ((GROUP_TEI << 1) | 1))) { st->ma.tei_m.printdebug(&st->ma.tei_m, "wrong mgr sapi/tei %x/%x", skb->data[0], skb->data[1]); @@ -391,7 +417,7 @@ tei_l1l2(struct PStack *st, int pr, void *arg) st->ma.tei_m.printdebug(&st->ma.tei_m, "tei handler wrong pr %x\n", pr); } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } static void @@ -468,6 +494,7 @@ release_tei(struct IsdnCardState *cs) static struct FsmNode TeiFnList[] HISAX_INITDATA = { {ST_TEI_NOP, EV_IDREQ, tei_id_request}, + {ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup}, {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}, diff --git a/drivers/isdn/hisax/teleint.c b/drivers/isdn/hisax/teleint.c index b37aded8921e..05a74bdf7f97 100644 --- a/drivers/isdn/hisax/teleint.c +++ b/drivers/isdn/hisax/teleint.c @@ -1,4 +1,4 @@ -/* $Id: teleint.c,v 1.7 1998/11/15 23:55:26 keil Exp $ +/* $Id: teleint.c,v 1.9 1999/07/12 21:05:30 keil Exp $ * teleint.c low level stuff for TeleInt isdn cards * @@ -6,6 +6,13 @@ * * * $Log: teleint.c,v $ + * Revision 1.9 1999/07/12 21:05:30 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.8 1999/07/01 08:12:12 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.7 1998/11/15 23:55:26 keil * changes from 2.0 * @@ -38,7 +45,7 @@ extern const char *CardType[]; -const char *TeleInt_revision = "$Revision: 1.7 $"; +const char *TeleInt_revision = "$Revision: 1.9 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -191,7 +198,7 @@ static void TeleInt_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; if (!cs) { printk(KERN_WARNING "TeleInt: Spurious interrupt!\n"); @@ -199,20 +206,16 @@ TeleInt_interrupt(int intno, void *dev_id, struct pt_regs *regs) } val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA); Start_ISAC: - if (val) { + 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); - } + 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 @@ -252,11 +255,11 @@ reset_TeleInt(struct IsdnCardState *cs) save_flags(flags); sti(); current->state = TASK_INTERRUPTIBLE; - schedule_timeout(3); + schedule_timeout((30*HZ)/1000); cs->hw.hfc.cirm &= ~HFC_RESET; byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); /* Reset Off */ current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); restore_flags(flags); } @@ -270,9 +273,6 @@ TeleInt_card_msg(struct IsdnCardState *cs, int mt, void *arg) 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); @@ -289,8 +289,8 @@ TeleInt_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init -setup_TeleInt(struct IsdnCard *card) +__initfunc(int +setup_TeleInt(struct IsdnCard *card)) { struct IsdnCardState *cs = card->cs; char tmp[64]; @@ -365,6 +365,7 @@ setup_TeleInt(struct IsdnCard *card) cs->BC_Read_Reg = &ReadHFC; cs->BC_Write_Reg = &WriteHFC; cs->cardmsg = &TeleInt_card_msg; + cs->irq_func = &TeleInt_interrupt; ISACVersion(cs, "TeleInt:"); return (1); } diff --git a/drivers/isdn/hisax/teles0.c b/drivers/isdn/hisax/teles0.c index c562de877d37..866e8dac5eff 100644 --- a/drivers/isdn/hisax/teles0.c +++ b/drivers/isdn/hisax/teles0.c @@ -1,4 +1,4 @@ -/* $Id: teles0.c,v 2.8 1998/04/15 16:44:28 keil Exp $ +/* $Id: teles0.c,v 2.9 1999/07/12 21:05:31 keil Exp $ * teles0.c low level stuff for Teles Memory IO isdn cards * based on the teles driver from Jan den Ouden @@ -10,6 +10,10 @@ * Beat Doebeli * * $Log: teles0.c,v $ + * Revision 2.9 1999/07/12 21:05:31 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * * Revision 2.8 1998/04/15 16:44:28 keil * new init code * @@ -54,7 +58,7 @@ extern const char *CardType[]; -const char *teles0_revision = "$Revision: 2.8 $"; +const char *teles0_revision = "$Revision: 2.9 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -177,7 +181,7 @@ static void teles0_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; int count = 0; if (!cs) { @@ -186,39 +190,31 @@ teles0_interrupt(int intno, void *dev_id, struct pt_regs *regs) } val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); Start_HSCX: - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } val = readisac(cs->hw.teles0.membase, ISAC_ISTA); Start_ISAC: - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } count++; val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); - if (val && count < 20) { + if (val && count < 5) { if (cs->debug & L1_DEB_HSCX) debugl1(cs, "HSCX IntStat after IntRoutine"); goto Start_HSCX; } val = readisac(cs->hw.teles0.membase, ISAC_ISTA); - if (val && count < 20) { + if (val && count < 5) { if (cs->debug & L1_DEB_ISAC) debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } - 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); - } + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0); } void @@ -290,9 +286,6 @@ Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg) 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); @@ -302,8 +295,8 @@ Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init -setup_teles0(struct IsdnCard *card) +__initfunc(int +setup_teles0(struct IsdnCard *card)) { u_char val; struct IsdnCardState *cs = card->cs; @@ -382,6 +375,7 @@ setup_teles0(struct IsdnCard *card) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &Teles_card_msg; + cs->irq_func = &teles0_interrupt; ISACVersion(cs, "Teles0:"); if (HscxVersion(cs, "Teles0:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/teles3.c b/drivers/isdn/hisax/teles3.c index addc0dbf1e93..e54745458845 100644 --- a/drivers/isdn/hisax/teles3.c +++ b/drivers/isdn/hisax/teles3.c @@ -1,4 +1,4 @@ -/* $Id: teles3.c,v 2.10 1999/02/15 14:37:15 cpetig Exp $ +/* $Id: teles3.c,v 2.12 1999/07/12 21:05:32 keil Exp $ * teles3.c low level stuff for Teles 16.3 & PNP isdn cards * @@ -11,6 +11,13 @@ * Beat Doebeli * * $Log: teles3.c,v $ + * Revision 2.12 1999/07/12 21:05:32 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 2.11 1999/07/01 08:12:14 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.10 1999/02/15 14:37:15 cpetig * oops, missed something in last commit * @@ -78,7 +85,7 @@ #include "isdnl1.h" extern const char *CardType[]; -const char *teles3_revision = "$Revision: 2.10 $"; +const char *teles3_revision = "$Revision: 2.12 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -160,9 +167,9 @@ WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) static void teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs) { -#define MAXCOUNT 20 +#define MAXCOUNT 5 struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; int count = 0; if (!cs) { @@ -171,16 +178,12 @@ teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs) } val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA); Start_HSCX: - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } val = readreg(cs->hw.teles3.isac, ISAC_ISTA); Start_ISAC: - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } count++; val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA); if (val && count < MAXCOUNT) { @@ -196,16 +199,12 @@ teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs) } if (count >= MAXCOUNT) printk(KERN_WARNING "Teles3: more than %d loops in teles3_interrupt\n", count); - if (stat & 1) { - 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(cs->hw.teles3.isac, ISAC_MASK, 0xFF); - writereg(cs->hw.teles3.isac, ISAC_MASK, 0x0); - } + writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.teles3.isac, ISAC_MASK, 0x0); + writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0x0); + writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0x0); } inline static void @@ -222,15 +221,16 @@ release_ioregs(struct IsdnCardState *cs, int mask) void release_io_teles3(struct IsdnCardState *cs) { - if (cs->typ == ISDN_CTYPE_TELESPCMCIA) + if (cs->typ == ISDN_CTYPE_TELESPCMCIA) { release_region(cs->hw.teles3.hscx[0], 97); - else { - if (cs->hw.teles3.cfg_reg) + } else { + 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); } } @@ -309,9 +309,6 @@ Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg) 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); @@ -321,8 +318,8 @@ Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init -setup_teles3(struct IsdnCard *card) +__initfunc(int +setup_teles3(struct IsdnCard *card)) { u_char val; struct IsdnCardState *cs = card->cs; @@ -405,12 +402,13 @@ setup_teles3(struct IsdnCard *card) CardType[cs->typ], cs->hw.teles3.isac + 32, cs->hw.teles3.isac + 64); - if (cs->hw.teles3.cfg_reg) + 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(cs->hw.teles3.isac + 32, 32, "HiSax isac"); @@ -420,12 +418,13 @@ setup_teles3(struct IsdnCard *card) CardType[cs->typ], cs->hw.teles3.hscx[0] + 32, cs->hw.teles3.hscx[0] + 64); - if (cs->hw.teles3.cfg_reg) + 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 @@ -436,12 +435,13 @@ setup_teles3(struct IsdnCard *card) CardType[cs->typ], cs->hw.teles3.hscx[1] + 32, cs->hw.teles3.hscx[1] + 64); - if (cs->hw.teles3.cfg_reg) + 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 @@ -494,6 +494,7 @@ setup_teles3(struct IsdnCard *card) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &Teles_card_msg; + cs->irq_func = &teles3_interrupt; ISACVersion(cs, "Teles3:"); if (HscxVersion(cs, "Teles3:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/telespci.c b/drivers/isdn/hisax/telespci.c index 88592aa8d919..dd9e5fb4a2fe 100644 --- a/drivers/isdn/hisax/telespci.c +++ b/drivers/isdn/hisax/telespci.c @@ -1,4 +1,4 @@ -/* $Id: telespci.c,v 2.5 1998/11/15 23:55:28 keil Exp $ +/* $Id: telespci.c,v 2.9 1999/08/11 21:01:34 keil Exp $ * telespci.c low level stuff for Teles PCI isdn cards * @@ -7,6 +7,19 @@ * * * $Log: telespci.c,v $ + * Revision 2.9 1999/08/11 21:01:34 keil + * new PCI codefix + * + * Revision 2.8 1999/08/10 16:02:10 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 2.7 1999/07/12 21:05:34 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 2.6 1999/07/01 08:12:15 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.5 1998/11/15 23:55:28 keil * changes from 2.0 * @@ -24,16 +37,17 @@ */ #define __NO_VERSION__ #include -#include #include "hisax.h" #include "isac.h" #include "hscx.h" #include "isdnl1.h" #include +#ifndef COMPAT_HAS_NEW_PCI +#include +#endif extern const char *CardType[]; - -const char *telespci_revision = "$Revision: 2.5 $"; +const char *telespci_revision = "$Revision: 2.9 $"; #define ZORAN_PO_RQ_PEN 0x02000000 #define ZORAN_PO_WR 0x00800000 @@ -243,35 +257,27 @@ telespci_interrupt(int intno, void *dev_id, struct pt_regs *regs) { #define MAXCOUNT 20 struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; if (!cs) { printk(KERN_WARNING "TelesPCI: Spurious interrupt!\n"); return; } val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } val = readisac(cs->hw.teles0.membase, ISAC_ISTA); - if (val) { + 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); - } + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0); } void @@ -289,9 +295,6 @@ TelesPCI_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_io_telespci(cs); return(0); - case CARD_SETIRQ: - return(request_irq(cs->irq, &telespci_interrupt, - I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs)); case CARD_INIT: inithscxisac(cs, 3); return(0); @@ -301,20 +304,29 @@ TelesPCI_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } +#ifdef COMPAT_HAS_NEW_PCI static struct pci_dev *dev_tel __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif __initfunc(int setup_telespci(struct IsdnCard *card)) { struct IsdnCardState *cs = card->cs; char tmp[64]; +#ifndef COMPAT_HAS_NEW_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_memaddr; + u_char found = 0; +#endif 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 +#ifdef COMPAT_HAS_NEW_PCI if (!pci_present()) { printk(KERN_ERR "TelesPCI: no PCI bus present\n"); return(0); @@ -325,14 +337,40 @@ setup_telespci(struct IsdnCard *card)) printk(KERN_WARNING "Teles: No IRQ for PCI card found\n"); return(0); } - cs->hw.teles0.membase = (u_int) ioremap(dev_tel->base_address[0], + cs->hw.teles0.membase = (u_int) ioremap(get_pcibase(dev_tel, 0), PAGE_SIZE); printk(KERN_INFO "Found: Zoran, base-address: 0x%lx, irq: 0x%x\n", - dev_tel->base_address[0], dev_tel->irq); + get_pcibase(dev_tel, 0), dev_tel->irq); } else { printk(KERN_WARNING "TelesPCI: No PCI card found\n"); return(0); } +#else + for (; 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->irq = pci_irq; + cs->hw.teles0.membase = (u_int) vremap(pci_memaddr, PAGE_SIZE); +#endif /* COMPAT_HAS_NEW_PCI */ #else printk(KERN_WARNING "HiSax: Teles/PCI and NO_PCI_BIOS\n"); printk(KERN_WARNING "HiSax: Teles/PCI unable to config\n"); @@ -361,6 +399,8 @@ setup_telespci(struct IsdnCard *card)) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &TelesPCI_card_msg; + cs->irq_func = &telespci_interrupt; + cs->irq_flags |= SA_SHIRQ; ISACVersion(cs, "TelesPCI:"); if (HscxVersion(cs, "TelesPCI:")) { printk(KERN_WARNING diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c index d08730e17c2e..0768ec0fce56 100644 --- a/drivers/isdn/icn/icn.c +++ b/drivers/isdn/icn/icn.c @@ -1,4 +1,4 @@ -/* $Id: icn.c,v 1.56 1999/04/12 13:15:07 fritz Exp $ +/* $Id: icn.c,v 1.57 1999/07/06 16:15:30 detabc Exp $ * ISDN low-level module for the ICN active ISDN-Card. * @@ -19,6 +19,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: icn.c,v $ + * Revision 1.57 1999/07/06 16:15:30 detabc + * remove unused messages + * * Revision 1.56 1999/04/12 13:15:07 fritz * Fixed a cast. * @@ -39,10 +42,6 @@ * Revision 1.51 1998/03/07 22:29:55 fritz * Adapted Detlef's chenges for 2.1. * - * Revision 1.50 1998/03/07 17:41:54 detabc - * add d-channel connect and disconnect support statcallback - * from icn low-level to link->level - * * Revision 1.49 1998/02/13 11:14:15 keil * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() * @@ -233,7 +232,7 @@ #undef MAP_DEBUG static char -*revision = "$Revision: 1.56 $"; +*revision = "$Revision: 1.57 $"; static int icn_addcard(int, char *, char *); diff --git a/drivers/isdn/isdn_audio.c b/drivers/isdn/isdn_audio.c index 67307a0ec37a..a6f13ef76432 100644 --- a/drivers/isdn/isdn_audio.c +++ b/drivers/isdn/isdn_audio.c @@ -1,4 +1,4 @@ -/* $Id: isdn_audio.c,v 1.13 1999/04/12 12:33:09 fritz Exp $ +/* $Id: isdn_audio.c,v 1.16 1999/08/06 12:47:35 calle Exp $ * Linux ISDN subsystem, audio conversion and compression (linklevel). * @@ -21,6 +21,27 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_audio.c,v $ + * Revision 1.16 1999/08/06 12:47:35 calle + * Using __GNUC__ == 2 && __GNUC_MINOR__ < 95 how to define + * ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT + * + * Revision 1.15 1999/08/06 12:02:52 calle + * egcs 2.95 complain about invalid asm statement: + * "fixed or forbidden register 2 (cx) was spilled for class CREG." + * Using ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT and not + * define it at the moment. + * + * Revision 1.14 1999/07/11 17:14:06 armin + * Added new layer 2 and 3 protocols for Fax and DSP functions. + * Moved "Add CPN to RING message" to new register S23, + * "Display message" is now correct on register S13 bit 7. + * New audio command AT+VDD implemented (deactivate DTMF decoder and + * activate possible existing hardware/DSP decoder). + * Moved some tty defines to .h file. + * Made whitespace possible in AT command line. + * Some AT-emulator output bugfixes. + * First Fax G3 implementations. + * * Revision 1.13 1999/04/12 12:33:09 fritz * Changes from 2.0 tree. * @@ -71,7 +92,7 @@ #include "isdn_audio.h" #include "isdn_common.h" -char *isdn_audio_revision = "$Revision: 1.13 $"; +char *isdn_audio_revision = "$Revision: 1.16 $"; /* * Misc. lookup-tables. @@ -268,7 +289,18 @@ static char dtmf_matrix[4][4] = {'*', '0', '#', 'D'} }; -#if ((CPU == 386) || (CPU == 486) || (CPU == 586)) + +/* + * egcs 2.95 complain about invalid asm statement: + * "fixed or forbidden register 2 (cx) was spilled for class CREG." + */ +#if ((CPU == 386) || (CPU == 486) || (CPU == 586)) || defined(__GNUC__) +#if __GNUC__ == 2 && __GNUC_MINOR__ < 95 +#define ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT +#endif +#endif + +#ifdef ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT static inline void isdn_audio_tlookup(const void *table, void *buff, unsigned long n) { @@ -711,16 +743,51 @@ isdn_audio_calc_silence(modem_info * info, unsigned char *buf, int len, int fmt) } void -isdn_audio_eval_silence(modem_info * info) +isdn_audio_put_dle_code(modem_info * info, u_char code) { - silence_state *s = info->silence_state; struct sk_buff *skb; unsigned long flags; int di; int ch; - char what; char *p; + skb = dev_alloc_skb(2); + if (!skb) { + printk(KERN_WARNING + "isdn_audio: Could not alloc skb for ttyI%d\n", + info->line); + return; + } + p = (char *) skb_put(skb, 2); + p[0] = 0x10; + p[1] = code; + if (skb_headroom(skb) < sizeof(isdn_audio_skb)) { + printk(KERN_WARNING + "isdn_audio: insufficient skb_headroom, dropping\n"); + kfree_skb(skb); + return; + } + ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; + ISDN_AUDIO_SKB_LOCK(skb) = 0; + save_flags(flags); + cli(); + di = info->isdn_driver; + ch = info->isdn_channel; + __skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb); + dev->drv[di]->rcvcount[ch] += 2; + restore_flags(flags); + /* Schedule dequeuing */ + if ((dev->modempoll) && (info->rcvsched)) + isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); + wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]); +} + +void +isdn_audio_eval_silence(modem_info * info) +{ + silence_state *s = info->silence_state; + char what; + what = ' '; if (s->idx > (info->emu.vpar[2] * 800)) { @@ -734,28 +801,6 @@ isdn_audio_eval_silence(modem_info * info) if ((what == 's') || (what == 'q')) { printk(KERN_DEBUG "ttyI%d: %s\n", info->line, (what=='s') ? "silence":"quiet"); - skb = dev_alloc_skb(2); - p = (char *) skb_put(skb, 2); - p[0] = 0x10; - p[1] = what; - if (skb_headroom(skb) < sizeof(isdn_audio_skb)) { - printk(KERN_WARNING - "isdn_audio: insufficient skb_headroom, dropping\n"); - kfree_skb(skb); - return; - } - ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; - ISDN_AUDIO_SKB_LOCK(skb) = 0; - save_flags(flags); - cli(); - di = info->isdn_driver; - ch = info->isdn_channel; - __skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb); - dev->drv[di]->rcvcount[ch] += 2; - restore_flags(flags); - /* Schedule dequeuing */ - if ((dev->modempoll) && (info->rcvsched)) - isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); - wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]); + isdn_audio_put_dle_code(info, what); } } diff --git a/drivers/isdn/isdn_audio.h b/drivers/isdn/isdn_audio.h index ee33ce020b9a..54dcb253e414 100644 --- a/drivers/isdn/isdn_audio.h +++ b/drivers/isdn/isdn_audio.h @@ -1,4 +1,4 @@ -/* $Id: isdn_audio.h,v 1.7 1999/04/12 12:33:11 fritz Exp $ +/* $Id: isdn_audio.h,v 1.8 1999/07/11 17:14:07 armin Exp $ * Linux ISDN subsystem, audio conversion and compression (linklevel). * @@ -19,6 +19,17 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_audio.h,v $ + * Revision 1.8 1999/07/11 17:14:07 armin + * Added new layer 2 and 3 protocols for Fax and DSP functions. + * Moved "Add CPN to RING message" to new register S23, + * "Display message" is now correct on register S13 bit 7. + * New audio command AT+VDD implemented (deactivate DTMF decoder and + * activate possible existing hardware/DSP decoder). + * Moved some tty defines to .h file. + * Made whitespace possible in AT command line. + * Some AT-emulator output bugfixes. + * First Fax G3 implementations. + * * Revision 1.7 1999/04/12 12:33:11 fritz * Changes from 2.0 tree. * @@ -74,3 +85,4 @@ dtmf_state *isdn_audio_dtmf_init(dtmf_state *); extern void isdn_audio_calc_silence(modem_info *, unsigned char *, int, int); extern void isdn_audio_eval_silence(modem_info *); silence_state *isdn_audio_silence_init(silence_state *); +extern void isdn_audio_put_dle_code(modem_info *, u_char); diff --git a/drivers/isdn/isdn_bsdcomp.c b/drivers/isdn/isdn_bsdcomp.c index e5cc0ea30be5..92012c8e5c7b 100644 --- a/drivers/isdn/isdn_bsdcomp.c +++ b/drivers/isdn/isdn_bsdcomp.c @@ -64,6 +64,7 @@ #include #include #include +#include /* to get the struct task_struct */ #include /* used in new tty drivers */ #include /* used in new tty drivers */ diff --git a/drivers/isdn/isdn_budget.c b/drivers/isdn/isdn_budget.c deleted file mode 100644 index 985c97ba89eb..000000000000 --- a/drivers/isdn/isdn_budget.c +++ /dev/null @@ -1,206 +0,0 @@ -/* $Id: isdn_budget.c,v 1.3 1998/10/23 10:18:39 paul Exp $ - * - * Linux ISDN subsystem, budget-accounting for network interfaces. - * - * Copyright 1997 by Christian Lademann - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Log: isdn_budget.c,v $ - * Revision 1.3 1998/10/23 10:18:39 paul - * Implementation of "dialmode" (successor of "status") - * You also need current isdnctrl for this! - * - * Revision 1.2 1998/03/07 23:17:30 fritz - * Added RCS keywords - * Bugfix: Did not compile without isdn_dumppkt beeing enabled. - * - */ - -/* -30.06.97:cal:angelegt -04.11.97:cal:budget.period: int --> time_t -*/ - -#include -#define __NO_VERSION__ -#include -#include -#include "isdn_common.h" -#include "isdn_net.h" - -#ifdef CONFIG_ISDN_BUDGET - -#define VERBOSE_PRINTK(v, l, p...) { \ - if(dev->net_verbose >= (v)) { \ - printk(l ## p); \ - } else { ; } \ -} - - -int -isdn_net_budget(int type, struct device *ndev) { - isdn_net_local *lp = (isdn_net_local *)ndev->priv; - int i, ret = 0; - - switch(type) { - case ISDN_BUDGET_INIT: - for(i = 0; i < ISDN_BUDGET_NUM_BUDGET; i++) { - lp->budget [i] .amount = -1; - lp->budget [i] .used = 0; - lp->budget [i] .period = (time_t)0; - lp->budget [i] .period_started = (time_t)0; - lp->budget [i] .last_check = CURRENT_TIME; - lp->budget [i] .notified = 0; - } - - return(0); - break; - - case ISDN_BUDGET_CHECK_DIAL: - case ISDN_BUDGET_CHECK_CHARGE: - case ISDN_BUDGET_CHECK_ONLINE: - ret = 0; - - for(i = 0; i < ISDN_BUDGET_NUM_BUDGET; i++) { - if(lp->budget [i] .amount < 0) - continue; - - if(lp->budget [i] .period_started + lp->budget [i] .period < CURRENT_TIME) { - lp->budget [i] .used = 0; - lp->budget [i] .period_started = CURRENT_TIME; - lp->budget [i] .notified = 0; - } - - if(lp->budget [i] .used >= lp->budget [i] .amount) - ret |= (1 << i); - } - - switch(type) { - case ISDN_BUDGET_CHECK_DIAL: - if(! ret) { - lp->budget [ISDN_BUDGET_DIAL] .used++; - lp->budget [ISDN_BUDGET_DIAL] .last_check = CURRENT_TIME; - } - break; - - case ISDN_BUDGET_CHECK_CHARGE: - lp->budget [ISDN_BUDGET_CHARGE] .used++; - lp->budget [ISDN_BUDGET_CHARGE] .last_check = CURRENT_TIME; - break; - - case ISDN_BUDGET_CHECK_ONLINE: - if(lp->budget [ISDN_BUDGET_ONLINE] .last_check) { - lp->budget [ISDN_BUDGET_ONLINE] .used += (CURRENT_TIME - lp->budget [ISDN_BUDGET_ONLINE] .last_check); - } - - lp->budget [ISDN_BUDGET_ONLINE] .last_check = CURRENT_TIME; - break; - } - -/* - if(ret) - lp->flags |= ISDN_NET_DM_OFF; -*/ - for(i = 0; i < ISDN_BUDGET_NUM_BUDGET; i++) { - if(ret & (1 << i) && ! lp->budget [i] .notified) { - switch(i) { - case ISDN_BUDGET_DIAL: - printk(KERN_WARNING "isdn_budget: dial budget used up.\n"); - break; - - case ISDN_BUDGET_CHARGE: - printk(KERN_WARNING "isdn_budget: charge budget used up.\n"); - break; - - case ISDN_BUDGET_ONLINE: - printk(KERN_WARNING "isdn_budget: online budget used up.\n"); - break; - - default: - printk(KERN_WARNING "isdn_budget: budget #%d used up.\n", i); - break; - } - - lp->budget [i] .notified = 1; - } - } - - return(ret); - break; - - case ISDN_BUDGET_START_ONLINE: - lp->budget [ISDN_BUDGET_ONLINE] .last_check = CURRENT_TIME; - return(0); - - break; - } - - return(-1); -} - - -int -isdn_budget_ioctl(isdn_ioctl_budget *iocmd) { - isdn_net_dev *p = isdn_net_findif(iocmd->name); - - if(p) { - switch(iocmd->command) { - case ISDN_BUDGET_SET_BUDGET: - if(! suser()) - return(-EPERM); - - if(iocmd->budget < 0 || iocmd->budget > ISDN_BUDGET_NUM_BUDGET) - return(-EINVAL); - - if(iocmd->amount < 0) - iocmd->amount = -1; - - p->local->budget [iocmd->budget] .amount = iocmd->amount; - p->local->budget [iocmd->budget] .period = iocmd->period; - - if(iocmd->used <= 0) - p->local->budget [iocmd->budget] .used = 0; - else - p->local->budget [iocmd->budget] .used = iocmd->used; - - if(iocmd->period_started == (time_t)0) - p->local->budget [iocmd->budget] .period_started = CURRENT_TIME; - else - p->local->budget [iocmd->budget] .period_started = iocmd->period_started; - - return(0); - break; - - case ISDN_BUDGET_GET_BUDGET: - if(iocmd->budget < 0 || iocmd->budget > ISDN_BUDGET_NUM_BUDGET) - return(-EINVAL); - - iocmd->amount = p->local->budget [iocmd->budget] .amount; - iocmd->used = p->local->budget [iocmd->budget] .used; - iocmd->period = p->local->budget [iocmd->budget] .period; - iocmd->period_started = p->local->budget [iocmd->budget] .period_started; - - return(0); - break; - - default: - return(-EINVAL); - break; - } - } - return(-ENODEV); -} -#endif diff --git a/drivers/isdn/isdn_cards.c b/drivers/isdn/isdn_cards.c index 06fa3e7d7ffc..bdbcd419188c 100644 --- a/drivers/isdn/isdn_cards.c +++ b/drivers/isdn/isdn_cards.c @@ -1,4 +1,4 @@ -/* $Id: isdn_cards.c,v 1.9 1999/04/12 12:33:11 fritz Exp $ +/* $Id: isdn_cards.c,v 1.10 1999/07/20 06:41:28 calle Exp $ * Linux ISDN subsystem, initialization for non-modularized drivers. * @@ -19,6 +19,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_cards.c,v $ + * Revision 1.10 1999/07/20 06:41:28 calle + * Bugfix: After the redesign of the AVM B1 driver, the driver didn't even + * compile, if not selected as modules. + * * Revision 1.9 1999/04/12 12:33:11 fritz * Changes from 2.0 tree. * @@ -67,12 +71,9 @@ extern void eicon_init(void); #endif #ifdef CONFIG_ISDN_DRV_AVMB1 -extern void avmb1_init(void); +extern void kcapi_init(void); extern void capi_init(void); extern void capidrv_init(void); -#ifdef CONFIG_PCI -extern int b1pci_init(void); -#endif #endif void @@ -88,10 +89,7 @@ isdn_cards_init(void) pcbit_init(); #endif #ifdef CONFIG_ISDN_DRV_AVMB1 - avmb1_init(); -#ifdef CONFIG_PCI - b1pci_init(); -#endif + kcapi_init(); capi_init(); capidrv_init(); #endif diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c index 779a327face4..cac4be6dcda5 100644 --- a/drivers/isdn/isdn_common.c +++ b/drivers/isdn/isdn_common.c @@ -1,4 +1,4 @@ -/* $Id: isdn_common.c,v 1.75 1999/04/18 14:06:47 fritz Exp $ +/* $Id: isdn_common.c,v 1.86 1999/07/31 12:59:42 armin Exp $ * Linux ISDN subsystem, common used functions (linklevel). * @@ -21,6 +21,51 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.c,v $ + * Revision 1.86 1999/07/31 12:59:42 armin + * Added tty fax capabilities. + * + * Revision 1.85 1999/07/29 16:58:35 armin + * Bugfix: DLE handling in isdn_readbchan() + * + * Revision 1.84 1999/07/25 16:21:10 keil + * fix number matching + * + * Revision 1.83 1999/07/13 21:02:05 werner + * Added limit possibilty of driver b_channel resources (ISDN_STAT_DISCH) + * + * Revision 1.82 1999/07/12 21:06:50 werner + * Fixed problem when loading more than one driver temporary + * + * Revision 1.81 1999/07/11 17:14:09 armin + * Added new layer 2 and 3 protocols for Fax and DSP functions. + * Moved "Add CPN to RING message" to new register S23, + * "Display message" is now correct on register S13 bit 7. + * New audio command AT+VDD implemented (deactivate DTMF decoder and + * activate possible existing hardware/DSP decoder). + * Moved some tty defines to .h file. + * Made whitespace possible in AT command line. + * Some AT-emulator output bugfixes. + * First Fax G3 implementations. + * + * Revision 1.80 1999/07/07 10:14:00 detabc + * remove unused messages + * + * Revision 1.79 1999/07/05 23:51:30 werner + * Allow limiting of available HiSax B-chans per card. Controlled by hisaxctrl + * hisaxctrl id 10 + * + * Revision 1.78 1999/07/05 20:21:15 werner + * changes to use diversion sources for all kernel versions. + * removed static device, only proc filesystem used + * + * Revision 1.77 1999/07/01 08:29:50 keil + * compatibility to 2.3 kernel + * + * Revision 1.76 1999/06/29 16:16:44 calle + * Let ISDN_CMD_UNLOAD work with open isdn devices without crash again. + * Also right unlocking (ISDN_CMD_UNLOCK) is done now. + * isdnlog should check returncode of read(2) calls. + * * Revision 1.75 1999/04/18 14:06:47 fritz * Removed TIMRU stuff. * @@ -65,16 +110,6 @@ * brute force fix to avoid Ugh's in isdn_tty_write() * cleaned up some dead code * - * Revision 1.65 1998/06/07 00:20:00 fritz - * abc cleanup. - * - * Revision 1.64 1998/06/02 12:10:03 detabc - * wegen einer einstweiliger verfuegung gegen DW ist zur zeit - * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar - * - * Revision 1.63 1998/05/03 17:40:38 detabc - * Include abc-extension-support for >= 2.1.x Kernels in - * isdn_net.c and isdn_common.c. alpha-test OK and running ! * * Revision 1.62 1998/04/14 16:28:43 he * Fixed user space access with interrupts off and remaining @@ -335,6 +370,9 @@ #ifdef CONFIG_ISDN_AUDIO #include "isdn_audio.h" #endif +#ifdef CONFIG_ISDN_DIVERSION +#include +#endif CONFIG_ISDN_DIVERSION #include "isdn_v110.h" #include "isdn_cards.h" @@ -343,7 +381,7 @@ isdn_dev *dev = (isdn_dev *) 0; -static char *isdn_revision = "$Revision: 1.75 $"; +static char *isdn_revision = "$Revision: 1.86 $"; extern char *isdn_net_revision; extern char *isdn_tty_revision; @@ -359,6 +397,11 @@ static char *isdn_audio_revision = ": none $"; #endif extern char *isdn_v110_revision; +#ifdef CONFIG_ISDN_DIVERSION +isdn_divert_if *divert_if = NULL; /* interface to diversion module */ +#endif CONFIG_ISDN_DIVERSION + + static int isdn_writebuf_stub(int, int, const u_char *, int, int); void @@ -449,6 +492,8 @@ isdn_wildmat(char *s, char *p) register int reverse; register int nostar = 1; + if (!(*s) && !(*p)) + return(1); for (; *p; s++, p++) switch (*p) { case '\\': @@ -760,7 +805,7 @@ isdn_status_callback(isdn_ctrl * c) return 0; } /* Try to find a network-interface which will accept incoming call */ - r = isdn_net_find_icall(di, c->arg, i, c->parm.setup); + r = ((c->command == ISDN_STAT_ICALLW) ? 0 : isdn_net_find_icall(di, c->arg, i, c->parm.setup)); switch (r) { case 0: /* No network-device replies. @@ -768,7 +813,13 @@ isdn_status_callback(isdn_ctrl * c) * These return 0 on no match, 1 on match and * 3 on eventually match, if CID is longer. */ - retval = isdn_tty_find_icall(di, c->arg, c->parm.setup); + if (c->command == ISDN_STAT_ICALL) + if ((retval = isdn_tty_find_icall(di, c->arg, c->parm.setup))) return(retval); +#ifdef CONFIG_ISDN_DIVERSION + if (divert_if) + if ((retval = divert_if->stat_callback(c))) + return(retval); /* processed */ +#endif CONFIG_ISDN_DIVERSION if ((!retval) && (dev->drv[di]->flags & DRV_FLAG_REJBUS)) { /* No tty responding */ cmd.driver = di; @@ -831,6 +882,20 @@ isdn_status_callback(isdn_ctrl * c) printk(KERN_INFO "isdn: %s,ch%ld cause: %s\n", dev->drvid[di], c->arg, c->parm.num); isdn_tty_stat_callback(i, c); +#ifdef CONFIG_ISDN_DIVERSION + if (divert_if) + divert_if->stat_callback(c); +#endif CONFIG_ISDN_DIVERSION + break; + case ISDN_STAT_DISPLAY: +#ifdef ISDN_DEBUG_STATCALLB + printk(KERN_DEBUG "DISPLAY: %ld %s\n", c->arg, c->parm.display); +#endif + isdn_tty_stat_callback(i, c); +#ifdef CONFIG_ISDN_DIVERSION + if (divert_if) + divert_if->stat_callback(c); +#endif CONFIG_ISDN_DIVERSION break; case ISDN_STAT_DCONN: if (i < 0) @@ -869,6 +934,11 @@ isdn_status_callback(isdn_ctrl * c) isdn_v110_stat_callback(i, c); if (isdn_tty_stat_callback(i, c)) break; +#ifdef CONFIG_ISDN_DIVERSION + if (divert_if) + divert_if->stat_callback(c); +#endif CONFIG_ISDN_DIVERSION + break; break; case ISDN_STAT_BCONN: if (i < 0) @@ -924,7 +994,34 @@ isdn_status_callback(isdn_ctrl * c) return -1; isdn_info_update(); break; + case ISDN_STAT_DISCH: + save_flags(flags); + cli(); + for (i = 0; i < ISDN_MAX_CHANNELS; i++) + if ((dev->drvmap[i] == di) && + (dev->chanmap[i] == c->arg)) { + if (c->parm.num[0]) + dev->usage[i] &= ~ISDN_USAGE_DISABLED; + else + if (USG_NONE(dev->usage[i])) { + dev->usage[i] |= ISDN_USAGE_DISABLED; + } + else + retval = -1; + break; + } + restore_flags(flags); + isdn_info_update(); + break; case ISDN_STAT_UNLOAD: + while (dev->drv[di]->locks > 0) { + isdn_ctrl cmd; + cmd.driver = di; + cmd.arg = 0; + cmd.command = ISDN_CMD_UNLOCK; + isdn_command(&cmd); + dev->drv[di]->locks--; + } save_flags(flags); cli(); isdn_tty_stat_callback(i, c); @@ -932,6 +1029,7 @@ isdn_status_callback(isdn_ctrl * c) if (dev->drvmap[i] == di) { dev->drvmap[i] = -1; dev->chanmap[i] = -1; + dev->usage[i] &= ~ISDN_USAGE_DISABLED; } dev->drivers--; dev->channels -= dev->drv[di]->channels; @@ -941,7 +1039,7 @@ isdn_status_callback(isdn_ctrl * c) isdn_free_queue(&dev->drv[di]->rpqueue[i]); kfree(dev->drv[di]->rpqueue); kfree(dev->drv[di]->rcv_waitq); -#if LINUX_VERSION_CODE < 131841 +#ifndef COMPAT_HAS_NEW_WAITQ kfree(dev->drv[di]->snd_waitq); #endif kfree(dev->drv[di]); @@ -954,6 +1052,22 @@ isdn_status_callback(isdn_ctrl * c) break; case CAPI_PUT_MESSAGE: return(isdn_capi_rec_hl_msg(&c->parm.cmsg)); +#ifdef CONFIG_ISDN_TTY_FAX + case ISDN_STAT_FAXIND: + isdn_tty_stat_callback(i, c); + break; +#endif +#ifdef CONFIG_ISDN_AUDIO + case ISDN_STAT_AUDIO: + isdn_tty_stat_callback(i, c); + break; +#endif +#ifdef CONFIG_ISDN_DIVERSION + case ISDN_STAT_PROT: + case ISDN_STAT_REDIR: + if (divert_if) + return(divert_if->stat_callback(c)); +#endif CONFIG_ISDN_DIVERSION default: return -1; } @@ -989,10 +1103,10 @@ isdn_getnum(char **p) * of the mapping (di,ch)<->minor, happen during the sleep? --he */ int -#if LINUX_VERSION_CODE < 131841 -isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, struct wait_queue **sleep) -#else +#ifdef COMPAT_HAS_NEW_WAITQ isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_queue_head_t *sleep) +#else +isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, struct wait_queue **sleep) #endif { int left; @@ -1021,7 +1135,7 @@ isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_que if (ISDN_AUDIO_SKB_LOCK(skb)) break; ISDN_AUDIO_SKB_LOCK(skb) = 1; - if (ISDN_AUDIO_SKB_DLECOUNT(skb)) { + if ((ISDN_AUDIO_SKB_DLECOUNT(skb)) || (dev->drv[di]->DLEflag & (1 << channel))) { char *p = skb->data; unsigned long DLEmask = (1 << channel); @@ -1320,11 +1434,11 @@ isdn_poll(struct file *file, poll_table * wait) return mask; } if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) { - poll_wait(file, &(dev->drv[drvidx]->st_waitq), wait); if (drvidx < 0) { - printk(KERN_ERR "isdn_common: isdn_poll 1 -> what the hell\n"); - return POLLERR; + /* driver deregistered while file open */ + return POLLHUP; } + poll_wait(file, &(dev->drv[drvidx]->st_waitq), wait); mask = POLLOUT | POLLWRNORM; if (dev->drv[drvidx]->stavail) { mask |= POLLIN | POLLRDNORM; @@ -1938,6 +2052,8 @@ isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) && ((pre_dev != d) || (pre_chan != dev->chanmap[i]))) continue; + if (dev->usage[i] & ISDN_USAGE_DISABLED) + continue; /* usage not allowed */ if (dev->drv[d]->flags & DRV_FLAG_RUNNING) { if (((dev->drv[d]->interface->features & features) == features) || (((dev->drv[d]->interface->features & vfeatures) == vfeatures) && @@ -2132,13 +2248,12 @@ isdn_add_channels(driver *d, int drvidx, int n, int adding) int j, k, m; ulong flags; -#if LINUX_VERSION_CODE >= 131841 +#ifdef COMPAT_HAS_NEW_WAITQ init_waitqueue_head(&d->st_waitq); #endif if (d->flags & DRV_FLAG_RUNNING) return -1; - if (n < 1) - return 0; + if (n < 1) return 0; m = (adding) ? d->channels + n : n; @@ -2185,13 +2300,13 @@ isdn_add_channels(driver *d, int drvidx, int n, int adding) if ((adding) && (d->rcv_waitq)) kfree(d->rcv_waitq); -#if LINUX_VERSION_CODE < 131841 - if (!(d->rcv_waitq = (struct wait_queue **) - kmalloc(sizeof(struct wait_queue *) * m, GFP_KERNEL))) { -#else +#ifdef COMPAT_HAS_NEW_WAITQ d->rcv_waitq = (wait_queue_head_t *) kmalloc(sizeof(wait_queue_head_t) * 2 * m, GFP_KERNEL); if (!d->rcv_waitq) { +#else + if (!(d->rcv_waitq = (struct wait_queue **) + kmalloc(sizeof(struct wait_queue *) * m, GFP_KERNEL))) { #endif printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n"); if (!adding) { @@ -2201,7 +2316,13 @@ isdn_add_channels(driver *d, int drvidx, int n, int adding) } return -1; } -#if LINUX_VERSION_CODE < 131841 +#ifdef COMPAT_HAS_NEW_WAITQ + d->snd_waitq = d->rcv_waitq + m; + for (j = 0; j < m; j++) { + init_waitqueue_head(&d->rcv_waitq[m]); + init_waitqueue_head(&d->snd_waitq[m]); + } +#else memset((char *) d->rcv_waitq, 0, sizeof(struct wait_queue *) * m); if ((adding) && (d->snd_waitq)) @@ -2218,12 +2339,6 @@ isdn_add_channels(driver *d, int drvidx, int n, int adding) return -1; } memset((char *) d->snd_waitq, 0, sizeof(struct wait_queue *) * m); -#else - d->snd_waitq = d->rcv_waitq + m; - for (j = 0; j < m; j++) { - init_waitqueue_head(&d->rcv_waitq[m]); - init_waitqueue_head(&d->snd_waitq[m]); - } #endif dev->channels += n; @@ -2245,6 +2360,60 @@ isdn_add_channels(driver *d, int drvidx, int n, int adding) * Low-level-driver registration */ + +#ifdef CONFIG_ISDN_DIVERSION +extern isdn_divert_if *divert_if; + +static char *map_drvname(int di) +{ + if ((di < 0) || (di >= ISDN_MAX_DRIVERS)) + return(NULL); + return(dev->drvid[di]); /* driver name */ +} /* map_drvname */ + +static int map_namedrv(char *id) +{ int i; + + for (i = 0; i < ISDN_MAX_DRIVERS; i++) + { if (!strcmp(dev->drvid[i],id)) + return(i); + } + return(-1); +} /* map_namedrv */ + +int DIVERT_REG_NAME(isdn_divert_if *i_div) +{ + if (i_div->if_magic != DIVERT_IF_MAGIC) + return(DIVERT_VER_ERR); + switch (i_div->cmd) + { + case DIVERT_CMD_REL: + if (divert_if != i_div) + return(DIVERT_REL_ERR); + divert_if = NULL; /* free interface */ + MOD_DEC_USE_COUNT; + return(DIVERT_NO_ERR); + + case DIVERT_CMD_REG: + if (divert_if) + return(DIVERT_REG_ERR); + i_div->ll_cmd = isdn_command; /* set command function */ + i_div->drv_to_name = map_drvname; + i_div->name_to_drv = map_namedrv; + MOD_INC_USE_COUNT; + divert_if = i_div; /* remember interface */ + return(DIVERT_NO_ERR); + + default: + return(DIVERT_CMD_ERR); + } +} /* DIVERT_REG_NAME */ + +EXPORT_SYMBOL(DIVERT_REG_NAME); + +#endif CONFIG_ISDN_DIVERSION + + EXPORT_SYMBOL(register_isdn); EXPORT_SYMBOL(register_isdn_module); EXPORT_SYMBOL(unregister_isdn_module); @@ -2352,18 +2521,18 @@ isdn_init(void) memset((char *) dev, 0, sizeof(isdn_dev)); init_timer(&dev->timer); dev->timer.function = isdn_timer_funct; -#if LINUX_VERSION_CODE < 131841 - dev->sem = MUTEX; -#else +#ifdef COMPAT_HAS_NEW_WAITQ init_MUTEX(&dev->sem); init_waitqueue_head(&dev->info_waitq); +#else + dev->sem = MUTEX; #endif for (i = 0; i < ISDN_MAX_CHANNELS; i++) { dev->drvmap[i] = -1; dev->chanmap[i] = -1; dev->m_idx[i] = -1; strcpy(dev->num[i], "???"); -#if LINUX_VERSION_CODE >= 131841 +#ifdef COMPAT_HAS_NEW_WAITQ init_waitqueue_head(&dev->mdm.info[i].open_wait); init_waitqueue_head(&dev->mdm.info[i].close_wait); #endif @@ -2452,6 +2621,9 @@ cleanup_module(void) for (i = 0; i < ISDN_MAX_CHANNELS; i++) { isdn_tty_cleanup_xmit(&dev->mdm.info[i]); kfree(dev->mdm.info[i].xmit_buf - 4); +#ifdef CONFIG_ISDN_TTY_FAX + kfree(dev->mdm.info[i].fax); +#endif } if (unregister_chrdev(ISDN_MAJOR, "isdn") != 0) { printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n"); diff --git a/drivers/isdn/isdn_common.h b/drivers/isdn/isdn_common.h index 3ba4855b72ae..e6fb122f6669 100644 --- a/drivers/isdn/isdn_common.h +++ b/drivers/isdn/isdn_common.h @@ -1,4 +1,4 @@ -/* $Id: isdn_common.h,v 1.15 1999/04/18 14:06:50 fritz Exp $ +/* $Id: isdn_common.h,v 1.16 1999/07/01 08:29:54 keil 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.16 1999/07/01 08:29:54 keil + * compatibility to 2.3 kernel + * * Revision 1.15 1999/04/18 14:06:50 fritz * Removed TIMRU stuff. * @@ -115,10 +118,10 @@ extern char *isdn_map_eaz2msn(char *msn, int di); extern void isdn_timer_ctrl(int tf, int onoff); extern void isdn_unexclusive_channel(int di, int ch); extern int isdn_getnum(char **); -#if LINUX_VERSION_CODE < 131841 -extern int isdn_readbchan(int, int, u_char *, u_char *, int, struct wait_queue**); -#else +#ifdef COMPAT_HAS_NEW_WAITQ extern int isdn_readbchan(int, int, u_char *, u_char *, int, wait_queue_head_t *); +#else +extern int isdn_readbchan(int, int, u_char *, u_char *, int, struct wait_queue**); #endif extern int isdn_get_free_channel(int, int, int, int, int); extern int isdn_writebuf_skb_stub(int, int, int, struct sk_buff *); diff --git a/drivers/isdn/isdn_net.c b/drivers/isdn/isdn_net.c index 9e4426af0890..b3e15ff1833e 100644 --- a/drivers/isdn/isdn_net.c +++ b/drivers/isdn/isdn_net.c @@ -1,4 +1,4 @@ -/* $Id: isdn_net.c,v 1.84 1999/04/18 14:06:55 fritz Exp $ +/* $Id: isdn_net.c,v 1.88 1999/07/07 10:13:31 detabc Exp $ * Linux ISDN subsystem, network interfaces and related functions (linklevel). * @@ -21,6 +21,19 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_net.c,v $ + * Revision 1.88 1999/07/07 10:13:31 detabc + * remove unused messages + * + * Revision 1.87 1999/07/06 07:53:53 calle + * calls to dev_alloc_skb waste 16 bytes of memory, if we calculate the + * right header space for the lowlevel driver. using alloc_skb instead. + * + * Revision 1.86 1999/06/09 10:12:05 paul + * thinko in previous patch + * + * Revision 1.85 1999/06/07 19:42:39 paul + * isdn_net_getpeer() fixed to return correct `outgoing' flag + * * Revision 1.84 1999/04/18 14:06:55 fritz * Removed TIMRU stuff. * @@ -68,7 +81,7 @@ * Added more locking stuff in tty_write. * * Revision 1.71 1998/06/18 22:43:08 fritz - * Bugfix: Setting ndev->do_ioctl had beed accidetly removed at abc-cleanup. + * Bugfix: Setting ndev->do_ioctl had beed accidetly removed at cleanup. * * Revision 1.70 1998/06/17 19:50:49 he * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay()) @@ -80,40 +93,13 @@ * so autodial is suppressed for that device until it is switched on using * 'isdnctrl status dev-name on'. * - * Revision 1.68 1998/06/07 00:20:05 fritz - * abc cleanup. * - * Revision 1.67 1998/06/02 12:10:08 detabc - * wegen einer einstweiliger verfuegung gegen DW ist zur zeit - * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar * * Revision 1.66 1998/05/26 22:39:24 he * sync'ed with 2.1.102 where appropriate (CAPABILITY changes) * concap typo * cleared dev.tbusy in isdn_net BCONN status callback * - * Revision 1.65 1998/05/22 10:01:11 detabc - * in case of a icmp-unreach condition the tcp-keepalive-entrys - * will be dropped from the internal double-link-list (only abc-extension). - * send icmp unreach only if the skb->protocol == ETH_P_IP - * speedup abc-no-dchan redial - * - * Revision 1.64 1998/05/07 19:58:39 detabc - * bugfix in abc_delayed_hangup - * optimize keepalive-tests for abc_rawip - * - * Revision 1.63 1998/05/05 23:23:36 detabc - * change ICMP_HOST_UNREACH to ICMP_NET_UNREACH (only abc-ext.) - * set dev->tbusy to zero in isdn_net_unreachable() (only abc-ext.) - * drop all new packets and send ICMP_NET_UNREACH for - * min. dialwait to max. dialwait * 6 time. (only abc-ext.) - * change random-deliver of packets (small first) from all emcapsulation - * to only rawip with ABC-Router-Flag enabled. - * - * Revision 1.62 1998/05/03 17:40:42 detabc - * Include abc-extension-support for >= 2.1.x Kernels in - * isdn_net.c and isdn_common.c. alpha-test OK and running ! - * * Revision 1.61 1998/04/16 19:19:42 keil * Fix from vger (tx max qlength) * @@ -374,7 +360,7 @@ int isdn_net_force_dial_lp(isdn_net_local *); static int isdn_net_start_xmit(struct sk_buff *, struct device *); static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *); -char *isdn_net_revision = "$Revision: 1.84 $"; +char *isdn_net_revision = "$Revision: 1.88 $"; /* * Code for raw-networking over ISDN @@ -1553,7 +1539,7 @@ static void isdn_net_slarp_send(isdn_net_local *lp, int is_reply) { unsigned short hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; - struct sk_buff *skb = dev_alloc_skb(hl + sizeof(cisco_hdr) + sizeof(cisco_slarp)); + struct sk_buff *skb = alloc_skb(hl + sizeof(cisco_hdr) + sizeof(cisco_slarp), GFP_ATOMIC); unsigned long t = (jiffies / HZ * 1000000); int len; cisco_hdr *ch; @@ -2928,7 +2914,7 @@ isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone *peer) /* for pre-bound channels, we need this extra check */ if ( strncmp(dev->num[idx],"???",3) == 0 ) return -ENOTCONN; strncpy(phone->phone,dev->num[idx],ISDN_MSNLEN); - phone->outgoing=USG_OUTGOING(idx); + phone->outgoing=USG_OUTGOING(dev->usage[idx]); if ( copy_to_user(peer,phone,sizeof(*peer)) ) return -EFAULT; return 0; } diff --git a/drivers/isdn/isdn_ppp.c b/drivers/isdn/isdn_ppp.c index 7bce66d44e42..35be7ed4e0ca 100644 --- a/drivers/isdn/isdn_ppp.c +++ b/drivers/isdn/isdn_ppp.c @@ -1,4 +1,4 @@ -/* $Id: isdn_ppp.c,v 1.47 1999/04/18 14:06:59 fritz Exp $ +/* $Id: isdn_ppp.c,v 1.49 1999/07/06 07:47:11 calle Exp $ * * Linux ISDN subsystem, functions for synchronous PPP (linklevel). * @@ -19,6 +19,14 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_ppp.c,v $ + * Revision 1.49 1999/07/06 07:47:11 calle + * bugfix: dev_alloc_skb only reserve 16 bytes. We need to look at the + * hdrlen the driver want. So I changed dev_alloc_skb calls + * to alloc_skb and skb_reserve. + * + * Revision 1.48 1999/07/01 08:29:56 keil + * compatibility to 2.3 kernel + * * Revision 1.47 1999/04/18 14:06:59 fritz * Removed TIMRU stuff. * @@ -261,7 +269,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.47 $"; +char *isdn_ppp_revision = "$Revision: 1.49 $"; static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS]; static struct isdn_ppp_compressor *ipc_head = NULL; @@ -430,7 +438,7 @@ isdn_ppp_wakeup_daemon(isdn_net_local * lp) ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK; -#if LINUX_VERSION_CODE < 131841 +#ifndef COMPAT_HAS_NEW_WAITQ if (ippp_table[lp->ppp_slot]->wq) #endif wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq); @@ -450,10 +458,10 @@ isdn_ppp_closewait(int slot) return 0; is = ippp_table[slot]; -#if LINUX_VERSION_CODE < 131841 - if (is->state && is->wq) -#else +#ifdef COMPAT_HAS_NEW_WAITQ if (is->state) +#else + if (is->state && is->wq) #endif wake_up_interruptible(&is->wq); @@ -519,10 +527,10 @@ isdn_ppp_open(int min, struct file *file) is->mru = 1524; /* MRU, default 1524 */ is->maxcid = 16; /* VJ: maxcid */ is->tk = current; -#if LINUX_VERSION_CODE < 131841 - is->wq = NULL; /* read() wait queue */ -#else +#ifdef COMPAT_HAS_NEW_WAITQ init_waitqueue_head(&is->wq); +#else + is->wq = NULL; /* read() wait queue */ #endif is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */ is->last = is->rq; @@ -891,7 +899,7 @@ isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot) is->last = bl->next; restore_flags(flags); -#if LINUX_VERSION_CODE < 131841 +#ifndef COMPAT_HAS_NEW_WAITQ if (is->wq) #endif wake_up_interruptible(&is->wq); @@ -983,13 +991,21 @@ isdn_ppp_write(int min, struct file *file, const char *buf, int count) if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) && lp->dialstate == 0 && (lp->flags & ISDN_NET_CONNECTED)) { + unsigned short hl; int cnt; struct sk_buff *skb; - skb = dev_alloc_skb(count); + /* + * we need to reserve enought space in front of + * sk_buff. old call to dev_alloc_skb only reserved + * 16 bytes, now we are looking what the driver want + */ + hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; + skb = alloc_skb(hl+count, GFP_ATOMIC); if (!skb) { printk(KERN_WARNING "isdn_ppp_write: out of memory!\n"); return count; } + skb_reserve(skb, hl); if (copy_from_user(skb_put(skb, count), buf, count)) return -EFAULT; if (is->debug & 0x40) { @@ -1416,9 +1432,9 @@ static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len) */ int -isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) +isdn_ppp_xmit(struct sk_buff *skb, struct device *netdev) { - struct device *mdev = ((isdn_net_local *) (dev->priv))->master; /* get master (for redundancy) */ + struct device *mdev = ((isdn_net_local *) (netdev->priv))->master; /* get master (for redundancy) */ isdn_net_local *lp,*mlp; isdn_net_dev *nd; unsigned int proto = PPP_IP; /* 0x21 */ @@ -1427,8 +1443,8 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) if (mdev) mlp = (isdn_net_local *) (mdev->priv); else { - mdev = dev; - mlp = (isdn_net_local *) (dev->priv); + mdev = netdev; + mlp = (isdn_net_local *) (netdev->priv); } nd = mlp->netdev; /* get master lp */ ipts = ippp_table[mlp->ppp_slot]; @@ -1441,7 +1457,7 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) ipts->old_pa_dstaddr = mdev->pa_dstaddr; #endif if (ipts->debug & 0x1) - printk(KERN_INFO "%s: IP frame delayed.\n", dev->name); + printk(KERN_INFO "%s: IP frame delayed.\n", netdev->name); return 1; } @@ -1506,12 +1522,19 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) #ifdef CONFIG_ISDN_PPP_VJ if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) { /* ipts here? probably yes, but check this again */ struct sk_buff *new_skb; - - new_skb = dev_alloc_skb(skb->len); + unsigned short hl; + /* + * we need to reserve enought space in front of + * sk_buff. old call to dev_alloc_skb only reserved + * 16 bytes, now we are looking what the driver want. + */ + hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; + new_skb = alloc_skb(hl+skb->len, GFP_ATOMIC); if (new_skb) { u_char *buf; int pktlen; + skb_reserve(new_skb, hl); new_skb->dev = skb->dev; skb_put(new_skb, skb->len); buf = skb->data; @@ -1611,9 +1634,9 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) printk(KERN_DEBUG "skb xmit: len: %d\n", (int) skb->len); isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,ipt->unit,lp->ppp_slot); } - if (isdn_net_send_skb(dev, lp, skb)) { + if (isdn_net_send_skb(netdev, lp, skb)) { if (lp->sav_skb) { /* whole sav_skb processing with disabled IRQs ?? */ - printk(KERN_ERR "%s: whoops .. there is another stored skb!\n", dev->name); + printk(KERN_ERR "%s: whoops .. there is another stored skb!\n", netdev->name); dev_kfree_skb(skb); } else lp->sav_skb = skb; diff --git a/drivers/isdn/isdn_tty.c b/drivers/isdn/isdn_tty.c index bfa895424886..d3a36c0e51b7 100644 --- a/drivers/isdn/isdn_tty.c +++ b/drivers/isdn/isdn_tty.c @@ -1,4 +1,4 @@ -/* $Id: isdn_tty.c,v 1.63 1999/04/12 12:33:39 fritz Exp $ +/* $Id: isdn_tty.c,v 1.72 1999/07/31 12:59:45 armin Exp $ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel). * @@ -20,6 +20,43 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_tty.c,v $ + * Revision 1.72 1999/07/31 12:59:45 armin + * Added tty fax capabilities. + * + * Revision 1.71 1999/07/27 10:34:34 armin + * Fixed last change. Did not compile with AUDIO support off. + * + * Revision 1.70 1999/07/25 16:17:58 keil + * Fix Suspend/Resume + * + * Revision 1.69 1999/07/25 12:56:15 armin + * isdn_tty_at_cout() now queues the message if online and + * data is in queue or flip buffer is full. + * needed for fax connections. + * + * Revision 1.68 1999/07/11 17:51:51 armin + * Bugfix, "-" was missing for AT&L settings. + * + * Revision 1.67 1999/07/11 17:14:12 armin + * Added new layer 2 and 3 protocols for Fax and DSP functions. + * Moved "Add CPN to RING message" to new register S23, + * "Display message" is now correct on register S13 bit 7. + * New audio command AT+VDD implemented (deactivate DTMF decoder and + * activate possible existing hardware/DSP decoder). + * Moved some tty defines to .h file. + * Made whitespace possible in AT command line. + * Some AT-emulator output bugfixes. + * First Fax G3 implementations. + * + * Revision 1.66 1999/07/07 10:13:46 detabc + * remove unused messages + * + * Revision 1.65 1999/07/04 21:01:59 werner + * Added support for keypad and display (ported from 2.0) + * + * Revision 1.64 1999/07/01 08:30:00 keil + * compatibility to 2.3 kernel + * * Revision 1.63 1999/04/12 12:33:39 fritz * Changes from 2.0 tree. * @@ -61,24 +98,12 @@ * brute force fix to avoid Ugh's in isdn_tty_write() * cleaned up some dead code * - * Revision 1.54 1998/06/07 00:20:13 fritz - * abc cleanup. * - * Revision 1.53 1998/06/02 12:10:16 detabc - * wegen einer einstweiliger verfuegung gegen DW ist zur zeit - * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar * * Revision 1.52 1998/03/19 13:18:21 keil * Start of a CAPI like interface for supplementary Service * first service: SUSPEND * - * Revision 1.51 1998/03/08 14:26:11 detabc - * change kfree_skb to dev_kfree_skb - * remove SET_SKB_FREE - * - * Revision 1.50 1998/03/08 13:14:28 detabc - * abc-extension support for kernels > 2.1.x - * first try (sorry experimental) * * Revision 1.49 1998/03/08 00:01:59 fritz * Bugfix: Lowlevel module usage and channel usage were not @@ -303,7 +328,6 @@ static int isdn_tty_edit_at(const char *, int, modem_info *, int); static void isdn_tty_check_esc(const u_char *, u_char, int, int *, int *, int); static void isdn_tty_modem_reset_regs(modem_info *, int); static void isdn_tty_cmd_ATA(modem_info *); -static void isdn_tty_at_cout(char *, modem_info *); static void isdn_tty_flush_buffer(struct tty_struct *); static void isdn_tty_modem_result(int, modem_info *); #ifdef CONFIG_ISDN_AUDIO @@ -321,64 +345,8 @@ static int bit2si[8] = static int si2bit[8] = {4, 1, 4, 4, 4, 4, 4, 4}; -char *isdn_tty_revision = "$Revision: 1.63 $"; - -#define DLE 0x10 -#define ETX 0x03 -#define DC4 0x14 +char *isdn_tty_revision = "$Revision: 1.72 $"; -/* - * Definition of some special Registers of AT-Emulator - */ -#define REG_RINGATA 0 -#define REG_RINGCNT 1 -#define REG_ESC 2 -#define REG_CR 3 -#define REG_LF 4 -#define REG_BS 5 - -#define REG_WAITC 7 - -#define REG_RESP 12 -#define BIT_RESP 1 -#define REG_RESPNUM 12 -#define BIT_RESPNUM 2 -#define REG_ECHO 12 -#define BIT_ECHO 4 -#define REG_DCD 12 -#define BIT_DCD 8 -#define REG_CTS 12 -#define BIT_CTS 16 -#define REG_DTRR 12 -#define BIT_DTRR 32 -#define REG_DSR 12 -#define BIT_DSR 64 -#define REG_CPPP 12 -#define BIT_CPPP 128 - -#define REG_T70 13 -#define BIT_T70 2 -#define BIT_T70_EXT 32 -#define REG_DTRHUP 13 -#define BIT_DTRHUP 4 -#define REG_RESPXT 13 -#define BIT_RESPXT 8 -#define REG_CIDONCE 13 -#define BIT_CIDONCE 16 -#define REG_RUNG 13 -#define BIT_RUNG 64 -#define REG_RESRXT 13 -#define BIT_RESRXT 128 - -#define REG_L2PROT 14 -#define REG_L3PROT 15 -#define REG_PSIZE 16 -#define REG_WSIZE 17 -#define REG_SI1 18 -#define REG_SI2 19 -#define REG_SI1I 20 -#define REG_PLAN 21 -#define REG_SCREEN 22 /* isdn_tty_try_read() is called from within isdn_tty_rcv_skb() * to stuff incoming data directly into a tty's flip-buffer. This @@ -514,7 +482,7 @@ isdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb) #ifdef CONFIG_ISDN_AUDIO ifmt = 1; - if (info->vonline) + if ((info->vonline) && (!info->emu.vpar[4])) isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt); if ((info->vonline & 1) && (info->emu.vpar[1])) isdn_audio_calc_silence(info, skb->data, skb->len, ifmt); @@ -580,6 +548,15 @@ isdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb) ISDN_AUDIO_SKB_DLECOUNT(skb) = isdn_tty_countDLE(skb->data, skb->len); } +#ifdef CONFIG_ISDN_TTY_FAX + else { + if (info->faxonline & 2) { + isdn_tty_fax_bitorder(info, skb); + ISDN_AUDIO_SKB_DLECOUNT(skb) = + isdn_tty_countDLE(skb->data, skb->len); + } + } +#endif #endif /* Try to deliver directly via tty-flip-buf if queue is empty */ save_flags(flags); @@ -909,6 +886,35 @@ isdn_tty_modem_ncarrier(modem_info * info) } } +/* + * return the usage calculated by si and layer 2 protocol + */ +int +isdn_calc_usage(int si, int l2) +{ + int usg = ISDN_USAGE_MODEM; + +#ifdef CONFIG_ISDN_AUDIO + if (si == 1) { + switch(l2) { + case ISDN_PROTO_L2_MODEM: + usg = ISDN_USAGE_MODEM; + break; +#ifdef CONFIG_ISDN_TTY_FAX + case ISDN_PROTO_L2_FAX: + usg = ISDN_USAGE_FAX; + break; +#endif + case ISDN_PROTO_L2_TRANS: + default: + usg = ISDN_USAGE_VOICE; + break; + } + } +#endif + return(usg); +} + /* isdn_tty_dial() performs dialing of a tty an the necessary * setup of the lower levels before that. */ @@ -928,8 +934,14 @@ isdn_tty_dial(char *n, modem_info * info, atemu * m) si = bit2si[j]; break; } + usg = isdn_calc_usage(si, l2); #ifdef CONFIG_ISDN_AUDIO - if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) { + if ((si == 1) && + (l2 != ISDN_PROTO_L2_MODEM) +#ifdef CONFIG_ISDN_TTY_FAX + && (l2 != ISDN_PROTO_L2_FAX) +#endif + ) { l2 = ISDN_PROTO_L2_TRANS; usg = ISDN_USAGE_VOICE; } @@ -967,6 +979,12 @@ isdn_tty_dial(char *n, modem_info * info, atemu * m) cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL3; cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); +#ifdef CONFIG_ISDN_TTY_FAX + if (l2 == ISDN_PROTO_L2_FAX) { + cmd.parm.fax = info->fax; + info->fax->direction = ISDN_TTY_FAX_CONN_OUT; + } +#endif isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; @@ -989,7 +1007,7 @@ isdn_tty_dial(char *n, modem_info * info, atemu * m) * ISDN-line (hangup). The usage-status is cleared * and some cleanup is done also. */ -static void +void isdn_tty_modem_hup(modem_info * info, int local) { isdn_ctrl cmd; @@ -1010,6 +1028,12 @@ isdn_tty_modem_hup(modem_info * info, int local) } #ifdef CONFIG_ISDN_AUDIO info->vonline = 0; +#ifdef CONFIG_ISDN_TTY_FAX + info->faxonline = 0; + info->fax->phase = ISDN_FAX_PHASE_IDLE; +#endif + info->emu.vpar[4] = 0; + info->emu.vpar[5] = 8; if (info->dtmf_state) { kfree(info->dtmf_state); info->dtmf_state = NULL; @@ -1041,9 +1065,8 @@ isdn_tty_modem_hup(modem_info * info, int local) } isdn_all_eaz(info->isdn_driver, info->isdn_channel); info->emu.mdmreg[REG_RINGCNT] = 0; - usage = ((info->emu.mdmreg[REG_SI1I] != 1) || - (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) ? - ISDN_USAGE_MODEM : ISDN_USAGE_VOICE; + usage = isdn_calc_usage(info->emu.mdmreg[REG_SI1I], + info->emu.mdmreg[REG_L2PROT]); isdn_free_channel(info->isdn_driver, info->isdn_channel, usage); } @@ -1130,8 +1153,14 @@ isdn_tty_resume(char *id, modem_info * info, atemu * m) si = bit2si[j]; break; } + usg = isdn_calc_usage(si, l2); #ifdef CONFIG_ISDN_AUDIO - if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) { + if ((si == 1) && + (l2 != ISDN_PROTO_L2_MODEM) +#ifdef CONFIG_ISDN_TTY_FAX + && (l2 != ISDN_PROTO_L2_FAX) +#endif + ) { l2 = ISDN_PROTO_L2_TRANS; usg = ISDN_USAGE_VOICE; } @@ -1184,11 +1213,11 @@ isdn_tty_resume(char *id, modem_info * info, atemu * m) cmd.parm.cmsg.para[5] = l; strncpy(&cmd.parm.cmsg.para[6], id, l); cmd.command =CAPI_PUT_MESSAGE; -/* info->dialing = 1; - strcpy(dev->num[i], n); + info->dialing = 1; +// strcpy(dev->num[i], n); isdn_info_update(); -*/ isdn_command(&cmd); + isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1); } } @@ -1218,8 +1247,14 @@ isdn_tty_send_msg(modem_info * info, atemu * m, char *msg) si = bit2si[j]; break; } + usg = isdn_calc_usage(si, l2); #ifdef CONFIG_ISDN_AUDIO - if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) { + if ((si == 1) && + (l2 != ISDN_PROTO_L2_MODEM) +#ifdef CONFIG_ISDN_TTY_FAX + && (l2 != ISDN_PROTO_L2_FAX) +#endif + ) { l2 = ISDN_PROTO_L2_TRANS; usg = ISDN_USAGE_VOICE; } @@ -1863,11 +1898,11 @@ isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_termios) static int isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info) { -#if LINUX_VERSION_CODE < 131841 +#ifdef COMPAT_HAS_NEW_WAITQ + DECLARE_WAITQUEUE(wait, NULL); +#else struct wait_queue wait = {current, NULL}; -#else - DECLARE_WAITQUEUE(wait, NULL); #endif int do_clocal = 0; unsigned long flags; @@ -2191,6 +2226,7 @@ isdn_tty_reset_profile(atemu * m) m->profile[18] = 4; m->profile[19] = 0; m->profile[20] = 0; + m->profile[23] = 0; m->pmsn[0] = '\0'; m->plmsn[0] = '\0'; } @@ -2203,6 +2239,42 @@ isdn_tty_modem_reset_vpar(atemu * m) m->vpar[1] = 0; /* Silence detection level (0 = none ) */ m->vpar[2] = 70; /* Silence interval (7 sec. ) */ m->vpar[3] = 2; /* Compression type (1 = ADPCM-2 ) */ + m->vpar[4] = 0; /* DTMF detection level (0 = softcode ) */ + m->vpar[5] = 8; /* DTMF interval (8 * 5 ms. ) */ +} +#endif + +#ifdef CONFIG_ISDN_TTY_FAX +static void +isdn_tty_modem_reset_faxpar(modem_info * info) +{ + T30_s *f = info->fax; + + f->code = 0; + f->phase = ISDN_FAX_PHASE_IDLE; + f->direction = 0; + f->resolution = 1; /* fine */ + f->rate = 5; /* 14400 bit/s */ + f->width = 0; + f->length = 0; + f->compression = 0; + f->ecm = 0; + f->binary = 0; + f->scantime = 0; + memset(&f->id[0], 32, FAXIDLEN - 1); + f->id[FAXIDLEN - 1] = 0; + f->badlin = 0; + f->badmul = 0; + f->bor = 0; + f->nbc = 0; + f->cq = 0; + f->cr = 0; + f->ctcrty = 0; + f->minsp = 0; + f->phcto = 30; + f->rel = 0; + memset(&f->pollid[0], 32, FAXIDLEN - 1); + f->pollid[FAXIDLEN - 1] = 0; } #endif @@ -2218,6 +2290,9 @@ isdn_tty_modem_reset_regs(modem_info * info, int force) } #ifdef CONFIG_ISDN_AUDIO isdn_tty_modem_reset_vpar(m); +#endif +#ifdef CONFIG_ISDN_TTY_FAX + isdn_tty_modem_reset_faxpar(info); #endif m->mdmcmdl = 0; } @@ -2291,10 +2366,16 @@ isdn_tty_modem_init(void) } for (i = 0; i < ISDN_MAX_CHANNELS; i++) { info = &m->info[i]; -#if LINUX_VERSION_CODE < 131841 - info->write_sem = MUTEX; -#else +#ifdef CONFIG_ISDN_TTY_FAX + if (!(info->fax = kmalloc(sizeof(T30_s), GFP_KERNEL))) { + printk(KERN_ERR "Could not allocate fax t30-buffer\n"); + return -3; + } +#endif +#ifdef COMPAT_HAS_NEW_WAITQ init_MUTEX(&info->write_sem); +#else + info->write_sem = MUTEX; #endif sprintf(info->last_cause, "0000"); sprintf(info->last_num, "none"); @@ -2312,14 +2393,14 @@ isdn_tty_modem_init(void) info->blocked_open = 0; info->callout_termios = m->cua_modem.init_termios; info->normal_termios = m->tty_modem.init_termios; -#if LINUX_VERSION_CODE < 131841 - info->open_wait = 0; - info->close_wait = 0; -#else +#ifdef COMPAT_HAS_NEW_WAITQ init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); -#endif +#else + info->open_wait = 0; + info->close_wait = 0; info->isdn_driver = -1; +#endif info->isdn_channel = -1; info->drv_index = -1; info->xmit_size = ISDN_SERIAL_XMIT_SIZE; @@ -2371,8 +2452,15 @@ isdn_tty_match_icall(char *cid, atemu *emu, int di) break; } return ret; - } else - return isdn_wildmat(cid, isdn_map_eaz2msn(emu->msn, di)); + } else { + int tmp; + tmp = isdn_wildmat(cid, isdn_map_eaz2msn(emu->msn, di)); +#ifdef ISDN_DEBUG_MODEM_ICALL + printk(KERN_DEBUG "m_fi: mmsn=%s -> tmp=%d\n", + isdn_map_eaz2msn(emu->msn, di), tmp); +#endif + return tmp; + } } /* @@ -2424,7 +2512,7 @@ isdn_tty_find_icall(int di, int ch, setup_parm setup) (info->emu.mdmreg[REG_SI2] == si2)) { /* SI2 is matching */ idx = isdn_dc2minor(di, ch); #ifdef ISDN_DEBUG_MODEM_ICALL - printk(KERN_DEBUG "m_fi: match1\n"); + printk(KERN_DEBUG "m_fi: match1 wret=%d\n", wret); printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx, info->flags, info->isdn_driver, info->isdn_channel, dev->usage[idx]); @@ -2446,8 +2534,7 @@ isdn_tty_find_icall(int di, int ch, setup_parm setup) info->drv_index = idx; dev->m_idx[idx] = info->line; dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE; - dev->usage[idx] |= ((si1 != 1) || (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) ? - ISDN_USAGE_MODEM : ISDN_USAGE_VOICE; + dev->usage[idx] |= isdn_calc_usage(si1, info->emu.mdmreg[REG_L2PROT]); strcpy(dev->num[idx], nr); strcpy(info->emu.cpn, eaz); info->emu.mdmreg[REG_SI1I] = si2bit[si1]; @@ -2514,6 +2601,19 @@ isdn_tty_stat_callback(int i, isdn_ctrl * c) /* Signal cause to tty-device */ strncpy(info->last_cause, c->parm.num, 5); return 1; + case ISDN_STAT_DISPLAY: +#ifdef ISDN_TTY_STAT_DEBUG + printk(KERN_DEBUG "tty_STAT_DISPLAY ttyI%d\n", info->line); +#endif + /* Signal display to tty-device */ + if ((info->emu.mdmreg[REG_DISPLAY] & BIT_DISPLAY) && + !(info->emu.mdmreg[REG_RESPNUM] & BIT_RESPNUM)) { + isdn_tty_at_cout("\r\n", info); + isdn_tty_at_cout("DISPLAY: ", info); + isdn_tty_at_cout(c->parm.display, info); + isdn_tty_at_cout("\r\n", info); + } + return 1; case ISDN_STAT_DCONN: #ifdef ISDN_TTY_STAT_DEBUG printk(KERN_DEBUG "tty_STAT_DCONN ttyI%d\n", info->line); @@ -2612,6 +2712,27 @@ isdn_tty_stat_callback(int i, isdn_ctrl * c) } } return 1; +#ifdef CONFIG_ISDN_TTY_FAX + case ISDN_STAT_FAXIND: + if (TTY_IS_ACTIVE(info)) { + isdn_tty_fax_command(info); + } + break; +#endif +#ifdef CONFIG_ISDN_AUDIO + case ISDN_STAT_AUDIO: + if (TTY_IS_ACTIVE(info)) { + switch(c->parm.num[0]) { + case ISDN_AUDIO_DTMF: + if (info->vonline) { + isdn_audio_put_dle_code(info, + c->parm.num[1]); + } + break; + } + } + break; +#endif } } return 0; @@ -2621,13 +2742,13 @@ isdn_tty_stat_callback(int i, isdn_ctrl * c) Modem-Emulator-Routines *********************************************************************/ -#define cmdchar(c) ((c>' ')&&(c<=0x7f)) +#define cmdchar(c) ((c>=' ')&&(c<=0x7f)) /* * Put a message from the AT-emulator into receive-buffer of tty, * convert CR, LF, and BS to values in modem-registers 3, 4 and 5. */ -static void +void isdn_tty_at_cout(char *msg, modem_info * info) { struct tty_struct *tty; @@ -2635,6 +2756,8 @@ isdn_tty_at_cout(char *msg, modem_info * info) char *p; char c; ulong flags; + struct sk_buff *skb = 0; + char *sp = 0; if (!msg) { printk(KERN_WARNING "isdn_tty: Null-Message in isdn_tty_at_cout\n"); @@ -2643,6 +2766,34 @@ isdn_tty_at_cout(char *msg, modem_info * info) save_flags(flags); cli(); tty = info->tty; + if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) { + restore_flags(flags); + return; + } + + /* use queue instead of direct flip, if online and */ + /* data is in queue or flip buffer is full */ + if ((info->online) && (((tty->flip.count + strlen(msg)) >= TTY_FLIPBUF_SIZE) || + (!skb_queue_empty(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel])))) { + skb = alloc_skb(strlen(msg) +#ifdef CONFIG_ISDN_AUDIO + + sizeof(isdn_audio_skb) +#endif + , GFP_ATOMIC); + if (!skb) { + restore_flags(flags); + return; + } +#ifdef CONFIG_ISDN_AUDIO + skb_reserve(skb, sizeof(isdn_audio_skb)); +#endif + sp = skb_put(skb, strlen(msg)); +#ifdef CONFIG_ISDN_AUDIO + ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; + ISDN_AUDIO_SKB_LOCK(skb) = 0; +#endif + } + for (p = msg; *p; p++) { switch (*p) { case '\r': @@ -2657,16 +2808,26 @@ isdn_tty_at_cout(char *msg, modem_info * info) default: c = *p; } - if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) { - restore_flags(flags); - return; + if (skb) { + *sp++ = c; + } else { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + break; + tty_insert_flip_char(tty, c, 0); } - if (tty->flip.count >= TTY_FLIPBUF_SIZE) - break; - tty_insert_flip_char(tty, c, 0); } - restore_flags(flags); - queue_task(&tty->flip.tqueue, &tq_timer); + if (skb) { + __skb_queue_tail(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel], skb); + dev->drv[info->isdn_driver]->rcvcount[info->isdn_channel] += skb->len; + restore_flags(flags); + /* Schedule dequeuing */ + if ((dev->modempoll) && (info->rcvsched)) + isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); + + } else { + restore_flags(flags); + queue_task(&tty->flip.tqueue, &tq_timer); + } } /* @@ -2847,7 +3008,7 @@ isdn_tty_modem_result(int code, modem_info * info) break; case 2: /* Append CPN, if enabled */ - if ((m->mdmreg[REG_RESRXT] & BIT_RESRXT)) { + if ((m->mdmreg[REG_CPN] & BIT_CPN)) { sprintf(s, "/%s", m->cpn); isdn_tty_at_cout(s, info); } @@ -2954,8 +3115,9 @@ isdn_tty_getdial(char *p, char *q,int cnt) int limit=39; /* MUST match the size in isdn_tty_parse to avoid buffer overflow */ - while (strchr("0123456789,#.*WPTS-", *p) && *p && --cnt>0) { - if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first)) + while (strchr(" 0123456789,#.*WPTS-", *p) && *p && --cnt>0) { + if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first) || + (*p == '*') || (*p == '#')) *q++ = *p; p++; if(!--limit) @@ -3008,6 +3170,9 @@ isdn_tty_report(modem_info * info) case ISDN_PROTO_L2_MODEM: isdn_tty_at_cout("modem", info); break; + case ISDN_PROTO_L2_FAX: + isdn_tty_at_cout("fax", info); + break; default: isdn_tty_at_cout("unknown", info); break; @@ -3107,8 +3272,8 @@ isdn_tty_cmd_ATand(char **p, modem_info * info) /* &L -Set Numbers to listen on */ p[0]++; i = 0; - while ((strchr("0123456789,*[]?;", *p[0])) && - (i < ISDN_LMSNLEN)) + while ((strchr("0123456789,-*[]?;", *p[0])) && + (i < ISDN_LMSNLEN) && *p[0]) m->lmsn[i++] = *p[0]++; m->lmsn[i] = '\0'; break; @@ -3264,7 +3429,7 @@ isdn_tty_cmd_ATS(char **p, modem_info * info) int bval; mreg = isdn_getnum(p); - if (mreg < 0 || mreg > ISDN_MODEM_ANZREG) + if (mreg < 0 || mreg >= ISDN_MODEM_ANZREG) PARSE_ERROR1; switch (*p[0]) { case '=': @@ -3338,7 +3503,7 @@ isdn_tty_cmd_ATA(modem_info * info) /* If more than one bit set in reg18, autoselect Layer2 */ if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) { if (m->mdmreg[REG_SI1I] == 1) { - if (l2 != ISDN_PROTO_L2_MODEM) + if ((l2 != ISDN_PROTO_L2_MODEM) && (l2 != ISDN_PROTO_L2_FAX)) l2 = ISDN_PROTO_L2_TRANS; } else l2 = ISDN_PROTO_L2_X75I; @@ -3352,6 +3517,12 @@ isdn_tty_cmd_ATA(modem_info * info) cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL3; cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); +#ifdef CONFIG_ISDN_TTY_FAX + if (l2 == ISDN_PROTO_L2_FAX) { + cmd.parm.fax = info->fax; + info->fax->direction = ISDN_TTY_FAX_CONN_IN; + } +#endif isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; @@ -3372,7 +3543,6 @@ static int isdn_tty_cmd_PLUSF(char **p, modem_info * info) { atemu *m = &info->emu; - int par; char rs[20]; if (!strncmp(p[0], "CLASS", 5)) { @@ -3382,6 +3552,10 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) p[0]++; sprintf(rs, "\r\n%d", (m->mdmreg[REG_SI1] & 1) ? 8 : 0); +#ifdef CONFIG_ISDN_TTY_FAX + if (m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) + sprintf(rs, "\r\n2"); +#endif isdn_tty_at_cout(rs, info); break; case '=': @@ -3389,23 +3563,37 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) switch (*p[0]) { case '0': p[0]++; + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; + m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS; m->mdmreg[REG_SI1] = 4; info->xmit_size = m->mdmreg[REG_PSIZE] * 16; break; +#ifdef CONFIG_ISDN_TTY_FAX case '2': - printk(KERN_DEBUG "isdn_tty: FCLASS=2\n"); p[0]++; + m->mdmreg[REG_SI1] = 1; + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX; + m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FAX; + info->xmit_size = + m->mdmreg[REG_PSIZE] * 16; break; +#endif case '8': p[0]++; + /* L2 will change on dialout with si=1 */ + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; + m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS; m->mdmreg[REG_SI1] = 5; info->xmit_size = VBUF; break; case '?': p[0]++; - isdn_tty_at_cout("\r\n0,2,8", - info); +#ifdef CONFIG_ISDN_TTY_FAX + isdn_tty_at_cout("\r\n0,2,8", info); +#else + isdn_tty_at_cout("\r\n0,8", info); +#endif break; default: PARSE_ERROR1; @@ -3416,115 +3604,11 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) } return 0; } - if (!strncmp(p[0], "AA", 2)) { - p[0] += 2; - switch (*p[0]) { - case '?': - p[0]++; - sprintf(rs, "\r\n%d", - m->mdmreg[REG_RINGATA]); - isdn_tty_at_cout(rs, info); - break; - case '=': - p[0]++; - par = isdn_getnum(p); - if ((par < 0) || (par > 255)) - PARSE_ERROR1; - m->mdmreg[REG_RINGATA] = par; - break; - default: - PARSE_ERROR1; - } - return 0; - } - if (!strncmp(p[0], "TBC=", 4)) { /* UNKLAR !! */ - p[0] += 4; - printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]); - switch (*p[0]) { - case '0': - p[0]++; - break; - default: - PARSE_ERROR1; - } - return 0; - } - if (!strncmp(p[0], "BOR=", 4)) { /* UNKLAR !! */ - p[0] += 4; - printk(KERN_DEBUG "isdn_tty: Fax FBOR=%c\n", *p[0]); - switch (*p[0]) { - case '0': - p[0]++; - break; - default: - PARSE_ERROR1; - } - return 0; - } - if (!strncmp(p[0], "DCC=", 4)) { /* SETUP irgendwie !! */ - int i, val[]={0,0,0,0,0,0,0,0}; - - p[0] += 4; - if (*p[0] == '?') { - isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0,1),(0),(0),(0-7)",info); - p[0]++; - } else { - for (i=0; (*p[0]>='0') && (*p[0]<='9'); i++) { - val[i] = *p[0] - 48; - p[0]++; - if (*p[0] == ',') - p[0]++; - } - printk(KERN_DEBUG "isdn_tty: Fax Setup values=%d,%d,%d,%d,%d,%d,%d,%d\n", - val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]); - } - return 0; - } - if (!strncmp(p[0], "LID=", 4)) { /* set sender ID !! */ - char senderID[80]; - int i; - - p[0] += 4; - if (*p[0] =='"') - p[0]++; - for(i=0; (*p[0]) && (*p[0] != '"'); i++) - senderID[i] = *p[0]++; - senderID[i] = 0; - if (*p[0] =='"') - p[0]++; - printk(KERN_DEBUG "isdn_tty: Fax sender=>%s<\n", senderID); - return 0; - } - if (!strncmp(p[0], "MFR?", 4)) { - p[0] += 4; - printk(KERN_DEBUG "isdn_tty: FMFR?\n"); - isdn_tty_at_cout("\r\nISDNfax", info); - return 0; - } - if (!strncmp(p[0], "MDL?", 4)) { - p[0] += 4; - printk(KERN_DEBUG "isdn_tty: FMDL?\n"); - isdn_tty_at_cout("\r\nAVM-B1", info); - return 0; - } - if (!strncmp(p[0], "AP=?", 4)) { - p[0] += 4; - printk(KERN_DEBUG "isdn_tty: FAP=?\n"); - return 0; - } - if (!strncmp(p[0], "PHCTO=", 6)) { - /* beim trace mit dem zyxel folgt der wert 30 ;*/ - p[0] += 6; - printk(KERN_DEBUG "isdn_tty: FPHCTO=%s\n", p[0]); - return 0; - } - if (!strncmp(p[0], "CR=", 3)) { - p[0] += 3; - printk(KERN_DEBUG "isdn_tty: FCR=%s\n", p[0]); - return 0; - } - printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]); +#ifdef CONFIG_ISDN_TTY_FAX + return (isdn_tty_cmd_PLUSF_FAX(p, info)); +#else PARSE_ERROR1; +#endif } /* @@ -3534,8 +3618,9 @@ static int isdn_tty_cmd_PLUSV(char **p, modem_info * info) { atemu *m = &info->emu; + isdn_ctrl cmd; static char *vcmd[] = - {"NH", "IP", "LS", "RX", "SD", "SM", "TX", NULL}; + {"NH", "IP", "LS", "RX", "SD", "SM", "TX", "DD", NULL}; int i; int par1; int par2; @@ -3711,9 +3796,9 @@ isdn_tty_cmd_PLUSV(char **p, modem_info * info) info); isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n", info); - isdn_tty_at_cout("5;ALAW;8;0;(8000)", + isdn_tty_at_cout("5;ALAW;8;0;(8000)\r\n", info); - isdn_tty_at_cout("6;ULAW;8;0;(8000)", + isdn_tty_at_cout("6;ULAW;8;0;(8000)\r\n", info); break; default: @@ -3752,6 +3837,52 @@ isdn_tty_cmd_PLUSV(char **p, modem_info * info) isdn_tty_modem_result(1, info); return 0; break; + case 7: + /* AT+VDD - DTMF detection */ + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n<%d>,<%d>", + m->vpar[4], + m->vpar[5]); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if ((*p[0]>='0') && (*p[0]<='9')) { + if (info->online != 1) + PARSE_ERROR1; + par1 = isdn_getnum(p); + if ((par1 < 0) || (par1 > 15)) + PARSE_ERROR1; + if (*p[0] != ',') + PARSE_ERROR1; + p[0]++; + par2 = isdn_getnum(p); + if ((par2 < 0) || (par2 > 255)) + PARSE_ERROR1; + m->vpar[4] = par1; + m->vpar[5] = par2; + cmd.driver = info->isdn_driver; + cmd.command = ISDN_CMD_AUDIO; + cmd.arg = info->isdn_channel + (ISDN_AUDIO_SETDD << 8); + cmd.parm.num[0] = par1; + cmd.parm.num[1] = par2; + isdn_command(&cmd); + break; + } else + if (*p[0] == '?') { + p[0]++; + isdn_tty_at_cout("\r\n<0-15>,<0-255>", + info); + break; + } else + PARSE_ERROR1; + break; + default: + PARSE_ERROR1; + } + break; default: PARSE_ERROR1; } @@ -3774,6 +3905,9 @@ isdn_tty_parse_at(modem_info * info) #endif for (p = &m->mdmcmd[2]; *p;) { switch (*p) { + case ' ': + p++; + break; case 'A': /* A - Accept incoming call */ p++; @@ -3919,8 +4053,10 @@ isdn_tty_parse_at(modem_info * info) isdn_tty_suspend(ds, info, m); break; case 'R': /* RESUME */ + p++; isdn_tty_get_msnstr(ds, &p); isdn_tty_resume(ds, info, m); + break; case 'M': /* MESSAGE */ p++; isdn_tty_send_msg(info, m, p); @@ -3981,7 +4117,7 @@ isdn_tty_edit_at(const char *p, int count, modem_info * info, int user) eb[1] = 0; isdn_tty_at_cout(eb, info); } - if (m->mdmcmdl >= 2) + if ((m->mdmcmdl >= 2) && (!(strncmp(m->mdmcmd, "AT", 2)))) isdn_tty_parse_at(info); m->mdmcmdl = 0; continue; @@ -4007,15 +4143,16 @@ isdn_tty_edit_at(const char *p, int count, modem_info * info, int user) switch (m->mdmcmdl) { case 0: if (c == 'A') - m->mdmcmd[m->mdmcmdl++] = c; + m->mdmcmd[m->mdmcmdl] = c; break; case 1: if (c == 'T') - m->mdmcmd[m->mdmcmdl++] = c; + m->mdmcmd[m->mdmcmdl] = c; break; default: - m->mdmcmd[m->mdmcmdl++] = c; + m->mdmcmd[m->mdmcmdl] = c; } + m->mdmcmd[++m->mdmcmdl] = 0; } } } diff --git a/drivers/isdn/isdn_tty.h b/drivers/isdn/isdn_tty.h index 663648e58018..4fe9568ca9e2 100644 --- a/drivers/isdn/isdn_tty.h +++ b/drivers/isdn/isdn_tty.h @@ -1,4 +1,4 @@ -/* $Id: isdn_tty.h,v 1.13 1999/04/12 12:33:46 fritz Exp $ +/* $Id: isdn_tty.h,v 1.15 1999/07/31 12:59:48 armin Exp $ * header for Linux ISDN subsystem, tty related functions (linklevel). * @@ -20,6 +20,20 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_tty.h,v $ + * Revision 1.15 1999/07/31 12:59:48 armin + * Added tty fax capabilities. + * + * Revision 1.14 1999/07/11 17:14:15 armin + * Added new layer 2 and 3 protocols for Fax and DSP functions. + * Moved "Add CPN to RING message" to new register S23, + * "Display message" is now correct on register S13 bit 7. + * New audio command AT+VDD implemented (deactivate DTMF decoder and + * activate possible existing hardware/DSP decoder). + * Moved some tty defines to .h file. + * Made whitespace possible in AT command line. + * Some AT-emulator output bugfixes. + * First Fax G3 implementations. + * * Revision 1.13 1999/04/12 12:33:46 fritz * Changes from 2.0 tree. * @@ -72,6 +86,68 @@ * */ + +#define DLE 0x10 +#define ETX 0x03 +#define DC4 0x14 + + +/* + * Definition of some special Registers of AT-Emulator + */ +#define REG_RINGATA 0 +#define REG_RINGCNT 1 +#define REG_ESC 2 +#define REG_CR 3 +#define REG_LF 4 +#define REG_BS 5 + +#define REG_WAITC 7 + +#define REG_RESP 12 +#define BIT_RESP 1 +#define REG_RESPNUM 12 +#define BIT_RESPNUM 2 +#define REG_ECHO 12 +#define BIT_ECHO 4 +#define REG_DCD 12 +#define BIT_DCD 8 +#define REG_CTS 12 +#define BIT_CTS 16 +#define REG_DTRR 12 +#define BIT_DTRR 32 +#define REG_DSR 12 +#define BIT_DSR 64 +#define REG_CPPP 12 +#define BIT_CPPP 128 + +#define REG_T70 13 +#define BIT_T70 2 +#define BIT_T70_EXT 32 +#define REG_DTRHUP 13 +#define BIT_DTRHUP 4 +#define REG_RESPXT 13 +#define BIT_RESPXT 8 +#define REG_CIDONCE 13 +#define BIT_CIDONCE 16 +#define REG_RUNG 13 +#define BIT_RUNG 64 +#define REG_DISPLAY 13 +#define BIT_DISPLAY 128 + +#define REG_L2PROT 14 +#define REG_L3PROT 15 +#define REG_PSIZE 16 +#define REG_WSIZE 17 +#define REG_SI1 18 +#define REG_SI2 19 +#define REG_SI1I 20 +#define REG_PLAN 21 +#define REG_SCREEN 22 + +#define REG_CPN 23 +#define BIT_CPN 1 + extern void isdn_tty_modem_escape(void); extern void isdn_tty_modem_ring(void); extern void isdn_tty_carrier_timeout(void); @@ -83,3 +159,10 @@ extern void isdn_tty_cleanup_xmit(modem_info *); extern int isdn_tty_stat_callback(int, isdn_ctrl *); extern int isdn_tty_rcv_skb(int, int, int, struct sk_buff *); extern int isdn_tty_capi_facility(capi_msg *cm); +extern void isdn_tty_at_cout(char *, modem_info *); +extern void isdn_tty_modem_hup(modem_info *, int); +#ifdef CONFIG_ISDN_TTY_FAX +extern int isdn_tty_cmd_PLUSF_FAX(char **, modem_info *); +extern int isdn_tty_fax_command(modem_info *); +extern void isdn_tty_fax_bitorder(modem_info *, struct sk_buff *); +#endif diff --git a/drivers/isdn/isdn_ttyfax.c b/drivers/isdn/isdn_ttyfax.c new file mode 100644 index 000000000000..fc17b5b830ac --- /dev/null +++ b/drivers/isdn/isdn_ttyfax.c @@ -0,0 +1,1201 @@ +/* $Id: isdn_ttyfax.c,v 1.2 1999/08/05 10:36:10 armin Exp $ + * Linux ISDN subsystem, tty_fax AT-command emulator (linklevel). + * + * Copyright 1999 by Armin Schindler (mac@melware.de) + * Copyright 1999 by Ralf Spachmann (mel@melware.de) + * Copyright 1999 by Cytronics & Melware + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: isdn_ttyfax.c,v $ + * Revision 1.2 1999/08/05 10:36:10 armin + * Bugfix: kernel oops on getting revision. + * + * Revision 1.1 1999/07/31 12:59:50 armin + * Added tty fax capabilities. + * + * + */ + +#undef ISDN_TTY_FAX_STAT_DEBUG +#undef ISDN_TTY_FAX_CMD_DEBUG + +#define __NO_VERSION__ +#include +#include +#include +#include "isdn_common.h" +#include "isdn_tty.h" +#include "isdn_ttyfax.h" + + +static char *isdn_tty_fax_revision = "$Revision: 1.2 $"; + +#define PARSE_ERROR1 { isdn_tty_fax_modem_result(1, info); return 1; } + +static char * +isdn_getrev(const char *revision) +{ + char *rev; + char *p; + + if ((p = strchr(revision, ':'))) { + rev = p + 2; + p = strchr(rev, '$'); + *--p = 0; + } else + rev = "???"; + return rev; +} + + +/* + * Fax Class 2 Modem results + * + */ +static void isdn_tty_fax_modem_result(int code, modem_info * info) +{ + atemu *m = &info->emu; + T30_s *f = info->fax; + char rs[50]; + char rss[50]; + char *rp; + int i; + static char *msg[] = + {"OK", "ERROR", "+FCON", "+FCSI:", "+FDIS:", + "+FHNG:", "+FDCS:", "CONNECT", "+FTSI:", + "+FCFR", "+FPTS:", "+FET:" }; + + + isdn_tty_at_cout("\r\n", info); + isdn_tty_at_cout(msg[code], info); + +#ifdef ISDN_TTY_FAX_CMD_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax send %s on ttyI%d\n", + msg[code], info->line); +#endif + switch (code) { + case 0: /* OK */ + break; + case 1: /* ERROR */ + break; + case 2: /* +FCON */ + /* Append CPN, if enabled */ + if ((m->mdmreg[REG_CPN] & BIT_CPN) && + (!(dev->usage[info->isdn_channel] & ISDN_USAGE_OUTGOING))) { + sprintf(rs, "/%s", m->cpn); + isdn_tty_at_cout(rs, info); + } + info->online = 1; + f->fet = 0; + if (f->phase == ISDN_FAX_PHASE_A) + f->phase = ISDN_FAX_PHASE_B; + break; + case 3: /* +FCSI */ + case 8: /* +FTSI */ + sprintf(rs, "\"%s\"", f->r_id); + isdn_tty_at_cout(rs, info); + break; + case 4: /* +FDIS */ + rs[0] = 0; + rp = &f->r_resolution; + for(i = 0; i < 8; i++) { + sprintf(rss, "%c%s", rp[i] + 48, + (i < 7) ? "," : ""); + strcat(rs, rss); + } + isdn_tty_at_cout(rs, info); +#ifdef ISDN_TTY_FAX_CMD_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax DIS=%s on ttyI%d\n", + rs, info->line); +#endif + break; + case 5: /* +FHNG */ + sprintf(rs, "%d", f->code); + isdn_tty_at_cout(rs, info); + info->faxonline = 0; + break; + case 6: /* +FDCS */ + rs[0] = 0; + rp = &f->r_resolution; + for(i = 0; i < 8; i++) { + sprintf(rss, "%c%s", rp[i] + 48, + (i < 7) ? "," : ""); + strcat(rs, rss); + } + isdn_tty_at_cout(rs, info); +#ifdef ISDN_TTY_FAX_CMD_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax DCS=%s on ttyI%d\n", + rs, info->line); +#endif + break; + case 7: /* CONNECT */ + info->faxonline |= 2; + break; + case 9: /* FCFR */ + break; + case 10: /* FPTS */ + isdn_tty_at_cout("1", info); + break; + case 11: /* FET */ + sprintf(rs, "%d", f->fet); + isdn_tty_at_cout(rs, info); + break; + } + + isdn_tty_at_cout("\r\n", info); + + switch (code) { + case 7: /* CONNECT */ + info->online = 2; + if (info->faxonline & 1) { + sprintf(rs, "%c", XON); + isdn_tty_at_cout(rs, info); + } + break; + } +} + +int +isdn_tty_fax_command(modem_info * info) +{ + T30_s *f = info->fax; + char rs[10]; + +#ifdef ISDN_TTY_FAX_CMD_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax cmd %d on ttyI%d\n", + f->r_code, info->line); +#endif + switch(f->r_code) { + case ISDN_TTY_FAX_FCON: + info->faxonline = 1; + isdn_tty_fax_modem_result(2, info); /* +FCON */ + return(0); + case ISDN_TTY_FAX_FCON_I: + info->faxonline = 16; + isdn_tty_fax_modem_result(2, info); /* +FCON */ + return(0); + case ISDN_TTY_FAX_RID: + if (info->faxonline & 1) + isdn_tty_fax_modem_result(3, info); /* +FCSI */ + if (info->faxonline & 16) + isdn_tty_fax_modem_result(8, info); /* +FTSI */ + return(0); + case ISDN_TTY_FAX_DIS: + isdn_tty_fax_modem_result(4, info); /* +FDIS */ + return(0); + case ISDN_TTY_FAX_HNG: + if (f->phase == ISDN_FAX_PHASE_C) { + if (f->direction == ISDN_TTY_FAX_CONN_IN) { + sprintf(rs, "%c%c", DLE, ETX); + isdn_tty_at_cout(rs, info); + } else { + sprintf(rs, "%c", 0x18); + isdn_tty_at_cout(rs, info); + } + info->faxonline &= ~2; /* leave data mode */ + info->online = 1; + } + f->phase = ISDN_FAX_PHASE_E; + isdn_tty_fax_modem_result(5, info); /* +FHNG */ + isdn_tty_fax_modem_result(0, info); /* OK */ + return(0); + case ISDN_TTY_FAX_DCS: + isdn_tty_fax_modem_result(6, info); /* +FDCS */ + isdn_tty_fax_modem_result(7, info); /* CONNECT */ + f->phase = ISDN_FAX_PHASE_C; + return(0); + case ISDN_TTY_FAX_TRAIN_OK: + isdn_tty_fax_modem_result(6, info); /* +FDCS */ + isdn_tty_fax_modem_result(0, info); /* OK */ + return(0); + case ISDN_TTY_FAX_SENT: + isdn_tty_fax_modem_result(0, info); /* OK */ + return(0); + case ISDN_TTY_FAX_CFR: + isdn_tty_fax_modem_result(9, info); /* +FCFR */ + return(0); + case ISDN_TTY_FAX_ET: + sprintf(rs, "%c%c", DLE, ETX); + isdn_tty_at_cout(rs, info); + isdn_tty_fax_modem_result(10, info); /* +FPTS */ + isdn_tty_fax_modem_result(11, info); /* +FET */ + isdn_tty_fax_modem_result(0, info); /* OK */ + info->faxonline &= ~2; /* leave data mode */ + info->online = 1; + f->phase = ISDN_FAX_PHASE_D; + return(0); + case ISDN_TTY_FAX_PTS: + isdn_tty_fax_modem_result(10, info); /* +FPTS */ + if (f->direction == ISDN_TTY_FAX_CONN_OUT) { + if (f->fet == 1) + f->phase = ISDN_FAX_PHASE_B; + if (f->fet == 0) + isdn_tty_fax_modem_result(0, info); /* OK */ + } + return(0); + case ISDN_TTY_FAX_EOP: + info->faxonline &= ~2; /* leave data mode */ + info->online = 1; + f->phase = ISDN_FAX_PHASE_D; + return(0); + + } + return(-1); +} + + +void +isdn_tty_fax_bitorder(modem_info *info, struct sk_buff *skb) +{ + __u8 LeftMask; + __u8 RightMask; + __u8 fBit; + __u8 Data; + int i; + + if (!info->fax->bor) { + for(i = 0; i < skb->len; i++) { + Data = skb->data[i]; + for ( + LeftMask = 0x80, RightMask = 0x01; + LeftMask > RightMask; + LeftMask >>= 1, RightMask <<= 1 + ) { + fBit = (Data & LeftMask); + if (Data & RightMask) + Data |= LeftMask; + else + Data &= ~LeftMask; + if (fBit) + Data |= RightMask; + else + Data &= ~RightMask; + + } + skb->data[i] = Data; + } + } +} + +/* + * Parse AT+F.. FAX class 2 commands + */ + +int isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info) +{ + atemu *m = &info->emu; + T30_s *f = info->fax; + isdn_ctrl cmd; + int par; + char rs[50]; + char rss[50]; + int maxdccval[]={1,5,2,2,3,2,0,7}; + + /* FAA still unchanged */ + if (!strncmp(p[0], "AA", 2)) { /* TODO */ + p[0] += 2; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", 0); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* BADLIN=value - dummy 0=disable errorchk disabled, 1-255 nr. of lines for making page bad */ + if (!strncmp(p[0], "BADLIN", 6)) { + p[0] += 6; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->badlin); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-255"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + f->badlin = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FBADLIN=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* BADMUL=value - dummy 0=disable errorchk disabled (treshold multiplier) */ + if (!strncmp(p[0], "BADMUL", 6)){ + p[0] +=6; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->badmul); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-255"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + f->badmul = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FBADMUL=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* BOR=n - Phase C bit order, 0=direct, 1=reverse */ + if (!strncmp(p[0], "BOR", 3)){ + p[0] +=3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->bor); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->bor = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FBOR=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* NBC=n - No Best Capabilities */ + if (!strncmp(p[0], "NBC", 3)){ + p[0] +=3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->nbc); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->nbc = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FNBC=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* BUF? - Readonly buffersize readout */ + if (!strncmp(p[0], "BUF?", 4)) { + p[0] += 4; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FBUF? (%d) \n", (16 * m->mdmreg[REG_PSIZE])); +#endif + p[0]++; + sprintf(rs, "\r\n %d ", (16 * m->mdmreg[REG_PSIZE])); + isdn_tty_at_cout(rs, info); + return 0; + } + + /* CIG=string - local fax station id string for polling rx */ + if (!strncmp(p[0], "CIG", 3)) { + int i, r; + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n\"%s\"", f->pollid); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n\"STRING\""); + isdn_tty_at_cout(rs, info); + } + else + { + if (*p[0] =='"') + p[0]++; + for(i=0; (*p[0]) && i < (FAXIDLEN-1) && (*p[0] != '"'); i++) + { + f->pollid[i] = *p[0]++; + } + if (*p[0] =='"') + p[0]++; + for(r=i; r < FAXIDLEN; r++) + { + f->pollid[r] = 32; + } + f->pollid[FAXIDLEN-1] = 0; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax local poll ID rx \"%s\"\n", f->pollid); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* CQ=n - copy qlty chk, 0= no chk, 1=only 1D chk, 2=1D+2D chk */ + if (!strncmp(p[0], "CQ", 2)) { + p[0] += 2; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->cq); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1,2"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 2)) + PARSE_ERROR1; + f->cq = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FCQ=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* CR=n - can receive? 0= no data rx or poll remote dev, 1=do receive data or poll remote dev */ + if (!strncmp(p[0], "CR", 2)) { + p[0] += 2; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->cr); /* read actual value from struct and print */ + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); /* display online help */ + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->cr = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FCR=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* CTCRTY=value - ECM retry count */ + if (!strncmp(p[0], "CTCRTY", 6)){ + p[0] +=6; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->ctcrty); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-255"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + f->ctcrty = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FCTCRTY=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* DCC=vr,br,wd,ln,df,ec,bf,st - DCE capabilities parms */ + if (!strncmp(p[0], "DCC", 3)) { + char *rp = &f->resolution; + int i; + + p[0] += 3; + switch(*p[0]) { + case '?': + p[0]++; + strcpy(rs, "\r\n"); + for(i = 0; i < 8; i++) { + sprintf(rss, "%c%s", rp[i] + 48, + (i < 7) ? "," : ""); + strcat(rs, rss); + } + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') { + isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)",info); + p[0]++; + } else { + for (i=0; (((*p[0]>='0')&&(*p[0]<='9'))||(*p[0]==','))&&(i<8); i++) { + if (*p[0] != ',') { + if ((*p[0] - 48) > maxdccval[i]) { + PARSE_ERROR1; + } + rp[i] = *p[0] - 48; + p[0]++; + if (*p[0] == ',') + p[0]++; + } else p[0]++; + } +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FDCC capabilities DCE=%d,%d,%d,%d,%d,%d,%d,%d\n", + rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* DIS=vr,br,wd,ln,df,ec,bf,st - current session parms */ + if (!strncmp(p[0], "DIS", 3)) { + char *rp = &f->resolution; + int i; + + p[0] += 3; + switch(*p[0]) { + case '?': + p[0]++; + strcpy(rs, "\r\n"); + for(i = 0; i < 8; i++) { + sprintf(rss, "%c%s", rp[i] + 48, + (i < 7) ? "," : ""); + strcat(rs, rss); + } + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') { + isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)",info); + p[0]++; + } else { + for (i=0; (((*p[0]>='0')&&(*p[0]<='9'))||(*p[0]==','))&&(i<8); i++) { + if (*p[0] != ',') { + if ((*p[0] - 48) > maxdccval[i]) { + PARSE_ERROR1; + } + rp[i] = *p[0] - 48; + p[0]++; + if (*p[0] == ',') + p[0]++; + } else p[0]++; + } +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FDIS session parms=%d,%d,%d,%d,%d,%d,%d,%d\n", + rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* DR - Receive Phase C data command, initiates document reception */ + if (!strncmp(p[0], "DR", 2)) { + p[0] += 2; + if ((info->faxonline & 16) && /* incoming connection */ + ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D))) { +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FDR\n"); +#endif + f->code = ISDN_TTY_FAX_DR; + cmd.driver = info->isdn_driver; + cmd.arg = info->isdn_channel; + cmd.command = ISDN_CMD_FAXCMD; + isdn_command(&cmd); + if (f->phase == ISDN_FAX_PHASE_B) { + f->phase = ISDN_FAX_PHASE_C; + } else if (f->phase == ISDN_FAX_PHASE_D) { + switch(f->fet) { + case 0: /* next page will be received */ + f->phase = ISDN_FAX_PHASE_C; + isdn_tty_fax_modem_result(7, info); /* CONNECT */ + break; + case 1: /* next doc will be received */ + f->phase = ISDN_FAX_PHASE_B; + break; + case 2: /* fax session is terminating */ + f->phase = ISDN_FAX_PHASE_E; + break; + default: + PARSE_ERROR1; + } + } + } else { + PARSE_ERROR1; + } + return 1; + } + + /* DT=df,vr,wd,ln - TX phase C data command (release DCE to proceed with negotiation) */ + if (!strncmp(p[0], "DT", 2)) { + int i, val[]={4,0,2,3}; + char *rp = &f->resolution; + + p[0] += 2; + if (!info->faxonline & 1) /* not outgoing connection */ + PARSE_ERROR1; + + for (i=0; (((*p[0]>='0')&&(*p[0]<='9'))||(*p[0]==','))&&(i<4); i++) { + if (*p[0] != ',') { + if ((*p[0] - 48) > maxdccval[val[i]]) { + PARSE_ERROR1; + } + rp[val[i]] = *p[0] - 48; + p[0]++; + if (*p[0] == ',') + p[0]++; + } else p[0]++; + } +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FDT tx data command parms=%d,%d,%d,%d\n", + rp[4], rp[0], rp[2], rp[3]); +#endif + if ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D)) { + f->code = ISDN_TTY_FAX_DT; + cmd.driver = info->isdn_driver; + cmd.arg = info->isdn_channel; + cmd.command = ISDN_CMD_FAXCMD; + isdn_command(&cmd); + if (f->phase == ISDN_FAX_PHASE_D) { + f->phase = ISDN_FAX_PHASE_C; + isdn_tty_fax_modem_result(7, info); /* CONNECT */ + } + } else { + PARSE_ERROR1; + } + return 1; + } + + /* ECM=n - Error mode control 0=disabled, 2=enabled, handled by DCE alone incl. buff of partial pages */ + if (!strncmp(p[0], "ECM", 3)) { + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->ecm); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,2"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par != 0) && (par != 2)) + PARSE_ERROR1; + f->ecm = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FECM=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* ET=n - End of page or document */ + if (!strncmp(p[0], "ET=", 3)) { + p[0] += 3; + if (*p[0] == '?') { + p[0]++; + sprintf(rs, "\r\n0-2"); + isdn_tty_at_cout(rs, info); + } else { + if ((f->phase != ISDN_FAX_PHASE_D) || (!info->faxonline & 1)) + PARSE_ERROR1; + par = isdn_getnum(p); + if ((par < 0) || (par > 2)) + PARSE_ERROR1; + f->fet = par; + f->code = ISDN_TTY_FAX_ET; + cmd.driver = info->isdn_driver; + cmd.arg = info->isdn_channel; + cmd.command = ISDN_CMD_FAXCMD; + isdn_command(&cmd); +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FET=%d\n", par); +#endif + return 1; + } + return 0; + } + + /* K - terminate */ + if (!strncmp(p[0], "K", 1)) { + p[0] += 1; + if ((f->phase == ISDN_FAX_PHASE_IDLE) || (f->phase == ISDN_FAX_PHASE_E)) + PARSE_ERROR1; + isdn_tty_modem_hup(info, 1); + return 1; + } + + /* LID=string - local fax ID */ + if (!strncmp(p[0], "LID", 3)) { + int i, r; + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n\"%s\"", f->id); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n\"STRING\""); + isdn_tty_at_cout(rs, info); + } + else + { + if (*p[0] =='"') + p[0]++; + for(i=0; (*p[0]) && i < (FAXIDLEN-1) && (*p[0] != '"'); i++) + { + f->id[i] = *p[0]++; + } + if (*p[0] =='"') + p[0]++; + for(r=i; r < FAXIDLEN; r++) + { + f->id[r] = 32; + } + f->id[FAXIDLEN-1] = 0; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax local ID \"%s\"\n", f->id); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + +#if 0 + /* LO=n - Flow control opts */ + if (!strncmp(p[0], "LO", 2)) { /* TODO */ + p[0] += 2; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->lo); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1,2"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 2)) + PARSE_ERROR1; + f->lo = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FLO=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } +#endif +#if 0 + /* LPL=n - Doc for polling cmd */ + if (!strncmp(p[0], "LPL", 3)) { /* TODO */ + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->lpl); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->lpl = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FLPL=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } +#endif + + /* MDL? - DCE Model */ + if (!strncmp(p[0], "MDL?", 4)) { + p[0] += 4; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: FMDL?\n"); +#endif + isdn_tty_at_cout("\r\nisdn4linux", info); + return 0; + } + + /* MFR? - DCE Manufacturer */ + if (!strncmp(p[0], "MFR?", 4)) { + p[0] += 4; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: FMFR?\n"); +#endif + isdn_tty_at_cout("\r\nisdn4linux", info); + return 0; + } + + /* MINSP=n - Minimum Speed for Phase C */ + if (!strncmp(p[0], "MINSP", 5)) { + p[0] += 5; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->minsp); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-5"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 5)) + PARSE_ERROR1; + f->minsp = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FMINSP=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* PHCTO=value - DTE phase C timeout */ + if (!strncmp(p[0], "PHCTO", 5)){ + p[0] +=5; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->phcto); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-255"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + f->phcto = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FPHCTO=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + +#if 0 + /* PTS=n - Page transfer status */ + if (!strncmp(p[0], "PTS", 3)) { /* TODO */ + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->pts); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-5"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 5)) + PARSE_ERROR1; + f->pts = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FPTS=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } +#endif + + /* REL=n - Phase C received EOL alignment */ + if (!strncmp(p[0], "REL", 3)) { + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->rel); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->rel = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FREL=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* REV? - DCE Revision */ + if (!strncmp(p[0], "REV?", 4)) { + p[0] += 4; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: FREV?\n"); +#endif + strcpy(rss, isdn_tty_fax_revision); + sprintf(rs, "\r\nRev: %s", isdn_getrev(rss)); + isdn_tty_at_cout(rs, info); + return 0; + } + +#if 0 + /* SPL=n - Enable polling */ + if (!strncmp(p[0], "SPL", 3)) { /* TODO */ + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->spl); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->spl = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FSPL=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } +#endif + + /* Phase C Transmit Data Block Size */ + if (!strncmp(p[0], "TBC=", 4)) { /* dummy, not used */ + p[0] += 4; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]); +#endif + switch (*p[0]) { + case '0': + p[0]++; + break; + default: + PARSE_ERROR1; + } + return 0; + } + + printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]); + PARSE_ERROR1; +} + diff --git a/drivers/isdn/isdn_ttyfax.h b/drivers/isdn/isdn_ttyfax.h new file mode 100644 index 000000000000..1ad75d2dfa85 --- /dev/null +++ b/drivers/isdn/isdn_ttyfax.h @@ -0,0 +1,33 @@ +/* $Id: isdn_ttyfax.h,v 1.1 1999/07/31 12:59:51 armin Exp $ + * header for Linux ISDN subsystem, tty_fax related functions (linklevel). + * + * Copyright 1999 by Armin Schindler (mac@melware.de) + * Copyright 1999 by Ralf Spachmann (mel@melware.de) + * Copyright 1999 by Cytronics & Melware + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: isdn_ttyfax.h,v $ + * Revision 1.1 1999/07/31 12:59:51 armin + * Added tty fax capabilities. + * + * + */ + + +#define XON 0x11 +#define XOFF 0x13 +#define DC2 0x12 + diff --git a/drivers/isdn/pcbit/drv.c b/drivers/isdn/pcbit/drv.c index a3186ac1168e..77f0ed45f7d9 100644 --- a/drivers/isdn/pcbit/drv.c +++ b/drivers/isdn/pcbit/drv.c @@ -86,7 +86,7 @@ int pcbit_init_dev(int board, int mem_base, int irq) dev_pcbit[board] = dev; memset(dev, 0, sizeof(struct pcbit_dev)); -#if LINUX_VERSION_CODE >= 131841 +#ifdef COMPAT_HAS_NEW_WAITQ init_waitqueue_head(&dev->set_running_wq); #endif diff --git a/drivers/isdn/pcbit/pcbit.h b/drivers/isdn/pcbit/pcbit.h index 0dbdb5fd138f..45c68871dd5b 100644 --- a/drivers/isdn/pcbit/pcbit.h +++ b/drivers/isdn/pcbit/pcbit.h @@ -68,10 +68,10 @@ struct pcbit_dev { struct frame_buf *write_queue; /* Protocol start */ -#if LINUX_VERSION_CODE < 131841 - struct wait_queue *set_running_wq; -#else +#ifdef COMPAT_HAS_NEW_WAITQ wait_queue_head_t set_running_wq; +#else + struct wait_queue *set_running_wq; #endif struct timer_list set_running_timer; diff --git a/drivers/macintosh/mac_keyb.c b/drivers/macintosh/mac_keyb.c index 1b4a5d667ad8..e0129325e6ed 100644 --- a/drivers/macintosh/mac_keyb.c +++ b/drivers/macintosh/mac_keyb.c @@ -44,7 +44,6 @@ #include #include #include -#include #include #include diff --git a/drivers/macintosh/macserial.c b/drivers/macintosh/macserial.c index f3c0a66f2eb7..0c5b3528e29c 100644 --- a/drivers/macintosh/macserial.c +++ b/drivers/macintosh/macserial.c @@ -39,7 +39,6 @@ #ifdef CONFIG_KGDB #include #endif -#include #include "macserial.h" diff --git a/drivers/misc/Config.in b/drivers/misc/Config.in new file mode 100644 index 000000000000..1c3b6aa7f0a3 --- /dev/null +++ b/drivers/misc/Config.in @@ -0,0 +1,12 @@ +# +# Misc strange devices +# +mainmenu_option next_comment +comment 'Misc devices' + +# PIIX4 ACPI requires PCI for setup and a hardcoded TSC for timing +if [ "$CONFIG_PCI" = "y" -a "$CONFIG_X86_TSC" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool 'PIIX4 ACPI support' CONFIG_PIIX4_ACPI +fi + +endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile new file mode 100644 index 000000000000..98e68c96bff5 --- /dev/null +++ b/drivers/misc/Makefile @@ -0,0 +1,27 @@ +# +# Makefile for misc devices that really don't fit anywhere else. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now inherited from the +# parent makes.. +# + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + +O_TARGET := misc.o +M_OBJS := +O_OBJS := +OX_OBJS := + +ifdef CONFIG_PIIX4_ACPI +O_OBJS += piix4_acpi.o +endif + +include $(TOPDIR)/Rules.make + +fastdep: diff --git a/drivers/misc/piix4_acpi.c b/drivers/misc/piix4_acpi.c new file mode 100644 index 000000000000..9cd3625b9aaf --- /dev/null +++ b/drivers/misc/piix4_acpi.c @@ -0,0 +1,214 @@ +/* + * linux/drivers/misc/piix4_acpi.c + * + * (C) Copyright 1999 Linus Torvalds + * + * A PM driver for the ACPI portion of the Intel PIIX4 + * chip. + * + * This has been known to occasionally work on some laptops. + * + * It probably only works on Intel PII machines that support + * the STPCLK protocol. + */ + +#include +#include +#include + +#include + +extern void (*acpi_idle)(void); + +/* + * This first part should be common to all ACPI + * CPU sleep functionality. Assuming we get the + * timing heuristics in a better shape than "none" ;) + */ + +typedef void (*sleep_fn_t)(void); + +/* + * Our "sleep mode" is a fixed point number + * with two binary places, ranging between + * [0 .. 3[ + */ +#define Cx_SHIFT 2 +#define MAXMODE ((3 << Cx_SHIFT)-1) + +/* + * NOTE! + * + * Right now this always defaults to C3, which is just broken. + * The exit latency is usually too high for much busy IO activity, + * and generally it's not always the best thing to do. + * + * We should just read the cycle counter around all the cases, + * and if we pause for a long time we go to a deeper sleep, while + * a short wait makes us go into a lighter sleep. + */ +static void common_acpi_idle(sleep_fn_t *sleep) +{ + int mode = MAXMODE; + + while (1) { + while (!current->need_resched) { + unsigned int time; + + time = get_cycles(); + sleep[(mode) >> Cx_SHIFT](); + time = get_cycles() - time; + + /* + * Yeah, yeah, yeah. + * if (time > Large && mode < MAXMODE) mode++; + * if (time < Small && mode > 0) mode--; + * Yadda-yadda-yadda. + * + * "Large" is on the order of half a timer tick. + * "Small" is on the order of Large >> 2 or so. + * + * Somebody should _really_ look at the exact + * details. The ACPI bios would give some made-up + * numbers, they might be useful (or maybe not: + * they are probably tuned for whatever Windows + * does, so don't take them for granted). + */ + } + schedule(); + check_pgt_cache(); + } +} + +/* Ok, here starts the magic PIIX4 knowledge */ + +/* + * Ehh.. We "know" about the northbridge + * bus arbitration stuff. Maybe somebody + * should actually verify this some day? + */ +#define NORTHBRIDGE_CONTROL 0x22 +#define NB_ARBITRATE 0x01 + +/* + * PIIX4 ACPI IO offsets and defines + */ +#define PMEN 0x02 +#define PMCNTRL 0x04 +#define PMTMR 0x08 +#define GPSTS 0x0c +#define GPEN 0x0E + +#define PCNTRL 0x10 +#define CC_EN 0x0200 +#define BST_EN 0x0400 +#define SLEEP_EN 0x0800 +#define STPCLK_EN 0x1000 +#define CLKRUN_EN 0x2000 + +#define PLVL2 0x14 +#define PLVL3 0x15 + +/* + * PIIX4 ACPI PCI configurations offsets and defines + */ +#define DEVACTB 0x58 +#define BRLD_EN_IRQ0 0x01 +#define BRLD_EN_IRQ 0x02 + +#define PMREGMISC 0x80 +#define PMIOSE 0x01 + +static unsigned int piix4_base_address = 0; + +static void piix4_c1_sleep(void) +{ + asm volatile("sti ; hlt" : : : "memory"); +} + +static void piix4_c2_sleep(void) +{ + outl(CLKRUN_EN | CC_EN, piix4_base_address + PCNTRL); + inb(piix4_base_address + PLVL2); +} + +static void piix4_c3_sleep(void) +{ + __cli(); + outl(CLKRUN_EN | CC_EN | STPCLK_EN | SLEEP_EN, piix4_base_address + PCNTRL); + outb(NB_ARBITRATE, NORTHBRIDGE_CONTROL); + inb(piix4_base_address + PLVL3); + outb(0, NORTHBRIDGE_CONTROL); + __sti(); +} + +static sleep_fn_t piix4_sleep[] = { + piix4_c1_sleep, /* low-latency C1 (ie "sti ; hlt") */ + piix4_c2_sleep, /* medium latency C2 (ie LVL2 stopckl) */ + piix4_c3_sleep /* high-latency C3 (ie LVL3 sleep) */ +}; + +static void piix4_acpi_idle(void) +{ + common_acpi_idle(piix4_sleep); +} + +static int __init piix4_acpi_init(void) +{ + /* This is the PIIX4 ACPI device */ + struct pci_dev *dev; + u32 base, val; + u16 cmd; + u8 pmregmisc; + +#ifdef __SMP__ + /* + * We can't really do idle things with multiple CPU's, I'm + * afraid. We'd need a per-CPU ACPI device. + */ + if (smp_num_cpus > 1) + return -1; +#endif + dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL); + + if (!dev) + return -1; + + /* + * Read the IO base value, and verify that it makes sense + * + * We could enable this if it wasn't enabled before, but + * let's walk before we run.. + */ + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (!(cmd & PCI_COMMAND_IO)) + return -1; + + pci_read_config_byte(dev, PMREGMISC, &pmregmisc); + if (!(pmregmisc & PMIOSE)) + return -1; + + pci_read_config_dword(dev, 0x40, &base); + if (!(base & PCI_BASE_ADDRESS_SPACE_IO)) + return -1; + + base &= PCI_BASE_ADDRESS_IO_MASK; + if (!base) + return -1; + + printk("Found PIIX4 ACPI device at %04x\n", base); + piix4_base_address = base; + + /* Enable stopcklock, sleep and bursts, along with clock control */ + outl(CLKRUN_EN | CC_EN | STPCLK_EN | SLEEP_EN, piix4_base_address + PCNTRL); + + /* Make all unmasked interrupts be BREAK events */ + pci_read_config_dword(dev, DEVACTB, &val); + pci_write_config_dword(dev, DEVACTB, val | BRLD_EN_IRQ0 | BRLD_EN_IRQ); + + /* Set up the new idle handler.. */ + acpi_idle = piix4_acpi_idle; + return 0; +} + +__initcall(piix4_acpi_init); diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 78c124d8dc06..19e865f70d83 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -1136,6 +1136,15 @@ ifeq ($(CONFIG_CYCLADES_SYNC),y) endif endif +ifeq ($(CONFIG_CYCLADES_SYNC),m) + MX_OBJS += cycx_drv.o + M_OBJS += cyclomx.o + CYCLOMX_OBJS = cycx_main.o + ifeq ($(CONFIG_CYCLOMX_X25),y) + CYCLOMX_OBJS += cycx_x25.o + endif +endif + ifeq ($(CONFIG_X25_ASY),y) L_OBJS += x25_asy.o else diff --git a/drivers/net/cycx_drv.c b/drivers/net/cycx_drv.c index 7f803c63dd6d..965a263a2e23 100644 --- a/drivers/net/cycx_drv.c +++ b/drivers/net/cycx_drv.c @@ -57,7 +57,7 @@ #include /* for inb(), outb(), etc. */ #define MOD_VERSION 0 -#define MOD_RELEASE 1 +#define MOD_RELEASE 2 #ifdef MODULE MODULE_AUTHOR("Arnaldo Carvalho de Melo"); @@ -122,7 +122,6 @@ int init_module (void) { printk(KERN_INFO "%s v%u.%u %s\n", fullname, MOD_VERSION, MOD_RELEASE, copyright); - printk(KERN_INFO "version=0x%X\n", LINUX_VERSION_CODE); return 0; } /* Module 'remove' entry point. diff --git a/drivers/net/cycx_main.c b/drivers/net/cycx_main.c index d0371c3a1d72..fe84d868e9f9 100644 --- a/drivers/net/cycx_main.c +++ b/drivers/net/cycx_main.c @@ -13,12 +13,15 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* 1999/08/09 acme removed references to enable_tx_int +* use spinlocks instead of cli/sti in +* cyclomx_set_state * 1999/05/19 acme works directly linked into the kernel * init_waitqueue_head for 2.3.* kernel * 1999/05/18 acme major cleanup (polling not needed), etc -* Aug 28, 1998 Arnaldo minor cleanup (ioctls for firmware deleted) +* 1998/08/28 acme minor cleanup (ioctls for firmware deleted) * queue_task activated -* Aug 08, 1998 Arnaldo Initial version. +* 1998/08/08 acme Initial version. */ #include /* OS configuration options */ @@ -43,7 +46,7 @@ MODULE_DESCRIPTION("Cyclades Sync Cards Driver."); /* Defines & Macros */ #define DRV_VERSION 0 /* version number */ -#define DRV_RELEASE 3 /* release (minor version) number */ +#define DRV_RELEASE 4 /* release (minor version) number */ #define MAX_CARDS 1 /* max number of adapters */ #ifndef CONFIG_CYCLOMX_CARDS /* configurable option */ @@ -118,7 +121,6 @@ __initfunc(int cyclomx_init (void)) wandev->magic = ROUTER_MAGIC; wandev->name = card->devname; wandev->private = card; - wandev->enable_tx_int = 0; wandev->setup = &setup; wandev->shutdown = &shutdown; wandev->ioctl = &ioctl; @@ -239,7 +241,7 @@ static int setup (wan_device_t *wandev, wandev_conf_t *conf) /* Protocol-specific initialization */ switch (card->hw.fwid) { -#ifdef CONFIG_CYCLOMX_X25 +#ifdef CONFIG_CYCLOMX_X25 case CFID_X25_2X: err = cyx_init(card, conf); break; #endif default: @@ -254,7 +256,6 @@ static int setup (wan_device_t *wandev, wandev_conf_t *conf) return err; } - wandev->critical = 0; return 0; } @@ -280,7 +281,6 @@ static int shutdown (wan_device_t *wandev) cycx_down(&card->hw); printk(KERN_INFO "%s: irq %d being freed!\n", wandev->name,wandev->irq); free_irq(wandev->irq, card); - wandev->critical = 0; return 0; } @@ -306,7 +306,8 @@ static int ioctl (wan_device_t *wandev, unsigned cmd, unsigned long arg) static void cycx_isr (int irq, void *dev_id, struct pt_regs *regs) { #define card ((cycx_t*)dev_id) - if (!card || card->wandev.state == WAN_UNCONFIGURED) return; + if (!card || card->wandev.state == WAN_UNCONFIGURED) + return; if (card->in_isr) { printk(KERN_WARNING "%s: interrupt re-entrancy on IRQ %d!\n", @@ -314,7 +315,8 @@ static void cycx_isr (int irq, void *dev_id, struct pt_regs *regs) return; } - if (card->isr) card->isr(card); + if (card->isr) + card->isr(card); #undef card } @@ -345,9 +347,9 @@ void cyclomx_close (cycx_t *card) /* Set WAN device state. */ void cyclomx_set_state (cycx_t *card, int state) { - unsigned long flags; + unsigned long host_cpu_flags; - save_flags(flags); cli(); + spin_lock_irqsave(&card->lock, host_cpu_flags); if (card->wandev.state != state) { switch (state) { @@ -371,7 +373,7 @@ void cyclomx_set_state (cycx_t *card, int state) } card->state_tick = jiffies; - restore_flags(flags); + spin_unlock_irqrestore(&card->lock, host_cpu_flags); } /* End */ diff --git a/drivers/net/cycx_x25.c b/drivers/net/cycx_x25.c index d6fbe07c35dc..78545f4a5513 100644 --- a/drivers/net/cycx_x25.c +++ b/drivers/net/cycx_x25.c @@ -11,6 +11,10 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* 1999/08/10 acme serialized access to the card thru a spinlock +* in x25_exec +* 1999/08/09 acme removed per channel spinlocks +* removed references to enable_tx_int * 1999/05/28 acme fixed nibble_to_byte, ackvc now properly treated * if_send simplified * 1999/05/25 acme fixed t1, t2, t21 & t23 configuration @@ -70,8 +74,6 @@ /* Defines & Macros */ #define MAX_CMD_RETRY 5 #define X25_CHAN_MTU 2048 /* unfragmented logical channel MTU */ -#define OUT_INTR 1 -#define IN_INTR 0 /* Data Structures */ /* This is an extention of the 'struct device' we create for each network @@ -84,7 +86,6 @@ typedef struct x25_channel { s16 lcn; /* logical channel number/conn.req.key*/ u8 link; struct timer_list timer; /* timer used for svc channel disc. */ - spinlock_t lock; u16 protocol; /* ethertype, 0 - multiplexed */ u8 svc; /* 0 - permanent, 1 - switched */ u8 state; /* channel state */ @@ -135,11 +136,11 @@ static int x25_configure (cycx_t *card, TX25Config *conf), static int chan_connect (struct device *dev), chan_send (struct device *dev, struct sk_buff *skb); -static void set_chan_state (struct device *dev, u8 state, u8 outside_intr), +static void set_chan_state (struct device *dev, u8 state), nibble_to_byte (u8 *s, u8 *d, u8 len, u8 nibble), reset_timer (struct device *dev), chan_disc (struct device *dev), - chan_timer (unsigned long data); + chan_timer (unsigned long d); static u8 bps_to_speed_code (u32 bps); static u8 log2 (u32 n); @@ -187,8 +188,8 @@ int cyx_init (cycx_t *card, wandev_conf_t *conf) /* Initialize protocol-specific fields */ card->mbox = card->hw.dpmbase + X25_MBOX_OFFS; - card->u.x.critical = 0; /* critical section flag */ card->u.x.connection_keys = 0; + card->u.x.lock = SPIN_LOCK_UNLOCKED; /* Configure adapter. Here we set resonable defaults, then parse * device configuration structure and set configuration options. @@ -286,7 +287,6 @@ int cyx_init (cycx_t *card, wandev_conf_t *conf) card->wandev.new_if = &new_if; card->wandev.del_if = &del_if; card->wandev.state = WAN_DISCONNECTED; - card->wandev.enable_tx_int = card->irq_dis_if_send_count = 0; return 0; } @@ -338,21 +338,23 @@ static int new_if (wan_device_t *wandev, struct device *dev, wanif_conf_t *conf) chan->rx_skb = NULL; /* only used in svc connected thru crossover cable */ chan->local_addr = NULL; - chan->lock = SPIN_LOCK_UNLOCKED; if (conf->addr[0] == '@') { /* SVC */ - int local_len = strlen(conf->local_addr); + int len = strlen(conf->local_addr); - if (local_len) { - if (local_len > WAN_ADDRESS_SZ) { + if (len) { + if (len > WAN_ADDRESS_SZ) { printk(KERN_ERR "%s: %s local addr too long!\n", wandev->name, chan->name); kfree(chan); return -EINVAL; - } else if ((chan->local_addr = kmalloc(local_len + 1, - GFP_KERNEL)) == NULL) { - kfree(chan); - return ENOMEM; + } else { + chan->local_addr = kmalloc(len + 1, GFP_KERNEL); + + if (!chan->local_addr) { + kfree(chan); + return ENOMEM; + } } strncpy(chan->local_addr, conf->local_addr, @@ -387,6 +389,7 @@ static int new_if (wan_device_t *wandev, struct device *dev, wanif_conf_t *conf) if (err) { if (chan->local_addr) kfree(chan->local_addr); + kfree(chan); return err; } @@ -408,6 +411,7 @@ static int del_if (wan_device_t *wandev, struct device *dev) if (dev->priv) { x25_channel_t *chan = dev->priv; + if (chan->svc) { if (chan->local_addr) kfree(chan->local_addr); @@ -415,6 +419,7 @@ static int del_if (wan_device_t *wandev, struct device *dev) if (chan->state == WAN_CONNECTED) del_timer(&chan->timer); } + kfree(chan); dev->priv = NULL; } @@ -464,7 +469,7 @@ static int if_init (struct device *dev) /* Initialize socket buffers */ dev_init_buffers(dev); - set_chan_state(dev, WAN_DISCONNECTED, OUT_INTR); + set_chan_state(dev, WAN_DISCONNECTED); return 0; } @@ -481,15 +486,11 @@ static int if_open (struct device *dev) if (dev->start) return -EBUSY; /* only one open is allowed */ - if (test_and_set_bit(0, (void*)&card->wandev.critical)) - return -EAGAIN; - dev->interrupt = 0; dev->tbusy = 0; dev->start = 1; cyclomx_open(card); - card->wandev.critical = 0; return 0; } @@ -501,17 +502,12 @@ static int if_close (struct device *dev) x25_channel_t *chan = dev->priv; cycx_t *card = chan->card; - if (test_and_set_bit(0, (void*)&card->wandev.critical)) - return -EAGAIN; - dev->start = 0; if (chan->state == WAN_CONNECTED || chan->state == WAN_CONNECTING) chan_disc(dev); cyclomx_close(card); - - card->wandev.critical = 0; return 0; } @@ -563,10 +559,6 @@ static int if_send (struct sk_buff *skb, struct device *dev) return -EBUSY; } - dev->tbusy = 1; - - reset_timer(dev); - if (!chan->svc) chan->protocol = skb->protocol; @@ -580,13 +572,17 @@ static int if_send (struct sk_buff *skb, struct device *dev) ++chan->ifstats.tx_errors; } else switch (chan->state) { case WAN_DISCONNECTED: - if (chan_connect(dev)) + if (chan_connect(dev)) { + dev->tbusy = 1; return -EBUSY; + } /* fall thru */ case WAN_CONNECTED: + reset_timer(dev); dev->trans_start = jiffies; + dev->tbusy = 1; + if (chan_send(dev, skb)) { - dev->tbusy = 1; return -EBUSY; } break; @@ -612,25 +608,13 @@ static struct net_device_stats *if_stats (struct device *dev) /* X.25 Interrupt Service Routine. */ static void cyx_isr (cycx_t *card) { - unsigned long host_cpu_flags; TX25Cmd cmd; u16 z = 0; card->in_isr = 1; card->buff_int_mode_unbusy = 0; - - if (test_and_set_bit(0, (void*)&card->wandev.critical)) { - printk(KERN_INFO "cyx_isr: %s, wandev.critical set to 0x%02X\n", - card->devname, card->wandev.critical); - card->in_isr = 0; - return; - } - - /* For all interrupts set the critical flag to CRITICAL_RX_INTR. - * If the if_send routine is called with this flag set it will set - * the enable transmit flag to 1. (for a delayed interrupt) */ - card->wandev.critical = CRITICAL_IN_ISR; cycx_peek(&card->hw, X25_RXMBOX_OFFS, &cmd, sizeof(cmd)); + switch (cmd.command) { case X25_DATA_INDICATION: rx_intr(card, &cmd); @@ -668,16 +652,7 @@ static void cyx_isr (cycx_t *card) cycx_poke(&card->hw, 0, &z, sizeof(z)); cycx_poke(&card->hw, X25_RXMBOX_OFFS, &z, sizeof(z)); - - card->wandev.critical = CRITICAL_INTR_HANDLED; - - if (card->wandev.enable_tx_int) - card->wandev.enable_tx_int = 0; - - spin_lock_irqsave(&card->lock, host_cpu_flags); card->in_isr = 0; - card->wandev.critical = 0; - spin_unlock_irqrestore(&card->lock, host_cpu_flags); if (card->buff_int_mode_unbusy) mark_bh(NET_BH); @@ -740,11 +715,12 @@ static void rx_intr (cycx_t *card, TX25Cmd *cmd) chan = dev->priv; reset_timer(dev); - if (chan->drop_sequence) + if (chan->drop_sequence) { if (!bitm) chan->drop_sequence = 0; else return; + } if ((skb = chan->rx_skb) == NULL) { /* Allocate new socket buffer */ @@ -801,27 +777,28 @@ static void connect_intr (cycx_t *card, TX25Cmd *cmd) wan_device_t *wandev = &card->wandev; struct device *dev = NULL; x25_channel_t *chan; - u8 data[32], - local[24], + u8 d[32], + loc[24], rem[24]; - u8 lcn, sizelocal, sizerem; + u8 lcn, sizeloc, sizerem; cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn)); - cycx_peek(&card->hw, cmd->buf + 5, &sizelocal, sizeof(sizelocal)); - cycx_peek(&card->hw, cmd->buf + 6, data, cmd->len - 6); + cycx_peek(&card->hw, cmd->buf + 5, &sizeloc, sizeof(sizeloc)); + cycx_peek(&card->hw, cmd->buf + 6, d, cmd->len - 6); - sizerem = sizelocal >> 4; - sizelocal &= 0x0F; + sizerem = sizeloc >> 4; + sizeloc &= 0x0F; - local[0] = rem[0] = '\0'; + loc[0] = rem[0] = '\0'; - if (sizelocal) - nibble_to_byte(data, local, sizelocal, 0); + if (sizeloc) + nibble_to_byte(d, loc, sizeloc, 0); if (sizerem) - nibble_to_byte(data + (sizelocal >> 1), rem, sizerem, sizelocal & 1); + nibble_to_byte(d + (sizeloc >> 1), rem, sizerem, sizeloc & 1); + dprintk(KERN_INFO "connect_intr:lcn=%d, local=%s, remote=%s\n", - lcn, local, rem); + lcn, loc, rem); if ((dev = get_dev_by_dte_addr(wandev, rem)) == NULL) { /* Invalid channel, discard packet */ printk(KERN_INFO "%s: connect not expected: remote %s!\n", @@ -832,7 +809,7 @@ static void connect_intr (cycx_t *card, TX25Cmd *cmd) chan = dev->priv; chan->lcn = lcn; x25_connect_response(card, chan); - set_chan_state(dev, WAN_CONNECTED, IN_INTR); + set_chan_state(dev, WAN_CONNECTED); } /* Connect confirm interrupt handler. */ @@ -858,7 +835,7 @@ static void connect_confirm_intr (cycx_t *card, TX25Cmd *cmd) clear_bit(--key, (void*)&card->u.x.connection_keys); chan = dev->priv; chan->lcn = lcn; - set_chan_state(dev, WAN_CONNECTED, IN_INTR); + set_chan_state(dev, WAN_CONNECTED); } /* Disonnect confirm interrupt handler. */ @@ -878,7 +855,7 @@ static void disconnect_confirm_intr (cycx_t *card, TX25Cmd *cmd) return; } - set_chan_state(dev, WAN_DISCONNECTED, IN_INTR); + set_chan_state(dev, WAN_DISCONNECTED); } /* disconnect interrupt handler. */ @@ -890,10 +867,14 @@ static void disconnect_intr (cycx_t *card, TX25Cmd *cmd) cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn)); dprintk(KERN_INFO "disconnect_intr:lcn=%d\n", lcn); - x25_disconnect_response(card, 0, lcn); - if ((dev = get_dev_by_lcn(wandev, lcn)) != NULL) - set_chan_state(dev, WAN_DISCONNECTED, IN_INTR); + if ((dev = get_dev_by_lcn(wandev, lcn)) != NULL) { + x25_channel_t *chan = dev->priv; + + x25_disconnect_response(card, chan->link, lcn); + set_chan_state(dev, WAN_DISCONNECTED); + } else + x25_disconnect_response(card, 0, lcn); } /* LOG interrupt handler. */ @@ -960,55 +941,51 @@ static void hex_dump(char *msg, unsigned char *p, int len) printk(KERN_INFO "%s: %s\n", msg, hex); } #endif -/* CYCLOM X Firmware-Specific Functions - * - * Almost all X.25 commands can unexpetedly fail due to so called 'X.25 - * asynchronous events' such as restart, interrupt, incoming call request, - * call clear request, etc. They can't be ignored and have to be dealt with - * immediately. To tackle with this problem we execute each interface command - * in a loop until good return code is received or maximum number of retries - * is reached. Each interface command returns non-zero return code, an - * asynchronous event/error handler x25_error() is called. - */ +/* CYCLOM X Firmware-Specific Functions */ /* Exec x25 command. */ static int x25_exec (cycx_t *card, int command, int link, - void *data1, int len1, void *data2, int len2) + void *d1, int len1, void *d2, int len2) { TX25Cmd c; + unsigned long flags; u32 addr = 0x1200 + 0x2E0 * link + 0x1E2; + u8 retry = MAX_CMD_RETRY; int err = 0; c.command = command; c.link = link; c.len = len1 + len2; - if (test_and_set_bit(0, (void*)&card->u.x.critical)) - return -EAGAIN; + spin_lock_irqsave(&card->u.x.lock, flags); /* write command */ cycx_poke(&card->hw, X25_MBOX_OFFS, &c, sizeof(c) - sizeof(c.buf)); /* write x25 data */ - if (data1) { - cycx_poke(&card->hw, addr, data1, len1); + if (d1) { + cycx_poke(&card->hw, addr, d1, len1); - if (data2) + if (d2) { if (len2 > 254) { u32 addr1 = 0xA00 + 0x400 * link; - cycx_poke(&card->hw, addr + len1, data2, 249); - cycx_poke(&card->hw, addr1, ((u8*) data2) + 249, + cycx_poke(&card->hw, addr + len1, d2, 249); + cycx_poke(&card->hw, addr1, ((u8*) d2) + 249, len2 - 249); } else - cycx_poke(&card->hw, addr + len1, data2, len2); + cycx_poke(&card->hw, addr + len1, d2, len2); + } } /* generate interruption, executing command */ cycx_intr(&card->hw); /* wait till card->mbox == 0 */ - err = cycx_exec(card->mbox); - card->u.x.critical = 0; + do { + err = cycx_exec(card->mbox); + } while (retry-- && err); + + spin_unlock_irqrestore(&card->u.x.lock, flags); return err; } @@ -1110,6 +1087,7 @@ static void nibble_to_byte(u8 *s, u8 *d, u8 len, u8 nibble) while (len) { *d++ = '0' + (*s >> 4); + if (--len) { *d++ = '0' + (*s & 0x0F); --len; @@ -1125,9 +1103,8 @@ static void nibble_to_byte(u8 *s, u8 *d, u8 len, u8 nibble) static int x25_place_call (cycx_t *card, x25_channel_t *chan) { int err = 0, - retry = MAX_CMD_RETRY, len; - char data[64], + char d[64], nibble = 0, mylen = chan->local_addr ? strlen(chan->local_addr) : 0, remotelen = strlen(chan->addr); @@ -1143,24 +1120,24 @@ static int x25_place_call (cycx_t *card, x25_channel_t *chan) set_bit(key, (void*)&card->u.x.connection_keys); ++key; dprintk(KERN_INFO "%s:x25_place_call:key=%d\n", card->devname, key); - memset(data, 0, sizeof(data)); - data[1] = key; /* user key */ - data[2] = 0x10; - data[4] = 0x0B; - - len = byte_to_nibble(chan->addr, data + 6, &nibble); - len += chan->local_addr ? byte_to_nibble(chan->local_addr, - data + 6 + len, &nibble) : 0; + memset(d, 0, sizeof(d)); + d[1] = key; /* user key */ + d[2] = 0x10; + d[4] = 0x0B; + + len = byte_to_nibble(chan->addr, d + 6, &nibble); + + if (chan->local_addr) + len += byte_to_nibble(chan->local_addr, d + 6 + len, &nibble); + if (nibble) ++len; - data[5] = mylen << 4 | remotelen; - data[6 + len + 1] = 0xCC; /* TCP/IP over X.25, thanx to Daniela :) */ - - do err = x25_exec(card, X25_CONNECT_REQUEST, chan->link, - &data, 7 + len + 1, NULL, 0); - while (err && retry--); - if (err) + d[5] = mylen << 4 | remotelen; + d[6 + len + 1] = 0xCC; /* TCP/IP over X.25, thanx to Daniela :) */ + + if ((err = x25_exec(card, X25_CONNECT_REQUEST, chan->link, + &d, 7 + len + 1, NULL, 0)) != 0) clear_bit(--key, (void*)&card->u.x.connection_keys); else { chan->lcn = -key; @@ -1173,75 +1150,53 @@ static int x25_place_call (cycx_t *card, x25_channel_t *chan) /* Place X.25 CONNECT RESPONSE. */ static int x25_connect_response (cycx_t *card, x25_channel_t *chan) { - int err = 0, - retry = MAX_CMD_RETRY; - char data[32]; + u8 d[8]; - memset(data, 0, sizeof(data)); - data[0] = data[3] = chan->lcn; - data[2] = 0x10; - data[4] = 0x0F; - data[7] = 0xCC; /* TCP/IP over X.25, thanx Daniela */ + memset(d, 0, sizeof(d)); + d[0] = d[3] = chan->lcn; + d[2] = 0x10; + d[4] = 0x0F; + d[7] = 0xCC; /* TCP/IP over X.25, thanx Daniela */ - do err = x25_exec(card, X25_CONNECT_RESPONSE, chan->link, - &data, 8, NULL, 0); - while (err && retry--); - - return err; + return x25_exec(card, X25_CONNECT_RESPONSE, chan->link, &d, 8, NULL, 0); } /* Place X.25 DISCONNECT RESPONSE. */ static int x25_disconnect_response (cycx_t *card, u8 link, u8 lcn) { - int err = 0, - retry = MAX_CMD_RETRY; - char data[5]; - - memset(data, 0, sizeof(data)); - data[0] = data[3] = lcn; - data[2] = 0x10; - data[4] = 0x17; - do err = x25_exec(card, X25_DISCONNECT_RESPONSE, link, - &data, 5, NULL, 0); - while (err && retry--); + char d[5]; - return err; + memset(d, 0, sizeof(d)); + d[0] = d[3] = lcn; + d[2] = 0x10; + d[4] = 0x17; + return x25_exec(card, X25_DISCONNECT_RESPONSE, link, &d, 5, NULL, 0); } /* Clear X.25 call. */ static int x25_clear_call (cycx_t *card, u8 link, u8 lcn, u8 cause, u8 diagn) { - int retry = MAX_CMD_RETRY, - err; - u8 data[7]; - - memset(data, 0, sizeof(data)); - data[0] = data[3] = lcn; - data[2] = 0x10; - data[4] = 0x13; - data[5] = cause; - data[6] = diagn; + u8 d[7]; - do err = x25_exec(card, X25_DISCONNECT_REQUEST, link, data, 7, NULL, 0); - while (err && retry--); + memset(d, 0, sizeof(d)); + d[0] = d[3] = lcn; + d[2] = 0x10; + d[4] = 0x13; + d[5] = cause; + d[6] = diagn; - return err; + return x25_exec(card, X25_DISCONNECT_REQUEST, link, d, 7, NULL, 0); } /* Send X.25 data packet. */ static int x25_send (cycx_t *card, u8 link, u8 lcn, u8 bitm, int len, void *buf) { - int err = 0, - retry = MAX_CMD_RETRY; - u8 data[] = "?\xFF\x10??"; - - data[0] = data[3] = lcn; - data[4] = bitm; + u8 d[] = "?\xFF\x10??"; - do err = x25_exec(card, X25_DATA_REQUEST, link, &data, 5, buf, len); - while (err && retry--); + d[0] = d[3] = lcn; + d[4] = bitm; - return err; + return x25_exec(card, X25_DATA_REQUEST, link, &d, 5, buf, len); } /* Miscellaneous */ @@ -1286,10 +1241,10 @@ static int chan_connect (struct device *dev) card->devname, chan->addr); if (x25_place_call(card, chan)) return -EIO; - set_chan_state(dev, WAN_CONNECTING, OUT_INTR); + set_chan_state(dev, WAN_CONNECTING); return 1; } else - set_chan_state(dev, WAN_CONNECTED, OUT_INTR); + set_chan_state(dev, WAN_CONNECTED); return 0; } @@ -1302,15 +1257,15 @@ static void chan_disc (struct device *dev) if (chan->svc) { x25_clear_call(chan->card, chan->link, chan->lcn, 0, 0); - set_chan_state(dev, WAN_DISCONNECTING, OUT_INTR); + set_chan_state(dev, WAN_DISCONNECTING); } else - set_chan_state(dev, WAN_DISCONNECTED, OUT_INTR); + set_chan_state(dev, WAN_DISCONNECTED); } /* Called by kernel timer */ -static void chan_timer (unsigned long data) +static void chan_timer (unsigned long d) { - struct device *dev = (struct device*) data; + struct device *dev = (struct device*) d; x25_channel_t *chan = dev->priv; switch (chan->state) { @@ -1325,16 +1280,13 @@ static void chan_timer (unsigned long data) } /* Set logical channel state. */ -static void set_chan_state (struct device *dev, u8 state, u8 outside_intr) +static void set_chan_state (struct device *dev, u8 state) { x25_channel_t *chan = dev->priv; cycx_t *card = chan->card; u32 flags = 0; - if (outside_intr) - spin_lock(&card->lock); - else - spin_lock_irqsave(&card->lock, flags); + spin_lock_irqsave(&card->lock, flags); if (chan->state != state) { if (chan->svc && chan->state == WAN_CONNECTED) @@ -1370,16 +1322,14 @@ static void set_chan_state (struct device *dev, u8 state, u8 outside_intr) *(unsigned short*)dev->dev_addr = 0; chan->lcn = 0; } + dev->tbusy = 0; break; } chan->state = state; } - if (outside_intr) - spin_unlock(&card->lock); - else - spin_unlock_irqrestore(&card->lock, flags); + spin_unlock_irqrestore(&card->lock, flags); } /* Send packet on a logical channel. diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index 36001d1b754a..4d6c2344a096 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -79,9 +79,6 @@ static int debug = -1; /* The debug level */ #include #endif -#if LINUX_VERSION_CODE < 0x20200 && defined(MODVERSIONS) -#include -#endif #include #include #include diff --git a/drivers/net/hamradio/Config.in b/drivers/net/hamradio/Config.in index 9ded63d1c918..d419ca8729e1 100644 --- a/drivers/net/hamradio/Config.in +++ b/drivers/net/hamradio/Config.in @@ -11,12 +11,13 @@ if [ "$CONFIG_SCC" != "n" ]; then bool ' support for TRX that feedback the tx signal to rx' CONFIG_SCC_TRXECHO fi -tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX -tristate 'BAYCOM ser12 halfduplex driver for AX.25' CONFIG_BAYCOM_SER_HDX -dep_tristate 'BAYCOM picpar and par96 driver for AX.25' CONFIG_BAYCOM_PAR $CONFIG_PARPORT -dep_tristate 'BAYCOM epp driver for AX.25' CONFIG_BAYCOM_EPP $CONFIG_PARPORT +dep_tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX $CONFIG_AX25 +dep_tristate 'BAYCOM ser12 halfduplex driver for AX.25' CONFIG_BAYCOM_SER_HDX $CONFIG_AX25 +dep_tristate 'BAYCOM picpar and par96 driver for AX.25' CONFIG_BAYCOM_PAR $CONFIG_PARPORT $CONFIG_AX25 +dep_tristate 'BAYCOM epp driver for AX.25' CONFIG_BAYCOM_EPP $CONFIG_PARPORT $CONFIG_AX25 +dep_tristate 'BAYCOM eppflex driver for AX.25' CONFIG_BAYCOM_EPPFLEX $CONFIG_PARPORT $CONFIG_AX25 -dep_tristate 'Soundcard modem driver' CONFIG_SOUNDMODEM $CONFIG_PARPORT +dep_tristate 'Soundcard modem driver' CONFIG_SOUNDMODEM $CONFIG_PARPORT $CONFIG_AX25 if [ "$CONFIG_SOUNDMODEM" != "n" ]; then bool ' soundmodem support for Soundblaster and compatible cards' CONFIG_SOUNDMODEM_SBC bool ' soundmodem support for WSS and Crystal cards' CONFIG_SOUNDMODEM_WSS diff --git a/drivers/net/hamradio/Makefile b/drivers/net/hamradio/Makefile index 962dbaa7fee4..d80857838c99 100644 --- a/drivers/net/hamradio/Makefile +++ b/drivers/net/hamradio/Makefile @@ -11,8 +11,8 @@ SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) -L_TARGET := hamradio.a -L_OBJS := +O_TARGET := hamradio.o +O_OBJS := M_OBJS := MOD_LIST_NAME := HAM_MODULES @@ -22,7 +22,7 @@ CONFIG_HDLCDRV_BUILTIN := CONFIG_HDLCDRV_MODULE := ifeq ($(CONFIG_DMASCC),y) -L_OBJS += dmascc.o +O_OBJS += dmascc.o else ifeq ($(CONFIG_DMASCC),m) M_OBJS += dmascc.o @@ -30,7 +30,7 @@ else endif ifeq ($(CONFIG_SCC),y) -L_OBJS += scc.o +O_OBJS += scc.o else ifeq ($(CONFIG_SCC),m) M_OBJS += scc.o @@ -38,7 +38,7 @@ else endif ifeq ($(CONFIG_MKISS),y) -L_OBJS += mkiss.o +O_OBJS += mkiss.o else ifeq ($(CONFIG_MKISS),m) M_OBJS += mkiss.o @@ -46,7 +46,7 @@ else endif ifeq ($(CONFIG_6PACK),y) -L_OBJS += 6pack.o +O_OBJS += 6pack.o else ifeq ($(CONFIG_6PACK),m) M_OBJS += 6pack.o @@ -54,7 +54,7 @@ else endif ifeq ($(CONFIG_YAM),y) -L_OBJS += yam.o +O_OBJS += yam.o else ifeq ($(CONFIG_YAM),m) M_OBJS += yam.o @@ -62,7 +62,7 @@ else endif ifeq ($(CONFIG_PI),y) -L_OBJS += pi2.o +O_OBJS += pi2.o else ifeq ($(CONFIG_PI),m) M_OBJS += pi2.o @@ -70,7 +70,7 @@ else endif ifeq ($(CONFIG_PT),y) -L_OBJS += pt.o +O_OBJS += pt.o else ifeq ($(CONFIG_PT),m) M_OBJS += pt.o @@ -78,7 +78,7 @@ else endif ifeq ($(CONFIG_BPQETHER),y) -L_OBJS += bpqether.o +O_OBJS += bpqether.o else ifeq ($(CONFIG_BPQETHER),m) M_OBJS += bpqether.o @@ -86,7 +86,7 @@ else endif ifeq ($(CONFIG_BAYCOM_SER_FDX),y) -L_OBJS += baycom_ser_fdx.o +O_OBJS += baycom_ser_fdx.o CONFIG_HDLCDRV_BUILTIN = y else ifeq ($(CONFIG_BAYCOM_SER_FDX),m) @@ -96,7 +96,7 @@ else endif ifeq ($(CONFIG_BAYCOM_SER_HDX),y) -L_OBJS += baycom_ser_hdx.o +O_OBJS += baycom_ser_hdx.o CONFIG_HDLCDRV_BUILTIN = y else ifeq ($(CONFIG_BAYCOM_SER_HDX),m) @@ -106,7 +106,7 @@ else endif ifeq ($(CONFIG_BAYCOM_PAR),y) -L_OBJS += baycom_par.o +O_OBJS += baycom_par.o CONFIG_HDLCDRV_BUILTIN = y else ifeq ($(CONFIG_BAYCOM_PAR),m) @@ -116,7 +116,7 @@ else endif ifeq ($(CONFIG_BAYCOM_EPP),y) -L_OBJS += baycom_epp.o +O_OBJS += baycom_epp.o CONFIG_HDLCDRV_BUILTIN = y else ifeq ($(CONFIG_BAYCOM_EPP),m) @@ -128,7 +128,7 @@ endif ifeq ($(CONFIG_SOUNDMODEM),y) ALL_SUB_DIRS += soundmodem SUB_DIRS += soundmodem -L_OBJS += soundmodem/soundmodem.o +O_OBJS += soundmodem/soundmodem.o CONFIG_HDLCDRV_BUILTIN = y else ifeq ($(CONFIG_SOUNDMODEM),m) @@ -141,7 +141,7 @@ endif # If anything built-in uses the hdlcdrv, then build it into the kernel also. # If not, but a module uses it, build as a module. ifdef CONFIG_HDLCDRV_BUILTIN -LX_OBJS += hdlcdrv.o +OX_OBJS += hdlcdrv.o else ifdef CONFIG_HDLCDRV_MODULE MX_OBJS += hdlcdrv.o diff --git a/drivers/net/sonic.h b/drivers/net/sonic.h index 526a7b1bb4a6..b2f6f79886f8 100644 --- a/drivers/net/sonic.h +++ b/drivers/net/sonic.h @@ -17,6 +17,8 @@ #ifndef SONIC_H #define SONIC_H +#include + /* * SONIC register offsets */ diff --git a/drivers/parport/ieee1284_ops.c b/drivers/parport/ieee1284_ops.c index 1315abc4c940..41780752cf2f 100644 --- a/drivers/parport/ieee1284_ops.c +++ b/drivers/parport/ieee1284_ops.c @@ -32,18 +32,12 @@ * One-way data transfer functions. * * ***/ -static inline -int polling (struct pardevice *dev) -{ - return dev->port->irq == PARPORT_IRQ_NONE; -} - /* Compatibility mode. */ size_t parport_ieee1284_write_compat (struct parport *port, const void *buffer, size_t len, int flags) { - int no_irq; + int no_irq = 1; ssize_t count = 0; const unsigned char *addr = buffer; unsigned char byte; @@ -51,11 +45,15 @@ size_t parport_ieee1284_write_compat (struct parport *port, unsigned char ctl = (PARPORT_CONTROL_SELECT | PARPORT_CONTROL_INIT); - if (port->irq != PARPORT_IRQ_NONE) + if (port->irq != PARPORT_IRQ_NONE) { parport_enable_irq (port); + no_irq = 0; + + /* Clear out previous irqs. */ + while (!down_trylock (&port->physport->ieee1284.irq)); + } port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA; - no_irq = polling (dev); while (count < len) { long expire = jiffies + dev->timeout; long wait = (HZ + 99) / 100; @@ -63,12 +61,6 @@ size_t parport_ieee1284_write_compat (struct parport *port, | PARPORT_STATUS_BUSY); unsigned char val = (PARPORT_STATUS_ERROR | PARPORT_STATUS_BUSY); - int i; - - /* Write the character to the data lines. */ - byte = *addr++; - parport_write_data (port, byte); - udelay (1); /* Wait until the peripheral's ready */ do { @@ -111,18 +103,23 @@ size_t parport_ieee1284_write_compat (struct parport *port, /* Is there a signal pending? */ if (signal_pending (current)) - goto stop; + break; /* Wait longer next time. */ wait *= 2; } while (time_before (jiffies, expire)); + if (signal_pending (current)) + break; + DPRINTK (KERN_DEBUG "%s: Timed out\n", port->name); break; ready: - /* Clear out previous irqs. */ - while (!down_trylock (&port->physport->ieee1284.irq)); + /* Write the character to the data lines. */ + byte = *addr++; + parport_write_data (port, byte); + udelay (1); /* Pulse strobe. */ parport_write_control (port, ctl | PARPORT_CONTROL_STROBE); @@ -131,30 +128,7 @@ size_t parport_ieee1284_write_compat (struct parport *port, parport_write_control (port, ctl); udelay (1); /* hold */ - if (no_irq) - /* Assume the peripheral received it. */ - goto done; - - /* Wait until it's received, up to 500us (this ought to be - * tuneable). */ - for (i = 500; i; i--) { - if (!down_trylock (&port->physport->ieee1284.irq) || - !(parport_read_status (port) & PARPORT_STATUS_ACK)) - goto done; - udelay (1); - } - - /* Two choices: - * 1. Assume that the peripheral got the data and just - * hasn't acknowledged it yet. - * 2. Assume that the peripheral never saw the strobe pulse. - * - * We can't know for sure, so let's be conservative. - */ - DPRINTK (KERN_DEBUG "%s: no ack", port->name); - break; - - done: + /* Assume the peripheral received it. */ count++; /* Let another process run if it needs to. */ @@ -547,7 +521,7 @@ size_t parport_ieee1284_ecp_read_data (struct parport *port, goto out; /* Yield the port for a while. */ - if (count && polling (dev)) { + if (count && dev->port->irq != PARPORT_IRQ_NONE) { parport_release (dev); current->state = TASK_INTERRUPTIBLE; schedule_timeout ((HZ + 99) / 25); diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index 938706d6db20..3b5d1812730a 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -76,7 +76,7 @@ static void frob_econtrol (struct parport *pb, unsigned char m, outb ((inb (ECONTROL (pb)) & ~m) ^ v, ECONTROL (pb)); } -#if defined(CONFIG_PARPORT_1284) || defined(CONFIG_PARPORT_PC_FIFO) +#ifdef CONFIG_PARPORT_PC_FIFO /* Safely change the mode bits in the ECR */ static int change_mode(struct parport *p, int m) { @@ -134,6 +134,7 @@ static int change_mode(struct parport *p, int m) return 0; } +#ifdef CONFIG_PARPORT_1284 /* Find FIFO lossage; FIFO is reset */ static int get_fifo_residue (struct parport *p) { @@ -180,8 +181,8 @@ static int get_fifo_residue (struct parport *p) return residue; } - -#endif /* IEEE 1284 support or FIFO support */ +#endif /* IEEE 1284 support */ +#endif /* FIFO support */ /* * Clear TIMEOUT BIT in EPP MODE @@ -1879,7 +1880,7 @@ void cleanup_module(void) if (p->irq != PARPORT_IRQ_NONE) free_irq(p->irq, p); release_region(p->base, 3); - if (p->size > 3); + if (p->size > 3) release_region(p->base + 3, p->size - 3); if (p->modes & PARPORT_MODE_ECP) release_region(p->base_hi, 3); diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index c82905010748..29a3a7e2932a 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -23,8 +23,12 @@ #include -#ifdef CONFIG_SYSCTL +#if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS) +#define PARPORT_MIN_TIMESLICE_VALUE 1ul +#define PARPORT_MAX_TIMESLICE_VALUE ((unsigned long) HZ) +#define PARPORT_MIN_SPINTIME_VALUE 1 +#define PARPORT_MAX_SPINTIME_VALUE 1000 static int do_active_device(ctl_table *table, int write, struct file *filp, void *result, size_t *lenp) @@ -167,6 +171,18 @@ static int do_hardware(ctl_table *table, int write, struct file *filp, #define PARPORT_DEVICES_ROOT_DIR { DEV_PARPORT_DEVICES, "devices", \ NULL, 0, 0555, NULL } +static const unsigned long parport_min_timeslice_value = +PARPORT_MIN_TIMESLICE_VALUE; + +static const unsigned long parport_max_timeslice_value = +PARPORT_MAX_TIMESLICE_VALUE; + +static const int parport_min_spintime_value = +PARPORT_MIN_SPINTIME_VALUE; + +static const int parport_max_spintime_value = +PARPORT_MAX_SPINTIME_VALUE; + struct parport_sysctl_table { struct ctl_table_header *sysctl_header; @@ -182,7 +198,9 @@ static const struct parport_sysctl_table parport_sysctl_template = { { { DEV_PARPORT_SPINTIME, "spintime", NULL, sizeof(int), 0644, NULL, - &proc_dointvec }, + &proc_dointvec_minmax, NULL, NULL, + (void*) &parport_min_spintime_value, + (void*) &parport_max_spintime_value }, { DEV_PARPORT_HARDWARE, "hardware", NULL, 0, 0444, NULL, &do_hardware }, @@ -230,7 +248,9 @@ parport_device_sysctl_template = { { { DEV_PARPORT_DEVICE_TIMESLICE, "timeslice", NULL, sizeof(int), 0644, NULL, - &proc_dointvec }, + &proc_doulongvec_ms_jiffies_minmax, NULL, NULL, + (void*) &parport_min_timeslice_value, + (void*) &parport_max_timeslice_value }, }, { {0, NULL, NULL, 0, 0555, NULL}, {0}}, { PARPORT_DEVICES_ROOT_DIR, {0}}, @@ -258,11 +278,15 @@ parport_default_sysctl_table = { { DEV_PARPORT_DEFAULT_TIMESLICE, "timeslice", &parport_default_timeslice, sizeof(parport_default_timeslice), 0644, NULL, - &proc_dointvec }, + &proc_doulongvec_ms_jiffies_minmax, NULL, NULL, + (void*) &parport_min_timeslice_value, + (void*) &parport_max_timeslice_value }, { DEV_PARPORT_DEFAULT_SPINTIME, "spintime", &parport_default_spintime, - sizeof(parport_default_timeslice), 0644, NULL, - &proc_dointvec }, + sizeof(parport_default_spintime), 0644, NULL, + &proc_dointvec_minmax, NULL, NULL, + (void*) &parport_min_spintime_value, + (void*) &parport_max_spintime_value }, {0} }, { { DEV_PARPORT_DEFAULT, "default", NULL, 0, 0555, @@ -395,7 +419,7 @@ int parport_default_proc_unregister(void) return 0; } -#else /* no sysctl */ +#else /* no sysctl or no procfs*/ int parport_proc_register(struct parport *pp) { diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 5a11000343e1..16a47b4089be 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -18,7 +18,7 @@ OX_OBJS = pcisyms.o O_OBJS = pci.o names.o L_OBJS := pci_syms.o else -L_OBJS := pci.o +L_OBJS := pci.o names.o endif ifdef CONFIG_PROC_FS diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1c4aedaacd93..ec9749d8d59e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -27,6 +27,8 @@ #define DBG(x...) #endif +extern void pci_namedevice(struct pci_dev *); + struct pci_bus pci_root; struct pci_dev *pci_devices = NULL; int pci_reverse __initdata = 0; diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c index 5eb4af9ddac2..a956e8402ae3 100644 --- a/drivers/scsi/BusLogic.c +++ b/drivers/scsi/BusLogic.c @@ -783,12 +783,10 @@ static int BusLogic_InitializeMultiMasterProbeInfo(BusLogic_HostAdapter_T unsigned int IRQ_Channel = PCI_Device->irq; unsigned long BaseAddress0 = PCI_Device->resource[0].start; unsigned long BaseAddress1 = PCI_Device->resource[1].start; - BusLogic_IO_Address_T IO_Address = - BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK; - BusLogic_PCI_Address_T PCI_Address = - BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK; - if ((BaseAddress0 & PCI_BASE_ADDRESS_SPACE) - != PCI_BASE_ADDRESS_SPACE_IO) + BusLogic_IO_Address_T IO_Address = BaseAddress0; + BusLogic_PCI_Address_T PCI_Address = BaseAddress1; + + if (!(PCI_Device->resource[0].flags & PCI_BASE_ADDRESS_SPACE_IO)) { BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for " "MultiMaster Host Adapter\n", NULL, BaseAddress0); @@ -796,8 +794,7 @@ static int BusLogic_InitializeMultiMasterProbeInfo(BusLogic_HostAdapter_T NULL, Bus, Device, IO_Address); continue; } - if ((BaseAddress1 & PCI_BASE_ADDRESS_SPACE) - != PCI_BASE_ADDRESS_SPACE_MEMORY) + if (PCI_Device->resource[1].flags & PCI_BASE_ADDRESS_SPACE_IO) { BusLogic_Error("BusLogic: Base Address1 0x%X not Memory for " "MultiMaster Host Adapter\n", NULL, BaseAddress1); @@ -986,8 +983,8 @@ static int BusLogic_InitializeMultiMasterProbeInfo(BusLogic_HostAdapter_T unsigned char Bus = PCI_Device->bus->number; unsigned char Device = PCI_Device->devfn >> 3; unsigned int IRQ_Channel = PCI_Device->irq; - BusLogic_IO_Address_T IO_Address = - PCI_Device->resource[0].start & PCI_BASE_ADDRESS_IO_MASK; + BusLogic_IO_Address_T IO_Address = PCI_Device->resource[0].start; + if (IO_Address == 0 || IRQ_Channel == 0) continue; for (i = 0; i < BusLogic_ProbeInfoCount; i++) { @@ -1032,13 +1029,10 @@ static int BusLogic_InitializeFlashPointProbeInfo(BusLogic_HostAdapter_T unsigned int IRQ_Channel = PCI_Device->irq; unsigned long BaseAddress0 = PCI_Device->resource[0].start; unsigned long BaseAddress1 = PCI_Device->resource[1].start; - BusLogic_IO_Address_T IO_Address = - BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK; - BusLogic_PCI_Address_T PCI_Address = - BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK; + BusLogic_IO_Address_T IO_Address = BaseAddress0; + BusLogic_PCI_Address_T PCI_Address = BaseAddress1; #ifndef CONFIG_SCSI_OMIT_FLASHPOINT - if ((BaseAddress0 & PCI_BASE_ADDRESS_SPACE) - != PCI_BASE_ADDRESS_SPACE_IO) + if (!(PCI_Device->resource[0].flags & PCI_BASE_ADDRESS_SPACE_IO)) { BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for " "FlashPoint Host Adapter\n", NULL, BaseAddress0); @@ -1046,8 +1040,7 @@ static int BusLogic_InitializeFlashPointProbeInfo(BusLogic_HostAdapter_T NULL, Bus, Device, IO_Address); continue; } - if ((BaseAddress1 & PCI_BASE_ADDRESS_SPACE) - != PCI_BASE_ADDRESS_SPACE_MEMORY) + if (PCI_Device->resource[1].flags & PCI_BASE_ADDRESS_SPACE_IO) { BusLogic_Error("BusLogic: Base Address1 0x%X not Memory for " "FlashPoint Host Adapter\n", NULL, BaseAddress1); diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 2363263927a4..ddcdfa7c7b19 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -18,7 +18,7 @@ CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS CFLAGS_seagate.o = -DARBITRATE -DPARITY -DSEAGATE_USE_ASM .SUFFIXES: -.SUFFIXES: .c .o .h .a +.SUFFIXES: .c .o .h .a .S ifeq (${CFLAGS},) CFLAGS = -D__KERNEL__=1 \ @@ -241,6 +241,14 @@ else endif endif +ifeq ($(CONFIG_OKTAGON_SCSI),y) +L_OBJS += NCR53C9x.o oktagon_esp.o oktagon_io.o +else + ifeq ($(CONFIG_OKTAGON_SCSI),m) + M_OBJS += NCR53C9x.o oktagon_esp.o oktagon_io.o + endif +endif + ifeq ($(CONFIG_ATARI_SCSI),y) L_OBJS += atari_scsi.o else @@ -623,6 +631,10 @@ ifeq ($(CONFIG_JAZZ_ESP),y) L_OBJS += NCR53C9x.o jazz_esp.o endif +ifeq ($(CONFIG_SUN3X_ESP),y) +L_OBJS += NCR53C9x.o sun3x_esp.o +endif + include $(TOPDIR)/Rules.make 53c8xx_d.h: 53c7,8xx.scr script_asm.pl diff --git a/drivers/scsi/NCR53C9x.c b/drivers/scsi/NCR53C9x.c index 2a3497eeca63..03da4ba48ea1 100644 --- a/drivers/scsi/NCR53C9x.c +++ b/drivers/scsi/NCR53C9x.c @@ -2,12 +2,12 @@ * * Originally esp.c : EnhancedScsiProcessor Sun SCSI driver code. * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1995, 1998 David S. Miller (davem@caip.rutgers.edu) * * Most DMA dependencies put in driver specific files by * Jesper Skov (jskov@cygnus.co.uk) * - * Set up to use GETREG/SETREG (preprocessor macros in NCR53c9x.h) by + * Set up to use esp_read/esp_write (preprocessor macros in NCR53c9x.h) by * Tymm Twillman (tymm@coe.missouri.edu) */ @@ -21,6 +21,10 @@ * 4) Maybe change use of "esp" to something more "NCR"'ish. */ +#ifdef MODULE +#include +#endif + #include #include #include @@ -30,28 +34,12 @@ #include #include #include - #include #include "scsi.h" #include "hosts.h" #include "NCR53C9x.h" -#ifdef CONFIG_SCSI_SUNESP -#include "sparc_esp.h" -#include -#include -#include -#include -#include -#endif - -#if defined(CONFIG_BLZ1230_SCSI)||defined(CONFIG_BLZ2060_SCSI)||defined(CONFIG_CYBERSTORMII_SCSI) -#define SYMBIOS_HACK -#else -#undef SYMBIOS_HACK -#endif - #include #include #include @@ -95,6 +83,17 @@ enum { in_tgterror = 0x84, /* Target did something stupid */ }; +enum { + /* Zero has special meaning, see skipahead[12]. */ +/*0*/ do_never, + +/*1*/ do_phase_determine, +/*2*/ do_reset_bus, +/*3*/ do_reset_complete, +/*4*/ do_work_bus, +/*5*/ do_intr_end +}; + struct proc_dir_entry proc_scsi_esp = { PROC_SCSI_ESP, 3, "esp", S_IFDIR | S_IRUGO | S_IXUGO, 2 @@ -227,7 +226,6 @@ static inline void esp_print_seqreg(unchar stepreg) "UNKNOWN")))))); } -#if defined(DEBUG_STATE_MACHINE) || defined(DEBUG_ESP) static char *phase_string(int phase) { switch(phase) { @@ -283,26 +281,31 @@ static char *phase_string(int phase) return "UNKNOWN"; }; } -#endif +#ifdef DEBUG_STATE_MACHINE static inline void esp_advance_phase(Scsi_Cmnd *s, int newphase) { -#ifdef DEBUG_STATE_MACHINE ESPLOG(("<%s>", phase_string(newphase))); -#endif s->SCp.sent_command = s->SCp.phase; s->SCp.phase = newphase; } +#else +#define esp_advance_phase(__s, __newphase) \ + (__s)->SCp.sent_command = (__s)->SCp.phase; \ + (__s)->SCp.phase = (__newphase); +#endif +#ifdef DEBUG_ESP_CMDS extern inline void esp_cmd(struct NCR_ESP *esp, struct ESP_regs *eregs, unchar cmd) { -#ifdef DEBUG_ESP_CMDS esp->espcmdlog[esp->espcmdent] = cmd; esp->espcmdent = (esp->espcmdent + 1) & 31; -#endif - SETREG(eregs->esp_cmnd, cmd); + esp_write(eregs->esp_cmnd, cmd); } +#else +#define esp_cmd(__esp, __eregs, __cmd) esp_write((__eregs)->esp_cmnd, (__cmd)) +#endif /* How we use the various Linux SCSI data structures for operation. * @@ -320,7 +323,6 @@ extern inline void esp_cmd(struct NCR_ESP *esp, struct ESP_regs *eregs, * the disconnect flag. */ - /* Manipulation of the ESP command queues. Thanks to the aha152x driver * and its author, Juergen E. Fischer, for the methods used here. * Note that these are per-ESP queues, not global queues like @@ -329,9 +331,7 @@ extern inline void esp_cmd(struct NCR_ESP *esp, struct ESP_regs *eregs, static inline void append_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC) { Scsi_Cmnd *end; - unsigned long flags; - save_flags(flags); cli(); new_SC->host_scribble = (unsigned char *) NULL; if(!*SC) *SC = new_SC; @@ -340,38 +340,28 @@ static inline void append_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC) ; end->host_scribble = (unsigned char *) new_SC; } - restore_flags(flags); } static inline void prepend_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC) { - unsigned long flags; - - save_flags(flags); cli(); new_SC->host_scribble = (unsigned char *) *SC; *SC = new_SC; - restore_flags(flags); } static inline Scsi_Cmnd *remove_first_SC(Scsi_Cmnd **SC) { Scsi_Cmnd *ptr; - unsigned long flags; - save_flags(flags); cli(); ptr = *SC; if(ptr) *SC = (Scsi_Cmnd *) (*SC)->host_scribble; - restore_flags(flags); return ptr; } static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, int target, int lun) { Scsi_Cmnd *ptr, *prev; - unsigned long flags; - save_flags(flags); cli(); for(ptr = *SC, prev = NULL; ptr && ((ptr->target != target) || (ptr->lun != lun)); prev = ptr, ptr = (Scsi_Cmnd *) ptr->host_scribble) @@ -382,14 +372,13 @@ static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, int target, int lun) else *SC=(Scsi_Cmnd *)ptr->host_scribble; } - restore_flags(flags); return ptr; } /* Resetting various pieces of the ESP scsi driver chipset */ /* Reset the ESP chip, _not_ the SCSI bus. */ -static inline void esp_reset_esp(struct NCR_ESP *esp, struct ESP_regs *eregs) +static void esp_reset_esp(struct NCR_ESP *esp, struct ESP_regs *eregs) { int family_code, version, i; volatile int trash; @@ -397,6 +386,8 @@ static inline void esp_reset_esp(struct NCR_ESP *esp, struct ESP_regs *eregs) /* Now reset the ESP chip */ esp_cmd(esp, eregs, ESP_CMD_RC); esp_cmd(esp, eregs, ESP_CMD_NULL | ESP_CMD_DMA); + if(esp->erev == fast) + esp_write(eregs->esp_cfg2, ESP_CONFIG2_FENAB); esp_cmd(esp, eregs, ESP_CMD_NULL | ESP_CMD_DMA); /* This is the only point at which it is reliable to read @@ -404,31 +395,45 @@ static inline void esp_reset_esp(struct NCR_ESP *esp, struct ESP_regs *eregs) */ esp->max_period = ((35 * esp->ccycle) / 1000); if(esp->erev == fast) { - version = GETREG(eregs->esp_uid); + char *erev2string[] = { + "Emulex FAS236", + "Emulex FPESP100A", + "fast", + "QLogic FAS366", + "Emulex FAS216", + "Symbios Logic 53CF9x-2", + "unknown!" + }; + + version = esp_read(eregs->esp_uid); family_code = (version & 0xf8) >> 3; -#ifdef SYMBIOS_HACK - if (version == 0 && family_code == 0) - { - printk ("Detected SymBIOS chip with no family code.\n"); - version = 3; - family_code = 2; - } -#endif - if(family_code == 0x02) + if(family_code == 0x02) { if ((version & 7) == 2) esp->erev = fas216; else esp->erev = fas236; - else if(family_code == 0x0a) - esp->erev = fashme; /* Version is usually '5'. */ - else - esp->erev = fas100a; - printk("esp%d: FAST chip is %s (family=%d, version=%d)\n", - esp->esp_id, - (esp->erev == fas236) ? "fas236" : - ((esp->erev == fas216) ? "fas216" : - (((esp->erev == fas100a) ? "fas100a" : - "fasHME"))), family_code, (version & 7)); + } else if(family_code == 0x0a) + esp->erev = fas366; /* Version is usually '5'. */ + else if(family_code == 0x00) { + if ((version & 7) == 2) + esp->erev = fas100a; /* NCR53C9X */ + else + esp->erev = espunknown; + } else if(family_code == 0x14) { + if ((version & 7) == 2) + esp->erev = fsc; + else + esp->erev = espunknown; + } else if(family_code == 0x00) { + if ((version & 7) == 2) + esp->erev = fas100a; /* NCR53C9X */ + else + esp->erev = espunknown; + } else + esp->erev = espunknown; + ESPLOG(("esp%d: FAST chip is %s (family=%d, version=%d)\n", + esp->esp_id, erev2string[esp->erev - fas236], + family_code, (version & 7))); esp->min_period = ((4 * esp->ccycle) / 1000); } else { @@ -436,56 +441,63 @@ static inline void esp_reset_esp(struct NCR_ESP *esp, struct ESP_regs *eregs) } /* Reload the configuration registers */ - SETREG(eregs->esp_cfact, esp->cfact); - SETREG(eregs->esp_stp, 0); - SETREG(eregs->esp_soff, 0); - SETREG(eregs->esp_timeo, esp->neg_defp); + esp_write(eregs->esp_cfact, esp->cfact); + esp->prev_stp = 0; + esp_write(eregs->esp_stp, 0); + esp->prev_soff = 0; + esp_write(eregs->esp_soff, 0); + esp_write(eregs->esp_timeo, esp->neg_defp); esp->max_period = (esp->max_period + 3)>>2; esp->min_period = (esp->min_period + 3)>>2; - SETREG(eregs->esp_cfg1, esp->config1); + esp_write(eregs->esp_cfg1, esp->config1); switch(esp->erev) { case esp100: /* nothing to do */ break; case esp100a: - SETREG(eregs->esp_cfg2, esp->config2); + esp_write(eregs->esp_cfg2, esp->config2); break; case esp236: /* Slow 236 */ - SETREG(eregs->esp_cfg2, esp->config2); - SETREG(eregs->esp_cfg3, esp->config3[0]); + esp_write(eregs->esp_cfg2, esp->config2); + esp->prev_cfg3 = esp->config3[0]; + esp_write(eregs->esp_cfg3, esp->prev_cfg3); break; - case fashme: - esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB); - /* fallthrough... */ - case fas216: + case fas366: + panic("esp: FAS366 support not present, please notify " + "jongk@cs.utwente.nl"); + break; + case fas216: case fas236: - /* Fast 236 or HME */ - SETREG(eregs->esp_cfg2, esp->config2); - for(i=0; i<8; i++) { - if(esp->erev == fashme) - esp->config3[i] |= - (ESP_CONFIG3_FCLOCK | ESP_CONFIG3_BIGID | ESP_CONFIG3_OBPUSH); - else - esp->config3[i] |= ESP_CONFIG3_FCLK; - } - SETREG(eregs->esp_cfg3, esp->config3[0]); - if(esp->erev == fashme) { - esp->radelay = 80; - } else { - if(esp->diff) - esp->radelay = 0; - else - esp->radelay = 96; - } + case fsc: + /* Fast ESP variants */ + esp_write(eregs->esp_cfg2, esp->config2); + for(i=0; i<8; i++) + esp->config3[i] |= ESP_CONFIG3_FCLK; + esp->prev_cfg3 = esp->config3[0]; + esp_write(eregs->esp_cfg3, esp->prev_cfg3); + if(esp->diff) + esp->radelay = 0; + else + esp->radelay = 16; + /* Different timeout constant for these chips */ + esp->neg_defp = + FSC_NEG_DEFP(esp->cfreq, + (esp->cfact == ESP_CCF_F0 ? + ESP_CCF_F7 + 1 : esp->cfact)); + esp_write(eregs->esp_timeo, esp->neg_defp); + /* Enable Active Negotiation if possible */ + if((esp->erev == fsc) && !esp->diff) + esp_write(eregs->esp_cfg4, ESP_CONFIG4_EAN); break; case fas100a: /* Fast 100a */ - SETREG(eregs->esp_cfg2, esp->config2); + esp_write(eregs->esp_cfg2, esp->config2); for(i=0; i<8; i++) esp->config3[i] |= ESP_CONFIG3_FCLOCK; - SETREG(eregs->esp_cfg3, esp->config3[0]); + esp->prev_cfg3 = esp->config3[0]; + esp_write(eregs->esp_cfg3, esp->prev_cfg3); esp->radelay = 32; break; default: @@ -494,12 +506,12 @@ static inline void esp_reset_esp(struct NCR_ESP *esp, struct ESP_regs *eregs) }; /* Eat any bitrot in the chip */ - trash = GETREG(eregs->esp_intrpt); + trash = esp_read(eregs->esp_intrpt); udelay(100); } /* This places the ESP into a known state at boot time. */ -inline void esp_bootup_reset(struct NCR_ESP *esp, struct ESP_regs *eregs) +void esp_bootup_reset(struct NCR_ESP *esp, struct ESP_regs *eregs) { volatile unchar trash; @@ -511,13 +523,13 @@ inline void esp_bootup_reset(struct NCR_ESP *esp, struct ESP_regs *eregs) esp_reset_esp(esp, eregs); /* Reset the SCSI bus, but tell ESP not to generate an irq */ - SETREG(eregs->esp_cfg1, GETREG(eregs->esp_cfg1) | ESP_CONFIG1_SRRDISAB); + esp_write(eregs->esp_cfg1, (esp_read(eregs->esp_cfg1) | ESP_CONFIG1_SRRDISAB)); esp_cmd(esp, eregs, ESP_CMD_RS); udelay(400); - SETREG(eregs->esp_cfg1, esp->config1); + esp_write(eregs->esp_cfg1, esp->config1); /* Eat any bitrot in the chip and we are done... */ - trash = GETREG(eregs->esp_intrpt); + trash = esp_read(eregs->esp_intrpt); } /* Allocate structure and insert basic data such as SCSI chip frequency @@ -538,6 +550,9 @@ struct NCR_ESP* esp_allocate(Scsi_Host_Template *tpnt, void *esp_dev) esp->edev = esp_dev; esp->esp_id = nesps++; + /* Set bitshift value (only used on Amiga with multiple ESPs) */ + esp->shift = 2; + /* Put into the chain of esp chips detected */ if(espchain) { elink = espchain; @@ -551,6 +566,20 @@ struct NCR_ESP* esp_allocate(Scsi_Host_Template *tpnt, void *esp_dev) return esp; } +void esp_deallocate(struct NCR_ESP *esp) +{ + struct NCR_ESP *elink; + + if(espchain == esp) { + espchain = 0; + } else { + for(elink = espchain; elink && (elink->next != esp); elink = elink->next); + if(elink) + elink->next = esp->next; + } + nesps--; +} + /* Complete initialization of ESP structure and device * Caller must have initialized appropriate parts of the ESP structure * between the call to esp_allocate and this function. @@ -639,9 +668,9 @@ void esp_initialize(struct NCR_ESP *esp) esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf); esp->sync_defp = SYNC_DEFP_SLOW; - printk("SCSI ID %d Clock %d MHz CCF=%d Time-Out %d ", + printk("SCSI ID %d Clk %dMHz CCF=%d TOut %d ", esp->scsi_id, (esp->cfreq / 1000000), - esp->ccf, (int) esp->neg_defp); + ccf, (int) esp->neg_defp); /* Fill in ehost data */ esp->ehost->base = (unsigned char *) eregs; @@ -654,61 +683,46 @@ void esp_initialize(struct NCR_ESP *esp) /* Probe the revision of this esp */ esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7)); esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY); - SETREG(eregs->esp_cfg2, esp->config2); -#ifndef SYMBIOS_HACK - if((GETREG(eregs->esp_cfg2) & ~(ESP_CONFIG2_MAGIC)) != + esp_write(eregs->esp_cfg2, esp->config2); + if((esp_read(eregs->esp_cfg2) & ~(ESP_CONFIG2_MAGIC)) != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) { - printk("NCR53C90(esp100) detected\n"); + printk("NCR53C90(esp100)\n"); esp->erev = esp100; } else { -#endif esp->config2 = 0; - SETREG(eregs->esp_cfg2, esp->config2); - SETREG(eregs->esp_cfg3, 0); - esp->config3[0] = 5; - SETREG(eregs->esp_cfg3, esp->config3[0]); -#ifndef SYMBIOS_HACK - if(GETREG(eregs->esp_cfg3) != 5) { - printk("NCR53C90A(esp100a) detected\n"); + esp_write(eregs->esp_cfg2, 0); + esp_write(eregs->esp_cfg3, 5); + if(esp_read(eregs->esp_cfg3) != 5) { + printk("NCR53C90A(esp100a)\n"); esp->erev = esp100a; } else { -#else - { -#endif int target; - + for(target=0; target<8; target++) esp->config3[target] = 0; - SETREG(eregs->esp_cfg3, 0); -#ifndef SYMBIOS_HACK + esp->prev_cfg3 = 0; + esp_write(eregs->esp_cfg3, 0); if(ccf > ESP_CCF_F5) { -#endif - printk("NCR53C9XF(espfast) detected\n"); + printk("NCR53C9XF(espfast)\n"); esp->erev = fast; - esp->config2 = 0; - SETREG(eregs->esp_cfg2, esp->config2); esp->sync_defp = SYNC_DEFP_FAST; -#ifndef SYMBIOS_HACK } else { - printk("NCR53C9x(esp236) detected\n"); + printk("NCR53C9x(esp236)\n"); esp->erev = esp236; - esp->config2 = 0; - SETREG(eregs->esp_cfg2, esp->config2); } } -#endif } - + /* Initialize the command queues */ esp->current_SC = 0; esp->disconnected_SC = 0; esp->issue_SC = 0; - + /* Clear the state machines. */ esp->targets_present = 0; esp->resetting_bus = 0; esp->snip = 0; - esp->targets_present = 0; + esp->fas_premature_intr_workaround = 0; for(i = 0; i < 32; i++) esp->espcmdlog[i] = 0; esp->espcmdent = 0; @@ -719,9 +733,15 @@ void esp_initialize(struct NCR_ESP *esp) esp->prevmsgout = esp->prevmsgin = 0; esp->msgout_len = esp->msgin_len = 0; + /* Clear the one behind caches to hold unmatchable values. */ + esp->prev_soff = esp->prev_stp = esp->prev_cfg3 = 0xff; + /* Reset the thing before we try anything... */ esp_bootup_reset(esp, eregs); - + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif esps_in_use++; } @@ -740,15 +760,17 @@ const char *esp_info(struct Scsi_Host *host) case esp100a: return "ESP100A (NCR53C90A)"; case esp236: - return "ESP236"; + return "ESP236 (NCR53C9x)"; case fas216: - return "ESP216-FAST"; + return "Emulex FAS216"; case fas236: - return "ESP236-FAST"; - case fashme: - return "ESP366-HME"; + return "Emulex FAS236"; + case fas366: + return "QLogic FAS366"; case fas100a: - return "ESP100A-FAST"; + return "FPESP100A"; + case fsc: + return "Symbios Logic 53CF9x-2"; default: panic("Bogon ESP revision"); }; @@ -807,69 +829,41 @@ static int esp_host_info(struct NCR_ESP *esp, char *ptr, off_t offset, int len) info.offset = offset; info.pos = 0; - copy_info(&info, "Sparc ESP Host Adapter:\n"); - copy_info(&info, "\tPROM node\t\t%08lx\n", (unsigned long) esp->prom_node); - copy_info(&info, "\tPROM name\t\t%s\n", esp->prom_name); + copy_info(&info, "ESP Host Adapter:\n"); copy_info(&info, "\tESP Model\t\t"); switch(esp->erev) { case esp100: - copy_info(&info, "ESP100\n"); + copy_info(&info, "ESP100 (NCR53C90)\n"); break; case esp100a: - copy_info(&info, "ESP100A\n"); + copy_info(&info, "ESP100A (NCR53C90A)\n"); break; case esp236: - copy_info(&info, "ESP236\n"); + copy_info(&info, "ESP236 (NCR53C9x)\n"); break; case fas216: - copy_info(&info, "FAS216\n"); + copy_info(&info, "Emulex FAS216\n"); break; case fas236: - copy_info(&info, "FAS236\n"); + copy_info(&info, "Emulex FAS236\n"); break; case fas100a: - copy_info(&info, "FAS100A\n"); + copy_info(&info, "FPESP100A\n"); break; case fast: - copy_info(&info, "FAST\n"); - break; - case fashme: - copy_info(&info, "Happy Meal FAS\n"); - break; - case espunknown: - default: - copy_info(&info, "Unknown!\n"); - break; - }; -#ifdef CONFIG_SCSI_SUNESP - copy_info(&info, "\tDMA Revision\t\t"); - switch(((struct Linux_SBus_DMA*) (esp->dma))->revision) { - case dvmarev0: - copy_info(&info, "Rev 0\n"); - break; - case dvmaesc1: - copy_info(&info, "ESC Rev 1\n"); - break; - case dvmarev1: - copy_info(&info, "Rev 1\n"); - break; - case dvmarev2: - copy_info(&info, "Rev 2\n"); + copy_info(&info, "Generic FAST\n"); break; - case dvmarev3: - copy_info(&info, "Rev 3\n"); + case fas366: + copy_info(&info, "QLogic FAS366\n"); break; - case dvmarevplus: - copy_info(&info, "Rev 1+\n"); - break; - case dvmahme: - copy_info(&info, "Rev HME/FAS\n"); + case fsc: + copy_info(&info, "Symbios Logic 53C9x-2\n"); break; + case espunknown: default: copy_info(&info, "Unknown!\n"); break; }; -#endif copy_info(&info, "\tLive Targets\t\t[ "); for(i = 0; i < 15; i++) { if(esp->targets_present & (1 << i)) @@ -878,7 +872,7 @@ static int esp_host_info(struct NCR_ESP *esp, char *ptr, off_t offset, int len) copy_info(&info, "]\n\n"); /* Now describe the state of each existing target. */ - copy_info(&info, "Target #\tconfig3\t\tSync Capabilities\tDisconnect\tWide\n"); + copy_info(&info, "Target #\tconfig3\t\tSync Capabilities\tDisconnect\n"); for(i = 0; i < 15; i++) { if(esp->targets_present & (1 << i)) { Scsi_Device *SDptr = esp->ehost->host_queue; @@ -892,9 +886,7 @@ static int esp_host_info(struct NCR_ESP *esp, char *ptr, off_t offset, int len) copy_info(&info, "%08lx\t", esp->config3[i]); copy_info(&info, "[%02lx,%02lx]\t\t\t", SDptr->sync_max_offset, SDptr->sync_min_period); - copy_info(&info, "%s\t\t", SDptr->disconnect ? "yes" : "no"); - copy_info(&info, "%s\n", - (esp->config3[i] & ESP_CONFIG3_EWIDE) ? "yes" : "no"); + copy_info(&info, "%s\n", SDptr->disconnect ? "yes" : "no"); } } @@ -923,6 +915,60 @@ int esp_proc_info(char *buffer, char **start, off_t offset, int length, return esp_host_info(esp, buffer, offset, length); } +static void esp_get_dmabufs(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + if(sp->use_sg == 0) { + sp->SCp.this_residual = sp->request_bufflen; + sp->SCp.buffer = (struct scatterlist *) sp->request_buffer; + sp->SCp.buffers_residual = 0; + if (esp->dma_mmu_get_scsi_one) + esp->dma_mmu_get_scsi_one(esp, sp); + else + sp->SCp.have_data_in = (int) sp->SCp.ptr = + (char *) virt_to_phys(sp->request_buffer); + } else { + sp->SCp.buffer = (struct scatterlist *) sp->buffer; + sp->SCp.buffers_residual = sp->use_sg - 1; + sp->SCp.this_residual = sp->SCp.buffer->length; + if (esp->dma_mmu_get_scsi_sgl) + esp->dma_mmu_get_scsi_sgl(esp, sp); + else + sp->SCp.ptr = + (char *) virt_to_phys(sp->SCp.buffer->address); + } +} + +static void esp_release_dmabufs(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + if(sp->use_sg == 0) { + if (esp->dma_mmu_release_scsi_one) + esp->dma_mmu_release_scsi_one(esp, sp); + } else { + if (esp->dma_mmu_release_scsi_sgl) + esp->dma_mmu_release_scsi_sgl(esp, sp); + } +} + +static void esp_restore_pointers(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + struct esp_pointers *ep = &esp->data_pointers[sp->target]; + + sp->SCp.ptr = ep->saved_ptr; + sp->SCp.buffer = ep->saved_buffer; + sp->SCp.this_residual = ep->saved_this_residual; + sp->SCp.buffers_residual = ep->saved_buffers_residual; +} + +static void esp_save_pointers(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + struct esp_pointers *ep = &esp->data_pointers[sp->target]; + + ep->saved_ptr = sp->SCp.ptr; + ep->saved_buffer = sp->SCp.buffer; + ep->saved_this_residual = sp->SCp.this_residual; + ep->saved_buffers_residual = sp->SCp.buffers_residual; +} + /* Some rules: * * 1) Never ever panic while something is live on the bus. @@ -970,29 +1016,7 @@ static inline void build_sync_nego_msg(struct NCR_ESP *esp, int period, int offs esp->msgout_len = 5; } -/* SIZE is in bits, currently HME only supports 16 bit wide transfers. */ -static inline void build_wide_nego_msg(struct NCR_ESP *esp, int size) -{ - esp->cur_msgout[0] = EXTENDED_MESSAGE; - esp->cur_msgout[1] = 2; - esp->cur_msgout[2] = EXTENDED_WDTR; - switch(size) { - case 32: - esp->cur_msgout[3] = 2; - break; - case 16: - esp->cur_msgout[3] = 1; - break; - case 8: - default: - esp->cur_msgout[3] = 0; - break; - }; - - esp->msgout_len = 4; -} - -static inline void esp_exec_cmd(struct NCR_ESP *esp) +static void esp_exec_cmd(struct NCR_ESP *esp) { struct ESP_regs *eregs = esp->eregs; Scsi_Cmnd *SCptr; @@ -1002,15 +1026,18 @@ static inline void esp_exec_cmd(struct NCR_ESP *esp) int lun, target; int i; - /* Hold off if we've been reselected or an IRQ is showing... */ - if(esp->disconnected_SC || esp->dma_irq_p(esp)) + /* Hold off if we have disconnected commands and + * an IRQ is showing... + */ + if(esp->disconnected_SC && esp->dma_irq_p(esp)) return; /* Grab first member of the issue queue. */ SCptr = esp->current_SC = remove_first_SC(&esp->issue_SC); /* Safe to panic here because current_SC is null. */ - if(!SCptr) panic("esp: esp_exec_cmd and issue queue is NULL"); + if(!SCptr) + panic("esp: esp_exec_cmd and issue queue is NULL"); SDptr = SCptr->device; lun = SCptr->lun; @@ -1053,7 +1080,7 @@ static inline void esp_exec_cmd(struct NCR_ESP *esp) if(SDptr->sync) { /* this targets sync is known */ -#ifdef CONFIG_SCSI_SUNESP +#ifdef CONFIG_SCSI_MAC_ESP do_sync_known: #endif if(SDptr->disconnect) @@ -1090,51 +1117,28 @@ do_sync_known: } else { int toshiba_cdrom_hwbug_wkaround = 0; -#ifdef CONFIG_SCSI_SUNESP - /* Never allow disconnects or synchronous transfers on - * SparcStation1 and SparcStation1+. Allowing those - * to be enabled seems to lockup the machine completely. +#ifdef CONFIG_SCSI_MAC_ESP + /* Never allow synchronous transfers (disconnect OK) on + * Macintosh. Well, maybe later when we figured out how to + * do DMA on the machines that support it ... */ - if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || - (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { - /* But we are nice and allow tapes to disconnect. */ - if(SDptr->type == TYPE_TAPE) - SDptr->disconnect = 1; - else - SDptr->disconnect = 0; - SDptr->sync_max_offset = 0; - SDptr->sync_min_period = 0; - SDptr->sync = 1; - esp->snip = 0; - goto do_sync_known; - } + SDptr->disconnect = 1; + SDptr->sync_max_offset = 0; + SDptr->sync_min_period = 0; + SDptr->sync = 1; + esp->snip = 0; + goto do_sync_known; #endif /* We've talked to this guy before, - * but never negotiated. Let's try, - * need to attempt WIDE first, before - * sync nego, as per SCSI 2 standard. + * but never negotiated. Let's try + * sync negotiation. */ - if(esp->erev == fashme && !SDptr->wide) { - if(!SDptr->borken && - (SDptr->type != TYPE_ROM || - strncmp(SDptr->vendor, "TOSHIBA", 7))) { - build_wide_nego_msg(esp, 16); - esp->config3[SCptr->target] |= ESP_CONFIG3_EWIDE; - SDptr->wide = 1; - esp->wnip = 1; - goto after_nego_msg_built; - } else { - SDptr->wide = 1; - /* Fall through and try sync. */ - } - } - if(!SDptr->borken) { if((SDptr->type == TYPE_ROM) && (!strncmp(SDptr->vendor, "TOSHIBA", 7))) { /* Nice try sucker... */ - printk(KERN_INFO "esp%d: Disabling sync for buggy " - "Toshiba CDROM.\n", esp->esp_id); + ESPMISC(("esp%d: Disabling sync for buggy " + "Toshiba CDROM.\n", esp->esp_id)); toshiba_cdrom_hwbug_wkaround = 1; build_sync_nego_msg(esp, 0, 0); } else { @@ -1146,7 +1150,6 @@ do_sync_known: SDptr->sync = 1; esp->snip = 1; -after_nego_msg_built: /* A fix for broken SCSI1 targets, when they disconnect * they lock up the bus and confuse ESP. So disallow * disconnects for SCSI1 targets for now until we @@ -1171,13 +1174,9 @@ after_nego_msg_built: * thank you very much. ;-) */ if(((SDptr->scsi_level < 3) && (SDptr->type != TYPE_TAPE)) || -#if 1 /* Until I find out why HME barfs with disconnects enabled... */ - toshiba_cdrom_hwbug_wkaround || SDptr->borken || esp->erev == fashme) { -#else toshiba_cdrom_hwbug_wkaround || SDptr->borken) { -#endif - printk(KERN_INFO "esp%d: Disabling DISCONNECT for target %d " - "lun %d\n", esp->esp_id, SCptr->target, SCptr->lun); + ESPMISC((KERN_INFO "esp%d: Disabling DISCONNECT for target %d " + "lun %d\n", esp->esp_id, SCptr->target, SCptr->lun)); SDptr->disconnect = 0; *cmdp++ = IDENTIFY(0, lun); } else { @@ -1199,49 +1198,48 @@ after_nego_msg_built: for(i = 0; i < SCptr->cmd_len; i++) *cmdp++ = SCptr->cmnd[i]; - /* HME sucks... */ - if(esp->erev == fashme) - SETREG(eregs->esp_busid, (target & 0xf) | - (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT)); - else - SETREG(eregs->esp_busid, (target & 7)); - SETREG(eregs->esp_soff, SDptr->sync_max_offset); - SETREG(eregs->esp_stp, SDptr->sync_min_period); - if(esp->erev > esp100a) - SETREG(eregs->esp_cfg3, esp->config3[target]); - + esp_write(eregs->esp_busid, (target & 7)); + if (esp->prev_soff != SDptr->sync_max_offset || + esp->prev_stp != SDptr->sync_min_period || + (esp->erev > esp100a && + esp->prev_cfg3 != esp->config3[target])) { + esp->prev_soff = SDptr->sync_max_offset; + esp_write(eregs->esp_soff, esp->prev_soff); + esp->prev_stp = SDptr->sync_min_period; + esp_write(eregs->esp_stp, esp->prev_stp); + if(esp->erev > esp100a) { + esp->prev_cfg3 = esp->config3[target]; + esp_write(eregs->esp_cfg3, esp->prev_cfg3); + } + } i = (cmdp - esp->esp_command); /* Set up the DMA and ESP counters */ if(esp->do_pio_cmds){ int j = 0; + /* + * XXX MSch: + * + * It seems this is required, at least to clean up + * after failed commands when using PIO mode ... + */ + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + for(;jesp_fdata, esp->esp_command[j]); + esp_write(eregs->esp_fdata, esp->esp_command[j]); the_esp_command &= ~ESP_CMD_DMA; /* Tell ESP to "go". */ esp_cmd(esp, eregs, the_esp_command); } else { - if(esp->erev == fashme) { - esp_cmd(esp, eregs, ESP_CMD_FLUSH); /* Grrr! */ - - /* Set up the HME counters */ - SETREG(eregs->esp_tclow, i); - SETREG(eregs->esp_tcmed, 0); - SETREG(eregs->fas_rlo, 0); - SETREG(eregs->fas_rhi, 0); - esp_cmd(esp, eregs, the_esp_command); - esp->dma_init_write(esp, esp->esp_command_dvma, 16); - } else { - /* Set up the ESP counters */ - SETREG(eregs->esp_tclow, i); - SETREG(eregs->esp_tcmed, 0); - esp->dma_init_write(esp, esp->esp_command_dvma, i); + /* Set up the ESP counters */ + esp_write(eregs->esp_tclow, i); + esp_write(eregs->esp_tcmed, 0); + esp->dma_init_write(esp, esp->esp_command_dvma, i); - /* Tell ESP to "go". */ - esp_cmd(esp, eregs, the_esp_command); - } + /* Tell ESP to "go". */ + esp_cmd(esp, eregs, the_esp_command); } } @@ -1249,7 +1247,6 @@ after_nego_msg_built: int esp_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { struct NCR_ESP *esp; - unsigned long flags; /* Set up func ptr and initial driver cmd-phase. */ SCpnt->scsi_done = done; @@ -1263,32 +1260,10 @@ int esp_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) /* We use the scratch area. */ ESPQUEUE(("esp_queue: target=%d lun=%d ", SCpnt->target, SCpnt->lun)); ESPDISC(("N<%02x,%02x>", SCpnt->target, SCpnt->lun)); - if(!SCpnt->use_sg) { - ESPQUEUE(("!use_sg\n")); - SCpnt->SCp.this_residual = SCpnt->request_bufflen; - SCpnt->SCp.buffer = - (struct scatterlist *) SCpnt->request_buffer; - SCpnt->SCp.buffers_residual = 0; - if (esp->dma_mmu_get_scsi_one) - esp->dma_mmu_get_scsi_one (esp, SCpnt); - else - SCpnt->SCp.have_data_in = (int) SCpnt->SCp.ptr = - (char *) virt_to_phys(SCpnt->request_buffer); - } else { - ESPQUEUE(("use_sg ")); -#ifdef DEBUG_ESP_SG - printk("esp%d: sglist at %p with %d buffers\n", - esp->esp_id, SCpnt->buffer, SCpnt->use_sg); -#endif - SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->buffer; - SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1; - SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; - if (esp->dma_mmu_get_scsi_sgl) - esp->dma_mmu_get_scsi_sgl (esp, SCpnt); - else - SCpnt->SCp.ptr = - (char *) virt_to_phys(SCpnt->SCp.buffer->address); - } + + esp_get_dmabufs(esp, SCpnt); + esp_save_pointers(esp, SCpnt); /* FIXME for tag queueing */ + SCpnt->SCp.Status = CHECK_CONDITION; SCpnt->SCp.Message = 0xff; SCpnt->SCp.sent_command = 0; @@ -1302,13 +1277,10 @@ int esp_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) append_SC(&esp->issue_SC, SCpnt); } - save_and_cli(flags); - /* Run it now if we can. */ if(!esp->current_SC && !esp->resetting_bus) esp_exec_cmd(esp); - restore_flags(flags); return 0; } @@ -1324,7 +1296,7 @@ int esp_command(Scsi_Cmnd *SCpnt) } /* Dump driver state. */ -static inline void esp_dump_cmd(Scsi_Cmnd *SCptr) +static void esp_dump_cmd(Scsi_Cmnd *SCptr) { ESPLOG(("[tgt<%02x> lun<%02x> " "pphase<%s> cphase<%s>]", @@ -1333,8 +1305,8 @@ static inline void esp_dump_cmd(Scsi_Cmnd *SCptr) phase_string(SCptr->SCp.phase))); } -static inline void esp_dump_state(struct NCR_ESP *esp, - struct ESP_regs *eregs) +static void esp_dump_state(struct NCR_ESP *esp, + struct ESP_regs *eregs) { Scsi_Cmnd *SCptr = esp->current_SC; #ifdef DEBUG_ESP_CMDS @@ -1349,8 +1321,8 @@ static inline void esp_dump_state(struct NCR_ESP *esp, ESPLOG(("esp%d: SW [sreg<%02x> sstep<%02x> ireg<%02x>]\n", esp->esp_id, esp->sreg, esp->seqreg, esp->ireg)); ESPLOG(("esp%d: HW reread [sreg<%02x> sstep<%02x> ireg<%02x>]\n", - esp->esp_id, GETREG(eregs->esp_status), - GETREG(eregs->esp_sstep), GETREG(eregs->esp_intrpt))); + esp->esp_id, esp_read(eregs->esp_status), esp_read(eregs->esp_sstep), + esp_read(eregs->esp_intrpt))); #ifdef DEBUG_ESP_CMDS printk("esp%d: last ESP cmds [", esp->esp_id); i = (esp->espcmdent - 1) & 31; @@ -1392,7 +1364,6 @@ int esp_abort(Scsi_Cmnd *SCptr) struct NCR_ESP *esp = (struct NCR_ESP *) SCptr->host->hostdata; struct ESP_regs *eregs = esp->eregs; int don; - unsigned long flags; ESPLOG(("esp%d: Aborting command\n", esp->esp_id)); esp_dump_state(esp, eregs); @@ -1403,16 +1374,13 @@ int esp_abort(Scsi_Cmnd *SCptr) * in the driver and timeout because the eventual phase change * will cause the ESP to (eventually) give an interrupt. */ - save_and_cli(flags); if(esp->current_SC == SCptr) { esp->cur_msgout[0] = ABORT; esp->msgout_len = 1; esp->msgout_ctr = 0; esp_cmd(esp, eregs, ESP_CMD_SATN); - restore_flags(flags); return SCSI_ABORT_PENDING; } - restore_flags(flags); /* If it is still in the issue queue then we can safely * call the completion routine and report abort success. @@ -1431,6 +1399,7 @@ int esp_abort(Scsi_Cmnd *SCptr) if(this == SCptr) { *prev = (Scsi_Cmnd *) this->host_scribble; this->host_scribble = NULL; + esp_release_dmabufs(esp, this); this->result = DID_ABORT << 16; this->done(this); if(don) @@ -1445,8 +1414,11 @@ int esp_abort(Scsi_Cmnd *SCptr) * on the bus at this time. So, we let the SCSI code wait * a little bit and try again later. */ - if(esp->current_SC) + if(esp->current_SC) { + if(don) + esp->dma_ints_on(esp); return SCSI_ABORT_BUSY; + } /* It's disconnected, we have to reconnect to re-establish * the nexus and tell the device to abort. However, we really @@ -1455,20 +1427,68 @@ int esp_abort(Scsi_Cmnd *SCptr) * happens, we are really hung so reset the bus. */ + if(don) + esp->dma_ints_on(esp); return SCSI_ABORT_SNOOZE; } +/* We've sent ESP_CMD_RS to the ESP, the interrupt had just + * arrived indicating the end of the SCSI bus reset. Our job + * is to clean out the command queues and begin re-execution + * of SCSI commands once more. + */ +static int esp_finish_reset(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + Scsi_Cmnd *sp = esp->current_SC; + + /* Clean up currently executing command, if any. */ + if (sp != NULL) { + esp_release_dmabufs(esp, sp); + sp->result = (DID_RESET << 16); + sp->scsi_done(sp); + esp->current_SC = NULL; + } + + /* Clean up disconnected queue, they have been invalidated + * by the bus reset. + */ + if (esp->disconnected_SC) { + while((sp = remove_first_SC(&esp->disconnected_SC)) != NULL) { + esp_release_dmabufs(esp, sp); + sp->result = (DID_RESET << 16); + sp->scsi_done(sp); + } + } + + /* SCSI bus reset is complete. */ + esp->resetting_bus = 0; + + /* Ok, now it is safe to get commands going once more. */ + if(esp->issue_SC) + esp_exec_cmd(esp); + + return do_intr_end; +} + +static int esp_do_resetbus(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + ESPLOG(("esp%d: Resetting scsi bus\n", esp->esp_id)); + esp->resetting_bus = 1; + esp_cmd(esp, eregs, ESP_CMD_RS); + + return do_intr_end; +} + /* Reset ESP chip, reset hanging bus, then kill active and * disconnected commands for targets without soft reset. */ int esp_reset(Scsi_Cmnd *SCptr, unsigned int how) { struct NCR_ESP *esp = (struct NCR_ESP *) SCptr->host->hostdata; - struct ESP_regs *eregs = esp->eregs; - ESPLOG(("esp%d: Resetting scsi bus\n", esp->esp_id)); - esp->resetting_bus = 1; - esp_cmd(esp, eregs, ESP_CMD_RS); + (void) esp_do_resetbus(esp, esp->eregs); return SCSI_RESET_PENDING; } @@ -1478,36 +1498,15 @@ static void esp_done(struct NCR_ESP *esp, int error) Scsi_Cmnd *done_SC; if(esp->current_SC) { - unsigned long flags; - done_SC = esp->current_SC; esp->current_SC = NULL; - - /* Free dvma entry. */ - if(!done_SC->use_sg) { - if (esp->dma_mmu_release_scsi_one) - esp->dma_mmu_release_scsi_one (esp, done_SC); - } else { -#ifdef DEBUG_ESP_SG - printk("esp%d: unmapping sg ", esp->esp_id); -#endif - if (esp->dma_mmu_release_scsi_sgl) - esp->dma_mmu_release_scsi_sgl (esp, done_SC); -#ifdef DEBUG_ESP_SG - printk("done.\n"); -#endif - } - + esp_release_dmabufs(esp, done_SC); done_SC->result = error; done_SC->scsi_done(done_SC); - save_and_cli(flags); - /* Bus is free, issue any commands in the queue. */ if(esp->issue_SC && !esp->current_SC) esp_exec_cmd(esp); - - restore_flags(flags); } else { /* Panic is safe as current_SC is null so we may still * be able to accept more commands to sync disk buffers. @@ -1519,11 +1518,6 @@ static void esp_done(struct NCR_ESP *esp, int error) /* Wheee, ESP interrupt engine. */ -enum { - do_phase_determine, do_reset_bus, do_reset_complete, - do_work_bus, do_intr_end, -}; - /* Forward declarations. */ static int esp_do_phase_determine(struct NCR_ESP *esp, struct ESP_regs *eregs); @@ -1535,67 +1529,8 @@ static int esp_do_msgindone(struct NCR_ESP *esp, struct ESP_regs *eregs); static int esp_do_msgout(struct NCR_ESP *esp, struct ESP_regs *eregs); static int esp_do_cmdbegin(struct NCR_ESP *esp, struct ESP_regs *eregs); -static inline int sreg_datainp(unchar sreg) -{ - return (sreg & ESP_STAT_PMASK) == ESP_DIP; -} - -static inline int sreg_dataoutp(unchar sreg) -{ - return (sreg & ESP_STAT_PMASK) == ESP_DOP; -} - -/* Did they drop these fabs on the floor or what?!?!! */ -static inline void hme_fifo_hwbug_workaround(struct NCR_ESP *esp, - struct ESP_regs *eregs) -{ - unchar status = esp->sreg; - - /* Cannot safely frob the fifo for these following cases. */ - if(sreg_datainp(status) || sreg_dataoutp(status) || - (esp->current_SC && esp->current_SC->SCp.phase == in_data_done)) { - ESPHME(("")); - return; - } else { - unsigned long count = 0; - unsigned long fcnt = GETREG(eregs->esp_fflags) & ESP_FF_FBYTES; - - /* The HME stores bytes in multiples of 2 in the fifo. */ - ESPHME(("hme_fifo[fcnt=%d", (int)fcnt)); - while(fcnt) { - esp->hme_fifo_workaround_buffer[count++] = - GETREG(eregs->esp_fdata); - esp->hme_fifo_workaround_buffer[count++] = - GETREG(eregs->esp_fdata); - ESPHME(("<%02x,%02x>", esp->hme_fifo_workaround_buffer[count-2], esp->hme_fifo_workaround_buffer[count-1])); - fcnt--; - } - if(GETREG(eregs->esp_status2) & ESP_STAT2_F1BYTE) { - ESPHME(("")); - SETREG(eregs->esp_fdata, 0); - esp->hme_fifo_workaround_buffer[count++] = - GETREG(eregs->esp_fdata); - ESPHME(("<%02x,0x00>", esp->hme_fifo_workaround_buffer[count-1])); - ESPHME(("CMD_FLUSH")); - esp_cmd(esp, eregs, ESP_CMD_FLUSH); - } else { - ESPHME(("no_xtra_byte")); - } - esp->hme_fifo_workaround_count = count; - ESPHME(("wkarnd_cnt=%d]", (int)count)); - } -} - -static inline void hme_fifo_push(struct NCR_ESP *esp, struct ESP_regs *eregs, - unchar *bytes, unchar count) -{ - esp_cmd(esp, eregs, ESP_CMD_FLUSH); - while(count) { - SETREG(eregs->esp_fdata, *bytes++); - SETREG(eregs->esp_fdata, 0); - count--; - } -} +#define sreg_datainp(__sreg) (((__sreg) & ESP_STAT_PMASK) == ESP_DIP) +#define sreg_dataoutp(__sreg) (((__sreg) & ESP_STAT_PMASK) == ESP_DOP) /* We try to avoid some interrupts by jumping ahead and see if the ESP * has gotten far enough yet. Hence the following. @@ -1608,20 +1543,8 @@ static inline int skipahead1(struct NCR_ESP *esp, struct ESP_regs *eregs, if(esp->dma_irq_p(esp)) { /* Yes, we are able to save an interrupt. */ - esp->sreg = GETREG(eregs->esp_status); - if(esp->erev == fashme) { - /* This chip is really losing. */ - ESPHME(("HME[")); - /* Must latch fifo before reading the interrupt - * register else garbage ends up in the FIFO - * which confuses the driver utterly. - * Happy Meal indeed.... - */ - ESPHME(("fifo_workaround]")); - hme_fifo_hwbug_workaround(esp, eregs); - } - esp->ireg = GETREG(eregs->esp_intrpt); - esp->sreg &= ~(ESP_STAT_INTR); + esp->sreg = (esp_read(eregs->esp_status) & ~(ESP_STAT_INTR)); + esp->ireg = esp_read(eregs->esp_intrpt); if(!(esp->ireg & ESP_INTR_SR)) return 0; else @@ -1642,21 +1565,8 @@ static inline int skipahead2(struct NCR_ESP *esp, return 0; if(esp->dma_irq_p(esp)) { /* Yes, we are able to save an interrupt. */ - esp->sreg = GETREG(eregs->esp_status); - if(esp->erev == fashme) { - /* This chip is really losing. */ - ESPHME(("HME[")); - - /* Must latch fifo before reading the interrupt - * register else garbage ends up in the FIFO - * which confuses the driver utterly. - * Happy Meal indeed.... - */ - ESPHME(("fifo_workaround]")); - hme_fifo_hwbug_workaround(esp, eregs); - } - esp->ireg = GETREG(eregs->esp_intrpt); - esp->sreg &= ~(ESP_STAT_INTR); + esp->sreg = (esp_read(eregs->esp_status) & ~(ESP_STAT_INTR)); + esp->ireg = esp_read(eregs->esp_intrpt); if(!(esp->ireg & ESP_INTR_SR)) return 0; else @@ -1667,56 +1577,31 @@ static inline int skipahead2(struct NCR_ESP *esp, return do_intr_end; } -/* Misc. esp helper routines. */ -static inline void esp_setcount(struct ESP_regs *eregs, int cnt, int hme) -{ - SETREG(eregs->esp_tclow, (cnt & 0xff)); - SETREG(eregs->esp_tcmed, ((cnt >> 8) & 0xff)); - if(hme) { - SETREG(eregs->fas_rlo, 0); - SETREG(eregs->fas_rhi, 0); - } -} +/* Misc. esp helper macros. */ +#define esp_setcount(__eregs, __cnt) \ + esp_write((__eregs)->esp_tclow, ((__cnt) & 0xff)); \ + esp_write((__eregs)->esp_tcmed, (((__cnt) >> 8) & 0xff)) -static inline int esp_getcount(struct ESP_regs *eregs) -{ - return (((GETREG(eregs->esp_tclow))&0xff) | - (((GETREG(eregs->esp_tcmed))&0xff) << 8)); -} +#define esp_getcount(__eregs) \ + ((esp_read((__eregs)->esp_tclow)&0xff) | \ + ((esp_read((__eregs)->esp_tcmed)&0xff) << 8)) -static inline int fcount(struct NCR_ESP *esp, struct ESP_regs *eregs) -{ - if(esp->erev == fashme) - return esp->hme_fifo_workaround_count; - else - return GETREG(eregs->esp_fflags) & ESP_FF_FBYTES; -} +#define fcount(__esp, __eregs) \ + (esp_read((__eregs)->esp_fflags) & ESP_FF_FBYTES) -static inline int fnzero(struct NCR_ESP *esp, struct ESP_regs *eregs) -{ - if(esp->erev == fashme) - return 0; - else - return GETREG(eregs->esp_fflags) & ESP_FF_ONOTZERO; -} +#define fnzero(__esp, __eregs) \ + (esp_read((__eregs)->esp_fflags) & ESP_FF_ONOTZERO) /* XXX speculative nops unnecessary when continuing amidst a data phase * XXX even on esp100!!! another case of flooding the bus with I/O reg * XXX writes... */ -static inline void esp_maybe_nop(struct NCR_ESP *esp, struct ESP_regs *eregs) -{ - if(esp->erev == esp100) - esp_cmd(esp, eregs, ESP_CMD_NULL); -} +#define esp_maybe_nop(__esp, __eregs) \ + if((__esp)->erev == esp100) \ + esp_cmd((__esp), (__eregs), ESP_CMD_NULL) -static inline int sreg_to_dataphase(unchar sreg) -{ - if((sreg & ESP_STAT_PMASK) == ESP_DOP) - return in_dataout; - else - return in_datain; -} +#define sreg_to_dataphase(__sreg) \ + ((((__sreg) & ESP_STAT_PMASK) == ESP_DOP) ? in_dataout : in_datain) /* The ESP100 when in synchronous data phase, can mistake a long final * REQ pulse from the target as an extra byte, it places whatever is on @@ -1731,8 +1616,8 @@ static inline int esp100_sync_hwbug(struct NCR_ESP *esp, struct ESP_regs *eregs, { /* Do not touch this piece of code. */ if((!(esp->erev == esp100)) || - (!(sreg_datainp((esp->sreg = GETREG(eregs->esp_status))) && !fifocnt) - && !(sreg_dataoutp(esp->sreg) && !fnzero(esp, eregs)))) { + (!(sreg_datainp((esp->sreg = esp_read(eregs->esp_status))) && !fifocnt) && + !(sreg_dataoutp(esp->sreg) && !fnzero(esp, eregs)))) { if(sp->SCp.phase == in_dataout) esp_cmd(esp, eregs, ESP_CMD_FLUSH); return 0; @@ -1759,7 +1644,7 @@ static inline int esp100_reconnect_hwbug(struct NCR_ESP *esp, if(esp->erev != esp100) return 0; - junk = GETREG(eregs->esp_intrpt); + junk = esp_read(eregs->esp_intrpt); if(junk & ESP_INTR_SR) return 1; @@ -1775,24 +1660,14 @@ static inline int reconnect_target(struct NCR_ESP *esp, struct ESP_regs *eregs) if(2 != fcount(esp, eregs)) return -1; - if(esp->erev == fashme) { - /* HME does not latch it's own BUS ID bits during - * a reselection. Also the target number is given - * as an unsigned char, not as a sole bit number - * like the other ESP's do. - * Happy Meal indeed.... - */ - targ = esp->hme_fifo_workaround_buffer[0]; - } else { - it = GETREG(eregs->esp_fdata); - if(!(it & me)) - return -1; - it &= ~me; - if(it & (it - 1)) - return -1; - while(!(it & 1)) - targ++, it >>= 1; - } + it = esp_read(eregs->esp_fdata); + if(!(it & me)) + return -1; + it &= ~me; + if(it & (it - 1)) + return -1; + while(!(it & 1)) + targ++, it >>= 1; return targ; } @@ -1805,14 +1680,20 @@ static inline int reconnect_lun(struct NCR_ESP *esp, struct ESP_regs *eregs) if((esp->sreg & ESP_STAT_PMASK) != ESP_MIP) return -1; - if(esp->erev == fashme) - lun = esp->hme_fifo_workaround_buffer[1]; - else - lun = GETREG(eregs->esp_fdata); + lun = esp_read(eregs->esp_fdata); + + /* Yes, you read this correctly. We report lun of zero + * if we see parity error. ESP reports parity error for + * the lun byte, and this is the only way to hope to recover + * because the target is connected. + */ if(esp->sreg & ESP_STAT_PERR) return 0; + + /* Check for illegal bits being set in the lun. */ if((lun & 0x40) || !(lun & 0x80)) return -1; + return lun & 7; } @@ -1823,13 +1704,20 @@ static inline void esp_connect(struct NCR_ESP *esp, struct ESP_regs *eregs, Scsi_Cmnd *sp) { Scsi_Device *dp = sp->device; - SETREG(eregs->esp_soff, dp->sync_max_offset); - SETREG(eregs->esp_stp, dp->sync_min_period); - if(esp->erev > esp100a) - SETREG(eregs->esp_cfg3, esp->config3[sp->target]); - if(esp->erev == fashme) - SETREG(eregs->esp_busid, (sp->target & 0xf) | - (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT)); + + if(esp->prev_soff != dp->sync_max_offset || + esp->prev_stp != dp->sync_min_period || + (esp->erev > esp100a && + esp->prev_cfg3 != esp->config3[sp->target])) { + esp->prev_soff = dp->sync_max_offset; + esp_write(eregs->esp_soff, esp->prev_soff); + esp->prev_stp = dp->sync_min_period; + esp_write(eregs->esp_stp, esp->prev_stp); + if(esp->erev > esp100a) { + esp->prev_cfg3 = esp->config3[sp->target]; + esp_write(eregs->esp_cfg3, esp->prev_cfg3); + } + } esp->current_SC = sp; } @@ -1839,8 +1727,8 @@ static inline void esp_connect(struct NCR_ESP *esp, struct ESP_regs *eregs, static inline void esp_reconnect(struct NCR_ESP *esp, Scsi_Cmnd *sp) { if(!esp->disconnected_SC) - printk("esp%d: Weird, being reselected but disconnected " - "command queue is empty.\n", esp->esp_id); + ESPLOG(("esp%d: Weird, being reselected but disconnected " + "command queue is empty.\n", esp->esp_id)); esp->snip = 0; esp->current_SC = 0; sp->SCp.phase = not_issued; @@ -1848,12 +1736,9 @@ static inline void esp_reconnect(struct NCR_ESP *esp, Scsi_Cmnd *sp) } /* Begin message in phase. */ -static inline int esp_do_msgin(struct NCR_ESP *esp, struct ESP_regs *eregs) +static int esp_do_msgin(struct NCR_ESP *esp, struct ESP_regs *eregs) { - /* Must be very careful with the fifo on the HME */ - if((esp->erev != fashme) || !(GETREG(eregs->esp_status2) & - ESP_STAT2_FEMPTY)) - esp_cmd(esp, eregs, ESP_CMD_FLUSH); + esp_cmd(esp, eregs, ESP_CMD_FLUSH); esp_maybe_nop(esp, eregs); esp_cmd(esp, eregs, ESP_CMD_TI); esp->msgin_len = 1; @@ -1867,10 +1752,10 @@ static inline void advance_sg(struct NCR_ESP *esp, Scsi_Cmnd *sp) ++sp->SCp.buffer; --sp->SCp.buffers_residual; sp->SCp.this_residual = sp->SCp.buffer->length; - if (esp->dma_advance_sg) - esp->dma_advance_sg (sp); - else - sp->SCp.ptr = (char *)virt_to_phys(sp->SCp.buffer->address); + if (esp->dma_advance_sg) + esp->dma_advance_sg (sp); + else + sp->SCp.ptr = (char *)virt_to_phys(sp->SCp.buffer->address); } /* Please note that the way I've coded these routines is that I _always_ @@ -1891,8 +1776,7 @@ static inline void advance_sg(struct NCR_ESP *esp, Scsi_Cmnd *sp) * within a buffer or sub-buffer should not upset us at all no matter * how bad the target and/or ESP fucks things up. */ - -static inline int esp_do_data(struct NCR_ESP *esp, struct ESP_regs *eregs) +static int esp_do_data(struct NCR_ESP *esp, struct ESP_regs *eregs) { Scsi_Cmnd *SCptr = esp->current_SC; int thisphase, hmuch; @@ -1903,31 +1787,204 @@ static inline int esp_do_data(struct NCR_ESP *esp, struct ESP_regs *eregs) esp_advance_phase(SCptr, thisphase); ESPDATA(("newphase<%s> ", (thisphase == in_datain) ? "DATAIN" : "DATAOUT")); hmuch = esp->dma_can_transfer(esp, SCptr); - ESPDATA(("hmuch<%d> ", hmuch)); - esp->current_transfer_size = hmuch; - if(esp->erev == fashme) { - /* Touchy chip, this stupid HME scsi adapter... */ - esp_setcount(eregs, hmuch, 1); - esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI); - if(thisphase == in_datain) - esp->dma_init_read(esp, (__u32)((unsigned long)SCptr->SCp.ptr), hmuch); - else - esp->dma_init_write(esp, (__u32)((unsigned long)SCptr->SCp.ptr), hmuch); - } else { - esp_setcount(eregs, hmuch, 0); - esp->dma_setup(esp, - (__u32)((unsigned long)SCptr->SCp.ptr), + /* + * XXX MSch: cater for PIO transfer here; PIO used if hmuch == 0 + */ + if (hmuch) { /* DMA */ + /* + * DMA + */ + ESPDATA(("hmuch<%d> ", hmuch)); + esp->current_transfer_size = hmuch; + esp_setcount(eregs, (esp->fas_premature_intr_workaround ? + (hmuch + 0x40) : hmuch)); + esp->dma_setup(esp, (__u32)((unsigned long)SCptr->SCp.ptr), hmuch, (thisphase == in_datain)); ESPDATA(("DMA|TI --> do_intr_end\n")); esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI); - } + return do_intr_end; + /* + * end DMA + */ + } else { + /* + * PIO + */ + int oldphase, i = 0; /* or where we left off last time ?? esp->current_data ?? */ + int fifocnt = 0; + + oldphase = esp_read(eregs->esp_status) & ESP_STAT_PMASK; + + /* + * polled transfer; ugly, can we make this happen in a DRQ + * interrupt handler ?? + * requires keeping track of state information in host or + * command struct! + * Problem: I've never seen a DRQ happen on Mac, not even + * with ESP_CMD_DMA ... + */ + + /* figure out how much needs to be transfered */ + hmuch = SCptr->SCp.this_residual; + ESPDATA(("hmuch<%d> pio ", hmuch)); + esp->current_transfer_size = hmuch; + + /* tell the ESP ... */ + esp_setcount(eregs, hmuch); + + /* loop */ + while (hmuch) { + int j, fifo_stuck = 0, newphase; + unsigned long flags, timeout; +#if 0 + if ( i % 10 ) + ESPDATA(("\r")); + else + ESPDATA(( /*"\n"*/ "\r")); +#endif + save_flags(flags); +#if 0 + cli(); +#endif + if(thisphase == in_datain) { + /* 'go' ... */ + esp_cmd(esp, eregs, ESP_CMD_TI); + + /* wait for data */ + timeout = 1000000; + while (!((esp->sreg=esp_read(eregs->esp_status)) & ESP_STAT_INTR) && --timeout) + udelay(2); + if (timeout == 0) + printk("DRQ datain timeout! \n"); + + newphase = esp->sreg & ESP_STAT_PMASK; + + /* see how much we got ... */ + fifocnt = (esp_read(eregs->esp_fflags) & ESP_FF_FBYTES); + + if (!fifocnt) + fifo_stuck++; + else + fifo_stuck = 0; + + ESPDATA(("\rgot %d st %x ph %x", fifocnt, esp->sreg, newphase)); + + /* read fifo */ + for(j=0;jSCp.ptr[i++] = esp_read(eregs->esp_fdata); + + ESPDATA(("(%d) ", i)); + + /* how many to go ?? */ + hmuch -= fifocnt; + + /* break if status phase !! */ + if(newphase == ESP_STATP) { + /* clear int. */ + esp->ireg = esp_read(eregs->esp_intrpt); + break; + } + } else { +#define MAX_FIFO 8 + /* how much will fit ? */ + int this_count = MAX_FIFO - fifocnt; + if (this_count > hmuch) + this_count = hmuch; + + /* fill fifo */ + for(j=0;jesp_fdata, SCptr->SCp.ptr[i++]); + + /* how many left if this goes out ?? */ + hmuch -= this_count; + + /* 'go' ... */ + esp_cmd(esp, eregs, ESP_CMD_TI); + + /* wait for 'got it' */ + timeout = 1000000; + while (!((esp->sreg=esp_read(eregs->esp_status)) & ESP_STAT_INTR) && --timeout) + udelay(2); + if (timeout == 0) + printk("DRQ dataout timeout! \n"); + + newphase = esp->sreg & ESP_STAT_PMASK; + + /* need to check how much was sent ?? */ + fifocnt = (esp_read(eregs->esp_fflags) & ESP_FF_FBYTES); + + ESPDATA(("\rsent %d st %x ph %x", this_count - fifocnt, esp->sreg, newphase)); + + ESPDATA(("(%d) ", i)); + + /* break if status phase !! */ + if(newphase == ESP_STATP) { + /* clear int. */ + esp->ireg = esp_read(eregs->esp_intrpt); + break; + } + + } + + /* clear int. */ + esp->ireg = esp_read(eregs->esp_intrpt); + + ESPDATA(("ir %x ... ", esp->ireg)); + + if (hmuch == 0) + ESPDATA(("done! \n")); + + restore_flags(flags); + + /* check new bus phase */ + if (newphase != oldphase && i < esp->current_transfer_size) { + /* something happened; disconnect ?? */ + ESPDATA(("phase change, dropped out with %d done ... ", i)); + break; + } + + /* check int. status */ + if (esp->ireg & ESP_INTR_DC) { + /* disconnect */ + ESPDATA(("disconnect; %d transfered ... ", i)); + break; + } else if (esp->ireg & ESP_INTR_FDONE) { + /* function done */ + ESPDATA(("function done; %d transfered ... ", i)); + break; + } + + /* XXX fixme: bail out on stall */ + if (fifo_stuck > 10) { + /* we're stuck */ + ESPDATA(("fifo stall; %d transfered ... ", i)); + break; + } + } + + ESPDATA(("\n")); + /* check successful completion ?? */ + + if (thisphase == in_dataout) + hmuch += fifocnt; /* stuck?? adjust data pointer ...*/ + + /* tell do_data_finale how much was transfered */ + esp->current_transfer_size -= hmuch; + + /* still not completely sure on this one ... */ + return /*do_intr_end*/ do_work_bus /*do_phase_determine*/ ; + + /* + * end PIO + */ + } return do_intr_end; } /* See how successful the data transfer was. */ -static inline int esp_do_data_finale(struct NCR_ESP *esp, - struct ESP_regs *eregs) +static int esp_do_data_finale(struct NCR_ESP *esp, + struct ESP_regs *eregs) { Scsi_Cmnd *SCptr = esp->current_SC; int bogus_data = 0, bytes_sent = 0, fifocnt, ecount = 0; @@ -1963,77 +2020,83 @@ static inline int esp_do_data_finale(struct NCR_ESP *esp, return esp_do_phase_determine(esp, eregs); } - /* Check for partial transfers and other horrible events. - * Note, here we read the real fifo flags register even - * on HME broken adapters because we skip the HME fifo - * workaround code in esp_handle() if we are doing data - * phase things. We don't want to fuck directly with - * the fifo like that, especially if doing syncronous - * transfers! Also, will need to double the count on - * HME if we are doing wide transfers, as the HME fifo - * will move and count 16-bit quantities during wide data. - * SMCC _and_ Qlogic can both bite me. - */ - fifocnt = GETREG(eregs->esp_fflags) & ESP_FF_FBYTES; - if(esp->erev != fashme) - ecount = esp_getcount(eregs); + /* Check for partial transfers and other horrible events. */ + fifocnt = (esp_read(eregs->esp_fflags) & ESP_FF_FBYTES); + ecount = esp_getcount(eregs); + if(esp->fas_premature_intr_workaround) + ecount -= 0x40; bytes_sent = esp->current_transfer_size; - /* Uhhh, might not want both of these conditionals to run - * at once on HME due to the fifo problems it has. Consider - * changing it to: - * - * if(!(esp->sreg & ESP_STAT_TCNT)) { - * bytes_sent -= ecount; - * } else if(SCptr->SCp.phase == in_dataout) { - * bytes_sent -= fifocnt; - * } - * - * But only for the HME case, leave the current code alone - * for all other ESP revisions as we know the existing code - * works just fine for them. - */ ESPDATA(("trans_sz=%d, ", bytes_sent)); - if(esp->erev == fashme) { - if(!(esp->sreg & ESP_STAT_TCNT)) { - bytes_sent -= esp_getcount(eregs); - } else if(SCptr->SCp.phase == in_dataout) { - bytes_sent -= fifocnt; - } - } else { - if(!(esp->sreg & ESP_STAT_TCNT)) - bytes_sent -= ecount; - if(SCptr->SCp.phase == in_dataout) - bytes_sent -= fifocnt; - } + if(!(esp->sreg & ESP_STAT_TCNT)) + bytes_sent -= ecount; + if(SCptr->SCp.phase == in_dataout) + bytes_sent -= fifocnt; - ESPDATA(("bytes_sent=%d, ", bytes_sent)); + ESPDATA(("bytes_sent=%d (ecount=%d, fifocnt=%d), ", bytes_sent, + ecount, fifocnt)); /* If we were in synchronous mode, check for peculiarities. */ - if(esp->erev == fashme) { - if(SCptr->device->sync_max_offset) { - if(SCptr->SCp.phase == in_dataout) - esp_cmd(esp, eregs, ESP_CMD_FLUSH); - } else { - esp_cmd(esp, eregs, ESP_CMD_FLUSH); - } - } else { - if(SCptr->device->sync_max_offset) - bogus_data = esp100_sync_hwbug(esp, eregs, SCptr, fifocnt); - else - esp_cmd(esp, eregs, ESP_CMD_FLUSH); - } + if(SCptr->device->sync_max_offset) + bogus_data = esp100_sync_hwbug(esp, eregs, SCptr, fifocnt); + else + esp_cmd(esp, eregs, ESP_CMD_FLUSH); /* Until we are sure of what has happened, we are certainly * in the dark. */ esp_advance_phase(SCptr, in_the_dark); + /* Check for premature interrupt condition. Can happen on FAS2x6 + * chips. QLogic recommends a workaround by overprogramming the + * transfer counters, but this makes doing scatter-gather impossible. + * Until there is a way to disable scatter-gather for a single target, + * and not only for the entire host adapter as it is now, the workaround + * is way to expensive performance wise. + * Instead, it turns out that when this happens the target has disconnected + * allready but it doesn't show in the interrupt register. Compensate for + * that here to try and avoid a SCSI bus reset. + */ + if(!esp->fas_premature_intr_workaround && (fifocnt == 1) && + sreg_dataoutp(esp->sreg)) { + ESPLOG(("esp%d: Premature interrupt, enabling workaround\n", + esp->esp_id)); +#if 0 + /* Disable scatter-gather operations, they are not possible + * when using this workaround. + */ + esp->ehost->sg_tablesize = 0; + esp->ehost->use_clustering = ENABLE_CLUSTERING; + esp->fas_premature_intr_workaround = 1; + bytes_sent = 0; + if(SCptr->use_sg) { + ESPLOG(("esp%d: Aborting scatter-gather operation\n", + esp->esp_id)); + esp->cur_msgout[0] = ABORT; + esp->msgout_len = 1; + esp->msgout_ctr = 0; + esp_cmd(esp, eregs, ESP_CMD_SATN); + esp_setcount(eregs, 0xffff); + esp_cmd(esp, eregs, ESP_CMD_NULL); + esp_cmd(esp, eregs, ESP_CMD_TPAD | ESP_CMD_DMA); + return do_intr_end; + } +#else + /* Just set the disconnected bit. That's what appears to + * happen anyway. The state machine will pick it up when + * we return. + */ + esp->ireg |= ESP_INTR_DC; +#endif + } + if(bytes_sent < 0) { /* I've seen this happen due to lost state in this * driver. No idea why it happened, but allowing * this value to be negative caused things to * lock up. This allows greater chance of recovery. + * In fact every time I've seen this, it has been + * a driver bug without question. */ ESPLOG(("esp%d: yieee, bytes_sent < 0!\n", esp->esp_id)); ESPLOG(("esp%d: csz=%d fifocount=%d ecount=%d\n", @@ -2042,6 +2105,10 @@ static inline int esp_do_data_finale(struct NCR_ESP *esp, ESPLOG(("esp%d: use_sg=%d ptr=%p this_residual=%d\n", esp->esp_id, SCptr->use_sg, SCptr->SCp.ptr, SCptr->SCp.this_residual)); + ESPLOG(("esp%d: Forcing async for target %d\n", esp->esp_id, + SCptr->target)); + SCptr->device->borken = 1; + SCptr->device->sync = 0; bytes_sent = 0; } @@ -2050,7 +2117,7 @@ static inline int esp_do_data_finale(struct NCR_ESP *esp, SCptr->SCp.this_residual -= bytes_sent; if(SCptr->SCp.this_residual < 0) { /* shit */ - printk("esp%d: Data transfer overrun.\n", esp->esp_id); + ESPLOG(("esp%d: Data transfer overrun.\n", esp->esp_id)); SCptr->SCp.this_residual = 0; } @@ -2066,13 +2133,15 @@ static inline int esp_do_data_finale(struct NCR_ESP *esp, * imagine the hell I went through trying to * figure this out. */ - if(SCptr->use_sg && !SCptr->SCp.this_residual) + if(!SCptr->SCp.this_residual && SCptr->SCp.buffers_residual) advance_sg(esp, SCptr); +#ifdef DEBUG_ESP_DATA if(sreg_datainp(esp->sreg) || sreg_dataoutp(esp->sreg)) { ESPDATA(("to more data\n")); - return esp_do_data(esp, eregs); + } else { + ESPDATA(("to new phase\n")); } - ESPDATA(("to new phase\n")); +#endif return esp_do_phase_determine(esp, eregs); } /* Bogus data, just wait for next interrupt. */ @@ -2081,10 +2150,48 @@ static inline int esp_do_data_finale(struct NCR_ESP *esp, return do_intr_end; } +/* We received a non-good status return at the end of + * running a SCSI command. This is used to decide if + * we should clear our synchronous transfer state for + * such a device when that happens. + * + * The idea is that when spinning up a disk or rewinding + * a tape, we don't want to go into a loop re-negotiating + * synchronous capabilities over and over. + */ +static int esp_should_clear_sync(Scsi_Cmnd *sp) +{ + unchar cmd1 = sp->cmnd[0]; + unchar cmd2 = sp->data_cmnd[0]; + + /* These cases are for spinning up a disk and + * waiting for that spinup to complete. + */ + if(cmd1 == START_STOP || + cmd2 == START_STOP) + return 0; + + if(cmd1 == TEST_UNIT_READY || + cmd2 == TEST_UNIT_READY) + return 0; + + /* One more special case for SCSI tape drives, + * this is what is used to probe the device for + * completion of a rewind or tape load operation. + */ + if(sp->device->type == TYPE_TAPE) { + if(cmd1 == MODE_SENSE || + cmd2 == MODE_SENSE) + return 0; + } + + return 1; +} + /* Either a command is completing or a target is dropping off the bus * to continue the command in the background so we can do other work. */ -static inline int esp_do_freebus(struct NCR_ESP *esp, struct ESP_regs *eregs) +static int esp_do_freebus(struct NCR_ESP *esp, struct ESP_regs *eregs) { Scsi_Cmnd *SCptr = esp->current_SC; int rval; @@ -2101,12 +2208,14 @@ static inline int esp_do_freebus(struct NCR_ESP *esp, struct ESP_regs *eregs) esp->prevmsgout = NOP; if(esp->prevmsgin == COMMAND_COMPLETE) { /* Normal end of nexus. */ - if(esp->disconnected_SC || (esp->erev == fashme)) + if(esp->disconnected_SC) esp_cmd(esp, eregs, ESP_CMD_ESEL); - if(SCptr->SCp.Status != GOOD && SCptr->SCp.Status != CONDITION_GOOD && + if(SCptr->SCp.Status != GOOD && + SCptr->SCp.Status != CONDITION_GOOD && ((1<target) & esp->targets_present) && - SCptr->device->sync && SCptr->device->sync_max_offset) { + SCptr->device->sync && + SCptr->device->sync_max_offset) { /* SCSI standard says that the synchronous capabilities * should be renegotiated at this point. Most likely * we are about to request sense from this target @@ -2123,15 +2232,7 @@ static inline int esp_do_freebus(struct NCR_ESP *esp, struct ESP_regs *eregs) * can report not ready many times right after * loading up a tape. */ - if(SCptr->cmnd[0] != START_STOP && - SCptr->data_cmnd[0] != START_STOP && - SCptr->cmnd[0] != TEST_UNIT_READY && - SCptr->data_cmnd[0] != TEST_UNIT_READY && - !(SCptr->device->type == TYPE_TAPE && - (SCptr->cmnd[0] == TEST_UNIT_READY || - SCptr->data_cmnd[0] == TEST_UNIT_READY || - SCptr->cmnd[0] == MODE_SENSE || - SCptr->data_cmnd[0] == MODE_SENSE))) + if(esp_should_clear_sync(SCptr) != 0) SCptr->device->sync = 0; } ESPDISC(("F<%02x,%02x>", SCptr->target, SCptr->lun)); @@ -2158,9 +2259,43 @@ static inline int esp_do_freebus(struct NCR_ESP *esp, struct ESP_regs *eregs) return do_intr_end; } +/* When a reselect occurs, and we cannot find the command to + * reconnect to in our queues, we do this. + */ +static int esp_bad_reconnect(struct NCR_ESP *esp) +{ + Scsi_Cmnd *sp; + + ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n", + esp->esp_id)); + ESPLOG(("QUEUE DUMP\n")); + sp = esp->issue_SC; + ESPLOG(("esp%d: issue_SC[", esp->esp_id)); + while(sp) { + ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); + sp = (Scsi_Cmnd *) sp->host_scribble; + } + ESPLOG(("]\n")); + sp = esp->current_SC; + ESPLOG(("esp%d: current_SC[", esp->esp_id)); + while(sp) { + ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); + sp = (Scsi_Cmnd *) sp->host_scribble; + } + ESPLOG(("]\n")); + sp = esp->disconnected_SC; + ESPLOG(("esp%d: disconnected_SC[", esp->esp_id)); + while(sp) { + ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); + sp = (Scsi_Cmnd *) sp->host_scribble; + } + ESPLOG(("]\n")); + return do_reset_bus; +} + /* Do the needy when a target tries to reconnect to us. */ -static inline int esp_do_reconnect(struct NCR_ESP *esp, - struct ESP_regs *eregs) +static int esp_do_reconnect(struct NCR_ESP *esp, + struct ESP_regs *eregs) { int lun, target; Scsi_Cmnd *SCptr; @@ -2180,53 +2315,21 @@ static inline int esp_do_reconnect(struct NCR_ESP *esp, /* Things look ok... */ ESPDISC(("R<%02x,%02x>", target, lun)); - /* Must flush both FIFO and the DVMA on HME. */ - if(esp->erev == fashme) { - /* XXX this still doesn't fix the problem... */ - esp_cmd(esp, eregs, ESP_CMD_FLUSH); - if(esp->dma_invalidate) - esp->dma_invalidate(esp); - } else { - esp_cmd(esp, eregs, ESP_CMD_FLUSH); - if(esp100_reconnect_hwbug(esp, eregs)) - return do_reset_bus; - esp_cmd(esp, eregs, ESP_CMD_NULL); - } + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + if(esp100_reconnect_hwbug(esp, eregs)) + return do_reset_bus; + esp_cmd(esp, eregs, ESP_CMD_NULL); SCptr = remove_SC(&esp->disconnected_SC, (unchar) target, (unchar) lun); - if(!SCptr) { - Scsi_Cmnd *sp; + if(!SCptr) + return esp_bad_reconnect(esp); - ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n", - esp->esp_id)); - ESPLOG(("QUEUE DUMP\n")); - sp = esp->issue_SC; - ESPLOG(("esp%d: issue_SC[", esp->esp_id)); - while(sp) { - ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); - sp = (Scsi_Cmnd *) sp->host_scribble; - } - ESPLOG(("]\n")); - sp = esp->current_SC; - ESPLOG(("esp%d: current_SC[", esp->esp_id)); - while(sp) { - ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); - sp = (Scsi_Cmnd *) sp->host_scribble; - } - ESPLOG(("]\n")); - sp = esp->disconnected_SC; - ESPLOG(("esp%d: disconnected_SC[", esp->esp_id)); - while(sp) { - ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); - sp = (Scsi_Cmnd *) sp->host_scribble; - } - ESPLOG(("]\n")); - return do_reset_bus; - } esp_connect(esp, eregs, SCptr); esp_cmd(esp, eregs, ESP_CMD_MOK); - /* No need for explicit restore pointers operation. */ + /* Reconnect implies a restore pointers operation. */ + esp_restore_pointers(esp, SCptr); + esp->snip = 0; esp_advance_phase(SCptr, in_the_dark); return do_intr_end; @@ -2348,121 +2451,132 @@ static int esp_do_status(struct NCR_ESP *esp, struct ESP_regs *eregs) } } -/* The target has control of the bus and we have to see where it has - * taken us. - */ -static int esp_do_phase_determine(struct NCR_ESP *esp, - struct ESP_regs *eregs) +static int esp_enter_status(struct NCR_ESP *esp, + struct ESP_regs *eregs) { - Scsi_Cmnd *SCptr = esp->current_SC; + unchar thecmd = ESP_CMD_ICCSEQ; - ESPPHASE(("esp_do_phase_determine: ")); - if(!(esp->ireg & ESP_INTR_DC)) { - switch(esp->sreg & ESP_STAT_PMASK) { - case ESP_DOP: - case ESP_DIP: - ESPPHASE(("to data phase\n")); - return esp_do_data(esp, eregs); + esp_cmd(esp, eregs, ESP_CMD_FLUSH); - case ESP_STATP: - /* Whee, status phase, finish up the command. */ - ESPPHASE(("to status phase\n")); + if(esp->do_pio_cmds) { + esp_advance_phase(esp->current_SC, in_status); + esp_cmd(esp, eregs, thecmd); + while(!(esp_read(esp->eregs->esp_status) & ESP_STAT_INTR)); + esp->esp_command[0] = esp_read(eregs->esp_fdata); + while(!(esp_read(esp->eregs->esp_status) & ESP_STAT_INTR)); + esp->esp_command[1] = esp_read(eregs->esp_fdata); + } else { + esp->esp_command[0] = esp->esp_command[1] = 0xff; + esp_write(eregs->esp_tclow, 2); + esp_write(eregs->esp_tcmed, 0); + esp->dma_init_read(esp, esp->esp_command_dvma, 2); + thecmd |= ESP_CMD_DMA; + esp_cmd(esp, eregs, thecmd); + esp_advance_phase(esp->current_SC, in_status); + } - esp_cmd(esp, eregs, ESP_CMD_FLUSH); + return esp_do_status(esp, eregs); +} - if(esp->do_pio_cmds){ - esp_advance_phase(SCptr, in_status); - esp_cmd(esp, eregs, ESP_CMD_ICCSEQ); - while(!(GETREG(esp->eregs->esp_status) - & ESP_STAT_INTR)); - esp->esp_command[0] = GETREG(eregs->esp_fdata); - while(!(GETREG(esp->eregs->esp_status) - & ESP_STAT_INTR)); - esp->esp_command[1] = GETREG(eregs->esp_fdata); - } else { - if(esp->erev != fashme) { - esp->esp_command[0] = 0xff; - esp->esp_command[1] = 0xff; - SETREG(eregs->esp_tclow, 2); - SETREG(eregs->esp_tcmed, 0); - esp->dma_init_read(esp, esp->esp_command_dvma, 2); - esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_ICCSEQ); - } else { - /* Using DVMA for status/message bytes is - * unreliable on HME, nice job QLogic. - * Happy Meal indeed.... - */ - esp_cmd(esp, eregs, ESP_CMD_ICCSEQ); - } - esp_advance_phase(SCptr, in_status); - } - return esp_do_status(esp, eregs); +static int esp_disconnect_amidst_phases(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + Scsi_Cmnd *sp = esp->current_SC; + Scsi_Device *dp = sp->device; - case ESP_MOP: - ESPPHASE(("to msgout phase\n")); - esp_advance_phase(SCptr, in_msgout); - return esp_do_msgout(esp, eregs); - - case ESP_MIP: - ESPPHASE(("to msgin phase\n")); - esp_advance_phase(SCptr, in_msgin); - return esp_do_msgin(esp, eregs); - - case ESP_CMDP: - /* Ugh, we're running a non-standard command the - * ESP doesn't understand, one byte at a time. - */ - ESPPHASE(("to cmd phase\n")); - esp_advance_phase(SCptr, in_cmdbegin); - return esp_do_cmdbegin(esp, eregs); - }; - } else { - Scsi_Device *dp = SCptr->device; + /* This means real problems if we see this + * here. Unless we were actually trying + * to force the device to abort/reset. + */ + ESPLOG(("esp%d: Disconnect amidst phases, ", esp->esp_id)); + ESPLOG(("pphase<%s> cphase<%s>, ", + phase_string(sp->SCp.phase), + phase_string(sp->SCp.sent_command))); - /* This means real problems if we see this - * here. Unless we were actually trying - * to force the device to abort/reset. - */ - ESPLOG(("esp%d Disconnect amidst phases, ", esp->esp_id)); - ESPLOG(("pphase<%s> cphase<%s>, ", - phase_string(SCptr->SCp.phase), - phase_string(SCptr->SCp.sent_command))); - if(esp->disconnected_SC || (esp->erev == fashme)) - esp_cmd(esp, eregs, ESP_CMD_ESEL); + if(esp->disconnected_SC) + esp_cmd(esp, eregs, ESP_CMD_ESEL); - switch(esp->cur_msgout[0]) { - default: - /* We didn't expect this to happen at all. */ - ESPLOG(("device is bolixed\n")); - esp_advance_phase(SCptr, in_tgterror); - esp_done(esp, (DID_ERROR << 16)); - break; + switch(esp->cur_msgout[0]) { + default: + /* We didn't expect this to happen at all. */ + ESPLOG(("device is bolixed\n")); + esp_advance_phase(sp, in_tgterror); + esp_done(esp, (DID_ERROR << 16)); + break; - case BUS_DEVICE_RESET: - ESPLOG(("device reset successful\n")); - dp->sync_max_offset = 0; - dp->sync_min_period = 0; - dp->sync = 0; - esp_advance_phase(SCptr, in_resetdev); - esp_done(esp, (DID_RESET << 16)); - break; + case BUS_DEVICE_RESET: + ESPLOG(("device reset successful\n")); + dp->sync_max_offset = 0; + dp->sync_min_period = 0; + dp->sync = 0; + esp_advance_phase(sp, in_resetdev); + esp_done(esp, (DID_RESET << 16)); + break; - case ABORT: - ESPLOG(("device abort successful\n")); - esp_advance_phase(SCptr, in_abortone); - esp_done(esp, (DID_ABORT << 16)); - break; + case ABORT: + ESPLOG(("device abort successful\n")); + esp_advance_phase(sp, in_abortone); + esp_done(esp, (DID_ABORT << 16)); + break; - }; - return do_intr_end; - } + }; + return do_intr_end; +} - ESPLOG(("esp%d: to unknown phase\n", esp->esp_id)); - printk("esp%d: Bizarre bus phase %2x.\n", esp->esp_id, - esp->sreg & ESP_STAT_PMASK); +static int esp_enter_msgout(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + esp_advance_phase(esp->current_SC, in_msgout); + return esp_do_msgout(esp, eregs); +} + +static int esp_enter_msgin(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + esp_advance_phase(esp->current_SC, in_msgin); + return esp_do_msgin(esp, eregs); +} + +static int esp_enter_cmd(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + esp_advance_phase(esp->current_SC, in_cmdbegin); + return esp_do_cmdbegin(esp, eregs); +} + +static int esp_enter_badphase(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + ESPLOG(("esp%d: Bizarre bus phase %2x.\n", esp->esp_id, + esp->sreg & ESP_STAT_PMASK)); return do_reset_bus; } +typedef int (*espfunc_t)(struct NCR_ESP *, + struct ESP_regs *); + +static espfunc_t phase_vector[] = { + esp_do_data, /* ESP_DOP */ + esp_do_data, /* ESP_DIP */ + esp_enter_cmd, /* ESP_CMDP */ + esp_enter_status, /* ESP_STATP */ + esp_enter_badphase, /* ESP_STAT_PMSG */ + esp_enter_badphase, /* ESP_STAT_PMSG | ESP_STAT_PIO */ + esp_enter_msgout, /* ESP_MOP */ + esp_enter_msgin, /* ESP_MIP */ +}; + +/* The target has control of the bus and we have to see where it has + * taken us. + */ +static int esp_do_phase_determine(struct NCR_ESP *esp, + struct ESP_regs *eregs) +{ + if ((esp->ireg & ESP_INTR_DC) != 0) + return esp_disconnect_amidst_phases(esp, eregs); + return phase_vector[esp->sreg & ESP_STAT_PMASK](esp, eregs); +} + /* First interrupt after exec'ing a cmd comes here. */ static int esp_select_complete(struct NCR_ESP *esp, struct ESP_regs *eregs) { @@ -2470,12 +2584,7 @@ static int esp_select_complete(struct NCR_ESP *esp, struct ESP_regs *eregs) Scsi_Device *SDptr = SCptr->device; int cmd_bytes_sent, fcnt; - if(esp->erev != fashme) - esp->seqreg = (GETREG(eregs->esp_sstep) & ESP_STEP_VBITS); - if(esp->erev == fashme) - fcnt = esp->hme_fifo_workaround_count; - else - fcnt = (GETREG(eregs->esp_fflags) & ESP_FF_FBYTES); + fcnt = (esp_read(eregs->esp_fflags) & ESP_FF_FBYTES); cmd_bytes_sent = esp->dma_bytes_sent(esp, fcnt); if(esp->dma_invalidate) esp->dma_invalidate(esp); @@ -2583,14 +2692,12 @@ static int esp_select_complete(struct NCR_ESP *esp, struct ESP_regs *eregs) if(SCptr->SCp.phase == in_slct_norm) cmd_bytes_sent -= 1; }; - if(esp->erev != fashme) - esp_cmd(esp, eregs, ESP_CMD_NULL); + esp_cmd(esp, eregs, ESP_CMD_NULL); /* Be careful, we could really get fucked during synchronous * data transfers if we try to flush the fifo now. */ - if((esp->erev != fashme) && /* not a Happy Meal and... */ - !fcnt && /* Fifo is empty and... */ + if(!fcnt && /* Fifo is empty and... */ /* either we are not doing synchronous transfers or... */ (!SDptr->sync_max_offset || /* We are not going into data in phase. */ @@ -2651,9 +2758,8 @@ static int esp_select_complete(struct NCR_ESP *esp, struct ESP_regs *eregs) SCptr->SCp.phase == in_slct_stop)) { /* shit */ esp->snip = 0; - printk("esp%d: Failed synchronous negotiation for target %d " - "lun %d\n", - esp->esp_id, SCptr->target, SCptr->lun); + ESPLOG(("esp%d: Failed synchronous negotiation for target %d " + "lun %d\n", esp->esp_id, SCptr->target, SCptr->lun)); SDptr->sync_max_offset = 0; SDptr->sync_min_period = 0; SDptr->sync = 1; /* so we don't negotiate again */ @@ -2680,8 +2786,8 @@ static int esp_select_complete(struct NCR_ESP *esp, struct ESP_regs *eregs) * But first make sure that is really what is happening. */ if(((1<target) & esp->targets_present)) { - printk("esp%d: Warning, live target %d not responding to " - "selection.\n", esp->esp_id, SCptr->target); + ESPLOG(("esp%d: Warning, live target %d not responding to " + "selection.\n", esp->esp_id, SCptr->target)); /* This _CAN_ happen. The SCSI standard states that * the target is to _not_ respond to selection if @@ -2710,9 +2816,9 @@ static int esp_select_complete(struct NCR_ESP *esp, struct ESP_regs *eregs) esp_print_seqreg(esp->seqreg); printk("\n"); printk("esp%d: New -- ", esp->esp_id); - esp->sreg = GETREG(eregs->esp_status); - esp->seqreg = GETREG(eregs->esp_sstep); - esp->ireg = GETREG(eregs->esp_intrpt); + esp->sreg = esp_read(eregs->esp_status); + esp->seqreg = esp_read(eregs->esp_sstep); + esp->ireg = esp_read(eregs->esp_intrpt); esp_print_ireg(esp->ireg); printk(" "); esp_print_statreg(esp->sreg); @@ -2743,14 +2849,14 @@ static int esp_do_msgincont(struct NCR_ESP *esp, struct ESP_regs *eregs) * have miscoded something..... so, try to * recover as best we can. */ - printk("esp%d: message in mis-carriage.\n", esp->esp_id); + ESPLOG(("esp%d: message in mis-carriage.\n", esp->esp_id)); } esp_advance_phase(esp->current_SC, in_the_dark); return do_phase_determine; } -static inline int check_singlebyte_msg(struct NCR_ESP *esp, - struct ESP_regs *eregs) +static int check_singlebyte_msg(struct NCR_ESP *esp, + struct ESP_regs *eregs) { esp->prevmsgin = esp->cur_msgin[0]; if(esp->cur_msgin[0] & 0x80) { @@ -2779,8 +2885,20 @@ static inline int check_singlebyte_msg(struct NCR_ESP *esp, return 0; case RESTORE_POINTERS: + /* In this case we might also have to backup the + * "slow command" pointer. It is rare to get such + * a save/restore pointer sequence so early in the + * bus transition sequences, but cover it. + */ + if(esp->esp_slowcmd) { + esp->esp_scmdleft = esp->current_SC->cmd_len; + esp->esp_scmdp = &esp->current_SC->cmnd[0]; + } + esp_restore_pointers(esp, esp->current_SC); + return 0; + case SAVE_POINTERS: - /* We handle this all automatically. */ + esp_save_pointers(esp, esp->current_SC); return 0; case COMMAND_COMPLETE: @@ -2815,9 +2933,9 @@ static inline int check_singlebyte_msg(struct NCR_ESP *esp, * the SCSI2 standard specifically recommends against targets doing * this because so many initiators cannot cope with this occuring. */ -static inline int target_with_ants_in_pants(struct NCR_ESP *esp, - Scsi_Cmnd *SCptr, - Scsi_Device *SDptr) +static int target_with_ants_in_pants(struct NCR_ESP *esp, + Scsi_Cmnd *SCptr, + Scsi_Device *SDptr) { if(SDptr->sync || SDptr->borken) { /* sorry, no can do */ @@ -2834,7 +2952,7 @@ static inline int target_with_ants_in_pants(struct NCR_ESP *esp, return 0; } -static inline void sync_report(struct NCR_ESP *esp) +static void sync_report(struct NCR_ESP *esp) { int msg3, msg4; char *type; @@ -2845,30 +2963,31 @@ static inline void sync_report(struct NCR_ESP *esp) int hz = 1000000000 / (msg3 * 4); int integer = hz / 1000000; int fraction = (hz - (integer * 1000000)) / 10000; - if((esp->erev == fashme) && - (esp->config3[esp->current_SC->target] & ESP_CONFIG3_EWIDE)) { - type = "FAST-WIDE"; - integer <<= 1; - fraction <<= 1; - } else if((msg3 * 4) < 200) { + if((msg3 * 4) < 200) { type = "FAST"; } else { type = "synchronous"; } - printk(KERN_INFO "esp%d: target %d [period %dns offset %d %d.%02dMHz %s SCSI%s]\n", - esp->esp_id, esp->current_SC->target, - (int) msg3 * 4, - (int) msg4, - integer, fraction, type, - (((msg3 * 4) < 200) ? "-II" : "")); + + /* Do not transform this back into one big printk + * again, it triggers a bug in our sparc64-gcc272 + * sibling call optimization. -DaveM + */ + ESPLOG((KERN_INFO "esp%d: target %d ", + esp->esp_id, esp->current_SC->target)); + ESPLOG(("[period %dns offset %d %d.%02dMHz ", + (int) msg3 * 4, (int) msg4, + integer, fraction)); + ESPLOG(("%s SCSI%s]\n", type, + (((msg3 * 4) < 200) ? "-II" : ""))); } else { - printk(KERN_INFO "esp%d: target %d asynchronous\n", - esp->esp_id, esp->current_SC->target); + ESPLOG((KERN_INFO "esp%d: target %d asynchronous\n", + esp->esp_id, esp->current_SC->target)); } } -static inline int check_multibyte_msg(struct NCR_ESP *esp, - struct ESP_regs *eregs) +static int check_multibyte_msg(struct NCR_ESP *esp, + struct ESP_regs *eregs) { Scsi_Cmnd *SCptr = esp->current_SC; Scsi_Device *SDptr = SCptr->device; @@ -2923,10 +3042,7 @@ static inline int check_multibyte_msg(struct NCR_ESP *esp, ESPSDTR(("period is ok, ")); tmp = esp->ccycle / 1000; regval = (((period << 2) + tmp - 1) / tmp); - if(regval && ((esp->erev == fas100a || - esp->erev == fas216 || - esp->erev == fas236 || - esp->erev == fashme))) { + if(regval && (esp->erev > esp236)) { if(period >= 50) regval--; } @@ -2937,8 +3053,8 @@ static inline int check_multibyte_msg(struct NCR_ESP *esp, SDptr->sync_min_period = (regval & 0x1f); SDptr->sync_max_offset = (offset | esp->radelay); - if((esp->erev == fas100a || esp->erev == fas216 || esp->erev == fas236 || esp->erev == fashme)) { - if((esp->erev == fas100a) || (esp->erev == fashme)) + if(esp->erev > esp236) { + if(esp->erev == fas100a) bit = ESP_CONFIG3_FAST; else bit = ESP_CONFIG3_FSCSI; @@ -2946,11 +3062,13 @@ static inline int check_multibyte_msg(struct NCR_ESP *esp, esp->config3[SCptr->target] |= bit; else esp->config3[SCptr->target] &= ~bit; - SETREG(eregs->esp_cfg3, - esp->config3[SCptr->target]); + esp->prev_cfg3 = esp->config3[SCptr->target]; + esp_write(eregs->esp_cfg3, esp->prev_cfg3); } - SETREG(eregs->esp_soff, SDptr->sync_min_period); - SETREG(eregs->esp_stp, SDptr->sync_max_offset); + esp->prev_soff = SDptr->sync_min_period; + esp_write(eregs->esp_soff, esp->prev_soff); + esp->prev_stp = SDptr->sync_max_offset; + esp_write(eregs->esp_stp, esp->prev_stp); ESPSDTR(("soff=%2x stp=%2x cfg3=%2x\n", SDptr->sync_max_offset, @@ -2965,16 +3083,18 @@ static inline int check_multibyte_msg(struct NCR_ESP *esp, ESPSDTR(("unaccaptable sync nego, forcing async\n")); SDptr->sync_max_offset = 0; SDptr->sync_min_period = 0; - SETREG(eregs->esp_soff, 0); - SETREG(eregs->esp_stp, 0); - if((esp->erev == fas100a || esp->erev == fas216 || esp->erev == fas236 || esp->erev == fashme)) { - if((esp->erev == fas100a) || (esp->erev == fashme)) + esp->prev_soff = 0; + esp_write(eregs->esp_soff, 0); + esp->prev_stp = 0; + esp_write(eregs->esp_stp, 0); + if(esp->erev > esp236) { + if(esp->erev == fas100a) bit = ESP_CONFIG3_FAST; else bit = ESP_CONFIG3_FSCSI; esp->config3[SCptr->target] &= ~bit; - SETREG(eregs->esp_cfg3, - esp->config3[SCptr->target]); + esp->prev_cfg3 = esp->config3[SCptr->target]; + esp_write(eregs->esp_cfg3, esp->prev_cfg3); } } @@ -2995,48 +3115,12 @@ static inline int check_multibyte_msg(struct NCR_ESP *esp, esp_advance_phase(SCptr, in_the_dark); /* ...or else! */ return 0; } else if(esp->cur_msgin[2] == EXTENDED_WDTR) { - int size = 8 << esp->cur_msgin[3]; - - esp->wnip = 0; - if(esp->erev != fashme) { - printk("esp%d: AIEEE wide msg received and not HME.\n", - esp->esp_id); - message_out = MESSAGE_REJECT; - } else if(size > 16) { - printk("esp%d: AIEEE wide transfer for %d size not supported.\n", - esp->esp_id, size); - message_out = MESSAGE_REJECT; - } else { - /* Things look good; let's see what we got. */ - if(size == 16) { - /* Set config 3 register for this target. */ - printk("esp%d: 16 byte WIDE transfers enabled for target %d.\n", - esp->esp_id, SCptr->target); - esp->config3[SCptr->target] |= ESP_CONFIG3_EWIDE; - } else { - /* Just make sure it was one byte sized. */ - if(size != 8) { - printk("esp%d: Aieee, wide nego of %d size.\n", - esp->esp_id, size); - message_out = MESSAGE_REJECT; - goto finish; - } - /* Pure paranoia. */ - esp->config3[SCptr->target] &= ~(ESP_CONFIG3_EWIDE); - } - SETREG(eregs->esp_cfg3, esp->config3[SCptr->target]); - - /* Regardless, next try for sync transfers. */ - build_sync_nego_msg(esp, esp->sync_defp, 15); - SDptr->sync = 1; - esp->snip = 1; - message_out = EXTENDED_MESSAGE; - } + ESPLOG(("esp%d: AIEEE wide msg received\n", esp->esp_id)); + message_out = MESSAGE_REJECT; } else if(esp->cur_msgin[2] == EXTENDED_MODIFY_DATA_POINTER) { ESPLOG(("esp%d: rejecting modify data ptr msg\n", esp->esp_id)); message_out = MESSAGE_REJECT; } -finish: esp_advance_phase(SCptr, in_the_dark); return message_out; } @@ -3054,26 +3138,17 @@ static int esp_do_msgindone(struct NCR_ESP *esp, struct ESP_regs *eregs) if(esp->msgin_len && (esp->sreg & ESP_STAT_PERR)) { message_out = MSG_PARITY_ERROR; esp_cmd(esp, eregs, ESP_CMD_FLUSH); - } else if(esp->erev != fashme && - (it = (GETREG(eregs->esp_fflags) - & ESP_FF_FBYTES))!=1) { + } else if((it = (esp_read(eregs->esp_fflags) & ESP_FF_FBYTES))!=1) { /* We certainly dropped the ball somewhere. */ message_out = INITIATOR_ERROR; esp_cmd(esp, eregs, ESP_CMD_FLUSH); } else if(!esp->msgin_len) { - if(esp->erev == fashme) - it = esp->hme_fifo_workaround_buffer[0]; - else - it = GETREG(eregs->esp_fdata); + it = esp_read(eregs->esp_fdata); esp_advance_phase(SCptr, in_msgincont); } else { /* it is ok and we want it */ - if(esp->erev == fashme) - it = esp->cur_msgin[esp->msgin_ctr] = - esp->hme_fifo_workaround_buffer[0]; - else - it = esp->cur_msgin[esp->msgin_ctr] = - GETREG(eregs->esp_fdata); + it = esp->cur_msgin[esp->msgin_ctr] = + esp_read(eregs->esp_fdata); esp->msgin_ctr++; } } else { @@ -3114,7 +3189,7 @@ static int esp_do_msgindone(struct NCR_ESP *esp, struct ESP_regs *eregs) esp_advance_phase(SCptr, in_the_dark); esp->msgin_len = 0; } - esp->sreg = GETREG(eregs->esp_status); + esp->sreg = esp_read(eregs->esp_status); esp->sreg &= ~(ESP_STAT_INTR); if((esp->sreg & (ESP_STAT_PMSG|ESP_STAT_PCD)) == (ESP_STAT_PMSG|ESP_STAT_PCD)) esp_cmd(esp, eregs, ESP_CMD_MOK); @@ -3126,35 +3201,21 @@ static int esp_do_msgindone(struct NCR_ESP *esp, struct ESP_regs *eregs) static int esp_do_cmdbegin(struct NCR_ESP *esp, struct ESP_regs *eregs) { + unsigned char tmp; Scsi_Cmnd *SCptr = esp->current_SC; esp_advance_phase(SCptr, in_cmdend); - if(esp->erev == fashme) { - int i; - - for(i = 0; i < esp->esp_scmdleft; i++) - esp->esp_command[i] = *esp->esp_scmdp++; - esp->esp_scmdleft = 0; - esp_cmd(esp, eregs, ESP_CMD_FLUSH); - esp_setcount(eregs, i, 1); - esp_cmd(esp, eregs, (ESP_CMD_DMA | ESP_CMD_TI)); - esp->dma_init_write(esp, esp->esp_command_dvma, i); - } else { - esp_cmd(esp, eregs, ESP_CMD_FLUSH); - SETREG(eregs->esp_fdata, *esp->esp_scmdp++); - esp->esp_scmdleft--; - esp_cmd(esp, eregs, ESP_CMD_TI); - } + esp_cmd(esp, eregs, ESP_CMD_FLUSH); + tmp = *esp->esp_scmdp++; + esp->esp_scmdleft--; + esp_write(eregs->esp_fdata, tmp); + esp_cmd(esp, eregs, ESP_CMD_TI); return do_intr_end; } -static inline int esp_do_cmddone(struct NCR_ESP *esp, struct ESP_regs *eregs) +static int esp_do_cmddone(struct NCR_ESP *esp, struct ESP_regs *eregs) { - if(esp->erev == fashme){ - if(esp->dma_invalidate) - esp->dma_invalidate(esp); - } else - esp_cmd(esp, eregs, ESP_CMD_NULL); + esp_cmd(esp, eregs, ESP_CMD_NULL); if(esp->ireg & ESP_INTR_BSERV) { esp_advance_phase(esp->current_SC, in_the_dark); return esp_do_phase_determine(esp, eregs); @@ -3169,79 +3230,61 @@ static int esp_do_msgout(struct NCR_ESP *esp, struct ESP_regs *eregs) esp_cmd(esp, eregs, ESP_CMD_FLUSH); switch(esp->msgout_len) { case 1: - if(esp->erev == fashme) - hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 1); - else - SETREG(eregs->esp_fdata, esp->cur_msgout[0]); + esp_write(eregs->esp_fdata, esp->cur_msgout[0]); esp_cmd(esp, eregs, ESP_CMD_TI); break; case 2: if(esp->do_pio_cmds){ - SETREG(eregs->esp_fdata, esp->cur_msgout[0]); - SETREG(eregs->esp_fdata, esp->cur_msgout[1]); + esp_write(eregs->esp_fdata, esp->cur_msgout[0]); + esp_write(eregs->esp_fdata, esp->cur_msgout[1]); esp_cmd(esp, eregs, ESP_CMD_TI); } else { esp->esp_command[0] = esp->cur_msgout[0]; esp->esp_command[1] = esp->cur_msgout[1]; - if(esp->erev == fashme) { - hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 2); - esp_cmd(esp, eregs, ESP_CMD_TI); - } else { - esp->dma_setup(esp, esp->esp_command_dvma, 2, 0); - esp_setcount(eregs, 2, 0); - esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI); - } + esp->dma_setup(esp, esp->esp_command_dvma, 2, 0); + esp_setcount(eregs, 2); + esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI); } break; case 4: esp->snip = 1; if(esp->do_pio_cmds){ - SETREG(eregs->esp_fdata, esp->cur_msgout[0]); - SETREG(eregs->esp_fdata, esp->cur_msgout[1]); - SETREG(eregs->esp_fdata, esp->cur_msgout[2]); - SETREG(eregs->esp_fdata, esp->cur_msgout[3]); + esp_write(eregs->esp_fdata, esp->cur_msgout[0]); + esp_write(eregs->esp_fdata, esp->cur_msgout[1]); + esp_write(eregs->esp_fdata, esp->cur_msgout[2]); + esp_write(eregs->esp_fdata, esp->cur_msgout[3]); esp_cmd(esp, eregs, ESP_CMD_TI); } else { esp->esp_command[0] = esp->cur_msgout[0]; esp->esp_command[1] = esp->cur_msgout[1]; esp->esp_command[2] = esp->cur_msgout[2]; esp->esp_command[3] = esp->cur_msgout[3]; - if(esp->erev == fashme) { - hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 4); - esp_cmd(esp, eregs, ESP_CMD_TI); - } else { - esp->dma_setup(esp, esp->esp_command_dvma, 4, 0); - esp_setcount(eregs, 4, 0); - esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI); - } + esp->dma_setup(esp, esp->esp_command_dvma, 4, 0); + esp_setcount(eregs, 4); + esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI); } break; case 5: esp->snip = 1; if(esp->do_pio_cmds){ - SETREG(eregs->esp_fdata, esp->cur_msgout[0]); - SETREG(eregs->esp_fdata, esp->cur_msgout[1]); - SETREG(eregs->esp_fdata, esp->cur_msgout[2]); - SETREG(eregs->esp_fdata, esp->cur_msgout[3]); - SETREG(eregs->esp_fdata, esp->cur_msgout[4]); + esp_write(eregs->esp_fdata, esp->cur_msgout[0]); + esp_write(eregs->esp_fdata, esp->cur_msgout[1]); + esp_write(eregs->esp_fdata, esp->cur_msgout[2]); + esp_write(eregs->esp_fdata, esp->cur_msgout[3]); + esp_write(eregs->esp_fdata, esp->cur_msgout[4]); esp_cmd(esp, eregs, ESP_CMD_TI); } else { - SETREG(esp->esp_command[0], esp->cur_msgout[0]); - SETREG(esp->esp_command[1], esp->cur_msgout[1]); - SETREG(esp->esp_command[2], esp->cur_msgout[2]); - SETREG(esp->esp_command[3], esp->cur_msgout[3]); - SETREG(esp->esp_command[4], esp->cur_msgout[4]); - if(esp->erev == fashme) { - hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 5); - esp_cmd(esp, eregs, ESP_CMD_TI); - } else { - esp->dma_setup(esp, esp->esp_command_dvma, 5, 0); - esp_setcount(eregs, 5, 0); - esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI); - } + esp->esp_command[0] = esp->cur_msgout[0]; + esp->esp_command[1] = esp->cur_msgout[1]; + esp->esp_command[2] = esp->cur_msgout[2]; + esp->esp_command[3] = esp->cur_msgout[3]; + esp->esp_command[4] = esp->cur_msgout[4]; + esp->dma_setup(esp, esp->esp_command_dvma, 5, 0); + esp_setcount(eregs, 5); + esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI); } break; @@ -3249,11 +3292,7 @@ static int esp_do_msgout(struct NCR_ESP *esp, struct ESP_regs *eregs) /* whoops */ ESPMISC(("bogus msgout sending NOP\n")); esp->cur_msgout[0] = NOP; - if(esp->erev == fashme) { - hme_fifo_push(esp, eregs, &esp->cur_msgout[0], 1); - } else { - SETREG(eregs->esp_fdata, esp->cur_msgout[0]); - } + esp_write(eregs->esp_fdata, esp->cur_msgout[0]); esp->msgout_len = 1; esp_cmd(esp, eregs, ESP_CMD_TI); break; @@ -3262,15 +3301,14 @@ static int esp_do_msgout(struct NCR_ESP *esp, struct ESP_regs *eregs) return do_intr_end; } -static inline int esp_do_msgoutdone(struct NCR_ESP *esp, - struct ESP_regs *eregs) +static int esp_do_msgoutdone(struct NCR_ESP *esp, + struct ESP_regs *eregs) { if((esp->msgout_len > 1) && esp->dma_barrier) esp->dma_barrier(esp); if(!(esp->ireg & ESP_INTR_DC)) { - if(esp->erev != fashme) - esp_cmd(esp, eregs, ESP_CMD_NULL); + esp_cmd(esp, eregs, ESP_CMD_NULL); switch(esp->sreg & ESP_STAT_PMASK) { case ESP_MOP: /* whoops, parity error */ @@ -3285,17 +3323,12 @@ static inline int esp_do_msgoutdone(struct NCR_ESP *esp, break; default: - /* Happy Meal fifo is touchy... */ - if((esp->erev != fashme) && - !fcount(esp, eregs) && + if(!fcount(esp, eregs) && !(esp->current_SC->device->sync_max_offset)) esp_cmd(esp, eregs, ESP_CMD_FLUSH); break; }; - } else { - ESPLOG(("esp%d: disconnect, resetting bus\n", esp->esp_id)); - return do_reset_bus; } /* If we sent out a synchronous negotiation message, update @@ -3312,89 +3345,66 @@ static inline int esp_do_msgoutdone(struct NCR_ESP *esp, return esp_do_phase_determine(esp, eregs); } +static int esp_bus_unexpected(struct NCR_ESP *esp, struct ESP_regs *eregs) +{ + ESPLOG(("esp%d: command in weird state %2x\n", + esp->esp_id, esp->current_SC->SCp.phase)); + return do_reset_bus; +} + +static espfunc_t bus_vector[] = { + esp_do_data_finale, + esp_do_data_finale, + esp_bus_unexpected, + esp_do_msgin, + esp_do_msgincont, + esp_do_msgindone, + esp_do_msgout, + esp_do_msgoutdone, + esp_do_cmdbegin, + esp_do_cmddone, + esp_do_status, + esp_do_freebus, + esp_do_phase_determine, + esp_bus_unexpected, + esp_bus_unexpected, + esp_bus_unexpected, +}; + /* This is the second tier in our dual-level SCSI state machine. */ -static inline int esp_work_bus(struct NCR_ESP *esp, struct ESP_regs *eregs) +static int esp_work_bus(struct NCR_ESP *esp, struct ESP_regs *eregs) { Scsi_Cmnd *SCptr = esp->current_SC; + unsigned int phase; ESPBUS(("esp_work_bus: ")); if(!SCptr) { ESPBUS(("reconnect\n")); return esp_do_reconnect(esp, eregs); } - - switch(SCptr->SCp.phase) { - case in_the_dark: - ESPBUS(("in the dark\n")); - return esp_do_phase_determine(esp, eregs); - - case in_slct_norm: - case in_slct_stop: - case in_slct_msg: - case in_slct_tag: - case in_slct_sneg: - ESPBUS(("finish selection\n")); + phase = SCptr->SCp.phase; + if ((phase & 0xf0) == in_phases_mask) + return bus_vector[(phase & 0x0f)](esp, eregs); + else if((phase & 0xf0) == in_slct_mask) return esp_select_complete(esp, eregs); - - case in_datain: - case in_dataout: - ESPBUS(("finish data\n")); - return esp_do_data_finale(esp, eregs); - - case in_msgout: - ESPBUS(("message out ")); - return esp_do_msgout(esp, eregs); - - case in_msgoutdone: - ESPBUS(("finish message out ")); - return esp_do_msgoutdone(esp, eregs); - - case in_msgin: - ESPBUS(("message in ")); - return esp_do_msgin(esp, eregs); - - case in_msgincont: - ESPBUS(("continue message in ")); - return esp_do_msgincont(esp, eregs); - - case in_msgindone: - ESPBUS(("finish message in ")); - return esp_do_msgindone(esp, eregs); - - case in_status: - ESPBUS(("status phase ")); - return esp_do_status(esp, eregs); - - case in_freeing: - ESPBUS(("freeing the bus ")); - return esp_do_freebus(esp, eregs); - - case in_cmdbegin: - ESPBUS(("begin slow cmd ")); - return esp_do_cmdbegin(esp, eregs); - - case in_cmdend: - ESPBUS(("end slow cmd ")); - return esp_do_cmddone(esp, eregs); - - default: - printk("esp%d: command in weird state %2x\n", - esp->esp_id, esp->current_SC->SCp.phase); - return do_reset_bus; - }; + else + return esp_bus_unexpected(esp, eregs); } +static espfunc_t isvc_vector[] = { + 0, + esp_do_phase_determine, + esp_do_resetbus, + esp_finish_reset, + esp_work_bus +}; + /* Main interrupt handler for an esp adapter. */ -inline void esp_handle(struct NCR_ESP *esp) +void esp_handle(struct NCR_ESP *esp) { struct ESP_regs *eregs; Scsi_Cmnd *SCptr; int what_next = do_intr_end; - unsigned long flags; -#ifdef CONFIG_SCSI_SUNESP - struct sparc_dma_registers *dregs = - (struct sparc_dma_registers*) esp->dregs; -#endif eregs = esp->eregs; SCptr = esp->current_SC; @@ -3402,12 +3412,12 @@ inline void esp_handle(struct NCR_ESP *esp) esp->dma_irq_entry(esp); /* Check for errors. */ - esp->sreg = GETREG(eregs->esp_status); + esp->sreg = esp_read(eregs->esp_status); esp->sreg &= (~ESP_STAT_INTR); - if(esp->erev == fashme) { - esp->sreg2 = GETREG(eregs->esp_status2); - esp->seqreg = (GETREG(eregs->esp_sstep) & ESP_STEP_VBITS); - } + esp->seqreg = (esp_read(eregs->esp_sstep) & ESP_STEP_VBITS); + esp->ireg = esp_read(eregs->esp_intrpt); /* Unlatch intr and stat regs */ + ESPIRQ(("handle_irq: [sreg<%02x> sstep<%02x> ireg<%02x>]\n", + esp->sreg, esp->seqreg, esp->ireg)); if(esp->sreg & (ESP_STAT_SPAM)) { /* Gross error, could be due to one of: * @@ -3435,56 +3445,10 @@ inline void esp_handle(struct NCR_ESP *esp) ESPLOG(("esp%d: No current cmd during gross error, " "resetting bus\n", esp->esp_id)); what_next = do_reset_bus; - goto again; - } - } - -#ifdef CONFIG_SCSI_SUNESP - if(dregs->cond_reg & DMA_HNDL_ERROR) { - /* A DMA gate array error. Here we must - * be seeing one of two things. Either the - * virtual to physical address translation - * on the SBUS could not occur, else the - * translation it did get pointed to a bogus - * page. Ho hum... - */ - ESPLOG(("esp%d: DMA error %08x\n", esp->esp_id, - dregs->cond_reg)); - - /* DMA gate array itself must be reset to clear the - * error condition. - */ - if(esp->dma_reset) - esp->dma_reset(esp); - - what_next = do_reset_bus; - goto again; - } -#endif /* CONFIG_SCSI_SUNESP */ - - if(esp->erev == fashme) { - /* This chip is really losing. */ - ESPHME(("HME[")); - - ESPHME(("sreg2=%02x,", esp->sreg2)); - /* Must latch fifo before reading the interrupt - * register else garbage ends up in the FIFO - * which confuses the driver utterly. - */ - if(!(esp->sreg2 & ESP_STAT2_FEMPTY) || - (esp->sreg2 & ESP_STAT2_F1BYTE)) { - ESPHME(("fifo_workaround]")); - hme_fifo_hwbug_workaround(esp, eregs); - } else { - ESPHME(("no_fifo_workaround]")); + goto state_machine; } } - esp->ireg = GETREG(eregs->esp_intrpt); /* Unlatch intr and stat regs */ - - /* This cannot be done until this very moment. -DaveM */ - synchronize_irq(); - /* No current cmd is only valid at this point when there are * commands off the bus or we are trying a reset. */ @@ -3497,7 +3461,7 @@ inline void esp_handle(struct NCR_ESP *esp) if(esp->ireg & (ESP_INTR_IC)) { /* Illegal command fed to ESP. Outside of obvious * software bugs that could cause this, there is - * a condition with esp100 where we can confuse the + * a condition with ESP100 where we can confuse the * ESP into an erroneous illegal command interrupt * because it does not scrape the FIFO properly * for reselection. See esp100_reconnect_hwbug() @@ -3523,10 +3487,7 @@ inline void esp_handle(struct NCR_ESP *esp) } what_next = do_reset_bus; - goto again; - } - - if(!(esp->ireg & ~(ESP_INTR_FDONE | ESP_INTR_BSERV | ESP_INTR_DC))) { + } else if(!(esp->ireg & ~(ESP_INTR_FDONE | ESP_INTR_BSERV | ESP_INTR_DC))) { int phase; if(SCptr) { @@ -3538,13 +3499,12 @@ inline void esp_handle(struct NCR_ESP *esp) } else { ESPLOG(("esp%d: interrupt for no good reason...\n", esp->esp_id)); - goto esp_handle_done; + what_next = do_intr_end; } } else { ESPLOG(("esp%d: BSERV or FDONE or DC while SCptr==NULL\n", esp->esp_id)); what_next = do_reset_bus; - goto again; } } else if(esp->ireg & ESP_INTR_SR) { ESPLOG(("esp%d: SCSI bus reset interrupt\n", esp->esp_id)); @@ -3553,7 +3513,6 @@ inline void esp_handle(struct NCR_ESP *esp) ESPLOG(("esp%d: AIEEE we have been selected by another initiator!\n", esp->esp_id)); what_next = do_reset_bus; - goto again; } else if(esp->ireg & ESP_INTR_RSEL) { if(!SCptr) { /* This is ok. */ @@ -3568,114 +3527,40 @@ inline void esp_handle(struct NCR_ESP *esp) ESPLOG(("esp%d: Reselected while bus is busy\n", esp->esp_id)); what_next = do_reset_bus; - goto again; } } - /* We're trying to fight stack problems, and inline as much as - * possible without making this driver a mess. hate hate hate - * This is tier-one in our dual level SCSI state machine. - */ -again: - switch(what_next) { - case do_intr_end: - goto esp_handle_done; - - case do_work_bus: - what_next = esp_work_bus(esp, eregs); - break; - - case do_phase_determine: - what_next = esp_do_phase_determine(esp, eregs); - break; - - case do_reset_bus: - ESPLOG(("esp%d: resetting bus...\n", esp->esp_id)); - esp->resetting_bus = 1; - esp_cmd(esp, eregs, ESP_CMD_RS); - goto esp_handle_done; - - case do_reset_complete: - /* Tricky, we don't want to cause any more commands to - * go out until we clear all the live cmds by hand. - */ - if(esp->current_SC) { - Scsi_Cmnd *SCptr = esp->current_SC; - - if(!SCptr->use_sg) { - if (esp->dma_mmu_release_scsi_one) - esp->dma_mmu_release_scsi_one (esp, SCptr); - } else { - if (esp->dma_mmu_release_scsi_sgl) - esp->dma_mmu_release_scsi_sgl (esp, SCptr); - } - SCptr->result = (DID_RESET << 16); - - spin_lock_irqsave(&io_request_lock,flags); - SCptr->scsi_done(SCptr); - spin_unlock_irqrestore(&io_request_lock, flags); - } - esp->current_SC = NULL; - if(esp->disconnected_SC) { - Scsi_Cmnd *SCptr; - while((SCptr = remove_first_SC(&esp->disconnected_SC))) { - if(!SCptr->use_sg) { - if (esp->dma_mmu_release_scsi_one) - esp->dma_mmu_release_scsi_one (esp, SCptr); - } else { - if (esp->dma_mmu_release_scsi_sgl) - esp->dma_mmu_release_scsi_sgl (esp, SCptr); - } - SCptr->result = (DID_RESET << 16); - - spin_lock_irqsave(&io_request_lock,flags); - SCptr->scsi_done(SCptr); - spin_unlock_irqrestore(&io_request_lock, flags); - } - } - esp->resetting_bus = 0; - - if(esp->current_SC) { - printk("esp%d: weird weird weird, current_SC not NULL after " - "SCSI bus reset.\n", esp->esp_id); - goto esp_handle_done; + /* This is tier-one in our dual level SCSI state machine. */ +state_machine: + while(what_next != do_intr_end) { + if (what_next >= do_phase_determine && + what_next < do_intr_end) + what_next = isvc_vector[what_next](esp, eregs); + else { + /* state is completely lost ;-( */ + ESPLOG(("esp%d: interrupt engine loses state, resetting bus\n", + esp->esp_id)); + what_next = do_reset_bus; } - - /* Now it is safe to execute more things. */ - if(esp->issue_SC) - esp_exec_cmd(esp); - goto esp_handle_done; - - default: - /* state is completely lost ;-( */ - ESPLOG(("esp%d: interrupt engine loses state, resetting bus\n", - esp->esp_id)); - what_next = do_reset_bus; - break; - - }; - goto again; - -esp_handle_done: + } if(esp->dma_irq_exit) esp->dma_irq_exit(esp); - return; } -#ifndef __sparc_v9__ - #ifndef __SMP__ void esp_intr(int irq, void *dev_id, struct pt_regs *pregs) { struct NCR_ESP *esp; + unsigned long flags; int again; /* Handle all ESP interrupts showing at this IRQ level. */ + spin_lock_irqsave(&io_request_lock, flags); repeat: again = 0; for_each_esp(esp) { #ifndef __mips__ - if(((esp)->irq & 0xf) == irq) { + if(((esp)->irq & 0xff) == irq) { #endif if(esp->dma_irq_p(esp)) { again = 1; @@ -3694,14 +3579,17 @@ repeat: } if(again) goto repeat; + spin_unlock_irqrestore(&io_request_lock, flags); } #else /* For SMP we only service one ESP on the list list at our IRQ level! */ -void esp_intr(int irq, void *dev_id, struct pt_regs *pregs) +static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs) { struct NCR_ESP *esp; + unsigned long flags; /* Handle all ESP interrupts showing at this IRQ level. */ + spin_lock_irqsave(&io_request_lock, flags); for_each_esp(esp) { if(((esp)->irq & 0xf) == irq) { if(esp->dma_irq_p(esp)) { @@ -3713,28 +3601,22 @@ void esp_intr(int irq, void *dev_id, struct pt_regs *pregs) ESPIRQ((")")); esp->dma_ints_on(esp); - return; + goto out; } } } +out: + spin_unlock_irqrestore(&io_request_lock, flags); } #endif -#else /* __sparc_v9__ */ - -static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs) +#ifdef MODULE +int init_module(void) { return 0; } +void cleanup_module(void) {} +void esp_release(void) { - struct NCR_ESP *esp = dev_id; - - if(esp->dma_irq_p(esp)) { - esp->dma_ints_off(dregs); - - ESPIRQ(("I[%d:%d](", smp_processor_id(), esp->esp_id)); - esp_handle(esp); - ESPIRQ((")")); - - esp->dma_ints_on(esp); - } + MOD_DEC_USE_COUNT; + esps_in_use--; + esps_running = esps_in_use; } - #endif diff --git a/drivers/scsi/NCR53C9x.h b/drivers/scsi/NCR53C9x.h index 175b6552abde..f6724ee2c5fa 100644 --- a/drivers/scsi/NCR53C9x.h +++ b/drivers/scsi/NCR53C9x.h @@ -1,4 +1,4 @@ -/* NCR53C9x.h: Defines and structures for the NCR53C9x generic driver. +/* NCR53C9x.c: Defines and structures for the NCR53C9x generic driver. * * Originaly esp.h: Defines and structures for the Sparc ESP * (Enhanced SCSI Processor) driver under Linux. @@ -15,10 +15,32 @@ #include +/* djweis for mac driver */ +#if defined(CONFIG_MAC) +#define PAD_SIZE 15 +#else +#define PAD_SIZE 3 +#endif + +/* Handle multiple hostadapters on Amiga + * generally PAD_SIZE = 3 + * but there is one exception: Oktagon (PAD_SIZE = 1) */ +#if defined(CONFIG_OKTAGON_SCSI) || defined(CONFIG_OKTAGON_SCSI_MODULE) +#undef PAD_SIZE +#if defined(CONFIG_BLZ1230_SCSI) || defined(CONFIG_BLZ1230_SCSI_MODULE) || \ + defined(CONFIG_BLZ2060_SCSI) || defined(CONFIG_BLZ2060_SCSI_MODULE) || \ + defined(CONFIG_CYBERSTORM_SCSI) || defined(CONFIG_CYBERSTORM_SCSI_MODULE) || \ + defined(CONFIG_CYBERSTORMII_SCSI) || defined(CONFIG_CYBERSTORMII_SCSI_MODULE) || \ + defined(CONFIG_FASTLANE_SCSI) || defined(CONFIG_FASTLANE_SCSI_MODULE) +#define MULTIPLE_PAD_SIZES +#else +#define PAD_SIZE 1 +#endif +#endif + /* Macros for debugging messages */ -/* #define DEBUG_ESP */ -/* #define DEBUG_ESP_HME */ +#define DEBUG_ESP /* #define DEBUG_ESP_DATA */ /* #define DEBUG_ESP_QUEUE */ /* #define DEBUG_ESP_DISCONNECT */ @@ -43,12 +65,6 @@ #define ESPLOG(foo) #endif /* (DEBUG_ESP) */ -#if defined(DEBUG_ESP_HME) -#define ESPHME(foo) printk foo -#else -#define ESPHME(foo) -#endif - #if defined(DEBUG_ESP_DATA) #define ESPDATA(foo) printk foo #else @@ -103,21 +119,15 @@ #define ESPMISC(foo) #endif -#define INTERNAL_ESP_ERROR \ - (panic ("Internal ESP driver error in file %s, line %d\n", \ - __FILE__, __LINE__)) - -#define INTERNAL_ESP_ERROR_NOPANIC \ - (printk ("Internal ESP driver error in file %s, line %d\n", \ - __FILE__, __LINE__)) - /* * padding for register structure */ #ifdef CONFIG_JAZZ_ESP #define EREGS_PAD(n) #else -#define EREGS_PAD(n) unchar n[3]; +#ifndef MULTIPLE_PAD_SIZES +#define EREGS_PAD(n) unchar n[PAD_SIZE]; +#endif #endif /* The ESP SCSI controllers have their register sets in three @@ -130,18 +140,12 @@ * Yet, they all live within the same IO space. */ -/* All the ESP registers are one byte each and are accessed longwords - * apart with a big-endian ordering to the bytes. - */ - -/* - * On intel, we must use inb() and outb() for register access, and the registers - * are consecutive; no padding. - */ - #ifndef __i386__ -#define SETREG(reg, val) (reg = val) -#define GETREG(reg) (reg) + +#ifndef MULTIPLE_PAD_SIZES + +#define esp_write(__reg, __val) ((__reg) = (__val)) +#define esp_read(__reg) (__reg) struct ESP_regs { /* Access Description Offset */ @@ -151,7 +155,7 @@ struct ESP_regs { EREGS_PAD(fdpad); volatile unchar esp_fdata; /* rw FIFO data bits 0x08 */ EREGS_PAD(cbpad); - volatile unchar esp_cmnd; /* rw SCSI command bits 0x0c */ + volatile unchar esp_cmnd; /* rw SCSI command bits 0x0c */ EREGS_PAD(stpad); volatile unchar esp_status; /* ro ESP status register 0x10 */ #define esp_busid esp_status /* wo Bus ID for select/reselect 0x10 */ @@ -168,7 +172,6 @@ struct ESP_regs { volatile unchar esp_cfg1; /* rw First configuration register 0x20 */ EREGS_PAD(cfpad); volatile unchar esp_cfact; /* wo Clock conversion factor 0x24 */ -#define esp_status2 esp_cfact /* ro HME status2 register 0x24 */ EREGS_PAD(ctpad); volatile unchar esp_ctest; /* wo Chip test register 0x28 */ EREGS_PAD(cf2pd); @@ -177,63 +180,89 @@ struct ESP_regs { /* The following is only found on the 53C9X series SCSI chips */ volatile unchar esp_cfg3; /* rw Third configuration register 0x30 */ - EREGS_PAD(holep); - volatile unchar esp_hole; /* hole in register map 0x34 */ - EREGS_PAD(thpd); + EREGS_PAD(cf4pd); + volatile unchar esp_cfg4; /* rw Fourth configuration register 0x34 */ + EREGS_PAD(thpd); /* The following is found on all chips except the NCR53C90 (ESP100) */ volatile unchar esp_tchi; /* rw High bits of transfer count 0x38 */ #define esp_uid esp_tchi /* ro Unique ID code 0x38 */ -#define fas_rlo esp_tchi /* rw HME extended counter 0x38 */ EREGS_PAD(fgpad); volatile unchar esp_fgrnd; /* rw Data base for fifo 0x3c */ -#define fas_rhi esp_fgrnd /* rw HME extended counter 0x3c */ }; -#else -#define SETREG(reg, val) outb(val, reg) -#define GETREG(reg) inb(reg) +#else /* MULTIPLE_PAD_SIZES */ + +#define esp_write(__reg, __val) (*(__reg) = (__val)) +#define esp_read(__reg) (*(__reg)) struct ESP_regs { -#ifdef CONFIG_MCA - unsigned int slot; + unsigned char io_addr[64]; /* dummy */ + /* Access Description Offset */ +#define esp_tclow io_addr /* rw Low bits of the transfer count 0x00 */ +#define esp_tcmed io_addr + (1<<(esp->shift)) /* rw Mid bits of the transfer count 0x04 */ +#define esp_fdata io_addr + (2<<(esp->shift)) /* rw FIFO data bits 0x08 */ +#define esp_cmnd io_addr + (3<<(esp->shift)) /* rw SCSI command bits 0x0c */ +#define esp_status io_addr + (4<<(esp->shift)) /* ro ESP status register 0x10 */ +#define esp_busid esp_status /* wo Bus ID for select/reselect 0x10 */ +#define esp_intrpt io_addr + (5<<(esp->shift)) /* ro Kind of interrupt 0x14 */ +#define esp_timeo esp_intrpt /* wo Timeout value for select/resel 0x14 */ +#define esp_sstep io_addr + (6<<(esp->shift)) /* ro Sequence step register 0x18 */ +#define esp_stp esp_sstep /* wo Transfer period per sync 0x18 */ +#define esp_fflags io_addr + (7<<(esp->shift)) /* ro Bits of current FIFO info 0x1c */ +#define esp_soff esp_fflags /* wo Sync offset 0x1c */ +#define esp_cfg1 io_addr + (8<<(esp->shift)) /* rw First configuration register 0x20 */ +#define esp_cfact io_addr + (9<<(esp->shift)) /* wo Clock conversion factor 0x24 */ +#define esp_ctest io_addr + (10<<(esp->shift)) /* wo Chip test register 0x28 */ +#define esp_cfg2 io_addr + (11<<(esp->shift)) /* rw Second configuration register 0x2c */ + + /* The following is only found on the 53C9X series SCSI chips */ +#define esp_cfg3 io_addr + (12<<(esp->shift)) /* rw Third configuration register 0x30 */ +#define esp_cfg4 io_addr + (13<<(esp->shift)) /* rw Fourth configuration register 0x34 */ + + /* The following is found on all chips except the NCR53C90 (ESP100) */ +#define esp_tchi io_addr + (14<<(esp->shift)) /* rw High bits of transfer count 0x38 */ +#define esp_uid esp_tchi /* ro Unique ID code 0x38 */ +#define esp_fgrnd io_addr + (15<<(esp->shift)) /* rw Data base for fifo 0x3c */ +}; + #endif + +#else /* !defined __i386__ */ + +#define esp_write(__reg, __val) outb((__val), (__reg)) +#define esp_read(__reg) inb((__reg)) + +struct ESP_regs { unsigned int io_addr; - /* Access Description Offset */ + /* Access Description Offset */ #define esp_tclow io_addr /* rw Low bits of the transfer count 0x00 */ #define esp_tcmed io_addr + 1 /* rw Mid bits of the transfer count 0x04 */ #define esp_fdata io_addr + 2 /* rw FIFO data bits 0x08 */ -#define esp_cmnd io_addr + 3 /* rw SCSI command bits 0x0c */ +#define esp_cmnd io_addr + 3 /* rw SCSI command bits 0x0c */ #define esp_status io_addr + 4 /* ro ESP status register 0x10 */ -#define esp_busid esp_status /* wo Bus ID for select/reselect 0x10 */ +#define esp_busid esp_status /* wo Bus ID for select/reselect 0x10 */ #define esp_intrpt io_addr + 5 /* ro Kind of interrupt 0x14 */ -#define esp_timeo esp_intrpt /* wo Timeout value for select/resel 0x14 */ +#define esp_timeo esp_intrpt /* wo Timeout value for select/resel 0x14 */ #define esp_sstep io_addr + 6 /* ro Sequence step register 0x18 */ -#define esp_stp esp_sstep /* wo Transfer period per sync 0x18 */ +#define esp_stp esp_sstep /* wo Transfer period per sync 0x18 */ #define esp_fflags io_addr + 7 /* ro Bits of current FIFO info 0x1c */ -#define esp_soff esp_fflags /* wo Sync offset 0x1c */ +#define esp_soff esp_fflags /* wo Sync offset 0x1c */ #define esp_cfg1 io_addr + 8 /* rw First configuration register 0x20 */ #define esp_cfact io_addr + 9 /* wo Clock conversion factor 0x24 */ -#define esp_status2 esp_cfact /* ro HME status2 register 0x24 */ #define esp_ctest io_addr + 10 /* wo Chip test register 0x28 */ #define esp_cfg2 io_addr + 11 /* rw Second configuration register 0x2c */ /* The following is only found on the 53C9X series SCSI chips */ #define esp_cfg3 io_addr + 12 /* rw Third configuration register 0x30 */ -#define esp_hole io_addr + 13 /* hole in register map 0x34 */ +#define esp_cfg4 io_addr + 13 /* rw Fourth configuration register 0x34 */ /* The following is found on all chips except the NCR53C90 (ESP100) */ #define esp_tchi io_addr + 14 /* rw High bits of transfer count 0x38 */ -#define esp_uid esp_tchi /* ro Unique ID code 0x38 */ -#define fas_rlo esp_tchi /* rw HME extended counter 0x38 */ +#define esp_uid esp_tchi /* ro Unique ID code 0x38 */ #define esp_fgrnd io_addr + 15 /* rw Data base for fifo 0x3c */ -#define fas_rhi esp_fgrnd /* rw HME extended counter 0x3c */ }; -#ifndef save_and_cli -#define save_and_cli(flags) save_flags(flags); cli(); -#endif - -#endif +#endif /* !defined(__i386__) */ /* Various revisions of the ESP board. */ enum esp_rev { @@ -243,26 +272,21 @@ enum esp_rev { fas236 = 0x03, fas100a = 0x04, fast = 0x05, - fashme = 0x06, - fas216 = 0x07, - espunknown = 0x08 + fas366 = 0x06, + fas216 = 0x07, + fsc = 0x08, /* SYM53C94-2 */ + espunknown = 0x09 }; /* We get one of these for each ESP probed. */ struct NCR_ESP { struct NCR_ESP *next; /* Next ESP on probed or NULL */ struct ESP_regs *eregs; /* All esp registers */ -#ifndef __i386__ - struct Linux_DMA *dma; /* Who I do transfers with. */ -#else - int dma; -#endif + int dma; /* Who I do transfers with. */ void *dregs; /* And his registers. */ struct Scsi_Host *ehost; /* Backpointer to SCSI Host */ void *edev; /* Pointer to controller base/SBus */ - char prom_name[64]; /* Name of ESP device from prom */ - int prom_node; /* Prom node where ESP found */ int esp_id; /* Unique per-ESP ID number */ /* ESP Configuration Registers */ @@ -292,11 +316,29 @@ struct NCR_ESP { unchar ireg; /* Copy of ESP interrupt register */ unchar sreg; /* Same for ESP status register */ unchar seqreg; /* The ESP sequence register */ - unchar sreg2; /* Copy of HME status2 register */ - /* The HME is the biggest piece of shit I have ever seen. */ - unchar hme_fifo_workaround_buffer[16 * 2]; /* 16-bit/entry fifo for wide scsi */ - unchar hme_fifo_workaround_count; + /* The following is set when a premature interrupt condition is detected + * in some FAS revisions. + */ + unchar fas_premature_intr_workaround; + + /* To save register writes to the ESP, which can be expensive, we + * keep track of the previous value that various registers had for + * the last target we connected to. If they are the same for the + * current target, we skip the register writes as they are not needed. + */ + unchar prev_soff, prev_stp, prev_cfg3; + + /* For each target we keep track of save/restore data + * pointer information. This needs to be updated majorly + * when we add support for tagged queueing. -DaveM + */ + struct esp_pointers { + char *saved_ptr; + struct scatterlist *saved_buffer; + int saved_this_residual; + int saved_buffers_residual; + } data_pointers[16] /*XXX [MAX_TAGS_PER_TARGET]*/; /* Clock periods, frequencies, synchronization, etc. */ unsigned int cfreq; /* Clock frequency in HZ */ @@ -308,7 +350,6 @@ struct NCR_ESP { unsigned int sync_defp; /* Default sync transfer period */ unsigned int max_period; /* longest our period can be */ unsigned int min_period; /* shortest period we can withstand */ - unsigned char ccf; /* Clock conversion factor */ /* For slow to medium speed input clock rates we shoot for 5mb/s, * but for high input clock rates we try to do 10mb/s although I * don't think a transfer can even run that fast with an ESP even @@ -328,15 +369,11 @@ struct NCR_ESP { /* Misc. info about this ESP */ enum esp_rev erev; /* ESP revision */ - int irq; /* SBus IRQ for this ESP */ + int irq; /* IRQ for this ESP */ int scsi_id; /* Who am I as initiator? */ int scsi_id_mask; /* Bitmask of 'me'. */ int diff; /* Differential SCSI bus? */ - int bursts; /* Burst sizes our DVMA supports */ - -#ifdef CONFIG_MCA - int slot; /* MCA slot the adapter occupies */ -#endif + int slot; /* Slot the adapter occupies */ /* Our command queues, only one cmd lives in the current_SC queue. */ Scsi_Cmnd *issue_SC; /* Commands to be issued */ @@ -357,6 +394,9 @@ struct NCR_ESP { unchar do_pio_cmds; /* Do command transfer with pio */ + /* How much bits do we have to shift the registers */ + unsigned char shift; + /* Functions handling DMA */ /* Required functions */ @@ -400,38 +440,38 @@ struct NCR_ESP { #define ESP_CONFIG1_SRRDISAB 0x40 /* Disable SCSI reset reports */ #define ESP_CONFIG1_SLCABLE 0x80 /* Enable slow cable mode */ -/* ESP config reg 2, read-write, found only on esp100a+esp200+esp236 chips */ -#define ESP_CONFIG2_DMAPARITY 0x01 /* enable DMA Parity (200,236) */ -#define ESP_CONFIG2_REGPARITY 0x02 /* enable reg Parity (200,236) */ +/* ESP config reg 2, read-write, found only on esp100a+esp200+esp236+fsc chips */ +#define ESP_CONFIG2_DMAPARITY 0x01 /* enable DMA Parity (200,236,fsc) */ +#define ESP_CONFIG2_REGPARITY 0x02 /* enable reg Parity (200,236,fsc) */ #define ESP_CONFIG2_BADPARITY 0x04 /* Bad parity target abort */ #define ESP_CONFIG2_SCSI2ENAB 0x08 /* Enable SCSI-2 features (tmode only) */ #define ESP_CONFIG2_HI 0x10 /* High Impedance DREQ ??? */ #define ESP_CONFIG2_HMEFENAB 0x10 /* HME features enable */ -#define ESP_CONFIG2_BCM 0x20 /* Enable byte-ctrl (236) */ -#define ESP_CONFIG2_DISPINT 0x20 /* Disable pause irq (hme) */ -#define ESP_CONFIG2_FENAB 0x40 /* Enable features (fas100,esp216) */ +#define ESP_CONFIG2_BCM 0x20 /* Enable byte-ctrl (236,fsc) */ +#define ESP_CONFIG2_FENAB 0x40 /* Enable features (fas100,esp216,fsc) */ #define ESP_CONFIG2_SPL 0x40 /* Enable status-phase latch (esp236) */ -#define ESP_CONFIG2_MKDONE 0x40 /* HME magic feature */ -#define ESP_CONFIG2_HME32 0x80 /* HME 32 extended */ +#define ESP_CONFIG2_RFB 0x80 /* Reserve FIFO byte (fsc) */ #define ESP_CONFIG2_MAGIC 0xe0 /* Invalid bits... */ -/* ESP config register 3 read-write, found only esp236+fas236+fas100a+hme chips */ -#define ESP_CONFIG3_FCLOCK 0x01 /* FAST SCSI clock rate (esp100a/hme) */ -#define ESP_CONFIG3_TEM 0x01 /* Enable thresh-8 mode (esp/fas236) */ -#define ESP_CONFIG3_FAST 0x02 /* Enable FAST SCSI (esp100a/hme) */ -#define ESP_CONFIG3_ADMA 0x02 /* Enable alternate-dma (esp/fas236) */ -#define ESP_CONFIG3_TENB 0x04 /* group2 SCSI2 support (esp100a/hme) */ -#define ESP_CONFIG3_SRB 0x04 /* Save residual byte (esp/fas236) */ -#define ESP_CONFIG3_TMS 0x08 /* Three-byte msg's ok (esp100a/hme) */ -#define ESP_CONFIG3_FCLK 0x08 /* Fast SCSI clock rate (esp/fas236) */ -#define ESP_CONFIG3_IDMSG 0x10 /* ID message checking (esp100a/hme) */ -#define ESP_CONFIG3_FSCSI 0x10 /* Enable FAST SCSI (esp/fas236) */ -#define ESP_CONFIG3_GTM 0x20 /* group2 SCSI2 support (esp/fas236) */ -#define ESP_CONFIG3_BIGID 0x20 /* SCSI-ID's are 4bits (hme) */ -#define ESP_CONFIG3_TBMS 0x40 /* Three-byte msg's ok (esp/fas236) */ -#define ESP_CONFIG3_EWIDE 0x40 /* Enable Wide-SCSI (hme) */ -#define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236) */ -#define ESP_CONFIG3_OBPUSH 0x80 /* Push odd-byte to dma (hme) */ +/* ESP config register 3 read-write, found only esp236+fas236+fas100a+fsc chips */ +#define ESP_CONFIG3_FCLOCK 0x01 /* FAST SCSI clock rate (esp100a/fas366) */ +#define ESP_CONFIG3_TEM 0x01 /* Enable thresh-8 mode (esp/fas236/fsc) */ +#define ESP_CONFIG3_FAST 0x02 /* Enable FAST SCSI (esp100a) */ +#define ESP_CONFIG3_ADMA 0x02 /* Enable alternate-dma (esp/fas236/fsc) */ +#define ESP_CONFIG3_TENB 0x04 /* group2 SCSI2 support (esp100a) */ +#define ESP_CONFIG3_SRB 0x04 /* Save residual byte (esp/fas236/fsc) */ +#define ESP_CONFIG3_TMS 0x08 /* Three-byte msg's ok (esp100a) */ +#define ESP_CONFIG3_FCLK 0x08 /* Fast SCSI clock rate (esp/fas236/fsc) */ +#define ESP_CONFIG3_IDMSG 0x10 /* ID message checking (esp100a) */ +#define ESP_CONFIG3_FSCSI 0x10 /* Enable FAST SCSI (esp/fas236/fsc) */ +#define ESP_CONFIG3_GTM 0x20 /* group2 SCSI2 support (esp/fas236/fsc) */ +#define ESP_CONFIG3_TBMS 0x40 /* Three-byte msg's ok (esp/fas236/fsc) */ +#define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236/fsc) */ + +/* ESP config register 4 read-write, found only on fsc chips */ +#define ESP_CONFIG4_BBTE 0x01 /* Back-to-Back transfer enable */ +#define ESP_CONFIG4_TEST 0x02 /* Transfer counter test mode */ +#define ESP_CONFIG4_EAN 0x04 /* Enable Active Negotiation */ /* ESP command register read-write */ /* Group 1 commands: These may be sent at any point in time to the ESP @@ -482,10 +522,9 @@ struct NCR_ESP { #define ESP_CMD_SA3 0x46 /* Select w/ATN3 */ #define ESP_CMD_RSEL3 0x47 /* Reselect3 */ -/* This bit enables the ESP's DMA on the SBus */ +/* This bit enables the ESP's DMA */ #define ESP_CMD_DMA 0x80 /* Do DMA? */ - /* ESP status register read-only */ #define ESP_STAT_PIO 0x01 /* IO phase bit */ #define ESP_STAT_PCD 0x02 /* CD phase bit */ @@ -495,21 +534,11 @@ struct NCR_ESP { #define ESP_STAT_TCNT 0x10 /* Transfer Counter Is Zero */ #define ESP_STAT_PERR 0x20 /* Parity error */ #define ESP_STAT_SPAM 0x40 /* Real bad error */ -/* This indicates the 'interrupt pending' condition on esp236, it is a reserved - * bit on other revs of the ESP. +/* This indicates the 'interrupt pending' condition, it is a reserved + * bit on old revs of the ESP (ESP100, ESP100A, FAS100A). */ #define ESP_STAT_INTR 0x80 /* Interrupt */ -/* HME only: status 2 register */ -#define ESP_STAT2_SCHBIT 0x01 /* Upper bits 3-7 of sstep enabled */ -#define ESP_STAT2_FFLAGS 0x02 /* The fifo flags are now latched */ -#define ESP_STAT2_XCNT 0x04 /* The transfer counter is latched */ -#define ESP_STAT2_CREGA 0x08 /* The command reg is active now */ -#define ESP_STAT2_WIDE 0x10 /* Interface on this adapter is wide */ -#define ESP_STAT2_F1BYTE 0x20 /* There is one byte at top of fifo */ -#define ESP_STAT2_FMSB 0x40 /* Next byte in fifo is most significant */ -#define ESP_STAT2_FEMPTY 0x80 /* FIFO is empty */ - /* The status register can be masked with ESP_STAT_PMASK and compared * with the following values to determine the current phase the ESP * (at least thinks it) is in. For our purposes we also add our own @@ -555,22 +584,24 @@ struct NCR_ESP { #define ESP_STEP_FINI5 0x05 #define ESP_STEP_FINI6 0x06 #define ESP_STEP_FINI7 0x07 +#define ESP_STEP_SOM 0x08 /* Synchronous Offset Max */ /* ESP chip-test register read-write */ #define ESP_TEST_TARG 0x01 /* Target test mode */ #define ESP_TEST_INI 0x02 /* Initiator test mode */ #define ESP_TEST_TS 0x04 /* Tristate test mode */ -/* ESP unique ID register read-only, found on fas236+fas100a only */ -#define ESP_UID_F100A 0x00 /* ESP FAS100A */ -#define ESP_UID_F236 0x02 /* ESP FAS236 */ +/* ESP unique ID register read-only, found on fas236+fas100a+fsc only */ +#define ESP_UID_F100A 0x00 /* FAS100A */ +#define ESP_UID_F236 0x02 /* FAS236 */ +#define ESP_UID_FSC 0xa2 /* NCR53CF9x-2 */ #define ESP_UID_REV 0x07 /* ESP revision */ #define ESP_UID_FAM 0xf8 /* ESP family */ /* ESP fifo flags register read-only */ /* Note that the following implies a 16 byte FIFO on the ESP. */ #define ESP_FF_FBYTES 0x1f /* Num bytes in FIFO */ -#define ESP_FF_ONOTZERO 0x20 /* offset ctr not zero (esp100) */ +#define ESP_FF_ONOTZERO 0x20 /* offset ctr not zero (esp100,fsc) */ #define ESP_FF_SSTEP 0xe0 /* Sequence step */ /* ESP clock conversion factor register write-only */ @@ -583,14 +614,13 @@ struct NCR_ESP { #define ESP_CCF_F6 0x06 /* 25.01MHz - 30MHz */ #define ESP_CCF_F7 0x07 /* 30.01MHz - 35MHz */ -/* HME only... */ -#define ESP_BUSID_RESELID 0x10 -#define ESP_BUSID_CTR32BIT 0x40 - #define ESP_BUS_TIMEOUT 275 /* In milli-seconds */ #define ESP_TIMEO_CONST 8192 +#define FSC_TIMEO_CONST 7668 #define ESP_NEG_DEFP(mhz, cfact) \ ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact))) +#define FSC_NEG_DEFP(mhz, cfact) \ + ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (7668 * (cfact))) #define ESP_MHZ_TO_CYCLE(mhertz) ((1000000000) / ((mhertz) / 1000)) #define ESP_TICK(ccf, cycle) ((7682 * (ccf) * (cycle) / 1000)) @@ -609,6 +639,8 @@ extern int nesps, esps_in_use, esps_running; extern inline void esp_cmd(struct NCR_ESP *esp, struct ESP_regs *eregs, unchar cmd); extern struct NCR_ESP *esp_allocate(Scsi_Host_Template *, void *); +extern void esp_deallocate(struct NCR_ESP *); +extern void esp_release(void); extern void esp_initialize(struct NCR_ESP *); extern void esp_intr(int, void *, struct pt_regs *); #endif /* !(NCR53C9X_H) */ diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index bb93250eb64f..fd82e36c9f98 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -95,6 +95,10 @@ #include "fastlane.h" #endif +#ifdef CONFIG_OKTAGON_SCSI +#include "oktagon_esp.h" +#endif + #ifdef CONFIG_ATARI_SCSI #include "atari_scsi.h" #endif @@ -327,6 +331,10 @@ #include "jazz_esp.h" #endif +#ifdef CONFIG_SUN3X_ESP +#include "sun3x_esp.h" +#endif + /* * Moved ppa driver to the end of the probe list * since it is a removable host adapter. @@ -402,6 +410,9 @@ static Scsi_Host_Template builtin_scsi_hosts[] = #ifdef CONFIG_FASTLANE_SCSI SCSI_FASTLANE, #endif +#ifdef CONFIG_OKTAGON_SCSI + SCSI_OKTAGON_ESP, +#endif #endif #ifdef CONFIG_ATARI @@ -596,6 +607,9 @@ static Scsi_Host_Template builtin_scsi_hosts[] = #ifdef CONFIG_SCSI_IMM IMM, #endif +#ifdef CONFIG_SUN3X_ESP + SCSI_SUN3X_ESP, +#endif #ifdef CONFIG_SCSI_DEBUG SCSI_DEBUG, #endif diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c index 637e6f960c9f..89f5649b083c 100644 --- a/drivers/scsi/ncr53c8xx.c +++ b/drivers/scsi/ncr53c8xx.c @@ -588,9 +588,7 @@ static spinlock_t driver_lock; #endif #if defined(__i386__) || !defined(NCR_IOMAPPED) -__initfunc( -static vm_offset_t remap_pci_mem(u_long base, u_long size) -) +static vm_offset_t __init remap_pci_mem(u_long base, u_long size) { u_long page_base = ((u_long) base) & PAGE_MASK; u_long page_offs = ((u_long) base) - page_base; @@ -599,9 +597,7 @@ static vm_offset_t remap_pci_mem(u_long base, u_long size) return (vm_offset_t) (page_remapped? (page_remapped + page_offs) : 0UL); } -__initfunc( -static void unmap_pci_mem(vm_offset_t vaddr, u_long size) -) +static void __init unmap_pci_mem(vm_offset_t vaddr, u_long size) { if (vaddr) iounmap((void *) (vaddr & PAGE_MASK)); @@ -3701,9 +3697,7 @@ static struct scripth scripth0 __initdata = { **========================================================== */ -__initfunc( -void ncr_script_fill (struct script * scr, struct scripth * scrh) -) +void __init ncr_script_fill (struct script * scr, struct scripth * scrh) { int i; ncrcmd *p; @@ -3778,9 +3772,7 @@ void ncr_script_fill (struct script * scr, struct scripth * scrh) **========================================================== */ -__initfunc( -static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len) -) +static void __init ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len) { ncrcmd opcode, new, old, tmp1, tmp2; ncrcmd *start, *end; @@ -4036,10 +4028,8 @@ static inline void ncr_init_burst(ncb_p np, u_char bc) ** Get target set-up from Symbios format NVRAM. */ -__initfunc( -static void - ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram) -) +static void __init +ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram) { tcb_p tp = &np->target[target]; Symbios_target *tn = &nvram->target[target]; @@ -4059,10 +4049,8 @@ static void ** Get target set-up from Tekram format NVRAM. */ -__initfunc( -static void - ncr_Tekram_setup_target(ncb_p np, int target, Tekram_nvram *nvram) -) +static void __init +ncr_Tekram_setup_target(ncb_p np, int target, Tekram_nvram *nvram) { tcb_p tp = &np->target[target]; struct Tekram_target *tn = &nvram->target[target]; @@ -4088,9 +4076,7 @@ static void } #endif /* SCSI_NCR_NVRAM_SUPPORT */ -__initfunc( -static int ncr_prepare_setting(ncb_p np, ncr_nvram *nvram) -) +static int __init ncr_prepare_setting(ncb_p np, ncr_nvram *nvram) { u_char burst_max; u_long period; @@ -4372,9 +4358,7 @@ static int ncr_prepare_setting(ncb_p np, ncr_nvram *nvram) #ifdef SCSI_NCR_DEBUG_NVRAM -__initfunc( -void ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram) -) +void __init ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram) { int i; @@ -4404,9 +4388,7 @@ void ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram) static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120}; -__initfunc( -void ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram) -) +void __init ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram) { int i, tags, boot_delay; char *rem; @@ -4465,9 +4447,8 @@ void ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram) ** start the timer daemon. */ -__initfunc( -static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device) -) +static int __init +ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device) { struct host_data *host_data; ncb_p np; @@ -8807,9 +8788,7 @@ static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd) */ #ifndef NCR_IOMAPPED -__initfunc( -static int ncr_regtest (struct ncb* np) -) +static int __init ncr_regtest (struct ncb* np) { register volatile u_int32 data; /* @@ -8833,9 +8812,7 @@ static int ncr_regtest (struct ncb* np) } #endif -__initfunc( -static int ncr_snooptest (struct ncb* np) -) +static int __init ncr_snooptest (struct ncb* np) { u_int32 ncr_rd, ncr_wr, ncr_bk, host_rd, host_wr, pc; int i, err=0; @@ -9098,9 +9075,7 @@ static void ncr_selectclock(ncb_p np, u_char scntl3) /* * calculate NCR SCSI clock frequency (in KHz) */ -__initfunc( -static unsigned ncrgetfreq (ncb_p np, int gen) -) +static unsigned __init ncrgetfreq (ncb_p np, int gen) { unsigned ms = 0; @@ -9148,9 +9123,7 @@ static unsigned ncrgetfreq (ncb_p np, int gen) /* * Get/probe NCR SCSI clock frequency */ -__initfunc( -static void ncr_getclock (ncb_p np, int mult) -) +static void __init ncr_getclock (ncb_p np, int mult) { unsigned char scntl3 = INB(nc_scntl3); unsigned char stest1 = INB(nc_stest1); @@ -9238,9 +9211,8 @@ static void ncr_getclock (ncb_p np, int mult) #define ARG_SEP ',' #endif -__initfunc( -void ncr53c8xx_setup(char *str, int *ints) -) + +void __init ncr53c8xx_setup(char *str, int *ints) { #ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT char *cur = str; @@ -9351,9 +9323,7 @@ static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, ** Returns the number of boards successfully attached. */ -__initfunc( -static void ncr_print_driver_setup(void) -) +static void __init ncr_print_driver_setup(void) { #define YesNo(y) y ? 'y' : 'n' printk ("ncr53c8xx: setup=disc:%c,specf:%d,ultra:%d,tags:%d,sync:%d," @@ -9392,10 +9362,8 @@ static ncr_chip ncr_chip_table[] __initdata = SCSI_NCR_CHIP_TABLE; static ushort ncr_chip_ids[] __initdata = SCSI_NCR_CHIP_IDS; #ifdef SCSI_NCR_NVRAM_SUPPORT -__initfunc( -static int +static int __init ncr_attach_using_nvram(Scsi_Host_Template *tpnt, int nvram_index, int count, ncr_device device[]) -) { int i, j; int attach_count = 0; @@ -9476,9 +9444,7 @@ out: } #endif /* SCSI_NCR_NVRAM_SUPPORT */ -__initfunc( -int ncr53c8xx_detect(Scsi_Host_Template *tpnt) -) +int __init ncr53c8xx_detect(Scsi_Host_Template *tpnt) { int i, j; int chips; @@ -9605,44 +9571,16 @@ if (ncr53c8xx) ** Return the offset immediately after the base address that has ** been read. Btw, we blindly assume that the high 32 bits of 64 bit ** base addresses are set to zero on 32 bit architectures. +** (the pci generic code now does this for us) ** */ -#if LINUX_VERSION_CODE <= LinuxVersionCode(2,1,92) -__initfunc( -static int -pci_read_base_address(u_char bus, u_char device_fn, int offset, u_long *base) -) -{ - u_int32 tmp; - pcibios_read_config_dword(bus, device_fn, offset, &tmp); - *base = tmp; - offset += sizeof(u_int32); - if ((tmp & 0x7) == 0x4) { -#if BITS_PER_LONG > 32 - pcibios_read_config_dword(bus, device_fn, offset, &tmp); - *base |= (((u_long)tmp) << 32); -#endif - offset += sizeof(u_int32); - } - return offset; -} -#else /* LINUX_VERSION_CODE > LinuxVersionCode(2,1,92) */ -__initfunc( -static int +static int __init pci_get_base_address(struct pci_dev *pdev, int index, u_long *base) -) { - *base = pdev->base_address[index++]; - if ((*base & 0x7) == 0x4) { -#if BITS_PER_LONG > 32 - *base |= (((u_long)pdev->base_address[index]) << 32); -#endif - ++index; - } + *base = pdev->resource[++index].start; return index; } -#endif /* ** Read and check the PCI configuration for any detected NCR @@ -9650,10 +9588,9 @@ pci_get_base_address(struct pci_dev *pdev, int index, u_long *base) ** been detected. */ -__initfunc( -static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, + +static int __init ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, uchar bus, uchar device_fn, ncr_device *device) -) { ushort vendor_id, device_id, command; uchar cache_line_size, latency_timer; @@ -10907,9 +10844,7 @@ static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char static void nvram_stop(ncr_slot *np, u_char *gpreg); static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode); -__initfunc( -static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram) -) +static int __init ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram) { static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0}; u_char gpcntl, gpreg; @@ -10998,9 +10933,8 @@ out: /* * Read Symbios NvRAM data and compute checksum. */ -__initfunc( -static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl) -) +static u_short __init nvram_read_data(ncr_slot *np, u_char *data, int len, + u_char *gpreg, u_char *gpcntl) { int x; u_short csum; @@ -11017,9 +10951,7 @@ static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpre /* * Send START condition to NVRAM to wake it up. */ -__initfunc( -static void nvram_start(ncr_slot *np, u_char *gpreg) -) +static void __init nvram_start(ncr_slot *np, u_char *gpreg) { nvram_setBit(np, 1, gpreg, SET_BIT); nvram_setBit(np, 0, gpreg, SET_CLK); @@ -11031,9 +10963,9 @@ static void nvram_start(ncr_slot *np, u_char *gpreg) * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK, * GPIO0 must already be set as an output */ -__initfunc( -static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl) -) +static void __init nvram_write_byte(ncr_slot *np, u_char *ack_data, + u_char write_data, u_char *gpreg, + u_char *gpcntl) { int x; @@ -11047,9 +10979,9 @@ static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, * READ a byte from the NVRAM and then send an ACK to say we have got it, * GPIO0 must already be set as an input */ -__initfunc( -static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl) -) +static void __init nvram_read_byte(ncr_slot *np, u_char *read_data, + u_char ack_data, u_char *gpreg, + u_char *gpcntl) { int x; u_char read_bit; @@ -11067,9 +10999,8 @@ static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_ * Output an ACK to the NVRAM after reading, * change GPIO0 to output and when done back to an input */ -__initfunc( -static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl) -) +static void __init nvram_writeAck(ncr_slot *np, u_char write_bit, + u_char *gpreg, u_char *gpcntl) { OUTB (nc_gpcntl, *gpcntl & 0xfe); nvram_doBit(np, 0, write_bit, gpreg); @@ -11080,9 +11011,8 @@ static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char * Input an ACK from NVRAM after writing, * change GPIO0 to input and when done back to an output */ -__initfunc( -static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl) -) +static void __init nvram_readAck(ncr_slot *np, u_char *read_bit, + u_char *gpreg, u_char *gpcntl) { OUTB (nc_gpcntl, *gpcntl | 0x01); nvram_doBit(np, read_bit, 1, gpreg); @@ -11093,9 +11023,8 @@ static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char * Read or write a bit to the NVRAM, * read if GPIO0 input else write if GPIO0 output */ -__initfunc( -static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg) -) +static void __init nvram_doBit(ncr_slot *np, u_char *read_bit, + u_char write_bit, u_char *gpreg) { nvram_setBit(np, write_bit, gpreg, SET_BIT); nvram_setBit(np, 0, gpreg, SET_CLK); @@ -11108,9 +11037,7 @@ static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char /* * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!! */ -__initfunc( -static void nvram_stop(ncr_slot *np, u_char *gpreg) -) +static void __init nvram_stop(ncr_slot *np, u_char *gpreg) { nvram_setBit(np, 0, gpreg, SET_CLK); nvram_setBit(np, 1, gpreg, SET_BIT); @@ -11119,9 +11046,8 @@ static void nvram_stop(ncr_slot *np, u_char *gpreg) /* * Set/clear data/clock bit in GPIO0 */ -__initfunc( -static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode) -) +static void __init nvram_setBit(ncr_slot *np, u_char write_bit, + u_char *gpreg, int bit_mode) { UDELAY (5); switch (bit_mode){ @@ -11172,9 +11098,7 @@ static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg); static void Tnvram_Stop(ncr_slot *np, u_char *gpreg); static void Tnvram_Clk(ncr_slot *np, u_char *gpreg); -__initfunc( -static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram) -) +static int __init ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram) { u_char gpcntl, gpreg; u_char old_gpcntl, old_gpreg; @@ -11209,9 +11133,8 @@ static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram) /* * Read Tekram NvRAM data and compute checksum. */ -__initfunc( -static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg) -) +static u_short __init Tnvram_read_data(ncr_slot *np, u_short *data, int len, + u_char *gpreg) { u_char read_bit; u_short csum; @@ -11236,9 +11159,8 @@ static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gp /* * Send read command and address to NVRAM */ -__initfunc( -static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg) -) +static void __init Tnvram_Send_Command(ncr_slot *np, u_short write_data, + u_char *read_bit, u_char *gpreg) { int x; @@ -11252,9 +11174,8 @@ static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_b /* * READ a byte from the NVRAM */ -__initfunc( -static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg) -) +static void __init Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, + u_char *gpreg) { int x; u_char read_bit; @@ -11273,9 +11194,8 @@ static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg) /* * Read bit from NVRAM */ -__initfunc( -static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg) -) +static void __init Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, + u_char *gpreg) { UDELAY (2); Tnvram_Clk(np, gpreg); @@ -11285,9 +11205,8 @@ static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg) /* * Write bit to GPIO0 */ -__initfunc( -static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg) -) +static void __init Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, + u_char *gpreg) { if (write_bit & 0x01) *gpreg |= 0x02; @@ -11305,9 +11224,7 @@ static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg) /* * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!! */ -__initfunc( -static void Tnvram_Stop(ncr_slot *np, u_char *gpreg) -) +static void __init Tnvram_Stop(ncr_slot *np, u_char *gpreg) { *gpreg &= 0xef; OUTB (nc_gpreg, *gpreg); @@ -11319,9 +11236,7 @@ static void Tnvram_Stop(ncr_slot *np, u_char *gpreg) /* * Pulse clock bit in GPIO0 */ -__initfunc( -static void Tnvram_Clk(ncr_slot *np, u_char *gpreg) -) +static void __init Tnvram_Clk(ncr_slot *np, u_char *gpreg) { OUTB (nc_gpreg, *gpreg | 0x04); UDELAY (2); diff --git a/drivers/scsi/oktagon_esp.c b/drivers/scsi/oktagon_esp.c new file mode 100644 index 000000000000..4223226a7b4d --- /dev/null +++ b/drivers/scsi/oktagon_esp.c @@ -0,0 +1,599 @@ +/* + * Oktagon_esp.c -- Driver for bsc Oktagon + * + * Written by Carsten Pluntke 1998 + * + * Based on cyber_esp.c + */ + +#include + +#if defined(CONFIG_AMIGA) || defined(CONFIG_APUS) +#define USE_BOTTOM_HALF +#endif + +#define __KERNEL_SYSCALLS__ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scsi.h" +#include "hosts.h" +#include "NCR53C9x.h" +#include "oktagon_esp.h" + +#include +#include +#include +#include + +#include + +#ifdef USE_BOTTOM_HALF +#include +#include +#endif + +#include + +static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count); +static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_dump_state(struct NCR_ESP *esp); +static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length); +static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length); +static void dma_ints_off(struct NCR_ESP *esp); +static void dma_ints_on(struct NCR_ESP *esp); +static int dma_irq_p(struct NCR_ESP *esp); +static void dma_led_off(struct NCR_ESP *esp); +static void dma_led_on(struct NCR_ESP *esp); +static int dma_ports_p(struct NCR_ESP *esp); +static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write); + +static void dma_irq_exit(struct NCR_ESP *esp); +static void dma_invalidate(struct NCR_ESP *esp); + +static void dma_mmu_get_scsi_one(struct NCR_ESP *,Scsi_Cmnd *); +static void dma_mmu_get_scsi_sgl(struct NCR_ESP *,Scsi_Cmnd *); +static void dma_mmu_release_scsi_one(struct NCR_ESP *,Scsi_Cmnd *); +static void dma_mmu_release_scsi_sgl(struct NCR_ESP *,Scsi_Cmnd *); +static void dma_advance_sg(Scsi_Cmnd *); +static int oktagon_notify_reboot(struct notifier_block *this, unsigned long code, void *x); + +void esp_bootup_reset(struct NCR_ESP *esp,struct ESP_regs *eregs); + +#ifdef USE_BOTTOM_HALF +static void dma_commit(void *opaque); + +long oktag_to_io(long *paddr, long *addr, long len); +long oktag_from_io(long *addr, long *paddr, long len); + +static struct tq_struct tq_fake_dma = { NULL, 0, dma_commit, NULL }; + +#define DMA_MAXTRANSFER 0x8000 + +#else + +/* + * No bottom half. Use transfer directly from IRQ. Find a narrow path + * between too much IRQ overhead and clogging the IRQ for too long. + */ + +#define DMA_MAXTRANSFER 0x1000 + +#endif + +static struct notifier_block oktagon_notifier = { + oktagon_notify_reboot, + NULL, + 0 +}; + +static long *paddress; +static long *address; +static long len; +static long dma_on; +static int direction; +static struct NCR_ESP *current_esp; + + +volatile unsigned char cmd_buffer[16]; + /* This is where all commands are put + * before they are trasfered to the ESP chip + * via PIO. + */ + +/***************************************************************** Detection */ +int oktagon_esp_detect(Scsi_Host_Template *tpnt) +{ + struct NCR_ESP *esp; + const struct ConfigDev *esp_dev; + int key; + unsigned long address; + struct ESP_regs *eregs; + + if((key = zorro_find(ZORRO_PROD_BSC_OKTAGON_2008, 0, 0))){ + esp_dev = zorro_get_board(key); + + /* + * It is a SCSI controller. + * Hardwire Host adapter to SCSI ID 7 + */ + + address = (unsigned long)ZTWO_VADDR(esp_dev->cd_BoardAddr); + eregs = (struct ESP_regs *)(address + OKTAGON_ESP_ADDR); + + /* This line was 5 lines lower */ + esp = esp_allocate(tpnt, (void *) esp_dev); + + /* we have to shift the registers only one bit for oktagon */ + esp->shift = 1; + + esp_write(eregs->esp_cfg1, (ESP_CONFIG1_PENABLE | 7)); + udelay(5); + if (esp_read(eregs->esp_cfg1) != (ESP_CONFIG1_PENABLE | 7)) + return 0; /* Bail out if address did not hold data */ + + /* Do command transfer with programmed I/O */ + esp->do_pio_cmds = 1; + + /* Required functions */ + esp->dma_bytes_sent = &dma_bytes_sent; + esp->dma_can_transfer = &dma_can_transfer; + esp->dma_dump_state = &dma_dump_state; + esp->dma_init_read = &dma_init_read; + esp->dma_init_write = &dma_init_write; + esp->dma_ints_off = &dma_ints_off; + esp->dma_ints_on = &dma_ints_on; + esp->dma_irq_p = &dma_irq_p; + esp->dma_ports_p = &dma_ports_p; + esp->dma_setup = &dma_setup; + + /* Optional functions */ + esp->dma_barrier = 0; + esp->dma_drain = 0; + esp->dma_invalidate = &dma_invalidate; + esp->dma_irq_entry = 0; + esp->dma_irq_exit = &dma_irq_exit; + esp->dma_led_on = &dma_led_on; + esp->dma_led_off = &dma_led_off; + esp->dma_poll = 0; + esp->dma_reset = 0; + + esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one; + esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl; + esp->dma_mmu_release_scsi_one = &dma_mmu_release_scsi_one; + esp->dma_mmu_release_scsi_sgl = &dma_mmu_release_scsi_sgl; + esp->dma_advance_sg = &dma_advance_sg; + + /* SCSI chip speed */ + /* Looking at the quartz of the SCSI board... */ + esp->cfreq = 25000000; + + /* The DMA registers on the CyberStorm are mapped + * relative to the device (i.e. in the same Zorro + * I/O block). + */ + esp->dregs = (void *)(address + OKTAGON_DMA_ADDR); + + paddress = (long *) esp->dregs; + + /* ESP register base */ + esp->eregs = eregs; + + /* Set the command buffer */ + esp->esp_command = (volatile unsigned char*) cmd_buffer; + + /* Yes, the virtual address. See below. */ + esp->esp_command_dvma = (__u32) cmd_buffer; + + esp->irq = IRQ_AMIGA_PORTS; + esp->slot = key; + request_irq(IRQ_AMIGA_PORTS, esp_intr, SA_SHIRQ, + "BSC Oktagon SCSI", esp_intr); + + /* Figure out our scsi ID on the bus */ + esp->scsi_id = 7; + + /* We don't have a differential SCSI-bus. */ + esp->diff = 0; + + esp_initialize(esp); + + zorro_config_board(key, 0); + printk("ESP_Oktagon Driver 1.1" +#ifdef USE_BOTTOM_HALF + " [BOTTOM_HALF]" +#else + " [IRQ]" +#endif + " registered.\n"); + printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps,esps_in_use); + esps_running = esps_in_use; + current_esp = esp; + register_reboot_notifier(&oktagon_notifier); + return esps_in_use; + } + return 0; +} + + +/* + * On certain configurations the SCSI equipment gets confused on reboot, + * so we have to reset it then. + */ + +static int +oktagon_notify_reboot(struct notifier_block *this, unsigned long code, void *x) +{ + struct NCR_ESP *esp; + + if((code == SYS_DOWN || code == SYS_HALT) && (esp = current_esp)) + { + esp_bootup_reset(esp,esp->eregs); + udelay(500); /* Settle time. Maybe unneccessary. */ + } + return NOTIFY_DONE; +} + + + +#ifdef USE_BOTTOM_HALF + + +/* + * The bsc Oktagon controller has no real DMA, so we have to do the 'DMA + * transfer' in the interrupt (Yikes!) or use a bottom half to not to clutter + * IRQ's for longer-than-good. + * + * FIXME + * BIG PROBLEM: 'len' is usually the buffer length, not the expected length + * of the data. So DMA may finish prematurely, further reads lead to + * 'machine check' on APUS systems (don't know about m68k systems, AmigaOS + * deliberately ignores the bus faults) and a normal copy-loop can't + * be exited prematurely just at the right moment by the dma_invalidate IRQ. + * So do it the hard way, write an own copier in assembler and + * catch the exception. + * -- Carsten + */ + + +static void dma_commit(void *opaque) +{ + long wait,len2,pos; + struct NCR_ESP *esp; + + ESPDATA(("Transfer: %ld bytes, Address 0x%08lX, Direction: %d\n", + len,(long) address,direction)); + dma_ints_off(current_esp); + + pos = 0; + wait = 1; + if(direction) /* write? (memory to device) */ + { + while(len > 0) + { + len2 = oktag_to_io(paddress, address+pos, len); + if(!len2) + { + if(wait > 1000) + { + printk("Expedited DMA exit (writing) %ld\n",len); + break; + } + mdelay(wait); + wait *= 2; + } + pos += len2; + len -= len2*sizeof(long); + } + } else { + while(len > 0) + { + len2 = oktag_from_io(address+pos, paddress, len); + if(!len2) + { + if(wait > 1000) + { + printk("Expedited DMA exit (reading) %ld\n",len); + break; + } + mdelay(wait); + wait *= 2; + } + pos += len2; + len -= len2*sizeof(long); + } + } + + /* to make esp->shift work */ + esp=current_esp; + +#if 0 + len2 = (esp_read(current_esp->eregs->esp_tclow) & 0xff) | + ((esp_read(current_esp->eregs->esp_tcmed) & 0xff) << 8); + + /* + * Uh uh. If you see this, len and transfer count registers were out of + * sync. That means really serious trouble. + */ + + if(len2) + printk("Eeeek!! Transfer count still %ld!\n",len2); +#endif + + /* + * Normally we just need to exit and wait for the interrupt to come. + * But at least one device (my Microtek ScanMaker 630) regularly mis- + * calculates the bytes it should send which is really ugly because + * it locks up the SCSI bus if not accounted for. + */ + + if(!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR)) + { + long len = 100; + long trash[10]; + + /* + * Interrupt bit was not set. Either the device is just plain lazy + * so we give it a 10 ms chance or... + */ + while(len-- && (!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR))) + udelay(100); + + + if(!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR)) + { + /* + * So we think that the transfer count is out of sync. Since we + * have all we want we are happy and can ditch the trash. + */ + + len = DMA_MAXTRANSFER; + + while(len-- && (!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR))) + oktag_from_io(trash,paddress,2); + + if(!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR)) + { + /* + * Things really have gone wrong. If we leave the system in that + * state, the SCSI bus is locked forever. I hope that this will + * turn the system in a more or less running state. + */ + printk("Device is bolixed, trying bus reset...\n"); + esp_bootup_reset(current_esp,current_esp->eregs); + } + } + } + + ESPDATA(("Transfer_finale: do_data_finale should come\n")); + + len = 0; + dma_on = 0; + dma_ints_on(current_esp); +} + +#endif + +/************************************************************* DMA Functions */ +static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count) +{ + /* Since the CyberStorm DMA is fully dedicated to the ESP chip, + * the number of bytes sent (to the ESP chip) equals the number + * of bytes in the FIFO - there is no buffering in the DMA controller. + * XXXX Do I read this right? It is from host to ESP, right? + */ + return fifo_count; +} + +static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + unsigned long sz = sp->SCp.this_residual; + if(sz > DMA_MAXTRANSFER) + sz = DMA_MAXTRANSFER; + return sz; +} + +static void dma_dump_state(struct NCR_ESP *esp) +{ +} + +/* + * What the f$@& is this? + * + * Some SCSI devices (like my Microtek ScanMaker 630 scanner) want to transfer + * more data than requested. How much? Dunno. So ditch the bogus data into + * the sink, hoping the device will advance to the next phase sooner or later. + * + * -- Carsten + */ + +static long oktag_eva_buffer[16]; /* The data sink */ + +static void oktag_check_dma(void) +{ + struct NCR_ESP *esp; + + esp=current_esp; + if(!len) + { + address = oktag_eva_buffer; + len = 2; + /* esp_do_data sets them to zero like len */ + esp_write(current_esp->eregs->esp_tclow,2); + esp_write(current_esp->eregs->esp_tcmed,0); + } +} + +static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length) +{ + /* Zorro is noncached, everything else done using processor. */ + /* cache_clear(addr, length); */ + + if(dma_on) + panic("dma_init_read while dma process is initialized/running!\n"); + direction = 0; + address = (long *) vaddress; + current_esp = esp; + len = length; + oktag_check_dma(); + dma_on = 1; +} + +static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length) +{ + /* cache_push(addr, length); */ + + if(dma_on) + panic("dma_init_write while dma process is initialized/running!\n"); + direction = 1; + address = (long *) vaddress; + current_esp = esp; + len = length; + oktag_check_dma(); + dma_on = 1; +} + +static void dma_ints_off(struct NCR_ESP *esp) +{ + disable_irq(esp->irq); +} + +static void dma_ints_on(struct NCR_ESP *esp) +{ + enable_irq(esp->irq); +} + +static int dma_irq_p(struct NCR_ESP *esp) +{ + /* It's important to check the DMA IRQ bit in the correct way! */ + return (esp_read(esp->eregs->esp_status) & ESP_STAT_INTR); +} + +static void dma_led_off(struct NCR_ESP *esp) +{ +} + +static void dma_led_on(struct NCR_ESP *esp) +{ +} + +static int dma_ports_p(struct NCR_ESP *esp) +{ + return ((custom.intenar) & IF_PORTS); +} + +static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write) +{ + /* On the Sparc, DMA_ST_WRITE means "move data from device to memory" + * so when (write) is true, it actually means READ! + */ + if(write){ + dma_init_read(esp, addr, count); + } else { + dma_init_write(esp, addr, count); + } +} + +/* + * IRQ entry when DMA transfer is ready to be started + */ + +static void dma_irq_exit(struct NCR_ESP *esp) +{ +#ifdef USE_BOTTOM_HALF + if(dma_on) + { + tq_fake_dma.sync = 0; + queue_task(&tq_fake_dma,&tq_immediate); + mark_bh(IMMEDIATE_BH); + } +#else + while(len && !dma_irq_p(esp)) + { + if(direction) + *paddress = *address++; + else + *address++ = *paddress; + len -= (sizeof(long)); + } + len = 0; + dma_on = 0; +#endif +} + +/* + * IRQ entry when DMA has just finished + */ + +static void dma_invalidate(struct NCR_ESP *esp) +{ +} + +/* + * Since the processor does the data transfer we have to use the custom + * mmu interface to pass the virtual address, not the physical. + */ + +void dma_mmu_get_scsi_one(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + sp->SCp.have_data_in = (int) sp->SCp.ptr = + sp->request_buffer; +} + +void dma_mmu_get_scsi_sgl(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + sp->SCp.ptr = + sp->SCp.buffer->address; +} + +void dma_mmu_release_scsi_one(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ +} + +void dma_mmu_release_scsi_sgl(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ +} + +void dma_advance_sg(Scsi_Cmnd *sp) +{ + sp->SCp.ptr = sp->SCp.buffer->address; +} + +#ifdef MODULE + +#define HOSTS_C + +#include "oktagon_esp.h" + +Scsi_Host_Template driver_template = SCSI_OKTAGON_ESP; + +#include "scsi_module.c" + +#endif + +int oktagon_esp_release(struct Scsi_Host *instance) +{ +#ifdef MODULE + unsigned int key; + + key = ((struct NCR_ESP *)instance->hostdata)->slot; + esp_release(); + zorro_unconfig_board(key, 0); + free_irq(IRQ_AMIGA_PORTS, esp_intr); + unregister_reboot_notifier(&oktagon_notifier); +#endif + return 1; +} diff --git a/drivers/scsi/oktagon_esp.h b/drivers/scsi/oktagon_esp.h new file mode 100644 index 000000000000..91a3a7cfedec --- /dev/null +++ b/drivers/scsi/oktagon_esp.h @@ -0,0 +1,57 @@ +/* oktagon_esp.h: Defines and structures for the CyberStorm SCSI Mk II driver. + * + * Copyright (C) 1996 Jesper Skov (jskov@cs.auc.dk) + */ + +#include "NCR53C9x.h" + +#ifndef OKTAGON_ESP_H +#define OKTAGON_ESP_H + +/* The controller registers can be found in the Z2 config area at these + * offsets: + */ +#define OKTAGON_ESP_ADDR 0x03000 +#define OKTAGON_DMA_ADDR 0x01000 + + +/* The CyberStorm II DMA interface */ +struct oktagon_dma_registers { + volatile unsigned char cond_reg; /* DMA cond (ro) [0x000] */ +#define ctrl_reg cond_reg /* DMA control (wo) [0x000] */ + unsigned char dmapad4[0x3f]; + volatile unsigned char dma_addr0; /* DMA address (MSB) [0x040] */ + unsigned char dmapad1[3]; + volatile unsigned char dma_addr1; /* DMA address [0x044] */ + unsigned char dmapad2[3]; + volatile unsigned char dma_addr2; /* DMA address [0x048] */ + unsigned char dmapad3[3]; + volatile unsigned char dma_addr3; /* DMA address (LSB) [0x04c] */ +}; + +extern int oktagon_esp_detect(struct SHT *); +extern int oktagon_esp_release(struct Scsi_Host *); +extern const char *esp_info(struct Scsi_Host *); +extern int esp_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +extern int esp_command(Scsi_Cmnd *); +extern int esp_abort(Scsi_Cmnd *); +extern int esp_reset(Scsi_Cmnd *, unsigned int); +extern int esp_proc_info(char *buffer, char **start, off_t offset, int length, + int hostno, int inout); + +#define SCSI_OKTAGON_ESP { \ + proc_dir: &proc_scsi_esp, \ + proc_info: &esp_proc_info, \ + name: "BSC Oktagon SCSI", \ + detect: oktagon_esp_detect, \ + release: oktagon_esp_release, \ + queuecommand: esp_queue, \ + abort: esp_abort, \ + reset: esp_reset, \ + can_queue: 7, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 1, \ + use_clustering: ENABLE_CLUSTERING } + +#endif /* OKTAGON_ESP_H */ diff --git a/drivers/scsi/oktagon_io.S b/drivers/scsi/oktagon_io.S new file mode 100644 index 000000000000..08ce8d80d8f5 --- /dev/null +++ b/drivers/scsi/oktagon_io.S @@ -0,0 +1,195 @@ +/* -*- mode: asm -*- + * Due to problems while transferring data I've put these routines as assembly + * code. + * Since I'm no PPC assembler guru, the code is just the assembler version of + +int oktag_to_io(long *paddr,long *addr,long len) +{ + long *addr2 = addr; + for(len=(len+sizeof(long)-1)/sizeof(long);len--;) + *paddr = *addr2++; + return addr2 - addr; +} + +int oktag_from_io(long *addr,long *paddr,long len) +{ + long *addr2 = addr; + for(len=(len+sizeof(long)-1)/sizeof(long);len--;) + *addr2++ = *paddr; + return addr2 - addr; +} + + * assembled using gcc -O2 -S, with two exception catch points where data + * is moved to/from the IO register. + */ + +#include + +#ifdef CONFIG_APUS + + .file "oktagon_io.c" + +gcc2_compiled.: +/* + .section ".text" +*/ + .align 2 + .globl oktag_to_io + .type oktag_to_io,@function +oktag_to_io: + addi 5,5,3 + srwi 5,5,2 + cmpwi 1,5,0 + mr 9,3 + mr 3,4 + addi 5,5,-1 + bc 12,6,.L3 +.L5: + cmpwi 1,5,0 + lwz 0,0(3) + addi 3,3,4 + addi 5,5,-1 +exp1: stw 0,0(9) + bc 4,6,.L5 +.L3: +ret1: subf 3,4,3 + srawi 3,3,2 + blr +.Lfe1: + .size oktag_to_io,.Lfe1-oktag_to_io + .align 2 + .globl oktag_from_io + .type oktag_from_io,@function +oktag_from_io: + addi 5,5,3 + srwi 5,5,2 + cmpwi 1,5,0 + mr 9,3 + addi 5,5,-1 + bc 12,6,.L9 +.L11: + cmpwi 1,5,0 +exp2: lwz 0,0(4) + addi 5,5,-1 + stw 0,0(3) + addi 3,3,4 + bc 4,6,.L11 +.L9: +ret2: subf 3,9,3 + srawi 3,3,2 + blr +.Lfe2: + .size oktag_from_io,.Lfe2-oktag_from_io + .ident "GCC: (GNU) egcs-2.90.29 980515 (egcs-1.0.3 release)" + +/* + * Exception table. + * Second longword shows where to jump when an exception at the addr the first + * longword is pointing to is caught. + */ + +.section __ex_table,"a" + .align 2 +oktagon_except: + .long exp1,ret1 + .long exp2,ret2 + +#else + +/* +The code which follows is for 680x0 based assembler and is meant for +Linux/m68k. It was created by cross compiling the code using the +instructions given above. I then added the four labels used in the +exception handler table at the bottom of this file. +- Kevin +*/ + +#ifdef CONFIG_AMIGA + + .file "oktagon_io.c" + .version "01.01" +gcc2_compiled.: +.text + .align 2 +.globl oktag_to_io + .type oktag_to_io,@function +oktag_to_io: + link.w %a6,#0 + move.l %d2,-(%sp) + move.l 8(%a6),%a1 + move.l 12(%a6),%d1 + move.l %d1,%a0 + move.l 16(%a6),%d0 + addq.l #3,%d0 + lsr.l #2,%d0 + subq.l #1,%d0 + moveq.l #-1,%d2 + cmp.l %d0,%d2 + jbeq .L3 +.L5: +exp1: + move.l (%a0)+,(%a1) + dbra %d0,.L5 + clr.w %d0 + subq.l #1,%d0 + jbcc .L5 +.L3: +ret1: + move.l %a0,%d0 + sub.l %d1,%d0 + asr.l #2,%d0 + move.l -4(%a6),%d2 + unlk %a6 + rts + +.Lfe1: + .size oktag_to_io,.Lfe1-oktag_to_io + .align 2 +.globl oktag_from_io + .type oktag_from_io,@function +oktag_from_io: + link.w %a6,#0 + move.l %d2,-(%sp) + move.l 8(%a6),%d1 + move.l 12(%a6),%a1 + move.l %d1,%a0 + move.l 16(%a6),%d0 + addq.l #3,%d0 + lsr.l #2,%d0 + subq.l #1,%d0 + moveq.l #-1,%d2 + cmp.l %d0,%d2 + jbeq .L9 +.L11: +exp2: + move.l (%a1),(%a0)+ + dbra %d0,.L11 + clr.w %d0 + subq.l #1,%d0 + jbcc .L11 +.L9: +ret2: + move.l %a0,%d0 + sub.l %d1,%d0 + asr.l #2,%d0 + move.l -4(%a6),%d2 + unlk %a6 + rts +.Lfe2: + .size oktag_from_io,.Lfe2-oktag_from_io + .ident "GCC: (GNU) 2.7.2.1" + +/* + * Exception table. + * Second longword shows where to jump when an exception at the addr the first + * longword is pointing to is caught. + */ + +.section __ex_table,"a" + .align 2 +oktagon_except: + .long exp1,ret1 + .long exp2,ret2 + +#endif +#endif diff --git a/drivers/scsi/pas16.c b/drivers/scsi/pas16.c index e73fb0f995e8..4e3594a44a4b 100644 --- a/drivers/scsi/pas16.c +++ b/drivers/scsi/pas16.c @@ -73,7 +73,6 @@ * software after reset using the default_irq for the * current board number. * - * * 2. With command line overrides - pas16=port,irq may be * used on the LILO command line to override the defaults. * @@ -83,6 +82,11 @@ * -DPAS16_OVERRIDE={{0x388, 10}} * NOTE: Untested. * + * 4. When included as a module, with arguments passed on the command line: + * pas16_irq=xx the interrupt + * pas16_addr=xx the port + * e.g. "modprobe pas16 pas16_addr=0x388 pas16_irq=5" + * * Note that if the override methods are used, place holders must * be specified for other boards in the system. * @@ -99,7 +103,11 @@ * interrupts. Ie, for a board at the default 0x388 base port, * boot: linux pas16=0x388,255 * - * (255 is the IRQ_NONE constant in NCR5380.h) + * IRQ_NONE (255) should be specified for no interrupt, + * IRQ_AUTO (254) to autoprobe for an IRQ line if overridden + * on the command line. + * + * (IRQ_AUTO == 254, IRQ_NONE == 255 in NCR5380.h) */ #ifdef MODULE @@ -129,6 +137,8 @@ struct proc_dir_entry proc_scsi_pas16 = { }; static int pas_maxi = 0; static int pas_wmaxi = 0; +static unsigned short pas16_addr = 0; +static int pas16_irq = 0; int scsi_irq_translate[] = @@ -385,6 +395,22 @@ int __init pas16_detect(Scsi_Host_Template * tpnt) tpnt->proc_dir = &proc_scsi_pas16; tpnt->proc_info = &pas16_proc_info; + if (pas16_addr != 0) { + overrides[0].io_port = pas16_addr; + /* + * This is how we avoid seeing more than + * one host adapter at the same I/O port. + * Cribbed shamelessly from pas16_setup(). + */ + for (count = 0; count < NO_BASES; ++count) + if (bases[count].io_port == pas16_addr) { + bases[count].noauto = 1; + break; + } + } + if (pas16_irq != 0) + overrides[0].irq = pas16_irq; + for (count = 0; current_override < NO_OVERRIDES; ++current_override) { io_port = 0; @@ -580,4 +606,7 @@ static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src Scsi_Host_Template driver_template = MV_PAS16; #include "scsi_module.c" + +MODULE_PARM(pas16_addr, "h"); +MODULE_PARM(pas16_irq, "i"); #endif diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 0cf1ad95c821..a26a0226362b 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -406,6 +406,11 @@ static void scan_scsis_done (Scsi_Cmnd * SCpnt) up(SCpnt->request.sem); } +MODULE_PARM(scsi_logging_level, "i"); +MODULE_PARM_DESC(scsi_logging_level, "SCSI logging level; should be zero or nonzero"); + +#ifndef MODULE + static int __init scsi_logging_setup (char *str) { int tmp; @@ -422,12 +427,19 @@ static int __init scsi_logging_setup (char *str) __setup("scsi_logging=", scsi_logging_setup); +#endif + #ifdef CONFIG_SCSI_MULTI_LUN static int max_scsi_luns = 8; #else static int max_scsi_luns = 1; #endif +MODULE_PARM(max_scsi_luns, "i"); +MODULE_PARM_DESC(max_scsi_luns, "last scsi LUN (should be between 1 and 8)"); + +#ifndef MODULE + static int __init scsi_luns_setup (char *str) { int tmp; @@ -443,6 +455,9 @@ static int __init scsi_luns_setup (char *str) } __setup("max_scsi_luns=", scsi_luns_setup); + +#endif + /* * Detecting SCSI devices : * We scan all present host adapter's busses, from ID 0 to ID (max_id). diff --git a/drivers/scsi/sun3x_esp.c b/drivers/scsi/sun3x_esp.c new file mode 100644 index 000000000000..84ad404f0347 --- /dev/null +++ b/drivers/scsi/sun3x_esp.c @@ -0,0 +1,290 @@ +/* sun3x_esp.c: EnhancedScsiProcessor Sun3x SCSI driver code. + * + * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de) + * + * Based on David S. Miller's esp driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scsi.h" +#include "hosts.h" +#include "NCR53C9x.h" + +#include "sun3x_esp.h" +#include +#include + +extern struct NCR_ESP *espchain; + +static void dma_barrier(struct NCR_ESP *esp); +static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count); +static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_drain(struct NCR_ESP *esp); +static void dma_dump_state(struct NCR_ESP *esp); +static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length); +static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length); +static void dma_ints_off(struct NCR_ESP *esp); +static void dma_ints_on(struct NCR_ESP *esp); +static int dma_irq_p(struct NCR_ESP *esp); +static void dma_poll(struct NCR_ESP *esp, unsigned char *vaddr); +static int dma_ports_p(struct NCR_ESP *esp); +static void dma_reset(struct NCR_ESP *esp); +static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write); +static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_advance_sg (Scsi_Cmnd *sp); + +volatile unsigned char cmd_buffer[16]; + /* This is where all commands are put + * before they are trasfered to the ESP chip + * via PIO. + */ + +/* Detecting ESP chips on the machine. This is the simple and easy + * version. + */ +int sun3x_esp_detect(Scsi_Host_Template *tpnt) +{ + struct NCR_ESP *esp; + struct ConfigDev *esp_dev; + + esp_dev = 0; + esp = esp_allocate(tpnt, (void *) esp_dev); + + /* Do command transfer with DMA */ + esp->do_pio_cmds = 0; + + /* Required functions */ + esp->dma_bytes_sent = &dma_bytes_sent; + esp->dma_can_transfer = &dma_can_transfer; + esp->dma_dump_state = &dma_dump_state; + esp->dma_init_read = &dma_init_read; + esp->dma_init_write = &dma_init_write; + esp->dma_ints_off = &dma_ints_off; + esp->dma_ints_on = &dma_ints_on; + esp->dma_irq_p = &dma_irq_p; + esp->dma_ports_p = &dma_ports_p; + esp->dma_setup = &dma_setup; + + /* Optional functions */ + esp->dma_barrier = &dma_barrier; + esp->dma_drain = &dma_drain; + esp->dma_irq_entry = &dma_ints_off; + esp->dma_irq_exit = &dma_ints_on; + esp->dma_led_on = 0; + esp->dma_led_off = 0; + esp->dma_poll = &dma_poll; + esp->dma_reset = &dma_reset; + + /* virtual DMA functions */ + esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one; + esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl; + esp->dma_mmu_release_scsi_one = &dma_mmu_release_scsi_one; + esp->dma_mmu_release_scsi_sgl = &dma_mmu_release_scsi_sgl; + esp->dma_advance_sg = &dma_advance_sg; + + /* SCSI chip speed */ + esp->cfreq = 20000000; + esp->eregs = (struct ESP_regs *)(SUN3X_ESP_BASE); + esp->dregs = (void *)SUN3X_ESP_DMA; + + esp->esp_command = (volatile unsigned char *)cmd_buffer; + esp->esp_command_dvma = dvma_alloc(virt_to_phys(cmd_buffer), + sizeof (cmd_buffer)); + + esp->irq = 2; + request_irq(esp->irq, esp_intr, SA_INTERRUPT, "SUN3X SCSI", NULL); + + esp->scsi_id = 7; + esp->diff = 0; + + esp_initialize(esp); + + printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps, + esps_in_use); + esps_running = esps_in_use; + return esps_in_use; +} + +static void dma_barrier(struct NCR_ESP *esp) +{ + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers *) esp->dregs; + + while(dregs->cond_reg & DMA_PEND_READ) + udelay(1); + dregs->cond_reg &= ~(DMA_ENABLE); +} + +/* This uses various DMA csr fields and the fifo flags count value to + * determine how many bytes were successfully sent/received by the ESP. + */ +static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count) +{ + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers *) esp->dregs; + + int rval = dregs->st_addr - esp->esp_command_dvma; + + return rval - fifo_count; +} + +static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + __u32 base, end, sz; + + base = ((__u32)sp->SCp.ptr); + base &= (0x1000000 - 1); + end = (base + sp->SCp.this_residual); + if(end > 0x1000000) + end = 0x1000000; + sz = (end - base); + return sz; +} + +static void dma_drain(struct NCR_ESP *esp) +{ + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers *) esp->dregs; + + if(dregs->cond_reg & DMA_FIFO_ISDRAIN) { + dregs->cond_reg |= DMA_FIFO_STDRAIN; + while(dregs->cond_reg & DMA_FIFO_ISDRAIN) + udelay(1); + } +} + +static void dma_dump_state(struct NCR_ESP *esp) +{ + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers *) esp->dregs; + + ESPLOG(("esp%d: dma -- cond_reg<%08lx> addr<%p>\n", + esp->esp_id, dregs->cond_reg, dregs->st_addr)); +} + +static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length) +{ + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers *) esp->dregs; + + dregs->cond_reg |= (DMA_ST_WRITE | DMA_ENABLE); + dregs->st_addr = vaddress; +} + +static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length) +{ + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers *) esp->dregs; + + /* Set up the DMA counters */ + dregs->cond_reg = ((dregs->cond_reg & ~(DMA_ST_WRITE)) | DMA_ENABLE); + dregs->st_addr = vaddress; +} + +static void dma_ints_off(struct NCR_ESP *esp) +{ + DMA_INTSOFF((struct sparc_dma_registers *) esp->dregs); +} + +static void dma_ints_on(struct NCR_ESP *esp) +{ + DMA_INTSON((struct sparc_dma_registers *) esp->dregs); +} + +static int dma_irq_p(struct NCR_ESP *esp) +{ + return DMA_IRQ_P((struct sparc_dma_registers *) esp->dregs); +} + +static void dma_poll(struct NCR_ESP *esp, unsigned char *vaddr) +{ + dma_drain(esp); + + /* Wait till the first bits settle. */ + while(vaddr[0] == 0xff) + udelay(1); +} + +static int dma_ports_p(struct NCR_ESP *esp) +{ + return (((struct sparc_dma_registers *) esp->dregs)->cond_reg + & DMA_INT_ENAB); +} + +/* Resetting various pieces of the ESP scsi driver chipset/buses. */ +static void dma_reset(struct NCR_ESP *esp) +{ + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers *)esp->dregs; + + /* Punt the DVMA into a known state. */ + dregs->cond_reg |= DMA_RST_SCSI; + dregs->cond_reg &= ~(DMA_RST_SCSI); + DMA_INTSON(dregs); +} + +static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write) +{ + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers *) esp->dregs; + unsigned long nreg = dregs->cond_reg; + + if(write) + nreg |= DMA_ST_WRITE; + else + nreg &= ~(DMA_ST_WRITE); + nreg |= DMA_ENABLE; + dregs->cond_reg = nreg; + dregs->st_addr = addr; +} + +static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + sp->SCp.have_data_in = dvma_alloc(virt_to_phys(sp->SCp.buffer), + sp->SCp.this_residual); + sp->SCp.ptr = (char *)((unsigned long)sp->SCp.have_data_in); +} + +static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + int sz = sp->SCp.buffers_residual; + struct mmu_sglist *sg = (struct mmu_sglist *) sp->SCp.buffer; + + while (sz >= 0) { + sg[sz].dvma_addr = dvma_alloc(virt_to_phys(sg[sz].addr), sg[sz].len); + sz--; + } + sp->SCp.ptr=(char *)((unsigned long)sp->SCp.buffer->dvma_address); +} + +static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + dvma_free(sp->SCp.have_data_in, sp->request_bufflen); +} + +static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + int sz = sp->use_sg - 1; + struct mmu_sglist *sg = (struct mmu_sglist *)sp->buffer; + + while(sz >= 0) { + dvma_free(sg[sz].dvma_addr,sg[sz].len); + sz--; + } +} + +static void dma_advance_sg (Scsi_Cmnd *sp) +{ + sp->SCp.ptr = (char *)((unsigned long)sp->SCp.buffer->dvma_address); +} diff --git a/drivers/scsi/sun3x_esp.h b/drivers/scsi/sun3x_esp.h new file mode 100644 index 000000000000..e00cd850d1de --- /dev/null +++ b/drivers/scsi/sun3x_esp.h @@ -0,0 +1,41 @@ +/* sun3x_esp.h: Defines and structures for the Sun3x ESP + * + * (C) 1995 Thomas Bogendoerfer (tsbogend@alpha.franken.de) + */ + +#ifndef _SUN3X_ESP_H +#define _SUN3X_ESP_H + +/* For dvma controller register definitions. */ +#include + +extern int sun3x_esp_detect(struct SHT *); +extern const char *esp_info(struct Scsi_Host *); +extern int esp_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +extern int esp_command(Scsi_Cmnd *); +extern int esp_abort(Scsi_Cmnd *); +extern int esp_reset(Scsi_Cmnd *, unsigned int); +extern int esp_proc_info(char *buffer, char **start, off_t offset, int length, + int hostno, int inout); + +extern struct proc_dir_entry proc_scsi_esp; + +#define DMA_PORTS_P (dregs->cond_reg & DMA_INT_ENAB) + +#define SCSI_SUN3X_ESP { \ + proc_dir: &proc_scsi_esp, \ + proc_info: &esp_proc_info, \ + name: "Sun ESP 100/100a/200", \ + detect: sun3x_esp_detect, \ + info: esp_info, \ + command: esp_command, \ + queuecommand: esp_queue, \ + abort: esp_abort, \ + reset: esp_reset, \ + can_queue: 7, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 1, \ + use_clustering: DISABLE_CLUSTERING, } + +#endif /* !(_SUN3X_ESP_H) */ diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index f7a3aad8b39e..911e1aaf1129 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -141,19 +141,19 @@ obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y) # Translate to Rules.make lists. -L_TARGET := sound.a +O_TARGET := sounddrivers.o # This is a nice idea but needs depmod altering #MOD_LIST_NAME := SOUND_MODULES -L_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) -LX_OBJS := $(sort $(filter $(export-objs), $(obj-y))) +O_OBJS := $(filter-out $(export-objs), $(obj-y)) +OX_OBJS := $(filter $(export-objs), $(obj-y)) M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m))) MIX_OBJS := $(sort $(filter $(export-objs), $(int-m))) ifeq ($(CONFIG_LOWLEVEL_SOUND),y) - L_OBJS += lowlevel/lowlevel.o + O_OBJS += lowlevel/lowlevel.o endif include $(TOPDIR)/Rules.make diff --git a/drivers/sound/es1370.c b/drivers/sound/es1370.c index 4052b116cd67..0e113ef9f2bd 100644 --- a/drivers/sound/es1370.c +++ b/drivers/sound/es1370.c @@ -106,6 +106,7 @@ * 03.08.99 0.26 adapt to Linus' new __setup/__initcall * added kernel command line option "es1370=joystick[,lineout[,micbias]]" * removed CONFIG_SOUND_ES1370_JOYPORT_BOOT kludge + * 12.08.99 0.27 module_init/__setup fixes * * some important things missing in Ensoniq documentation: * @@ -1067,9 +1068,10 @@ static int drain_dac1(struct es1370_state *s, int nonblock) current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; + tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 + / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; - if (!schedule_timeout(tmo ? : 1) && tmo) + if (!schedule_timeout(tmo + 1)) DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) } remove_wait_queue(&s->dma_dac1.wait, &wait); @@ -1102,9 +1104,10 @@ static int drain_dac2(struct es1370_state *s, int nonblock) current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); + tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 + / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; - if (!schedule_timeout(tmo ? : 1) && tmo) + if (!schedule_timeout(tmo + 1)) DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) } remove_wait_queue(&s->dma_dac2.wait, &wait); @@ -2300,6 +2303,16 @@ static int joystick[NR_DEVICE] = { 0, }; static int lineout[NR_DEVICE] = { 0, }; static int micbias[NR_DEVICE] = { 0, }; +MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(joystick, "if 1 enables joystick interface (still need separate driver)"); +MODULE_PARM(lineout, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out"); +MODULE_PARM(micbias, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(micbias, "sets the +5V bias for an electret microphone"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("ES1370 AudioPCI Driver"); + /* --------------------------------------------------------------------- */ static struct initvol { @@ -2318,10 +2331,7 @@ static struct initvol { { SOUND_MIXER_WRITE_OGAIN, 0x4040 } }; -#ifndef MODULE -static -#endif -int __init init_module(void) +static int __init init_es1370(void) { struct es1370_state *s; struct pci_dev *pcidev = NULL; @@ -2330,7 +2340,7 @@ int __init init_module(void) if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "es1370: version v0.26 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "es1370: version v0.27 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) { if (pcidev->resource[0].flags == 0 || @@ -2438,21 +2448,7 @@ int __init init_module(void) return 0; } -/* --------------------------------------------------------------------- */ - -#ifdef MODULE - -MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(joystick, "if 1 enables joystick interface (still need separate driver)"); -MODULE_PARM(lineout, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out"); -MODULE_PARM(micbias, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(micbias, "sets the +5V bias for an electret microphone"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("ES1370 AudioPCI Driver"); - -void cleanup_module(void) +static void __exit cleanup_es1370(void) { struct es1370_state *s; @@ -2472,7 +2468,12 @@ void cleanup_module(void) printk(KERN_INFO "es1370: unloading\n"); } -#else /* MODULE */ +module_init(init_es1370); +module_exit(cleanup_es1370); + +/* --------------------------------------------------------------------- */ + +#ifndef MODULE /* format is: es1370=[joystick[,lineout[,micbias]]] */ @@ -2493,6 +2494,5 @@ static int __init es1370_setup(char *str) } __setup("es1370=", es1370_setup); -__initcall(init_module); #endif /* MODULE */ diff --git a/drivers/sound/es1371.c b/drivers/sound/es1371.c index 2d9cdaaa6275..6f4723f5cae7 100644 --- a/drivers/sound/es1371.c +++ b/drivers/sound/es1371.c @@ -3,7 +3,7 @@ /* * es1371.c -- Creative Ensoniq ES1371. * - * Copyright (C) 1998 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) * * 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 @@ -71,6 +71,9 @@ * 03.08.99 0.14 adapt to Linus' new __setup/__initcall * added kernel command line option "es1371=joystickaddr" * removed CONFIG_SOUND_ES1371_JOYPORT_BOOT kludge + * 10.08.99 0.15 (Re)added S/PDIF module option for cards revision >= 4. + * Initial version by Dave Platt . + * module_init/__setup fixes * */ @@ -145,6 +148,7 @@ static const unsigned sample_size[] = { 1, 2, 2, 4 }; static const unsigned sample_shift[] = { 0, 1, 1, 2 }; +#define CTRL_SPDIFEN_B 0x04000000 #define CTRL_JOY_SHIFT 24 #define CTRL_JOY_MASK 3 #define CTRL_JOY_200 0x00000000 /* joystick base address */ @@ -180,6 +184,9 @@ static const unsigned sample_shift[] = { 0, 1, 1, 2 }; #define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ +#define STAT_EN_SPDIF 0x00040000 /* enable S/PDIF circuitry */ +#define STAT_TS_SPDIF 0x00020000 /* test S/PDIF circuitry */ +#define STAT_TESTMODE 0x00010000 /* test ASIC */ #define STAT_SYNC_ERR 0x00000100 /* 1 = codec sync error */ #define STAT_VC 0x000000c0 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */ #define STAT_SH_VC 6 @@ -1481,9 +1488,9 @@ static int drain_dac1(struct es1371_state *s, int nonblock) current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / s->dac1rate; + tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 / s->dac1rate; tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; - if (!schedule_timeout(tmo ? : 1) && tmo) + if (!schedule_timeout(tmo + 1)) printk(KERN_DEBUG "es1371: dma timed out??\n"); } remove_wait_queue(&s->dma_dac1.wait, &wait); @@ -1516,9 +1523,9 @@ static int drain_dac2(struct es1371_state *s, int nonblock) current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / s->dac2rate; + tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 / s->dac2rate; tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; - if (!schedule_timeout(tmo ? : 1) && tmo) + if (!schedule_timeout(tmo + 1)) printk(KERN_DEBUG "es1371: dma timed out??\n"); } remove_wait_queue(&s->dma_dac2.wait, &wait); @@ -2697,6 +2704,15 @@ static /*const*/ struct file_operations es1371_midi_fops = { #define NR_DEVICE 5 static int joystick[NR_DEVICE] = { 0, }; +static int spdif[NR_DEVICE] = { 0, }; + +MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(joystick, "sets address and enables joystick interface (still need separate driver)"); +MODULE_PARM(spdif, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(spdif, "if 1 the output is in S/PDIF digital mode"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("ES1371 AudioPCI97 Driver"); /* --------------------------------------------------------------------- */ @@ -2719,19 +2735,18 @@ static struct initvol { { SOUND_MIXER_WRITE_IGAIN, 0x4040 } }; -#ifndef MODULE -static -#endif -int __init init_module(void) +static int __init init_es1371(void) { struct es1371_state *s; struct pci_dev *pcidev = NULL; mm_segment_t fs; int i, val, val2, index = 0; + u8 revision; + unsigned cssr; if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "es1371: version v0.14 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "es1371: version v0.15 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, pcidev))) { if (pcidev->resource[0].flags == 0 || @@ -2784,6 +2799,18 @@ int __init init_module(void) } } s->sctrl = 0; + cssr = 0; + /* check to see if s/pdif mode is being requested */ + pci_read_config_byte(pcidev, PCI_REVISION_ID, &revision); + if (spdif[index]) { + if (revision >= 4) { + printk(KERN_INFO "es1371: enabling S/PDIF output\n"); + cssr |= STAT_EN_SPDIF; + s->ctrl |= CTRL_SPDIFEN_B; + } else { + printk(KERN_ERR "es1371: revision %d does not support S/PDIF\n", revision); + } + } /* initialize the chips */ outl(s->ctrl, s->io+ES1371_REG_CONTROL); outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); @@ -2858,6 +2885,8 @@ int __init init_module(void) mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); } set_fs(fs); + /* turn on S/PDIF output driver if requested */ + outl(cssr, s->io+ES1371_REG_STATUS); /* queue it for later freeing */ s->next = devs; devs = s; @@ -2883,17 +2912,7 @@ int __init init_module(void) return 0; } -/* --------------------------------------------------------------------- */ - -#ifdef MODULE - -MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(joystick, "sets address and enables joystick interface (still need separate driver)"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("ES1371 AudioPCI97 Driver"); - -void cleanup_module(void) +static void __exit cleanup_es1371(void) { struct es1371_state *s; @@ -2913,23 +2932,27 @@ void cleanup_module(void) printk(KERN_INFO "es1371: unloading\n"); } -#else /* MODULE */ +module_init(init_es1371); +module_exit(cleanup_es1371); + +/* --------------------------------------------------------------------- */ + +#ifndef MODULE /* format is: es1371=[joystick] */ static int __init es1371_setup(char *str) { static unsigned __initdata nr_dev = 0; - int ints[11]; if (nr_dev >= NR_DEVICE) return 0; - get_option(&str, &joystick[nr_dev]); + if (get_option(&str, &joystick[nr_dev]) == 2) + get_option(&str, &spdif[nr_dev]); nr_dev++; return 1; } __setup("es1371=", es1371_setup); -__initcall(init_module); #endif /* MODULE */ diff --git a/drivers/sound/esssolo1.c b/drivers/sound/esssolo1.c index 065efa7f39a5..4189dc37c2fa 100644 --- a/drivers/sound/esssolo1.c +++ b/drivers/sound/esssolo1.c @@ -39,13 +39,26 @@ * 15.06.99 0.4 Fix bad allocation bug. * Thanks to Deti Fliegl * 28.06.99 0.5 Add pci_set_master - * + * 12.08.99 0.6 Fix MIDI UART crashing the driver + * Changed mixer semantics from OSS documented + * behaviour to OSS "code behaviour". + * Recording might actually work now. + * The real DDMA controller address register is at PCI config + * 0x60, while the register at 0x18 is used as a placeholder + * register for BIOS address allocation. This register + * is supposed to be copied into 0x60, according + * to the Solo1 datasheet. When I do that, I can access + * the DDMA registers except the mask bit, which + * is stuck at 1. When I copy the contents of 0x18 +0x10 + * to the DDMA base register, everything seems to work. + * The fun part is that the Windows Solo1 driver doesn't + * seem to do these tricks. + * Bugs remaining: plops and clicks when starting/stopping playback * */ /*****************************************************************************/ -#include #include #include #include @@ -69,6 +82,10 @@ /* --------------------------------------------------------------------- */ +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +/* --------------------------------------------------------------------- */ + #ifndef PCI_VENDOR_ID_ESS #define PCI_VENDOR_ID_ESS 0x125d #endif @@ -78,9 +95,12 @@ #define SOLO1_MAGIC ((PCI_VENDOR_ID_ESS<<16)|PCI_DEVICE_ID_ESS_SOLO1) +#define DDMABASE_OFFSET 0x10 /* chip bug workaround kludge */ +#define DDMABASE_EXTENT 16 + #define IOBASE_EXTENT 16 #define SBBASE_EXTENT 16 -#define VCBASE_EXTENT 16 +#define VCBASE_EXTENT (DDMABASE_EXTENT+DDMABASE_OFFSET) #define MPUBASE_EXTENT 4 #define GPBASE_EXTENT 4 @@ -98,16 +118,15 @@ /* --------------------------------------------------------------------- */ -#define DEBUGREC - -/* --------------------------------------------------------------------- */ - struct solo1_state { /* magic */ unsigned int magic; - /* we keep sb cards in a linked list */ + /* we keep the cards in a linked list */ struct solo1_state *next; + + /* pcidev is needed to turn off the DDMA controller at driver shutdown */ + struct pci_dev *pcidev; /* soundcore stuff */ int dev_audio; @@ -116,10 +135,10 @@ struct solo1_state { int dev_dmfm; /* hardware resources */ - unsigned long iobase, sbbase, vcbase, mpubase, gpbase; /* long for SPARC */ + unsigned long iobase, sbbase, vcbase, ddmabase, mpubase, gpbase; /* long for SPARC */ unsigned int irq; - /* mixer registers; there is no HW readback */ + /* mixer registers */ struct { unsigned short vol[10]; unsigned int recsrc; @@ -302,6 +321,8 @@ static void start_dac(struct solo1_state *s) spin_lock_irqsave(&s->lock, flags); if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { s->ena |= FMODE_WRITE; + write_mixer(s, 0x78, 0x12); + udelay(10); write_mixer(s, 0x78, 0x13); } spin_unlock_irqrestore(&s->lock, flags); @@ -326,47 +347,24 @@ static void start_adc(struct solo1_state *s) && s->dma_adc.ready) { s->ena |= FMODE_READ; write_ctrl(s, 0xb8, 0xf); -#ifdef DEBUGREC +#if 0 printk(KERN_DEBUG "solo1: DMAbuffer: 0x%08lx\n", (long)s->dma_adc.rawbuf); printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x\n", - inb(s->vcbase+0xf), inw(s->vcbase+4), inl(s->vcbase), inb(s->vcbase+8)); -#endif - outb(0, s->vcbase+0xd); /* master reset */ -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x (a. clr)\n", - inb(s->vcbase+0xf), inw(s->vcbase+4), inl(s->vcbase), inb(s->vcbase+8)); -#endif - outb(1, s->vcbase+0xf); /* mask */ -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x (a. mask)\n", - inb(s->vcbase+0xf), inw(s->vcbase+4), inl(s->vcbase), inb(s->vcbase+8)); -#endif - outb(0x54/*0x14*/, s->vcbase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x (a. wrmode)\n", - inb(s->vcbase+0xf), inw(s->vcbase+4), inl(s->vcbase), inb(s->vcbase+8)); -#endif - outl(virt_to_bus(s->dma_adc.rawbuf), s->vcbase); -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x (a. wrbase)\n", - inb(s->vcbase+0xf), inw(s->vcbase+4), inl(s->vcbase), inb(s->vcbase+8)); -#endif - outw(s->dma_adc.dmasize-1, s->vcbase+4); -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x (a. wrcnt)\n", - inb(s->vcbase+0xf), inw(s->vcbase+4), inl(s->vcbase), inb(s->vcbase+8)); -#endif - outb(0, s->vcbase+0xf); -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x (a. clrmask)\n", - inb(s->vcbase+0xf), inw(s->vcbase+4), inl(s->vcbase), inb(s->vcbase+8)); + inb(s->ddmabase+0xf), inw(s->ddmabase+4), inl(s->ddmabase), inb(s->ddmabase+8)); #endif + outb(0, s->ddmabase+0xd); /* master reset */ + outb(1, s->ddmabase+0xf); /* mask */ + outb(0x54/*0x14*/, s->ddmabase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ + outl(virt_to_bus(s->dma_adc.rawbuf), s->ddmabase); + outw(s->dma_adc.dmasize-1, s->ddmabase+4); + outb(0, s->ddmabase+0xf); } spin_unlock_irqrestore(&s->lock, flags); -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1: start DMA: reg B8: 0x%02x DMAstat: 0x%02x DMAcnt: 0x%04x DMAmask: 0x%02x SBstat: 0x%02x\n", - read_ctrl(s, 0xb8), inb(s->vcbase+8), inw(s->vcbase+4), inb(s->vcbase+0xf), inb(s->sbbase+0xc)); - +#if 0 + printk(KERN_DEBUG "solo1: start DMA: reg B8: 0x%02x SBstat: 0x%02x\n" + KERN_DEBUG "solo1: DMA: stat: 0x%02x cnt: 0x%04x mask: 0x%02x\n", + read_ctrl(s, 0xb8), inb(s->sbbase+0xc), + inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->ddmabase+0xf)); printk(KERN_DEBUG "solo1: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" KERN_DEBUG "solo1: B1: 0x%02x B2: 0x%02x B4: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n", read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), @@ -456,16 +454,16 @@ extern inline int prog_dmabuf_adc(struct solo1_state *s) va = virt_to_bus(s->dma_adc.rawbuf); if ((va & ~((1<<24)-1))) panic("solo1: buffer above 16M boundary"); - outb(0, s->vcbase+0xd); /* clear */ - outb(1, s->vcbase+0xf); /* mask */ - //outb(0, s->vcbase+8); /* enable (enable is active low!) */ - outb(0x54, s->vcbase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ - outl(va, s->vcbase); - outw(s->dma_adc.dmasize-1, s->vcbase+4); + outb(0, s->ddmabase+0xd); /* clear */ + outb(1, s->ddmabase+0xf); /* mask */ + /*outb(0, s->ddmabase+8);*/ /* enable (enable is active low!) */ + outb(0x54, s->ddmabase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ + outl(va, s->ddmabase); + outw(s->dma_adc.dmasize-1, s->ddmabase+4); c = - s->dma_adc.fragsamples; write_ctrl(s, 0xa4, c); write_ctrl(s, 0xa5, c >> 8); - outb(0, s->vcbase+0xf); + outb(0, s->ddmabase+0xf); s->dma_adc.ready = 1; return 0; } @@ -513,12 +511,12 @@ static void solo1_update_ptr(struct solo1_state *s) /* update ADC pointer */ if (s->ena & FMODE_READ) { - hwptr = (s->dma_adc.dmasize - 1 - inw(s->vcbase+4)) % s->dma_adc.dmasize; + hwptr = (s->dma_adc.dmasize - 1 - inw(s->ddmabase+4)) % s->dma_adc.dmasize; diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; s->dma_adc.hwptr = hwptr; s->dma_adc.total_bytes += diff; s->dma_adc.count += diff; -#ifdef DEBUGREC +#if 0 printk(KERN_DEBUG "solo1: rd: hwptr %u swptr %u dmasize %u count %u\n", s->dma_adc.hwptr, s->dma_adc.swptr, s->dma_adc.dmasize, s->dma_adc.count); #endif @@ -633,30 +631,32 @@ static int mixer_ioctl(struct solo1_state *s, unsigned int cmd, unsigned long ar SOUND_MASK_MIC, SOUND_MASK_MIC, SOUND_MASK_CD, SOUND_MASK_VOLUME, SOUND_MASK_MIC, 0, SOUND_MASK_LINE, 0 }; - static const unsigned char mixtable[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_PCM] = 0x7c, /* voice */ - [SOUND_MIXER_SYNTH] = 0x36, /* FM */ - [SOUND_MIXER_CD] = 0x38, /* CD */ - [SOUND_MIXER_LINE] = 0x3e, /* Line */ - [SOUND_MIXER_LINE1] = 0x3a, /* AUX */ - [SOUND_MIXER_MIC] = 0x1a, /* Mic */ - [SOUND_MIXER_LINE2] = 0x6d /* Mono in */ + static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = 1, /* voice */ + [SOUND_MIXER_SYNTH] = 2, /* FM */ + [SOUND_MIXER_CD] = 3, /* CD */ + [SOUND_MIXER_LINE] = 4, /* Line */ + [SOUND_MIXER_LINE1] = 5, /* AUX */ + [SOUND_MIXER_MIC] = 6, /* Mic */ + [SOUND_MIXER_LINE2] = 7, /* Mono in */ + [SOUND_MIXER_SPEAKER] = 8, /* Speaker */ + [SOUND_MIXER_RECLEV] = 9, /* Recording level */ + [SOUND_MIXER_VOLUME] = 10 /* Master Volume */ + }; + static const unsigned char mixreg[] = { + 0x7c, /* voice */ + 0x36, /* FM */ + 0x38, /* CD */ + 0x3e, /* Line */ + 0x3a, /* AUX */ + 0x1a, /* Mic */ + 0x6d /* Mono in */ }; - unsigned char l, r, rl, rr; + unsigned char l, r, rl, rr, vidx; int i, val; VALIDATE_STATE(s); - if (cmd == SOUND_MIXER_PRIVATE1) { - /* enable/disable/query mixer preamp */ - get_user_ret(val, (int *)arg, -EFAULT); - if (val != -1) { - val = val ? 0xff : 0xf7; - write_mixer(s, 0x7d, (read_mixer(s, 0x7d) | 0x08) & val); - } - val = (read_mixer(s, 0x7d) & 0x08) ? 1 : 0; - return put_user(val, (int *)arg); - } if (cmd == SOUND_MIXER_PRIVATE1) { /* enable/disable/query mixer preamp */ get_user_ret(val, (int *)arg, -EFAULT); @@ -720,36 +720,11 @@ static int mixer_ioctl(struct solo1_state *s, unsigned int cmd, unsigned long ar case SOUND_MIXER_CAPS: return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg); - case SOUND_MIXER_VOLUME: - rl = read_mixer(s, 0x60); - rr = read_mixer(s, 0x62); - l = (rl * 3 + 11) / 2; - if (rl & 0x40) - l = 0; - r = (rr * 3 + 11) / 2; - if (rr & 0x40) - r = 0; - return put_user((((unsigned int)r) << 8) | l, (int *)arg); - - case SOUND_MIXER_SPEAKER: - rl = read_mixer(s, 0x3c); - l = (rl & 7) * 14 + 2; - return put_user(l * 0x101, (int *)arg); - - case SOUND_MIXER_RECLEV: - rl = read_ctrl(s, 0xb4); - r = ((rl & 0xf) * 13 + 5) / 2; - l = (((rl >> 4) & 0xf) * 13 + 5) / 2; - return put_user((((unsigned int)r) << 8) | l, (int *)arg); - default: i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i]) + if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) return -EINVAL; - rl = read_mixer(s, mixtable[i]); - r = ((rl & 0xf) * 13 + 5) / 2; - l = (((rl >> 4) & 0xf) * 13 + 5) / 2; - return put_user((((unsigned int)r) << 8) | l, (int *)arg); + return put_user(s->mix.vol[vidx-1], (int *)arg); } } if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) @@ -757,21 +732,21 @@ static int mixer_ioctl(struct solo1_state *s, unsigned int cmd, unsigned long ar s->mix.modcnt++; switch (_IOC_NR(cmd)) { case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - - { - static const unsigned char regs[] = { - 0x1c, 0x1a, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x60, 0x62, 0x6d, 0x7c - }; - int i; - - for (i = 0; i < sizeof(regs); i++) - printk(KERN_DEBUG "solo1: mixer reg 0x%02x: 0x%02x\n", - regs[i], read_mixer(s, regs[i])); - printk(KERN_DEBUG "solo1: ctrl reg 0x%02x: 0x%02x\n", - 0xb4, read_ctrl(s, 0xb4)); - } - - get_user_ret(val, (int *)arg, -EFAULT); +#if 0 + { + static const unsigned char regs[] = { + 0x1c, 0x1a, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x60, 0x62, 0x6d, 0x7c + }; + int i; + + for (i = 0; i < sizeof(regs); i++) + printk(KERN_DEBUG "solo1: mixer reg 0x%02x: 0x%02x\n", + regs[i], read_mixer(s, regs[i])); + printk(KERN_DEBUG "solo1: ctrl reg 0x%02x: 0x%02x\n", + 0xb4, read_ctrl(s, 0xb4)); + } +#endif + get_user_ret(val, (int *)arg, -EFAULT); i = hweight32(val); if (i == 0) return 0; @@ -810,7 +785,12 @@ static int mixer_ioctl(struct solo1_state *s, unsigned int cmd, unsigned long ar } write_mixer(s, 0x60, rl); write_mixer(s, 0x62, rr); - return put_user((((unsigned int)r) << 8) | l, (int *)arg); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[9] = ((unsigned int)r << 8) | l; +#else + s->mix.vol[9] = val; +#endif + return put_user(s->mix.vol[9], (int *)arg); case SOUND_MIXER_SPEAKER: get_user_ret(val, (int *)arg, -EFAULT); @@ -822,7 +802,12 @@ static int mixer_ioctl(struct solo1_state *s, unsigned int cmd, unsigned long ar rl = (l - 2) / 14; l = rl * 14 + 2; write_mixer(s, 0x3c, rl); - return put_user(l * 0x101, (int *)arg); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[7] = l * 0x101; +#else + s->mix.vol[7] = val; +#endif + return put_user(s->mix.vol[7], (int *)arg); case SOUND_MIXER_RECLEV: get_user_ret(val, (int *)arg, -EFAULT); @@ -841,11 +826,16 @@ static int mixer_ioctl(struct solo1_state *s, unsigned int cmd, unsigned long ar r = (rl * 13 + 5) / 2; l = (rr * 13 + 5) / 2; write_ctrl(s, 0xb4, (rl << 4) | rr); - return put_user((((unsigned int)r) << 8) | l, (int *)arg); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[8] = ((unsigned int)r << 8) | l; +#else + s->mix.vol[8] = val; +#endif + return put_user(s->mix.vol[8], (int *)arg); default: i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i]) + if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) return -EINVAL; get_user_ret(val, (int *)arg, -EFAULT); l = (val << 1) & 0x1fe; @@ -862,8 +852,13 @@ static int mixer_ioctl(struct solo1_state *s, unsigned int cmd, unsigned long ar rr = (r - 5) / 13; r = (rl * 13 + 5) / 2; l = (rr * 13 + 5) / 2; - write_mixer(s, mixtable[i], (rl << 4) | rr); - return put_user((((unsigned int)r) << 8) | l, (int *)arg); + write_mixer(s, mixreg[vidx-1], (rl << 4) | rr); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[vidx-1] = ((unsigned int)r << 8) | l; +#else + s->mix.vol[vidx-1] = val; +#endif + return put_user(s->mix.vol[vidx-1], (int *)arg); } } @@ -919,10 +914,8 @@ static /*const*/ struct file_operations solo1_mixer_fops = { NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ -#if 0 NULL, /* revalidate */ NULL, /* lock */ -#endif }; /* --------------------------------------------------------------------- */ @@ -931,7 +924,8 @@ static int drain_dac(struct solo1_state *s, int nonblock) { DECLARE_WAITQUEUE(wait, current); unsigned long flags; - int count, tmo; + int count; + unsigned tmo; if (s->dma_dac.mapped) return 0; @@ -950,12 +944,12 @@ static int drain_dac(struct solo1_state *s, int nonblock) current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / s->rate; + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->rate; if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) tmo >>= 1; if (s->channels > 1) tmo >>= 1; - if (!schedule_timeout(tmo ? : 1) && tmo) + if (!schedule_timeout(tmo + 1)) printk(KERN_DEBUG "solo1: dma timed out??\n"); } remove_wait_queue(&s->dma_dac.wait, &wait); @@ -996,7 +990,7 @@ static ssize_t solo1_read(struct file *file, char *buffer, size_t count, loff_t cnt = count; #ifdef DEBUGREC printk(KERN_DEBUG "solo1_read: reg B8: 0x%02x DMAstat: 0x%02x DMAcnt: 0x%04x SBstat: 0x%02x cnt: %u\n", - read_ctrl(s, 0xb8), inb(s->vcbase+8), inw(s->vcbase+4), inb(s->sbbase+0xc), cnt); + read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc), cnt); #endif if (cnt <= 0) { start_adc(s); @@ -1007,12 +1001,10 @@ static ssize_t solo1_read(struct file *file, char *buffer, size_t count, loff_t KERN_DEBUG "solo1_read: SBstat: 0x%02x cnt: %u\n", read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), - inl(s->vcbase), inw(s->vcbase+4), inb(s->vcbase+8), inb(s->vcbase+15), inb(s->sbbase+0xc), cnt); + inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt); #endif - if (inb(s->vcbase+15) & 1) { + if (inb(s->ddmabase+15) & 1) printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n"); - return -EIO; - } if (file->f_flags & O_NONBLOCK) return ret ? ret : -EAGAIN; interruptible_sleep_on(&s->dma_adc.wait); @@ -1023,7 +1015,7 @@ static ssize_t solo1_read(struct file *file, char *buffer, size_t count, loff_t KERN_DEBUG "solo1_read: SBstat: 0x%02x cnt: %u\n", read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), - inl(s->vcbase), inw(s->vcbase+4), inb(s->vcbase+8), inb(s->vcbase+15), inb(s->sbbase+0xc), cnt); + inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt); #endif if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; @@ -1042,7 +1034,7 @@ static ssize_t solo1_read(struct file *file, char *buffer, size_t count, loff_t start_adc(s); #ifdef DEBUGREC printk(KERN_DEBUG "solo1_read: reg B8: 0x%02x DMAstat: 0x%02x DMAcnt: 0x%04x SBstat: 0x%02x\n", - read_ctrl(s, 0xb8), inb(s->vcbase+8), inw(s->vcbase+4), inb(s->sbbase+0xc)); + read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc)); #endif } return ret; @@ -1304,7 +1296,7 @@ static int solo1_ioctl(struct inode *inode, struct file *file, unsigned int cmd, if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) return ret; start_adc(s); - if (inb(s->vcbase+15) & 1) + if (inb(s->ddmabase+15) & 1) printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n"); } else stop_adc(s); @@ -1466,8 +1458,8 @@ static int solo1_release(struct inode *inode, struct file *file) } if (file->f_mode & FMODE_READ) { stop_adc(s); - outb(1, s->vcbase+0xf); /* mask DMA channel */ - //outb(0, s->vcbase+0xd); /* DMA master clear */ + outb(1, s->ddmabase+0xf); /* mask DMA channel */ + outb(0, s->ddmabase+0xd); /* DMA master clear */ dealloc_dmabuf(&s->dma_adc); } s->open_mode &= ~(FMODE_READ | FMODE_WRITE); @@ -1533,10 +1525,8 @@ static /*const*/ struct file_operations solo1_audio_fops = { NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ -#if 0 NULL, /* revalidate */ NULL, /* lock */ -#endif }; /* --------------------------------------------------------------------- */ @@ -1550,7 +1540,7 @@ static void solo1_handle_midi(struct solo1_state *s) if (!(s->mpubase)) return; wake = 0; - while (inb(s->mpubase+1) & 0x80) { + while (!(inb(s->mpubase+1) & 0x80)) { ch = inb(s->mpubase); if (s->midi.icnt < MIDIINBUF) { s->midi.ibuf[s->midi.iwr] = ch; @@ -1562,7 +1552,7 @@ static void solo1_handle_midi(struct solo1_state *s) if (wake) wake_up(&s->midi.iwait); wake = 0; - while ((inb(s->mpubase+1) & 0x40) && s->midi.ocnt > 0) { + while (!(inb(s->mpubase+1) & 0x40) && s->midi.ocnt > 0) { outb(s->midi.obuf[s->midi.ord], s->mpubase); s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; s->midi.ocnt--; @@ -1577,22 +1567,12 @@ static void solo1_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct solo1_state *s = (struct solo1_state *)dev_id; unsigned int intsrc; - - static unsigned lim = 0; /* fastpath out, to ease interrupt sharing */ intsrc = inb(s->iobase+7); /* get interrupt source(s) */ if (!intsrc) return; (void)inb(s->sbbase+0xe); /* clear interrupt */ -#ifdef DEBUGREC - if (intsrc & 0x10 && lim < 20) { - lim++; - printk(KERN_DEBUG "solo1: audio1int reg B8: 0x%02x DMAstat: 0x%02x DMAcnt: 0x%04x SBstat: 0x%02x\n", - read_ctrl(s, 0xb8), inb(s->vcbase+8), inw(s->vcbase+4), inb(s->sbbase+0xc)); - } - //printk(KERN_DEBUG "solo1: interrupt 0x%02x\n", intsrc); -#endif spin_lock(&s->lock); /* clear audio interrupts first */ if (intsrc & 0x20) @@ -1849,10 +1829,8 @@ static /*const*/ struct file_operations solo1_midi_fops = { NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ -#if 0 NULL, /* revalidate */ NULL, /* lock */ -#endif }; /* --------------------------------------------------------------------- */ @@ -2025,10 +2003,8 @@ static /*const*/ struct file_operations solo1_dmfm_fops = { NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ -#if 0 NULL, /* revalidate */ NULL, /* lock */ -#endif }; /* --------------------------------------------------------------------- */ @@ -2060,11 +2036,10 @@ static int __init init_solo1(void) struct pci_dev *pcidev = NULL; mm_segment_t fs; int i, val, index = 0; - u16 ddmabase; if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "solo1: version v0.5 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "solo1: version v0.6 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, pcidev))) { if (pcidev->resource[0].start == 0 || @@ -2090,35 +2065,32 @@ static int __init init_solo1(void) init_waitqueue_head(&s->midi.owait); init_MUTEX(&s->open_sem); s->magic = SOLO1_MAGIC; + s->pcidev = pcidev; s->iobase = pcidev->resource[0].start; s->sbbase = pcidev->resource[1].start; s->vcbase = pcidev->resource[2].start; + s->ddmabase = s->vcbase + DDMABASE_OFFSET; s->mpubase = pcidev->resource[3].start; s->gpbase = pcidev->resource[4].start; s->irq = pcidev->irq; if (check_region(s->iobase, IOBASE_EXTENT) || check_region(s->sbbase, SBBASE_EXTENT) || - check_region(s->vcbase, VCBASE_EXTENT) || + check_region(s->ddmabase, DDMABASE_EXTENT) || check_region(s->mpubase, MPUBASE_EXTENT)) { printk(KERN_ERR "solo1: io ports in use\n"); goto err_region; } request_region(s->iobase, IOBASE_EXTENT, "ESS Solo1"); request_region(s->sbbase, SBBASE_EXTENT, "ESS Solo1"); - request_region(s->vcbase, VCBASE_EXTENT, "ESS Solo1"); + request_region(s->ddmabase, DDMABASE_EXTENT, "ESS Solo1"); request_region(s->mpubase, MPUBASE_EXTENT, "ESS Solo1"); if (request_irq(s->irq, solo1_interrupt, SA_SHIRQ, "ESS Solo1", s)) { printk(KERN_ERR "solo1: irq %u in use\n", s->irq); goto err_irq; } /* initialize DDMA base address */ - /* use PCI config reg, and not vcbase, we need the bus view */ - pci_read_config_word(pcidev, 0x18, &ddmabase); - pci_write_config_word(pcidev, 0x60, (ddmabase & (~0xf)) | 1); -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x\n", - inb(s->vcbase+0xf), inw(s->vcbase+4), inl(s->vcbase), inb(s->vcbase+8)); -#endif + printk(KERN_DEBUG "solo1: ddma base address: 0x%lx\n", s->ddmabase); + pci_write_config_word(pcidev, 0x60, (s->ddmabase & (~0xf)) | 1); /* set DMA policy to DDMA, IRQ emulation off (CLKRUN disabled for now) */ pci_write_config_dword(pcidev, 0x50, 0); /* disable legacy audio address decode */ @@ -2147,21 +2119,9 @@ static int __init init_solo1(void) write_mixer(s, 0x52, 0); write_mixer(s, 0x14, 0); /* DAC1 minimum volume */ write_mixer(s, 0x71, 0x20); /* enable new 0xA1 reg format */ -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x\n", - inb(s->vcbase+0xf), inw(s->vcbase+4), inl(s->vcbase), inb(s->vcbase+8)); -#endif - outb(0, s->vcbase+0xd); /* DMA master clear */ -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x\n", - inb(s->vcbase+0xf), inw(s->vcbase+4), inl(s->vcbase), inb(s->vcbase+8)); -#endif - outb(1, s->vcbase+0xf); /* mask channel */ -#ifdef DEBUGREC - printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x\n", - inb(s->vcbase+0xf), inw(s->vcbase+4), inl(s->vcbase), inb(s->vcbase+8)); -#endif - //outb(0, s->vcbase+0x8); /* enable controller (enable is low active!!) */ + outb(0, s->ddmabase+0xd); /* DMA master clear */ + outb(1, s->ddmabase+0xf); /* mask channel */ + /*outb(0, s->ddmabase+0x8);*/ /* enable controller (enable is low active!!) */ pci_set_master(pcidev); /* enable bus mastering */ @@ -2173,6 +2133,8 @@ static int __init init_solo1(void) val = initvol[i].vol; mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); } + val = 1; /* enable mic preamp */ + mixer_ioctl(s, SOUND_MIXER_PRIVATE1, (unsigned long)&val); set_fs(fs); /* queue it for later freeing */ s->next = devs; @@ -2192,7 +2154,7 @@ static int __init init_solo1(void) err_irq: release_region(s->iobase, IOBASE_EXTENT); release_region(s->sbbase, SBBASE_EXTENT); - release_region(s->vcbase, VCBASE_EXTENT); + release_region(s->ddmabase, DDMABASE_EXTENT); release_region(s->mpubase, MPUBASE_EXTENT); err_region: kfree_s(s, sizeof(struct solo1_state)); @@ -2215,13 +2177,14 @@ static void __exit cleanup_solo1(void) devs = devs->next; /* stop DMA controller */ outb(0, s->iobase+6); - outb(0, s->vcbase+0xd); /* DMA master clear */ + outb(0, s->ddmabase+0xd); /* DMA master clear */ outb(3, s->sbbase+6); /* reset sequencer and FIFO */ synchronize_irq(); + pci_write_config_word(s->pcidev, 0x60, 0); /* turn off DDMA controller address space */ free_irq(s->irq, s); release_region(s->iobase, IOBASE_EXTENT); release_region(s->sbbase, SBBASE_EXTENT); - release_region(s->vcbase, VCBASE_EXTENT); + release_region(s->ddmabase, DDMABASE_EXTENT); release_region(s->mpubase, MPUBASE_EXTENT); unregister_sound_dsp(s->dev_audio); unregister_sound_mixer(s->dev_mixer); diff --git a/drivers/sound/lowlevel/awe_compat-fbsd.h b/drivers/sound/lowlevel/awe_compat-fbsd.h index cbb01dc5c56c..8ebe5cab4ba7 100644 --- a/drivers/sound/lowlevel/awe_compat-fbsd.h +++ b/drivers/sound/lowlevel/awe_compat-fbsd.h @@ -49,9 +49,9 @@ /* reading configuration of sound driver */ +#include #ifdef AWE_OBSOLETE_VOXWARE -#include #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AWE32) #define CONFIG_AWE32_SYNTH #endif @@ -62,7 +62,6 @@ #include "lowlevel.h" #endif -#include #if defined(CONFIGURE_SOUNDCARD) && defined(CONFIG_AWE32) # define CONFIG_AWE32_SYNTH #endif diff --git a/drivers/sound/sonicvibes.c b/drivers/sound/sonicvibes.c index 154d3b098122..c4ac970e75e1 100644 --- a/drivers/sound/sonicvibes.c +++ b/drivers/sound/sonicvibes.c @@ -73,6 +73,7 @@ * 28.06.99 0.16 Add pci_set_master * 03.08.99 0.17 adapt to Linus' new __setup/__initcall * added kernel command line options "sonicvibes=reverb" and "sonicvibesdmaio=dmaioaddr" + * 12.08.99 0.18 module_init/__setup fixes * */ @@ -1268,9 +1269,9 @@ static int drain_dac(struct sv_state *s, int nonblock) current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / s->ratedac; + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; tmo >>= sample_shift[(s->fmt >> SV_CFMT_ASHIFT) & SV_CFMT_MASK]; - if (!schedule_timeout(tmo ? : 1) && tmo) + if (!schedule_timeout(tmo + 1)) printk(KERN_DEBUG "sv: dma timed out??\n"); } remove_wait_queue(&s->dma_dac.wait, &wait); @@ -2297,6 +2298,19 @@ static int wavetable[NR_DEVICE] = { 0, }; static unsigned dmaio = 0xac00; +MODULE_PARM(reverb, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(reverb, "if 1 enables the reverb circuitry. NOTE: your card must have the reverb RAM"); +#if 0 +MODULE_PARM(wavetable, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(wavetable, "if 1 the wavetable synth is enabled"); +#endif + +MODULE_PARM(dmaio, "i"); +MODULE_PARM_DESC(dmaio, "if the motherboard BIOS did not allocate DDMA io, allocate them starting at this address"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("S3 SonicVibes Driver"); + /* --------------------------------------------------------------------- */ static struct initvol { @@ -2314,10 +2328,7 @@ static struct initvol { { SOUND_MIXER_WRITE_PCM, 0x4040 } }; -#ifndef MODULE -static -#endif -int __init init_module(void) +static int __init init_sonicvibes(void) { struct sv_state *s; struct pci_dev *pcidev = NULL; @@ -2326,7 +2337,7 @@ int __init init_module(void) if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "sv: version v0.17 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "sv: version v0.18 time " __TIME__ " " __DATE__ "\n"); #if 0 if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); @@ -2499,24 +2510,7 @@ int __init init_module(void) return 0; } -/* --------------------------------------------------------------------- */ - -#ifdef MODULE - -MODULE_PARM(reverb, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(reverb, "if 1 enables the reverb circuitry. NOTE: your card must have the reverb RAM"); -#if 0 -MODULE_PARM(wavetable, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(wavetable, "if 1 the wavetable synth is enabled"); -#endif - -MODULE_PARM(dmaio, "i"); -MODULE_PARM_DESC(dmaio, "if the motherboard BIOS did not allocate DDMA io, allocate them starting at this address"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("S3 SonicVibes Driver"); - -void cleanup_module(void) +static void __exit cleanup_sonicvibes(void) { struct sv_state *s; @@ -2545,7 +2539,12 @@ void cleanup_module(void) printk(KERN_INFO "sv: unloading\n"); } -#else /* MODULE */ +module_init(init_sonicvibes); +module_exit(cleanup_sonicvibes); + +/* --------------------------------------------------------------------- */ + +#ifndef MODULE /* format is: sonicvibes=[reverb] sonicvibesdmaio=dmaioaddr */ @@ -2555,12 +2554,12 @@ static int __init sonicvibes_setup(char *str) if (nr_dev >= NR_DEVICE) return 0; - - ( (get_option(&str, &reverb [nr_dev]) == 2) #if 0 - && get_option(&str, &wavetable[nr_dev]) + if (get_option(&str, &reverb[nr_dev]) == 2) + get_option(&str, &wavetable[nr_dev]); +#else + get_option(&str, &reverb[nr_dev]); #endif - }; nr_dev++; return 1; @@ -2568,16 +2567,14 @@ static int __init sonicvibes_setup(char *str) static int __init sonicvibesdmaio_setup(char *str) { - int ints[11]; + int io; - get_options(str, ints); - if (ints[0] >= 1) - dmaio = ints[1]; + if (get_option(&str, &io)) + dmaio = io; return 1; } __setup("sonicvibes=", sonicvibes_setup); __setup("sonicvibesdmaio=", sonicvibesdmaio_setup); -__initcall(init_module); #endif /* MODULE */ diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index fc2f642e7db7..fddf8382222e 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -15,6 +15,7 @@ ALL_SUB_DIRS := $(SUB_DIRS) L_TARGET := usb.a M_OBJS := L_OBJS := +MOD_LIST_NAME := USB_MODULES ifeq ($(CONFIG_USB),y) L_OBJS +=usbcore.o diff --git a/drivers/usb/ohci.c b/drivers/usb/ohci.c index 9e5b966094f5..e443e448d9d3 100644 --- a/drivers/usb/ohci.c +++ b/drivers/usb/ohci.c @@ -266,7 +266,7 @@ struct ohci_ed *ohci_get_periodic_ed(struct ohci_device *dev, int period, int_ed = &root_hub->ed[ms_to_ed_int(period)]; /* decide on what the status field should look like */ - req_status = ed_set_maxpacket(usb_maxpacket(ohci_to_usb(dev), pipe)) + req_status = ed_set_maxpacket(usb_maxpacket(ohci_to_usb(dev), pipe, usb_pipeout(pipe))) | ed_set_speed(usb_pipeslow(pipe)) | (usb_pipe_endpdev(pipe) & 0x7ff) | ed_set_type_isoc(isoc); @@ -943,7 +943,7 @@ static void* ohci_request_irq(struct usb_device *usb, unsigned int pipe, struct ohci_device *dev = usb_to_ohci(usb); struct ohci_td *td; struct ohci_ed *interrupt_ed; /* endpoint descriptor for this irq */ - int maxps = usb_maxpacket(usb, pipe); + int maxps = usb_maxpacket(usb, pipe, usb_pipeout(pipe)); unsigned long flags; /* Get an ED and TD */ @@ -1120,8 +1120,8 @@ static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, * device number (function address), and type of TD. * */ - ohci_fill_ed(dev, control_ed, usb_maxpacket(usb,pipe), usb_pipeslow(pipe), - usb_pipe_endpdev(pipe), 0 /* normal TDs */); + ohci_fill_ed(dev, control_ed, usb_maxpacket(usb, pipe, usb_pipeout(pipe)), + usb_pipeslow(pipe), usb_pipe_endpdev(pipe), 0 /* normal TDs */); /* * Build the control TD @@ -1398,7 +1398,7 @@ static struct ohci_ed* ohci_request_bulk(struct ohci_bulk_request_state *bulk_re /* Set the max packet size, device speed, endpoint number, usb * device number (function address), and type of TD. */ ohci_fill_ed(dev, bulk_ed, - usb_maxpacket(usb_dev, pipe), + usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)), usb_pipeslow(pipe), usb_pipe_endpdev(pipe), 0 /* bulk uses normal TDs */); @@ -1480,7 +1480,7 @@ static int ohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *da if (bytes_transferred_p) *bytes_transferred_p = 0; - if (usb_endpoint_halted(usb_dev, usb_pipeendpoint(pipe)) + if (usb_endpoint_halted(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) && usb_clear_halt(usb_dev, usb_pipeendpoint(pipe) | (pipe & 0x80))) return USB_ST_STALL; @@ -1979,6 +1979,23 @@ static struct ohci_td * ohci_reverse_donelist(struct ohci * ohci) return td_list; } /* ohci_reverse_donelist() */ +/* + * Look at the ed (and td if necessary) + * and return its direction as 0 = IN, 1 = OUT. + */ +int ed_get_dir (struct ohci_ed *ed, struct ohci_td *td) +{ + __u32 status; + + status = le32_to_cpu(ed->status) & OHCI_ED_D; /* keep only the Direction bits */ + if (status == OHCI_ED_D_IN) return 0; + if (status == OHCI_ED_D_OUT) return 1; + + /* but if status == 0 or 3, look at the td for the Direction */ + status = le32_to_cpu(td->info) & OHCI_TD_D; /* keep only the Direction bits */ + if (status == OHCI_TD_D_IN) return 0; + return 1; +} /* * Collect this interrupt's goodies off of the list of finished TDs @@ -2017,7 +2034,7 @@ static void ohci_reap_donelist(struct ohci *ohci) if (cc == USB_ST_STALL) { /* mark endpoint as halted */ - usb_endpoint_halt(ed->ohci_dev->usb, ed_get_en(ed)); + usb_endpoint_halt(ed->ohci_dev->usb, ed_get_en(ed), ed_get_dir(ed, td)); } if (cc != 0 && ohci_ed_halted(ed) && !td_endofchain(*td)) { diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index d42a6b3471a2..56441efb7cf1 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -117,7 +117,8 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned (unsigned int)tmp, rval ? *rval : 0); } usb_settoggle(dev->usb, usb_pipeendpoint(tmp->info), - usb_pipeout(tmp->info), (tmp->info >> 19) & 1); + usb_pipeout(tmp->info) ^ 1, + (tmp->info >> 19) & 1); break; } else { if (rval) @@ -153,7 +154,8 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned if (status & 0x40) { /* endpoint has stalled - mark it halted */ - usb_endpoint_halt(dev->usb, usb_pipeendpoint(tmp->info)); + usb_endpoint_halt(dev->usb, usb_pipeendpoint(tmp->info), + usb_pipeout(tmp->info) ^ 1); return USB_ST_STALL; } @@ -162,7 +164,7 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned if (!rval) return USB_ST_DATAUNDERRUN; } - return uhci_map_status(status, usb_pipeout(tmp->info)); + return uhci_map_status(status, usb_pipeout(tmp->info) ^ 1); } /* @@ -431,9 +433,8 @@ static void *uhci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb td->link = UHCI_PTR_TERM; /* Terminate */ td->status = status; /* In */ - td->info = destination | ((usb_maxpacket(usb_dev, pipe) - 1) << 21) | - (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), - usb_pipeout(pipe)) << 19); + td->info = destination | ((usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)) - 1) << 21) | + (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) << 19); td->buffer = virt_to_bus(dev->data); td->qh = qh; td->dev = dev; @@ -782,7 +783,7 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devre struct uhci_td *first, *td, *prevtd; unsigned long destination, status; int ret, count; - int maxsze = usb_maxpacket(usb_dev, pipe); + int maxsze = usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)); __u32 nextlink; first = td = uhci_td_alloc(dev); @@ -962,9 +963,9 @@ static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *da struct uhci_td *first, *td, *prevtd; unsigned long destination, status; int ret; - int maxsze = usb_maxpacket(usb_dev, pipe); + int maxsze = usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)); - if (usb_endpoint_halted(usb_dev, usb_pipeendpoint(pipe)) && + if (usb_endpoint_halted(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) && usb_clear_halt(usb_dev, usb_pipeendpoint(pipe) | (pipe & 0x80))) return USB_ST_STALL; @@ -991,7 +992,8 @@ static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *da td->status = status; /* Status */ td->info = destination | ((pktsze-1) << 21) | - (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) << 19); /* pktsze bytes of data */ + (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), + usb_pipeout(pipe)) << 19); /* pktsze bytes of data */ td->buffer = virt_to_bus(data); td->backptr = &prevtd->link; @@ -1049,10 +1051,10 @@ static void * uhci_request_bulk(struct usb_device *usb_dev, unsigned int pipe, u struct uhci_td *first, *td, *prevtd; struct uhci_qh *bulk_qh = uhci_qh_alloc(dev); unsigned long destination, status; - int maxsze = usb_maxpacket(usb_dev, pipe); + int maxsze = usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)); /* The "pipe" thing contains the destination in bits 8--18, 0x69 is IN */ - destination = (pipe & 0x0007ff00) | usb_packetid(pipe); + destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid(pipe); /* Infinite errors is 0 */ status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_SPD; @@ -1336,7 +1338,7 @@ static void uhci_interrupt_notify(struct uhci *uhci) list_del(&td->irq_list); INIT_LIST_HEAD(&td->irq_list); - if (td->completed(uhci_map_status(status, 0), + if (td->completed(uhci_map_status(status, usb_pipeout(td->info) ^ 1), bus_to_virt(td->buffer), -1, td->dev_id)) { list_add(&td->irq_list, &uhci->interrupt_list); @@ -1344,11 +1346,12 @@ static void uhci_interrupt_notify(struct uhci *uhci) if (!(td->status & TD_CTRL_IOS)) { struct usb_device *usb_dev = td->dev->usb; - usb_dotoggle(usb_dev, usb_pipeendpoint(td->info), usb_pipeout(td->info)); + usb_dotoggle(usb_dev, usb_pipeendpoint(td->info), usb_pipeout(td->info) ^ 1); td->info &= ~(1 << 19); /* clear data toggle */ - td->info |= usb_gettoggle(usb_dev, usb_pipeendpoint(td->info), usb_pipeout(td->info)) << 19; /* toggle between data0 and data1 */ + td->info |= usb_gettoggle(usb_dev, usb_pipeendpoint(td->info), + usb_pipeout(td->info) ^ 1) << 19; /* toggle between data0 and data1 */ td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC; - /* The HC removes it, so readd it */ + /* The HC removes it, so re-add it */ uhci_insert_td_in_qh(td->qh, td); } } else if (td->flags & UHCI_TD_REMOVE) { @@ -1356,7 +1359,7 @@ static void uhci_interrupt_notify(struct uhci *uhci) /* marked for removal */ td->flags &= ~UHCI_TD_REMOVE; - usb_dotoggle(usb_dev, usb_pipeendpoint(td->info), usb_pipeout(td->info)); + usb_dotoggle(usb_dev, usb_pipeendpoint(td->info), usb_pipeout(td->info) ^ 1); uhci_remove_qh(td->qh->skel, td->qh); uhci_qh_free(td->qh); uhci_td_free(td); @@ -1433,7 +1436,7 @@ static void uhci_init_ticktd(struct uhci *uhci) /* Don't clobber the frame */ td->link = uhci->fl->frame[0]; td->status = TD_CTRL_IOC; - td->info = (15 << 21) | 0x7f69; /* (ignored) input packet, 16 bytes, device 127 */ + td->info = (15 << 21) | 0x7f69; /* (ignored) input packet, 16 bytes, device 127 */ td->buffer = 0; td->qh = NULL; diff --git a/drivers/usb/uhci.h b/drivers/usb/uhci.h index 4f71f30cc7a9..51457fb5663c 100644 --- a/drivers/usb/uhci.h +++ b/drivers/usb/uhci.h @@ -77,6 +77,9 @@ struct uhci_framelist { __u32 frame[1024]; } __attribute__((aligned(4096))); +/* + * for TD : + */ #define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */ #define TD_CTRL_LS (1 << 26) /* Low Speed Device */ #define TD_CTRL_IOS (1 << 25) /* Isochronous Select */ @@ -91,8 +94,26 @@ struct uhci_framelist { #define uhci_ptr_to_virt(x) bus_to_virt(x & ~UHCI_PTR_BITS) +/* + * for TD : + */ #define UHCI_TD_REMOVE 0x0001 /* Remove when done */ +/* + * for TD : (a.k.a. Token) + */ +#define TD_TOKEN_TOGGLE 19 + +#define uhci_maxlen(token) ((token) >> 21) +#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1) +#define uhci_endpoint(token) (((token) >> 15) & 0xf) +#define uhci_devaddr(token) (((token) >> 8) & 0x7f) +#define uhci_devep(token) (((token) >> 8) & 0x7ff) +#define uhci_packetid(token) ((token) & 0xff) +#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN) +#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN) + + /* * The documentation says "4 words for hardware, 4 words for software". * diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index fbe9ba12d349..a2359d041542 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -510,18 +510,10 @@ void usb_destroy_configuration(struct usb_device *dev) } kfree(dev->config); - for (i = 1; i < USB_MAXSTRINGS; ++i) { - if (dev->stringindex[i]) { - kfree(dev->stringindex[i]); - dev->stringindex[i] = 0; - } - } -#if 0 - if (dev->stringindex) - kfree(dev->stringindex); - if (dev->stringtable) - kfree(dev->stringtable); -#endif + if (dev->string) { + kfree(dev->string); + dev->string = 0; + } } void usb_init_root_hub(struct usb_device *dev) @@ -718,19 +710,27 @@ static void usb_set_maxpacket(struct usb_device *dev) int e; for (e=0; ebNumEndpoints; e++) { - dev->epmaxpacket[ep[e].bEndpointAddress & 0x0f] = - ep[e].wMaxPacketSize; + if (usb_endpoint_out(ep[e].bEndpointAddress)) + dev->epmaxpacketout[ep[e].bEndpointAddress & 0x0f] = + ep[e].wMaxPacketSize; + else + dev->epmaxpacketin [ep[e].bEndpointAddress & 0x0f] = + ep[e].wMaxPacketSize; } } } +/* + * endp: endpoint number in bits 0-3; + * direction flag in bit 7 (1 = IN, 0 = OUT) + */ int usb_clear_halt(struct usb_device *dev, int endp) { devrequest dr; int result; __u16 status; - //if (!usb_endpoint_halted(dev, endp)) + //if (!usb_endpoint_halted(dev, endp & 0x0f, usb_endpoint_out(endp))) // return 0; dr.requesttype = USB_RT_ENDPOINT; @@ -741,11 +741,11 @@ int usb_clear_halt(struct usb_device *dev, int endp) result = dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); - /* dont clear if failed */ + /* don't clear if failed */ if (result) return result; -#if 1 /* lets be really tough */ +#if 1 /* let's be really tough */ dr.requesttype = 0x80 | USB_RT_ENDPOINT; dr.request = USB_REQ_GET_STATUS; dr.length = 2; @@ -758,11 +758,11 @@ int usb_clear_halt(struct usb_device *dev, int endp) if (status & 1) return 1; /* still halted */ #endif - usb_endpoint_running(dev, endp & 0x0f); + usb_endpoint_running(dev, endp & 0x0f, usb_endpoint_out(endp)); /* toggle is reset on clear */ - usb_settoggle(dev, endp & 0x0f, ((endp >> 7) & 1) ^ 1, 0); + usb_settoggle(dev, endp & 0x0f, usb_endpoint_out(endp), 0); return 0; } @@ -874,62 +874,6 @@ int usb_get_configuration(struct usb_device *dev) return parse; } -#if 0 -int usb_get_stringtable(struct usb_device *dev) -{ - int i; - int maxindex; - int langid; - unsigned char buffer[256]; - int totalchars; - struct usb_string_descriptor *sd = (struct usb_string_descriptor *)buffer; - char *string; - __u8 bLengths[USB_MAXSTRINGS+1]; - int j; - - dev->maxstring = 0; - if(usb_get_string(dev, 0, 0, buffer, 2) || - usb_get_string(dev, 0, 0, buffer, sd->bLength)) - return -1; - /* we are going to assume that the first ID is good */ - langid = le16_to_cpup(&sd->wData[0]); - - /* whip through and find total length and max index */ - for (maxindex = 1, totalchars = 0; maxindex<=USB_MAXSTRINGS; maxindex++) { - if(usb_get_string(dev, langid, maxindex, buffer, 2)) - break; - totalchars += (sd->bLength - 2)/2 + 1; - bLengths[maxindex] = sd->bLength; - } - if (--maxindex <= 0) - return -1; - - /* get space for strings and index */ - dev->stringindex = kmalloc(sizeof(char *) * (maxindex+1), GFP_KERNEL); - if (!dev->stringindex) - return -1; - dev->stringtable = kmalloc(totalchars, GFP_KERNEL); - if (!dev->stringtable) { - kfree(dev->stringindex); - dev->stringindex = NULL; - return -1; - } - - /* fill them in */ - memset(dev->stringindex, 0, sizeof(char *) * (maxindex+1)); - for (i=1, string = dev->stringtable; i <= maxindex; i++) { - if (usb_get_string(dev, langid, i, buffer, bLengths[i])) - continue; - dev->stringindex[i] = string; - for (j=0; j < (bLengths[i] - 2)/2; j++) { - *string++ = le16_to_cpup(&sd->wData[j]); - } - *string++ = '\0'; - } - dev->maxstring = maxindex; - return 0; -} -#endif char *usb_string(struct usb_device *dev, int index) { @@ -940,10 +884,10 @@ char *usb_string(struct usb_device *dev, int index) struct usb_string_descriptor desc; } u; - if (index <= 0 || index >= USB_MAXSTRINGS) + if (index <= 0) return 0; - if (dev->stringindex[index] != 0) - return dev->stringindex[index]; + if (dev->string) + kfree (dev->string); if (dev->string_langid == 0) { /* read string descriptor 0 */ @@ -968,7 +912,7 @@ char *usb_string(struct usb_device *dev, int index) ptr[i] = le16_to_cpup(&u.desc.wData[i]); ptr[i] = 0; - dev->stringindex[index] = ptr; + dev->string = ptr; return ptr; } @@ -985,7 +929,8 @@ int usb_new_device(struct usb_device *dev) dev->devnum); dev->maxpacketsize = 0; /* Default to 8 byte max packet size */ - dev->epmaxpacket[0] = 8; + dev->epmaxpacketin [0] = 8; + dev->epmaxpacketout[0] = 8; /* We still haven't set the Address yet */ addr = dev->devnum; @@ -998,7 +943,8 @@ int usb_new_device(struct usb_device *dev) return 1; } - dev->epmaxpacket[0] = dev->descriptor.bMaxPacketSize0; + dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0; + dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; switch (dev->descriptor.bMaxPacketSize0) { case 8: dev->maxpacketsize = 0; break; case 16: dev->maxpacketsize = 1; break; diff --git a/drivers/usb/usb.h b/drivers/usb/usb.h index bccde88c89c6..b73d73f25a5c 100644 --- a/drivers/usb/usb.h +++ b/drivers/usb/usb.h @@ -170,7 +170,6 @@ struct usb_devmap { #define USB_MAXALTSETTING 5 #define USB_MAXINTERFACES 32 #define USB_MAXENDPOINTS 32 -#define USB_MAXSTRINGS 32 struct usb_device_descriptor { __u8 bLength; @@ -319,16 +318,18 @@ struct usb_device { int slow; /* Slow device? */ int maxpacketsize; /* Maximum packet size; encoded as 0,1,2,3 = 8,16,32,64 */ unsigned int toggle[2]; /* one bit for each endpoint ([0] = IN, [1] = OUT) */ - unsigned int halted; /* endpoint halts */ + unsigned int halted[2]; /* endpoint halts; one bit per endpoint # & direction; */ + /* [0] = IN, [1] = OUT */ struct usb_config_descriptor *actconfig;/* the active configuration */ - int epmaxpacket[16]; /* endpoint specific maximums */ + int epmaxpacketin[16]; /* INput endpoint specific maximums */ + int epmaxpacketout[16]; /* OUTput endpoint specific maximums */ int ifnum; /* active interface number */ - struct usb_bus *bus; /* Bus we're apart of */ + struct usb_bus *bus; /* Bus we're part of */ struct usb_driver *driver; /* Driver */ struct usb_device_descriptor descriptor;/* Descriptor */ struct usb_config_descriptor *config; /* All of the configs */ struct usb_device *parent; - char *stringindex[USB_MAXSTRINGS]; /* pointers to strings */ + char *string; /* pointer to the last string read from the device */ int string_langid; /* language ID for strings */ /* @@ -410,7 +411,9 @@ extern int usb_compress_isochronous (struct usb_device *usb_dev, void *_isodesc) * appropriately. */ -#define usb_maxpacket(dev,pipe) ((dev)->epmaxpacket[usb_pipeendpoint(pipe)]) +#define usb_maxpacket(dev, pipe, out) (out \ + ? (dev)->epmaxpacketout[usb_pipeendpoint(pipe)] \ + : (dev)->epmaxpacketin [usb_pipeendpoint(pipe)] ) #define usb_packetid(pipe) (((pipe) & 0x80) ? 0x69 : 0xE1) #define usb_pipeout(pipe) ((((pipe) >> 7) & 1) ^ 1) @@ -433,10 +436,11 @@ extern int usb_compress_isochronous (struct usb_device *usb_dev, void *_isodesc) #define usb_dotoggle(dev, ep, out) ((dev)->toggle[out] ^= (1 << ep)) #define usb_settoggle(dev, ep, out, bit) ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << ep)) | ((bit) << ep)) -/* Endpoint halt */ -#define usb_endpoint_halt(dev, ep) ((dev)->halted |= (1 << (ep))) -#define usb_endpoint_running(dev, ep) ((dev)->halted &= ~(1 << (ep))) -#define usb_endpoint_halted(dev, ep) ((dev)->halted & (1 << (ep))) +/* Endpoint halt control/status */ +#define usb_endpoint_out(ep_dir) (((ep_dir >> 7) & 1) ^ 1) +#define usb_endpoint_halt(dev, ep, out) ((dev)->halted[out] |= (1 << (ep))) +#define usb_endpoint_running(dev, ep, out) ((dev)->halted[out] &= ~(1 << (ep))) +#define usb_endpoint_halted(dev, ep, out) ((dev)->halted[out] & (1 << (ep))) static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint) { diff --git a/drivers/usb/usb_scsi.c b/drivers/usb/usb_scsi.c index 3099c9826e43..646bb039d698 100644 --- a/drivers/usb/usb_scsi.c +++ b/drivers/usb/usb_scsi.c @@ -130,7 +130,7 @@ static struct usb_driver scsi_driver = { static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length) { - int max_size = usb_maxpacket(us->pusb_dev, pipe) * 16; + int max_size = usb_maxpacket(us->pusb_dev, pipe, usb_pipeout(pipe)) * 16; int this_xfer; int result; unsigned long partial; diff --git a/drivers/usb/uss720.c b/drivers/usb/uss720.c index d88d204ed79e..ffbdfd167a90 100644 --- a/drivers/usb/uss720.c +++ b/drivers/usb/uss720.c @@ -29,12 +29,12 @@ * usb_request_irq crashes somewhere within ohci.c * for no apparent reason (that is for me, anyway) * ECP currently untested + * 0.3 10.08.99 fixing merge errors * */ /*****************************************************************************/ -#include #include #include #include @@ -187,10 +187,6 @@ static int clear_epp_timeout(struct parport *pp) * Access functions. */ -static void parport_uss720_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ -} - static int uss720_irq(int usbstatus, void *buffer, int len, void *dev_id) { struct parport *pp = (struct parport *)dev_id; @@ -653,16 +649,13 @@ static struct usb_driver uss720_driver = { /* --------------------------------------------------------------------- */ -#define __exit -#define module_exit(x) void cleanup_module(void) { x(); } -#define module_init(x) int init_module(void) { return x(); } - -/* --------------------------------------------------------------------- */ +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch"); +MODULE_DESCRIPTION("USB Parport Cable driver for Cables using the Lucent Technologies USS720 Chip"); static int __init uss720_init(void) { usb_register(&uss720_driver); - printk(KERN_INFO "uss720: USB<->IEEE1284 cable driver v0.2 registered.\n" + printk(KERN_INFO "uss720: USB<->IEEE1284 cable driver v0.3 registered.\n" KERN_INFO "uss720: (C) 1999 by Thomas Sailer, \n"); return 0; } diff --git a/drivers/video/Config.in b/drivers/video/Config.in index d38a157a2578..29b03e9535b8 100644 --- a/drivers/video/Config.in +++ b/drivers/video/Config.in @@ -6,6 +6,7 @@ if [ "$CONFIG_FB" = "y" ]; then define_bool CONFIG_DUMMY_CONSOLE y if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_PCI" = "y" ]; then + tristate 'Cirrus Logic suport' CONFIG_FB_CLGEN bool 'Permedia2 support (experimental)' CONFIG_FB_PM2 if [ "$CONFIG_FB_PM2" = "y" ]; then if [ "$CONFIG_PCI" = "y" ]; then @@ -43,7 +44,6 @@ if [ "$CONFIG_FB" = "y" ]; then if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'Amiga CyberVision3D support (experimental)' CONFIG_FB_VIRGE tristate 'Amiga RetinaZ3 support' CONFIG_FB_RETINAZ3 - tristate 'Amiga CLgen driver' CONFIG_FB_CLGEN bool 'Amiga FrameMaster II/Rainbow II support (experimental)' CONFIG_FB_FM2 fi fi diff --git a/drivers/video/S3triofb.c b/drivers/video/S3triofb.c index 6e73954b74a8..0cce81aefb90 100644 --- a/drivers/video/S3triofb.c +++ b/drivers/video/S3triofb.c @@ -99,7 +99,7 @@ static int s3trio_ioctl(struct inode *inode, struct file *file, u_int cmd, * Interface to the low level console driver */ -void s3triofb_init(void); +int s3triofb_init(void); static int s3triofbcon_switch(int con, struct fb_info *info); static int s3triofbcon_updatevar(int con, struct fb_info *info); static void s3triofbcon_blank(int blank, struct fb_info *info); @@ -288,7 +288,7 @@ static int s3trio_ioctl(struct inode *inode, struct file *file, u_int cmd, return -EINVAL; } -void __init s3triofb_init(void) +int __init s3triofb_init(void) { #ifdef __powerpc__ /* We don't want to be called like this. */ @@ -296,6 +296,7 @@ void __init s3triofb_init(void) #else /* !__powerpc__ */ /* To be merged with cybervision */ #endif /* !__powerpc__ */ + return 0; } void __init s3trio_resetaccel(void){ @@ -721,9 +722,9 @@ static void do_install_cmap(int con, struct fb_info *info) s3trio_setcolreg, &fb_info); } -void s3triofb_setup(char *options, int *ints) { +int s3triofb_setup(char *options) { - return; + return 0; } diff --git a/drivers/video/acornfb.c b/drivers/video/acornfb.c index b20445d0f060..7f22c2e9edc0 100644 --- a/drivers/video/acornfb.c +++ b/drivers/video/acornfb.c @@ -1728,14 +1728,14 @@ static struct options { { NULL, NULL } }; -void __init -acornfb_setup(char *options, int *ints) +int __init +acornfb_setup(char *options) { struct options *optp; char *opt; if (!options || !*options) - return; + return 0; acornfb_init_fbinfo(); @@ -1759,6 +1759,7 @@ acornfb_setup(char *options, int *ints) printk(KERN_ERR "acornfb: unknown parameter: %s\n", opt); } + return 0; } /* @@ -1802,7 +1803,7 @@ free_unused_pages(unsigned int virtual_start, unsigned int virtual_end) printk("acornfb: freed %dK memory\n", mb_freed); } -void __init +int __init acornfb_init(void) { unsigned long size; @@ -1904,5 +1905,7 @@ acornfb_init(void) VIDC_NAME, init_var.xres, init_var.yres, h_sync / 1000, h_sync % 1000, v_sync); - register_framebuffer(&fb_info); + if (register_framebuffer(&fb_info) < 0) + return -EINVAL; + return 0; } diff --git a/drivers/video/amifb.c b/drivers/video/amifb.c index 51f45aa71ae6..00ea52500d40 100644 --- a/drivers/video/amifb.c +++ b/drivers/video/amifb.c @@ -1162,7 +1162,7 @@ static u_short sprfetchmode[3] = { * Interface used by the world */ -void amifb_setup(char *options, int *ints); +int amifb_setup(char*); static int amifb_open(struct fb_info *info, int user); static int amifb_release(struct fb_info *info, int user); @@ -1193,7 +1193,7 @@ static int amifb_set_cursorstate(struct fb_cursorstate *state, int con); * Interface to the low level console driver */ -void amifb_init(void); +int amifb_init(void); static int amifbcon_switch(int con, struct fb_info *info); static int amifbcon_updatevar(int con, struct fb_info *info); static void amifbcon_blank(int blank, struct fb_info *info); @@ -1259,7 +1259,7 @@ static struct fb_ops amifb_ops = { amifb_pan_display, amifb_ioctl }; -void __init amifb_setup(char *options, int *ints) +int __init amifb_setup(char *options) { char *this_opt; char mcap_spec[80]; @@ -1268,7 +1268,7 @@ void __init amifb_setup(char *options, int *ints) fb_info.fontname[0] = '\0'; if (!options || !*options) - return; + return 0; for (this_opt = strtok(options, ","); this_opt; this_opt = strtok(NULL, ",")) { char *p; @@ -1374,6 +1374,7 @@ void __init amifb_setup(char *options, int *ints) cap_invalid: ; } + return 0; } /* @@ -1714,13 +1715,13 @@ static int amifb_set_cursorstate(struct fb_cursorstate *state, int con) * Initialisation */ -void __init amifb_init(void) +int __init amifb_init(void) { int tag, i; u_long chipptr; if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_VIDEO)) - return; + return -ENXIO; /* * TODO: where should we put this? The DMI Resolver doesn't have a @@ -1731,7 +1732,7 @@ void __init amifb_init(void) if (amifb_resolver){ custom.dmacon = DMAF_MASTER | DMAF_RASTER | DMAF_COPPER | DMAF_BLITTER | DMAF_SPRITE; - return; + return 0; } #endif @@ -1802,7 +1803,7 @@ default_chipset: strcat(amifb_name, "Unknown"); goto default_chipset; #else /* CONFIG_FB_AMIGA_OCS */ - return; + return -ENXIO; #endif /* CONFIG_FB_AMIGA_OCS */ break; } @@ -1916,7 +1917,7 @@ default_chipset: amifb_set_var(&amifb_default, -1, &fb_info); if (register_framebuffer(&fb_info) < 0) - return; + return -EINVAL; printk("fb%d: %s frame buffer device, using %ldK of video memory\n", GET_FB_IDX(fb_info.node), fb_info.modename, @@ -1924,6 +1925,8 @@ default_chipset: /* TODO: This driver cannot be unloaded yet */ MOD_INC_USE_COUNT; + + return 0; } static int amifbcon_switch(int con, struct fb_info *info) @@ -3519,8 +3522,7 @@ static void ami_rebuild_copper(void) #ifdef MODULE int init_module(void) { - amifb_init(); - return 0; + return amifb_init(); } void cleanup_module(void) diff --git a/drivers/video/atafb.c b/drivers/video/atafb.c index dc080d62ce94..8f163cd8ca75 100644 --- a/drivers/video/atafb.c +++ b/drivers/video/atafb.c @@ -2745,14 +2745,14 @@ atafb_blank(int blank, struct fb_info *info) do_install_cmap(currcon, info); } -void __init atafb_init(void) +int __init atafb_init(void) { int pad; int detected_mode; unsigned long mem_req; if (!MACH_IS_ATARI) - return; + return -ENXIO; do { #ifdef ATAFB_EXT @@ -2858,7 +2858,7 @@ void __init atafb_init(void) do_install_cmap(0, &fb_info); if (register_framebuffer(&fb_info) < 0) - return; + return -EINVAL; printk("Determined %dx%d, depth %d\n", disp.var.xres, disp.var.yres, disp.var.bits_per_pixel); @@ -2871,6 +2871,8 @@ void __init atafb_init(void) /* TODO: This driver cannot be unloaded yet */ MOD_INC_USE_COUNT; + + return 0; } /* a strtok which returns empty strings, too */ @@ -2895,7 +2897,7 @@ static char * strtoke(char * s,const char * ct) return sbegin; } -void __init atafb_setup( char *options, int *ints ) +int __init atafb_setup( char *options ) { char *this_opt; int temp; @@ -2910,7 +2912,7 @@ void __init atafb_setup( char *options, int *ints ) fb_info.fontname[0] = '\0'; if (!options || !*options) - return; + return 0; for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) { if (!*this_opt) continue; @@ -3146,13 +3148,13 @@ void __init atafb_setup( char *options, int *ints ) user_invalid: ; } + return 0; } #ifdef MODULE int init_module(void) { - atafb_init(); - return 0; + return atafb_init(); } void cleanup_module(void) diff --git a/drivers/video/atyfb.c b/drivers/video/atyfb.c index 06e3061d3b1f..f2f5e674d1d0 100644 --- a/drivers/video/atyfb.c +++ b/drivers/video/atyfb.c @@ -386,11 +386,11 @@ static int read_aty_sense(const struct fb_info_aty *info); * Interface used by the world */ -void atyfb_init(void); +int atyfb_init(void); #ifdef CONFIG_FB_OF void atyfb_of_init(struct device_node *dp); #endif -void atyfb_setup(char *options, int *ints); +int atyfb_setup(char*); static int currcon = 0; @@ -479,10 +479,10 @@ static inline u32 aty_ld_le32(unsigned int regindex, #if defined(__powerpc__) temp = info->ati_regbase; - asm("lwbrx %0,%1,%2" : "=r"(val) : "b" (regindex), "r" (temp)); + asm volatile("lwbrx %0,%1,%2" : "=r"(val) : "b" (regindex), "r" (temp)); #elif defined(__sparc_v9__) temp = info->ati_regbase + regindex; - asm("lduwa [%1] %2, %0" : "=r" (val) : "r" (temp), "i" (ASI_PL)); + asm volatile("lduwa [%1] %2, %0" : "=r" (val) : "r" (temp), "i" (ASI_PL)); #else temp = info->ati_regbase+regindex; val = le32_to_cpu(*((volatile u32 *)(temp))); @@ -497,11 +497,11 @@ static inline void aty_st_le32(unsigned int regindex, u32 val, #if defined(__powerpc__) temp = info->ati_regbase; - asm("stwbrx %0,%1,%2" : : "r" (val), "b" (regindex), "r" (temp) : + asm volatile("stwbrx %0,%1,%2" : : "r" (val), "b" (regindex), "r" (temp) : "memory"); #elif defined(__sparc_v9__) temp = info->ati_regbase + regindex; - asm("stwa %0, [%1] %2" : : "r" (val), "r" (temp), "i" (ASI_PL) : "memory"); + asm volatile("stwa %0, [%1] %2" : : "r" (val), "r" (temp), "i" (ASI_PL) : "memory"); #else temp = info->ati_regbase+regindex; *((volatile u32 *)(temp)) = cpu_to_le32(val); @@ -2821,7 +2821,7 @@ static int __init aty_init(struct fb_info_aty *info, const char *name) return 1; } -void __init atyfb_init(void) +int __init atyfb_init(void) { #if defined(CONFIG_FB_OF) /* We don't want to be called like this. */ @@ -2841,7 +2841,7 @@ void __init atyfb_init(void) /* Do not attach when we have a serial console. */ if (!con_is_present()) - return; + return -ENXIO; #else u16 tmp; #endif @@ -2854,7 +2854,7 @@ void __init atyfb_init(void) info = kmalloc(sizeof(struct fb_info_aty), GFP_ATOMIC); if (!info) { printk("atyfb_init: can't alloc fb_info_aty\n"); - return; + return -ENXIO; } memset(info, 0, sizeof(struct fb_info_aty)); @@ -2890,7 +2890,7 @@ void __init atyfb_init(void) if (!info->mmap_map) { printk("atyfb_init: can't alloc mmap_map\n"); kfree(info); - return; + return -ENXIO; } memset(info->mmap_map, 0, j * sizeof(*info->mmap_map)); @@ -2904,14 +2904,12 @@ void __init atyfb_init(void) io = (rp->flags & IORESOURCE_IOPORT); + size = rp->end - base + 1; + pci_read_config_dword(pdev, breg, &pbase); - pci_write_config_dword(pdev, breg, 0xffffffff); - pci_read_config_dword(pdev, breg, &size); - pci_write_config_dword(pdev, breg, pbase); if (io) size &= ~1; - size = ~(size) + 1; /* * Map the framebuffer a second time, this time without @@ -3071,7 +3069,7 @@ void __init atyfb_init(void) if(!info->ati_regbase) { kfree(info); - return; + return -ENOMEM; } info->ati_regbase_phys += 0xc00; @@ -3098,7 +3096,7 @@ void __init atyfb_init(void) if(!info->frame_buffer) { kfree(info); - return; + return -ENXIO; } #endif /* __sparc__ */ @@ -3107,7 +3105,7 @@ void __init atyfb_init(void) if (info->mmap_map) kfree(info->mmap_map); kfree(info); - return; + return -ENXIO; } #ifdef __sparc__ @@ -3145,7 +3143,7 @@ void __init atyfb_init(void) info = kmalloc(sizeof(struct fb_info_aty), GFP_ATOMIC); if (!info) { printk("atyfb_init: can't alloc fb_info_aty\n"); - return; + return -ENOMEM; } memset(info, 0, sizeof(struct fb_info_aty)); @@ -3161,10 +3159,11 @@ void __init atyfb_init(void) if (!aty_init(info, "ISA bus")) { kfree(info); /* This is insufficient! kernel_map has added two large chunks!! */ - return; + return -ENXIO; } } #endif + return 0; } #ifdef CONFIG_FB_OF @@ -3252,12 +3251,12 @@ void __init atyfb_of_init(struct device_node *dp) #endif /* CONFIG_FB_OF */ -void __init atyfb_setup(char *options, int *ints) +int __init atyfb_setup(char *options) { char *this_opt; if (!options || !*options) - return; + return 0; for (this_opt = strtok(options, ","); this_opt; this_opt = strtok(NULL, ",")) { @@ -3320,6 +3319,7 @@ void __init atyfb_setup(char *options, int *ints) } #endif } + return 0; } #ifdef CONFIG_ATARI diff --git a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c index a46e039358f5..85152dec2fca 100644 --- a/drivers/video/chipsfb.c +++ b/drivers/video/chipsfb.c @@ -112,7 +112,7 @@ static struct notifier_block chips_sleep_notifier = { /* * Exported functions */ -void chips_init(void); +int chips_init(void); void chips_of_init(struct device_node *dp); static int chips_open(struct fb_info *info, int user); @@ -645,7 +645,7 @@ static void __init init_chips(struct fb_info_chips *p) all_chips = p; } -void __init chips_init(void) +int __init chips_init(void) { #ifndef CONFIG_FB_OF struct device_node *dp; @@ -654,6 +654,7 @@ void __init chips_init(void) if (dp != 0) chips_of_init(dp); #endif /* CONFIG_FB_OF */ + return 0; } void __init chips_of_init(struct device_node *dp) diff --git a/drivers/video/clgenfb.c b/drivers/video/clgenfb.c index 8ce2ddf69bd9..9930eb89fcf3 100644 --- a/drivers/video/clgenfb.c +++ b/drivers/video/clgenfb.c @@ -1,7 +1,38 @@ /* - * Based on retz3fb.c and clgen.c + * drivers/video/clgenfb.c - driver for Cirrus Logic chipsets + * + * Copyright 1999 Jeff Garzik + * + * Contributors (thanks, all!) + * + * Jeff Rugen: + * Major contributions; Motorola PowerStack (PPC and PCI) support, + * GD54xx, 1280x1024 mode support, change MCLK based on VCLK. + * + * Geert Uytterhoeven: + * Excellent code review. + * + * Lars Hecking: + * Amiga updates and testing. + * + * Original clgenfb author: Frank Neumann + * + * Based on retz3fb.c and clgen.c: + * Copyright (C) 1997 Jes Sorensen + * Copyright (C) 1996 Frank Neumann + * + *************************************************************** + * + * Format this code with GNU indent '-kr -i8 -pcs' options. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * */ - + +#define CLGEN_VERSION "1.9.3" + #include #include #include @@ -11,12 +42,22 @@ #include #include #include -#include #include -#include #include #include #include +#ifdef CONFIG_ZORRO +#include +#endif +#ifdef CONFIG_PCI +#include +#endif +#ifdef CONFIG_AMIGA +#include +#endif +#ifdef CONFIG_FB_OF +#include +#endif #include